From c746cbc686c46904a5d381725079a69e38b201cd Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 3 Mar 2023 18:06:25 +0100 Subject: [PATCH 001/294] codegen: move gen logic for typed values, consts and decl ref to common codegen --- src/arch/aarch64/CodeGen.zig | 6 +- src/arch/arm/CodeGen.zig | 6 +- src/arch/riscv64/CodeGen.zig | 14 +- src/arch/sparc64/CodeGen.zig | 6 +- src/arch/wasm/CodeGen.zig | 2 - src/arch/x86_64/CodeGen.zig | 216 +++--------------------- src/codegen.zig | 307 ++++++++++++++++++++++++++++++++--- src/link/Coff.zig | 2 +- src/link/Elf.zig | 2 +- src/link/MachO.zig | 2 +- src/link/Plan9.zig | 2 +- src/link/Wasm.zig | 2 +- src/register_manager.zig | 3 + 13 files changed, 323 insertions(+), 247 deletions(-) diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 818b04f890..23f458f910 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -41,11 +41,7 @@ const c_abi_int_param_regs = abi.c_abi_int_param_regs; const c_abi_int_return_regs = abi.c_abi_int_return_regs; const gp = abi.RegisterClass.gp; -const InnerError = error{ - OutOfMemory, - CodegenFail, - OutOfRegisters, -}; +const InnerError = codegen.CodeGenError || error{OutOfRegisters}; gpa: Allocator, air: Air, diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index ceabe70438..87806223e3 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -42,11 +42,7 @@ const c_abi_int_param_regs = abi.c_abi_int_param_regs; const c_abi_int_return_regs = abi.c_abi_int_return_regs; const gp = abi.RegisterClass.gp; -const InnerError = error{ - OutOfMemory, - CodegenFail, - OutOfRegisters, -}; +const InnerError = codegen.CodeGenError || error{OutOfRegisters}; gpa: Allocator, air: Air, diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index afcf4b0bb7..fad5482cbc 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -21,10 +21,10 @@ const DW = std.dwarf; const leb128 = std.leb; const log = std.log.scoped(.codegen); const build_options = @import("build_options"); +const codegen = @import("../../codegen.zig"); -const Result = @import("../../codegen.zig").Result; -const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError; -const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; +const Result = codegen.Result; +const DebugInfoOutput = codegen.DebugInfoOutput; const bits = @import("bits.zig"); const abi = @import("abi.zig"); @@ -35,11 +35,7 @@ const Instruction = abi.Instruction; const callee_preserved_regs = abi.callee_preserved_regs; const gp = abi.RegisterClass.gp; -const InnerError = error{ - OutOfMemory, - CodegenFail, - OutOfRegisters, -}; +const InnerError = codegen.CodeGenError || error{OutOfRegisters}; gpa: Allocator, air: Air, @@ -225,7 +221,7 @@ pub fn generate( liveness: Liveness, code: *std.ArrayList(u8), debug_output: DebugInfoOutput, -) GenerateSymbolError!Result { +) codegen.CodeGenError!Result { if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) { @panic("Attempted to compile for architecture that was disabled by build configuration"); } diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index c8f77fe702..5a108eca85 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -38,11 +38,7 @@ const gp = abi.RegisterClass.gp; const Self = @This(); -const InnerError = error{ - OutOfMemory, - CodegenFail, - OutOfRegisters, -}; +const InnerError = codegen.CodeGenError || error{OutOfRegisters}; const RegisterView = enum(u1) { caller, diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 2f191fd834..511a10769e 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -733,8 +733,6 @@ const InnerError = error{ OutOfMemory, /// An error occurred when trying to lower AIR to MIR. CodegenFail, - /// Can occur when dereferencing a pointer that points to a `Decl` of which the analysis has failed - AnalysisFail, /// Compiler implementation could not handle a large integer. Overflow, }; diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 53d38f520a..2ec1a33619 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -40,11 +40,7 @@ const Register = bits.Register; const gp = abi.RegisterClass.gp; const sse = abi.RegisterClass.sse; -const InnerError = error{ - OutOfMemory, - CodegenFail, - OutOfRegisters, -}; +const InnerError = codegen.CodeGenError || error{OutOfRegisters}; gpa: Allocator, air: Air, @@ -6683,7 +6679,7 @@ fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, pl_op.operand }); } -pub fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { +fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { // First section of indexes correspond to a set number of constant values. const ref_int = @enumToInt(inst); if (ref_int < Air.Inst.Ref.typed_value_map.len) { @@ -6752,200 +6748,26 @@ fn limitImmediateType(self: *Self, operand: Air.Inst.Ref, comptime T: type) !MCV return mcv; } -fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) InnerError!MCValue { - log.debug("lowerDeclRef: ty = {}, val = {}", .{ tv.ty.fmtDebug(), tv.val.fmtDebug() }); - const ptr_bits = self.target.cpu.arch.ptrBitWidth(); - const ptr_bytes: u64 = @divExact(ptr_bits, 8); - - // TODO this feels clunky. Perhaps we should check for it in `genTypedValue`? - if (tv.ty.zigTypeTag() == .Pointer) blk: { - if (tv.ty.castPtrToFn()) |_| break :blk; - if (!tv.ty.elemType2().hasRuntimeBits()) { - return MCValue.none; - } - } - - const module = self.bin_file.options.module.?; - const decl = module.declPtr(decl_index); - module.markDeclAlive(decl); - - if (self.bin_file.cast(link.File.Elf)) |elf_file| { - const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index); - const atom = elf_file.getAtom(atom_index); - return MCValue{ .memory = atom.getOffsetTableAddress(elf_file) }; - } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { - const atom_index = try macho_file.getOrCreateAtomForDecl(decl_index); - const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?; - return MCValue{ .linker_load = .{ - .type = .got, - .sym_index = sym_index, - } }; - } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { - const atom_index = try coff_file.getOrCreateAtomForDecl(decl_index); - const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?; - return MCValue{ .linker_load = .{ - .type = .got, - .sym_index = sym_index, - } }; - } else if (self.bin_file.cast(link.File.Plan9)) |p9| { - const decl_block_index = try p9.seeDecl(decl_index); - const decl_block = p9.getDeclBlock(decl_block_index); - const got_addr = p9.bases.data + decl_block.got_index.? * ptr_bytes; - return MCValue{ .memory = got_addr }; - } else { - return self.fail("TODO codegen non-ELF const Decl pointer", .{}); - } -} - -fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue { - log.debug("lowerUnnamedConst: ty = {}, val = {}", .{ tv.ty.fmtDebug(), tv.val.fmtDebug() }); - const local_sym_index = self.bin_file.lowerUnnamedConst(tv, self.mod_fn.owner_decl) catch |err| { - return self.fail("lowering unnamed constant failed: {s}", .{@errorName(err)}); - }; - if (self.bin_file.cast(link.File.Elf)) |elf_file| { - return MCValue{ .memory = elf_file.getSymbol(local_sym_index).st_value }; - } else if (self.bin_file.cast(link.File.MachO)) |_| { - return MCValue{ .linker_load = .{ - .type = .direct, - .sym_index = local_sym_index, - } }; - } else if (self.bin_file.cast(link.File.Coff)) |_| { - return MCValue{ .linker_load = .{ - .type = .direct, - .sym_index = local_sym_index, - } }; - } else if (self.bin_file.cast(link.File.Plan9)) |p9| { - const ptr_bits = self.target.cpu.arch.ptrBitWidth(); - const ptr_bytes: u64 = @divExact(ptr_bits, 8); - const got_index = local_sym_index; // the plan9 backend returns the got_index - const got_addr = p9.bases.data + got_index * ptr_bytes; - return MCValue{ .memory = got_addr }; - } else { - return self.fail("TODO lower unnamed const", .{}); - } -} - fn genTypedValue(self: *Self, arg_tv: TypedValue) InnerError!MCValue { - var typed_value = arg_tv; - if (typed_value.val.castTag(.runtime_value)) |rt| { - typed_value.val = rt.data; - } - log.debug("genTypedValue: ty = {}, val = {}", .{ typed_value.ty.fmtDebug(), typed_value.val.fmtDebug() }); - if (typed_value.val.isUndef()) - return MCValue{ .undef = {} }; - const ptr_bits = self.target.cpu.arch.ptrBitWidth(); - - if (typed_value.val.castTag(.decl_ref)) |payload| { - return self.lowerDeclRef(typed_value, payload.data); - } - if (typed_value.val.castTag(.decl_ref_mut)) |payload| { - return self.lowerDeclRef(typed_value, payload.data.decl_index); - } - - const target = self.target.*; - - switch (typed_value.ty.zigTypeTag()) { - .Void => return MCValue{ .none = {} }, - .Pointer => switch (typed_value.ty.ptrSize()) { - .Slice => {}, - else => { - switch (typed_value.val.tag()) { - .int_u64 => { - return MCValue{ .immediate = typed_value.val.toUnsignedInt(target) }; - }, - else => {}, - } - }, + const mcv: MCValue = switch (try codegen.genTypedValue( + self.bin_file, + self.src_loc, + arg_tv, + self.mod_fn.owner_decl, + )) { + .mcv => |mcv| switch (mcv) { + .none => .none, + .undef => .undef, + .linker_load => |ll| .{ .linker_load = ll }, + .immediate => |imm| .{ .immediate = imm }, + .memory => |addr| .{ .memory = addr }, }, - .Int => { - const info = typed_value.ty.intInfo(self.target.*); - if (info.bits <= ptr_bits and info.signedness == .signed) { - return MCValue{ .immediate = @bitCast(u64, typed_value.val.toSignedInt(target)) }; - } - if (!(info.bits > ptr_bits or info.signedness == .signed)) { - return MCValue{ .immediate = typed_value.val.toUnsignedInt(target) }; - } + .fail => |msg| { + self.err_msg = msg; + return error.CodegenFail; }, - .Bool => { - return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) }; - }, - .Optional => { - if (typed_value.ty.isPtrLikeOptional()) { - if (typed_value.val.isNull()) - return MCValue{ .immediate = 0 }; - - var buf: Type.Payload.ElemType = undefined; - return self.genTypedValue(.{ - .ty = typed_value.ty.optionalChild(&buf), - .val = typed_value.val, - }); - } else if (typed_value.ty.abiSize(self.target.*) == 1) { - return MCValue{ .immediate = @boolToInt(!typed_value.val.isNull()) }; - } - }, - .Enum => { - if (typed_value.val.castTag(.enum_field_index)) |field_index| { - switch (typed_value.ty.tag()) { - .enum_simple => { - return MCValue{ .immediate = field_index.data }; - }, - .enum_full, .enum_nonexhaustive => { - const enum_full = typed_value.ty.cast(Type.Payload.EnumFull).?.data; - if (enum_full.values.count() != 0) { - const tag_val = enum_full.values.keys()[field_index.data]; - return self.genTypedValue(.{ .ty = enum_full.tag_ty, .val = tag_val }); - } else { - return MCValue{ .immediate = field_index.data }; - } - }, - else => unreachable, - } - } else { - var int_tag_buffer: Type.Payload.Bits = undefined; - const int_tag_ty = typed_value.ty.intTagType(&int_tag_buffer); - return self.genTypedValue(.{ .ty = int_tag_ty, .val = typed_value.val }); - } - }, - .ErrorSet => { - switch (typed_value.val.tag()) { - .@"error" => { - const err_name = typed_value.val.castTag(.@"error").?.data.name; - const module = self.bin_file.options.module.?; - const global_error_set = module.global_error_set; - const error_index = global_error_set.get(err_name).?; - return MCValue{ .immediate = error_index }; - }, - else => { - // In this case we are rendering an error union which has a 0 bits payload. - return MCValue{ .immediate = 0 }; - }, - } - }, - .ErrorUnion => { - const error_type = typed_value.ty.errorUnionSet(); - const payload_type = typed_value.ty.errorUnionPayload(); - const is_pl = typed_value.val.errorUnionIsPayload(); - - if (!payload_type.hasRuntimeBitsIgnoreComptime()) { - // We use the error type directly as the type. - const err_val = if (!is_pl) typed_value.val else Value.initTag(.zero); - return self.genTypedValue(.{ .ty = error_type, .val = err_val }); - } - }, - - .ComptimeInt => unreachable, - .ComptimeFloat => unreachable, - .Type => unreachable, - .EnumLiteral => unreachable, - .NoReturn => unreachable, - .Undefined => unreachable, - .Null => unreachable, - .Opaque => unreachable, - - else => {}, - } - - return self.lowerUnnamedConst(typed_value); + }; + return mcv; } const CallMCValues = struct { diff --git a/src/codegen.zig b/src/codegen.zig index df7ceff1f0..245745d6f6 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -29,13 +29,14 @@ pub const Result = union(enum) { fail: *ErrorMsg, }; -pub const GenerateSymbolError = error{ +pub const CodeGenError = error{ OutOfMemory, Overflow, - /// A Decl that this symbol depends on had a semantic analysis failure. - AnalysisFail, + CodegenFail, }; +pub const GenerateSymbolError = CodeGenError; + pub const DebugInfoOutput = union(enum) { dwarf: *link.File.Dwarf.DeclState, /// the plan9 debuginfo output is a bytecode with 4 opcodes @@ -63,19 +64,6 @@ pub const DebugInfoOutput = union(enum) { none, }; -/// Helper struct to denote that the value is in memory but requires a linker relocation fixup: -/// * got - the value is referenced indirectly via GOT entry index (the linker emits a got-type reloc) -/// * direct - the value is referenced directly via symbol index index (the linker emits a displacement reloc) -/// * import - the value is referenced indirectly via import entry index (the linker emits an import-type reloc) -pub const LinkerLoad = struct { - type: enum { - got, - direct, - import, - }, - sym_index: u32, -}; - pub fn generateFunction( bin_file: *link.File, src_loc: Module.SrcLoc, @@ -84,7 +72,7 @@ pub fn generateFunction( liveness: Liveness, code: *std.ArrayList(u8), debug_output: DebugInfoOutput, -) GenerateSymbolError!Result { +) CodeGenError!Result { switch (bin_file.options.target.cpu.arch) { .arm, .armeb, @@ -120,7 +108,7 @@ pub fn generateSymbol( code: *std.ArrayList(u8), debug_output: DebugInfoOutput, reloc_info: RelocInfo, -) GenerateSymbolError!Result { +) CodeGenError!Result { const tracy = trace(@src()); defer tracy.end(); @@ -823,7 +811,7 @@ fn lowerDeclRef( code: *std.ArrayList(u8), debug_output: DebugInfoOutput, reloc_info: RelocInfo, -) GenerateSymbolError!Result { +) CodeGenError!Result { const target = bin_file.options.target; const module = bin_file.options.module.?; if (typed_value.ty.isSlice()) { @@ -880,6 +868,287 @@ fn lowerDeclRef( return Result.ok; } +/// Helper struct to denote that the value is in memory but requires a linker relocation fixup: +/// * got - the value is referenced indirectly via GOT entry index (the linker emits a got-type reloc) +/// * direct - the value is referenced directly via symbol index index (the linker emits a displacement reloc) +/// * import - the value is referenced indirectly via import entry index (the linker emits an import-type reloc) +pub const LinkerLoad = struct { + type: enum { + got, + direct, + import, + }, + sym_index: u32, +}; + +pub const GenResult = union(enum) { + mcv: MCValue, + fail: *ErrorMsg, + + const MCValue = union(enum) { + none, + undef, + /// The bit-width of the immediate may be smaller than `u64`. For example, on 32-bit targets + /// such as ARM, the immediate will never exceed 32-bits. + immediate: u64, + linker_load: LinkerLoad, + /// Direct by-address reference to memory location. + memory: u64, + }; + + fn mcv(val: MCValue) GenResult { + return .{ .mcv = val }; + } + + fn fail( + gpa: Allocator, + src_loc: Module.SrcLoc, + comptime format: []const u8, + args: anytype, + ) Allocator.Error!GenResult { + const msg = try ErrorMsg.create(gpa, src_loc, format, args); + return .{ .fail = msg }; + } +}; + +fn genDeclRef( + bin_file: *link.File, + src_loc: Module.SrcLoc, + tv: TypedValue, + decl_index: Module.Decl.Index, +) CodeGenError!GenResult { + log.debug("genDeclRef: ty = {}, val = {}", .{ tv.ty.fmtDebug(), tv.val.fmtDebug() }); + + const target = bin_file.options.target; + const ptr_bits = target.cpu.arch.ptrBitWidth(); + const ptr_bytes: u64 = @divExact(ptr_bits, 8); + + const module = bin_file.options.module.?; + const decl = module.declPtr(decl_index); + + if (decl.ty.zigTypeTag() != .Fn and !decl.ty.hasRuntimeBitsIgnoreComptime()) { + const imm: u64 = switch (ptr_bytes) { + 1 => 0xaa, + 2 => 0xaaaa, + 4 => 0xaaaaaaaa, + 8 => 0xaaaaaaaaaaaaaaaa, + else => unreachable, + }; + return GenResult.mcv(.{ .immediate = imm }); + } + + // TODO this feels clunky. Perhaps we should check for it in `genTypedValue`? + if (tv.ty.zigTypeTag() == .Pointer) blk: { + if (tv.ty.castPtrToFn()) |_| break :blk; + if (!tv.ty.elemType2().hasRuntimeBits()) { + return GenResult.mcv(.none); + } + } + + module.markDeclAlive(decl); + + if (bin_file.cast(link.File.Elf)) |elf_file| { + const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index); + const atom = elf_file.getAtom(atom_index); + return GenResult.mcv(.{ .memory = atom.getOffsetTableAddress(elf_file) }); + } else if (bin_file.cast(link.File.MachO)) |macho_file| { + const atom_index = try macho_file.getOrCreateAtomForDecl(decl_index); + const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?; + return GenResult.mcv(.{ .linker_load = .{ + .type = .got, + .sym_index = sym_index, + } }); + } else if (bin_file.cast(link.File.Coff)) |coff_file| { + const atom_index = try coff_file.getOrCreateAtomForDecl(decl_index); + const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?; + return GenResult.mcv(.{ .linker_load = .{ + .type = .got, + .sym_index = sym_index, + } }); + } else if (bin_file.cast(link.File.Plan9)) |p9| { + const decl_block_index = try p9.seeDecl(decl_index); + const decl_block = p9.getDeclBlock(decl_block_index); + const got_addr = p9.bases.data + decl_block.got_index.? * ptr_bytes; + return GenResult.mcv(.{ .memory = got_addr }); + } else { + return GenResult.fail(bin_file.allocator, src_loc, "TODO genDeclRef for target {}", .{target}); + } +} + +fn genUnnamedConst( + bin_file: *link.File, + src_loc: Module.SrcLoc, + tv: TypedValue, + owner_decl_index: Module.Decl.Index, +) CodeGenError!GenResult { + log.debug("genUnnamedConst: ty = {}, val = {}", .{ tv.ty.fmtDebug(), tv.val.fmtDebug() }); + + const target = bin_file.options.target; + const local_sym_index = bin_file.lowerUnnamedConst(tv, owner_decl_index) catch |err| { + return GenResult.fail(bin_file.allocator, src_loc, "lowering unnamed constant failed: {s}", .{@errorName(err)}); + }; + if (bin_file.cast(link.File.Elf)) |elf_file| { + return GenResult.mcv(.{ .memory = elf_file.getSymbol(local_sym_index).st_value }); + } else if (bin_file.cast(link.File.MachO)) |_| { + return GenResult.mcv(.{ .linker_load = .{ + .type = .direct, + .sym_index = local_sym_index, + } }); + } else if (bin_file.cast(link.File.Coff)) |_| { + return GenResult.mcv(.{ .linker_load = .{ + .type = .direct, + .sym_index = local_sym_index, + } }); + } else if (bin_file.cast(link.File.Plan9)) |p9| { + const ptr_bits = target.cpu.arch.ptrBitWidth(); + const ptr_bytes: u64 = @divExact(ptr_bits, 8); + const got_index = local_sym_index; // the plan9 backend returns the got_index + const got_addr = p9.bases.data + got_index * ptr_bytes; + return GenResult.mcv(.{ .memory = got_addr }); + } else { + return GenResult.fail(bin_file.allocator, src_loc, "TODO genUnnamedConst for target {}", .{target}); + } +} + +pub fn genTypedValue( + bin_file: *link.File, + src_loc: Module.SrcLoc, + arg_tv: TypedValue, + owner_decl_index: Module.Decl.Index, +) CodeGenError!GenResult { + var typed_value = arg_tv; + if (typed_value.val.castTag(.runtime_value)) |rt| { + typed_value.val = rt.data; + } + + log.debug("genTypedValue: ty = {}, val = {}", .{ typed_value.ty.fmtDebug(), typed_value.val.fmtDebug() }); + + if (typed_value.val.isUndef()) + return GenResult.mcv(.undef); + + const target = bin_file.options.target; + const ptr_bits = target.cpu.arch.ptrBitWidth(); + + if (typed_value.val.castTag(.decl_ref)) |payload| { + return genDeclRef(bin_file, src_loc, typed_value, payload.data); + } + if (typed_value.val.castTag(.decl_ref_mut)) |payload| { + return genDeclRef(bin_file, src_loc, typed_value, payload.data.decl_index); + } + + switch (typed_value.ty.zigTypeTag()) { + .Void => return GenResult.mcv(.none), + .Pointer => switch (typed_value.ty.ptrSize()) { + .Slice => {}, + else => { + switch (typed_value.val.tag()) { + .int_u64 => { + return GenResult.mcv(.{ .immediate = typed_value.val.toUnsignedInt(target) }); + }, + else => {}, + } + }, + }, + .Int => { + const info = typed_value.ty.intInfo(target); + if (info.bits <= ptr_bits and info.signedness == .signed) { + return GenResult.mcv(.{ .immediate = @bitCast(u64, typed_value.val.toSignedInt(target)) }); + } + if (!(info.bits > ptr_bits or info.signedness == .signed)) { + return GenResult.mcv(.{ .immediate = typed_value.val.toUnsignedInt(target) }); + } + }, + .Bool => { + return GenResult.mcv(.{ .immediate = @boolToInt(typed_value.val.toBool()) }); + }, + .Optional => { + if (typed_value.ty.isPtrLikeOptional()) { + if (typed_value.val.isNull()) + return GenResult.mcv(.{ .immediate = 0 }); + + var buf: Type.Payload.ElemType = undefined; + return genTypedValue(bin_file, src_loc, .{ + .ty = typed_value.ty.optionalChild(&buf), + .val = typed_value.val, + }, owner_decl_index); + } else if (typed_value.ty.abiSize(target) == 1) { + return GenResult.mcv(.{ .immediate = @boolToInt(!typed_value.val.isNull()) }); + } + }, + .Enum => { + if (typed_value.val.castTag(.enum_field_index)) |field_index| { + switch (typed_value.ty.tag()) { + .enum_simple => { + return GenResult.mcv(.{ .immediate = field_index.data }); + }, + .enum_full, .enum_nonexhaustive => { + const enum_full = typed_value.ty.cast(Type.Payload.EnumFull).?.data; + if (enum_full.values.count() != 0) { + const tag_val = enum_full.values.keys()[field_index.data]; + return genTypedValue(bin_file, src_loc, .{ + .ty = enum_full.tag_ty, + .val = tag_val, + }, owner_decl_index); + } else { + return GenResult.mcv(.{ .immediate = field_index.data }); + } + }, + else => unreachable, + } + } else { + var int_tag_buffer: Type.Payload.Bits = undefined; + const int_tag_ty = typed_value.ty.intTagType(&int_tag_buffer); + return genTypedValue(bin_file, src_loc, .{ + .ty = int_tag_ty, + .val = typed_value.val, + }, owner_decl_index); + } + }, + .ErrorSet => { + switch (typed_value.val.tag()) { + .@"error" => { + const err_name = typed_value.val.castTag(.@"error").?.data.name; + const module = bin_file.options.module.?; + const global_error_set = module.global_error_set; + const error_index = global_error_set.get(err_name).?; + return GenResult.mcv(.{ .immediate = error_index }); + }, + else => { + // In this case we are rendering an error union which has a 0 bits payload. + return GenResult.mcv(.{ .immediate = 0 }); + }, + } + }, + .ErrorUnion => { + const error_type = typed_value.ty.errorUnionSet(); + const payload_type = typed_value.ty.errorUnionPayload(); + const is_pl = typed_value.val.errorUnionIsPayload(); + + if (!payload_type.hasRuntimeBitsIgnoreComptime()) { + // We use the error type directly as the type. + const err_val = if (!is_pl) typed_value.val else Value.initTag(.zero); + return genTypedValue(bin_file, src_loc, .{ + .ty = error_type, + .val = err_val, + }, owner_decl_index); + } + }, + + .ComptimeInt => unreachable, + .ComptimeFloat => unreachable, + .Type => unreachable, + .EnumLiteral => unreachable, + .NoReturn => unreachable, + .Undefined => unreachable, + .Null => unreachable, + .Opaque => unreachable, + + else => {}, + } + + return genUnnamedConst(bin_file, src_loc, typed_value, owner_decl_index); +} + pub fn errUnionPayloadOffset(payload_ty: Type, target: std.Target) u64 { const payload_align = payload_ty.abiAlignment(target); const error_align = Type.anyerror.abiAlignment(target); diff --git a/src/link/Coff.zig b/src/link/Coff.zig index c0ac7e0b88..f210f2f2b3 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -1060,7 +1060,7 @@ pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.In decl.analysis = .codegen_failure; try mod.failed_decls.put(mod.gpa, decl_index, em); log.err("{s}", .{em.msg}); - return error.AnalysisFail; + return error.CodegenFail; }, }; diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 1a9d594c56..f499a9952a 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -2618,7 +2618,7 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module decl.analysis = .codegen_failure; try mod.failed_decls.put(mod.gpa, decl_index, em); log.err("{s}", .{em.msg}); - return error.AnalysisFail; + return error.CodegenFail; }, }; diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 7c1d4776af..eaf16e4009 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -2089,7 +2089,7 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu decl.analysis = .codegen_failure; try module.failed_decls.put(module.gpa, decl_index, em); log.err("{s}", .{em.msg}); - return error.AnalysisFail; + return error.CodegenFail; }, }; diff --git a/src/link/Plan9.zig b/src/link/Plan9.zig index 87e3ca5c22..cf6e4f8418 100644 --- a/src/link/Plan9.zig +++ b/src/link/Plan9.zig @@ -377,7 +377,7 @@ pub fn lowerUnnamedConst(self: *Plan9, tv: TypedValue, decl_index: Module.Decl.I decl.analysis = .codegen_failure; try mod.failed_decls.put(mod.gpa, decl_index, em); log.err("{s}", .{em.msg}); - return error.AnalysisFail; + return error.CodegenFail; }, }; // duped_code is freed when the unnamed const is freed diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 00a52177f7..ac0c8e9ca5 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -1255,7 +1255,7 @@ pub fn lowerUnnamedConst(wasm: *Wasm, tv: TypedValue, decl_index: Module.Decl.In .fail => |em| { decl.analysis = .codegen_failure; try mod.failed_decls.put(mod.gpa, decl_index, em); - return error.AnalysisFail; + return error.CodegenFail; }, }; }; diff --git a/src/register_manager.zig b/src/register_manager.zig index 2fe0cd2b6a..4d16348c27 100644 --- a/src/register_manager.zig +++ b/src/register_manager.zig @@ -19,6 +19,9 @@ pub const AllocateRegistersError = error{ /// Can happen when spilling an instruction in codegen runs out of /// memory, so we propagate that error OutOfMemory, + /// Can happen when spilling an instruction in codegen triggers integer + /// overflow, so we propagate that error + Overflow, /// Can happen when spilling an instruction triggers a codegen /// error, so we propagate that error CodegenFail, From 1024332adc88928299dfc07426f11624ae8ba18b Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 3 Mar 2023 18:24:58 +0100 Subject: [PATCH 002/294] arm: use common implementation of genTypedValue helper --- src/arch/arm/CodeGen.zig | 192 ++++-------------------------------- src/arch/x86_64/CodeGen.zig | 6 +- 2 files changed, 23 insertions(+), 175 deletions(-) diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 87806223e3..7d8708c44d 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -24,7 +24,7 @@ const log = std.log.scoped(.codegen); const build_options = @import("build_options"); const Result = codegen.Result; -const GenerateSymbolError = codegen.GenerateSymbolError; +const CodeGenError = codegen.CodeGenError; const DebugInfoOutput = codegen.DebugInfoOutput; const bits = @import("bits.zig"); @@ -42,7 +42,7 @@ const c_abi_int_param_regs = abi.c_abi_int_param_regs; const c_abi_int_return_regs = abi.c_abi_int_return_regs; const gp = abi.RegisterClass.gp; -const InnerError = codegen.CodeGenError || error{OutOfRegisters}; +const InnerError = CodeGenError || error{OutOfRegisters}; gpa: Allocator, air: Air, @@ -339,7 +339,7 @@ pub fn generate( liveness: Liveness, code: *std.ArrayList(u8), debug_output: DebugInfoOutput, -) GenerateSymbolError!Result { +) CodeGenError!Result { if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) { @panic("Attempted to compile for architecture that was disabled by build configuration"); } @@ -6083,178 +6083,26 @@ fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue { } } -fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) InnerError!MCValue { - const ptr_bits = self.target.cpu.arch.ptrBitWidth(); - const ptr_bytes: u64 = @divExact(ptr_bits, 8); - - const mod = self.bin_file.options.module.?; - const decl = mod.declPtr(decl_index); - mod.markDeclAlive(decl); - - if (self.bin_file.cast(link.File.Elf)) |elf_file| { - const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index); - const atom = elf_file.getAtom(atom_index); - return MCValue{ .memory = atom.getOffsetTableAddress(elf_file) }; - } else if (self.bin_file.cast(link.File.MachO)) |_| { - unreachable; // unsupported architecture for MachO - } else if (self.bin_file.cast(link.File.Coff)) |_| { - return self.fail("TODO codegen COFF const Decl pointer", .{}); - } else if (self.bin_file.cast(link.File.Plan9)) |p9| { - const decl_block_index = try p9.seeDecl(decl_index); - const decl_block = p9.getDeclBlock(decl_block_index); - const got_addr = p9.bases.data + decl_block.got_index.? * ptr_bytes; - return MCValue{ .memory = got_addr }; - } else { - return self.fail("TODO codegen non-ELF const Decl pointer", .{}); - } - - _ = tv; -} - -fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue { - const local_sym_index = self.bin_file.lowerUnnamedConst(tv, self.mod_fn.owner_decl) catch |err| { - return self.fail("lowering unnamed constant failed: {s}", .{@errorName(err)}); - }; - if (self.bin_file.cast(link.File.Elf)) |elf_file| { - return MCValue{ .memory = elf_file.getSymbol(local_sym_index).st_value }; - } else if (self.bin_file.cast(link.File.MachO)) |_| { - unreachable; - } else if (self.bin_file.cast(link.File.Coff)) |_| { - return self.fail("TODO lower unnamed const in COFF", .{}); - } else if (self.bin_file.cast(link.File.Plan9)) |_| { - return self.fail("TODO lower unnamed const in Plan9", .{}); - } else { - return self.fail("TODO lower unnamed const", .{}); - } -} - fn genTypedValue(self: *Self, arg_tv: TypedValue) InnerError!MCValue { - var typed_value = arg_tv; - if (typed_value.val.castTag(.runtime_value)) |rt| { - typed_value.val = rt.data; - } - log.debug("genTypedValue: ty = {}, val = {}", .{ typed_value.ty.fmtDebug(), typed_value.val.fmtDebug() }); - if (typed_value.val.isUndef()) - return MCValue{ .undef = {} }; - const ptr_bits = self.target.cpu.arch.ptrBitWidth(); - - if (typed_value.val.castTag(.decl_ref)) |payload| { - return self.lowerDeclRef(typed_value, payload.data); - } - if (typed_value.val.castTag(.decl_ref_mut)) |payload| { - return self.lowerDeclRef(typed_value, payload.data.decl_index); - } - const target = self.target.*; - - switch (typed_value.ty.zigTypeTag()) { - .Pointer => switch (typed_value.ty.ptrSize()) { - .Slice => {}, - else => { - switch (typed_value.val.tag()) { - .int_u64 => { - return MCValue{ .immediate = @intCast(u32, typed_value.val.toUnsignedInt(target)) }; - }, - else => {}, - } - }, + const mcv: MCValue = switch (try codegen.genTypedValue( + self.bin_file, + self.src_loc, + arg_tv, + self.mod_fn.owner_decl, + )) { + .mcv => |mcv| switch (mcv) { + .none => .none, + .undef => .undef, + .linker_load => unreachable, // TODO + .immediate => |imm| .{ .immediate = @truncate(u32, imm) }, + .memory => |addr| .{ .memory = addr }, }, - .Int => { - const info = typed_value.ty.intInfo(self.target.*); - if (info.bits <= ptr_bits) { - const unsigned = switch (info.signedness) { - .signed => blk: { - const signed = @intCast(i32, typed_value.val.toSignedInt(target)); - break :blk @bitCast(u32, signed); - }, - .unsigned => @intCast(u32, typed_value.val.toUnsignedInt(target)), - }; - - return MCValue{ .immediate = unsigned }; - } else { - return self.lowerUnnamedConst(typed_value); - } + .fail => |msg| { + self.err_msg = msg; + return error.CodegenFail; }, - .Bool => { - return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) }; - }, - .Optional => { - if (typed_value.ty.isPtrLikeOptional()) { - if (typed_value.val.isNull()) - return MCValue{ .immediate = 0 }; - - var buf: Type.Payload.ElemType = undefined; - return self.genTypedValue(.{ - .ty = typed_value.ty.optionalChild(&buf), - .val = typed_value.val, - }); - } else if (typed_value.ty.abiSize(self.target.*) == 1) { - return MCValue{ .immediate = @boolToInt(typed_value.val.isNull()) }; - } - }, - .Enum => { - if (typed_value.val.castTag(.enum_field_index)) |field_index| { - switch (typed_value.ty.tag()) { - .enum_simple => { - return MCValue{ .immediate = field_index.data }; - }, - .enum_full, .enum_nonexhaustive => { - const enum_full = typed_value.ty.cast(Type.Payload.EnumFull).?.data; - if (enum_full.values.count() != 0) { - const tag_val = enum_full.values.keys()[field_index.data]; - return self.genTypedValue(.{ .ty = enum_full.tag_ty, .val = tag_val }); - } else { - return MCValue{ .immediate = field_index.data }; - } - }, - else => unreachable, - } - } else { - var int_tag_buffer: Type.Payload.Bits = undefined; - const int_tag_ty = typed_value.ty.intTagType(&int_tag_buffer); - return self.genTypedValue(.{ .ty = int_tag_ty, .val = typed_value.val }); - } - }, - .ErrorSet => { - switch (typed_value.val.tag()) { - .@"error" => { - const err_name = typed_value.val.castTag(.@"error").?.data.name; - const module = self.bin_file.options.module.?; - const global_error_set = module.global_error_set; - const error_index = global_error_set.get(err_name).?; - return MCValue{ .immediate = error_index }; - }, - else => { - // In this case we are rendering an error union which has a 0 bits payload. - return MCValue{ .immediate = 0 }; - }, - } - }, - .ErrorUnion => { - const error_type = typed_value.ty.errorUnionSet(); - const payload_type = typed_value.ty.errorUnionPayload(); - const is_pl = typed_value.val.errorUnionIsPayload(); - - if (!payload_type.hasRuntimeBitsIgnoreComptime()) { - // We use the error type directly as the type. - const err_val = if (!is_pl) typed_value.val else Value.initTag(.zero); - return self.genTypedValue(.{ .ty = error_type, .val = err_val }); - } - }, - - .ComptimeInt => unreachable, // semantic analysis prevents this - .ComptimeFloat => unreachable, // semantic analysis prevents this - .Type => unreachable, - .EnumLiteral => unreachable, - .Void => unreachable, - .NoReturn => unreachable, - .Undefined => unreachable, - .Null => unreachable, - .Opaque => unreachable, - - else => {}, - } - - return self.lowerUnnamedConst(typed_value); + }; + return mcv; } const CallMCValues = struct { diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 2ec1a33619..a2c11b332b 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -12,12 +12,12 @@ const trace = @import("../../tracy.zig").trace; const Air = @import("../../Air.zig"); const Allocator = mem.Allocator; +const CodeGenError = codegen.CodeGenError; const Compilation = @import("../../Compilation.zig"); const DebugInfoOutput = codegen.DebugInfoOutput; const DW = std.dwarf; const ErrorMsg = Module.ErrorMsg; const Result = codegen.Result; -const GenerateSymbolError = codegen.GenerateSymbolError; const Emit = @import("Emit.zig"); const Liveness = @import("../../Liveness.zig"); const Mir = @import("Mir.zig"); @@ -40,7 +40,7 @@ const Register = bits.Register; const gp = abi.RegisterClass.gp; const sse = abi.RegisterClass.sse; -const InnerError = codegen.CodeGenError || error{OutOfRegisters}; +const InnerError = CodeGenError || error{OutOfRegisters}; gpa: Allocator, air: Air, @@ -253,7 +253,7 @@ pub fn generate( liveness: Liveness, code: *std.ArrayList(u8), debug_output: DebugInfoOutput, -) GenerateSymbolError!Result { +) CodeGenError!Result { if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) { @panic("Attempted to compile for architecture that was disabled by build configuration"); } From c413ac100fa5a4cece5702d3afb6b0898e9c6214 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 3 Mar 2023 18:40:16 +0100 Subject: [PATCH 003/294] codegen: refactor generating Int as immediate where appropriate --- src/codegen.zig | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/codegen.zig b/src/codegen.zig index 245745d6f6..7e7f34f992 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -1051,11 +1051,12 @@ pub fn genTypedValue( }, .Int => { const info = typed_value.ty.intInfo(target); - if (info.bits <= ptr_bits and info.signedness == .signed) { - return GenResult.mcv(.{ .immediate = @bitCast(u64, typed_value.val.toSignedInt(target)) }); - } - if (!(info.bits > ptr_bits or info.signedness == .signed)) { - return GenResult.mcv(.{ .immediate = typed_value.val.toUnsignedInt(target) }); + if (info.bits <= ptr_bits) { + const unsigned = switch (info.signedness) { + .signed => @bitCast(u64, typed_value.val.toSignedInt(target)), + .unsigned => typed_value.val.toUnsignedInt(target), + }; + return GenResult.mcv(.{ .immediate = unsigned }); } }, .Bool => { From d8d8842190214cf727611b965e830ccbfffb52d1 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 3 Mar 2023 18:42:29 +0100 Subject: [PATCH 004/294] arm: skip unimplemented behavior test for @fieldParentPtr --- test/behavior/field_parent_ptr.zig | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/behavior/field_parent_ptr.zig b/test/behavior/field_parent_ptr.zig index 6bbd6ad7ef..bf99fd1795 100644 --- a/test/behavior/field_parent_ptr.zig +++ b/test/behavior/field_parent_ptr.zig @@ -3,6 +3,7 @@ const builtin = @import("builtin"); test "@fieldParentPtr non-first field" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO try testParentFieldPtr(&foo.c); comptime try testParentFieldPtr(&foo.c); @@ -10,6 +11,7 @@ test "@fieldParentPtr non-first field" { test "@fieldParentPtr first field" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO try testParentFieldPtrFirst(&foo.a); comptime try testParentFieldPtrFirst(&foo.a); @@ -47,6 +49,7 @@ fn testParentFieldPtrFirst(a: *const bool) !void { test "@fieldParentPtr untagged union" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO @@ -73,6 +76,7 @@ fn testFieldParentPtrUnion(c: *const i32) !void { test "@fieldParentPtr tagged union" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO @@ -99,6 +103,7 @@ fn testFieldParentPtrTaggedUnion(c: *const i32) !void { test "@fieldParentPtr extern union" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO From 0d2c25ca9d0794b1c822a12f3bdf8e57ede4c840 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 3 Mar 2023 18:46:08 +0100 Subject: [PATCH 005/294] aarch64: use common implementation of genTypedValue --- src/arch/aarch64/CodeGen.zig | 215 ++++------------------------------- 1 file changed, 20 insertions(+), 195 deletions(-) diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 23f458f910..28f8370bd9 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -23,7 +23,7 @@ const leb128 = std.leb; const log = std.log.scoped(.codegen); const build_options = @import("build_options"); -const GenerateSymbolError = codegen.GenerateSymbolError; +const CodeGenError = codegen.CodeGenError; const Result = codegen.Result; const DebugInfoOutput = codegen.DebugInfoOutput; @@ -41,7 +41,7 @@ const c_abi_int_param_regs = abi.c_abi_int_param_regs; const c_abi_int_return_regs = abi.c_abi_int_return_regs; const gp = abi.RegisterClass.gp; -const InnerError = codegen.CodeGenError || error{OutOfRegisters}; +const InnerError = CodeGenError || error{OutOfRegisters}; gpa: Allocator, air: Air, @@ -333,7 +333,7 @@ pub fn generate( liveness: Liveness, code: *std.ArrayList(u8), debug_output: DebugInfoOutput, -) GenerateSymbolError!Result { +) CodeGenError!Result { if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) { @panic("Attempted to compile for architecture that was disabled by build configuration"); } @@ -6133,201 +6133,26 @@ fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue { } } -fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) InnerError!MCValue { - const ptr_bits = self.target.cpu.arch.ptrBitWidth(); - const ptr_bytes: u64 = @divExact(ptr_bits, 8); - - // TODO this feels clunky. Perhaps we should check for it in `genTypedValue`? - if (tv.ty.zigTypeTag() == .Pointer) blk: { - if (tv.ty.castPtrToFn()) |_| break :blk; - if (!tv.ty.elemType2().hasRuntimeBits()) { - return MCValue.none; - } - } - - const mod = self.bin_file.options.module.?; - const decl = mod.declPtr(decl_index); - mod.markDeclAlive(decl); - - if (self.bin_file.cast(link.File.Elf)) |elf_file| { - const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index); - const atom = elf_file.getAtom(atom_index); - return MCValue{ .memory = atom.getOffsetTableAddress(elf_file) }; - } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { - const atom = try macho_file.getOrCreateAtomForDecl(decl_index); - const sym_index = macho_file.getAtom(atom).getSymbolIndex().?; - return MCValue{ .linker_load = .{ - .type = .got, - .sym_index = sym_index, - } }; - } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { - const atom_index = try coff_file.getOrCreateAtomForDecl(decl_index); - const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?; - return MCValue{ .linker_load = .{ - .type = .got, - .sym_index = sym_index, - } }; - } else if (self.bin_file.cast(link.File.Plan9)) |p9| { - const decl_block_index = try p9.seeDecl(decl_index); - const decl_block = p9.getDeclBlock(decl_block_index); - const got_addr = p9.bases.data + decl_block.got_index.? * ptr_bytes; - return MCValue{ .memory = got_addr }; - } else { - return self.fail("TODO codegen non-ELF const Decl pointer", .{}); - } -} - -fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue { - log.debug("lowerUnnamedConst: ty = {}, val = {}", .{ tv.ty.fmtDebug(), tv.val.fmtDebug() }); - const local_sym_index = self.bin_file.lowerUnnamedConst(tv, self.mod_fn.owner_decl) catch |err| { - return self.fail("lowering unnamed constant failed: {s}", .{@errorName(err)}); - }; - if (self.bin_file.cast(link.File.Elf)) |elf_file| { - return MCValue{ .memory = elf_file.getSymbol(local_sym_index).st_value }; - } else if (self.bin_file.cast(link.File.MachO)) |_| { - return MCValue{ .linker_load = .{ - .type = .direct, - .sym_index = local_sym_index, - } }; - } else if (self.bin_file.cast(link.File.Coff)) |_| { - return MCValue{ .linker_load = .{ - .type = .direct, - .sym_index = local_sym_index, - } }; - } else if (self.bin_file.cast(link.File.Plan9)) |_| { - return self.fail("TODO lower unnamed const in Plan9", .{}); - } else { - return self.fail("TODO lower unnamed const", .{}); - } -} - fn genTypedValue(self: *Self, arg_tv: TypedValue) InnerError!MCValue { - var typed_value = arg_tv; - if (typed_value.val.castTag(.runtime_value)) |rt| { - typed_value.val = rt.data; - } - log.debug("genTypedValue: ty = {}, val = {}", .{ typed_value.ty.fmtDebug(), typed_value.val.fmtDebug() }); - if (typed_value.val.isUndef()) - return MCValue{ .undef = {} }; - - if (typed_value.val.castTag(.decl_ref)) |payload| { - return self.lowerDeclRef(typed_value, payload.data); - } - if (typed_value.val.castTag(.decl_ref_mut)) |payload| { - return self.lowerDeclRef(typed_value, payload.data.decl_index); - } - const target = self.target.*; - - switch (typed_value.ty.zigTypeTag()) { - .Pointer => switch (typed_value.ty.ptrSize()) { - .Slice => {}, - else => { - switch (typed_value.val.tag()) { - .int_u64 => { - return MCValue{ .immediate = typed_value.val.toUnsignedInt(target) }; - }, - else => {}, - } - }, + const mcv: MCValue = switch (try codegen.genTypedValue( + self.bin_file, + self.src_loc, + arg_tv, + self.mod_fn.owner_decl, + )) { + .mcv => |mcv| switch (mcv) { + .none => .none, + .undef => .undef, + .linker_load => |ll| .{ .linker_load = ll }, + .immediate => |imm| .{ .immediate = imm }, + .memory => |addr| .{ .memory = addr }, }, - .Int => { - const info = typed_value.ty.intInfo(self.target.*); - if (info.bits <= 64) { - const unsigned = switch (info.signedness) { - .signed => blk: { - const signed = typed_value.val.toSignedInt(target); - break :blk @bitCast(u64, signed); - }, - .unsigned => typed_value.val.toUnsignedInt(target), - }; - - return MCValue{ .immediate = unsigned }; - } + .fail => |msg| { + self.err_msg = msg; + return error.CodegenFail; }, - .Bool => { - return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) }; - }, - .Optional => { - if (typed_value.ty.isPtrLikeOptional()) { - if (typed_value.val.isNull()) - return MCValue{ .immediate = 0 }; - - var buf: Type.Payload.ElemType = undefined; - return self.genTypedValue(.{ - .ty = typed_value.ty.optionalChild(&buf), - .val = typed_value.val, - }); - } else if (typed_value.ty.abiSize(self.target.*) == 1) { - return MCValue{ .immediate = @boolToInt(typed_value.val.isNull()) }; - } - }, - .Enum => { - if (typed_value.val.castTag(.enum_field_index)) |field_index| { - switch (typed_value.ty.tag()) { - .enum_simple => { - return MCValue{ .immediate = field_index.data }; - }, - .enum_full, .enum_nonexhaustive => { - const enum_full = typed_value.ty.cast(Type.Payload.EnumFull).?.data; - if (enum_full.values.count() != 0) { - const tag_val = enum_full.values.keys()[field_index.data]; - return self.genTypedValue(.{ .ty = enum_full.tag_ty, .val = tag_val }); - } else { - return MCValue{ .immediate = field_index.data }; - } - }, - else => unreachable, - } - } else { - var int_tag_buffer: Type.Payload.Bits = undefined; - const int_tag_ty = typed_value.ty.intTagType(&int_tag_buffer); - return self.genTypedValue(.{ .ty = int_tag_ty, .val = typed_value.val }); - } - }, - .ErrorSet => { - switch (typed_value.val.tag()) { - .@"error" => { - const err_name = typed_value.val.castTag(.@"error").?.data.name; - const module = self.bin_file.options.module.?; - const global_error_set = module.global_error_set; - const error_index = global_error_set.get(err_name).?; - return MCValue{ .immediate = error_index }; - }, - else => { - // In this case we are rendering an error union which has a 0 bits payload. - return MCValue{ .immediate = 0 }; - }, - } - }, - .ErrorUnion => { - const error_type = typed_value.ty.errorUnionSet(); - const payload_type = typed_value.ty.errorUnionPayload(); - - const is_pl = typed_value.val.errorUnionIsPayload(); - - if (!payload_type.hasRuntimeBitsIgnoreComptime()) { - // We use the error type directly as the type. - const err_val = if (!is_pl) typed_value.val else Value.initTag(.zero); - return self.genTypedValue(.{ .ty = error_type, .val = err_val }); - } - - return self.lowerUnnamedConst(typed_value); - }, - - .ComptimeInt => unreachable, // semantic analysis prevents this - .ComptimeFloat => unreachable, // semantic analysis prevents this - .Type => unreachable, - .EnumLiteral => unreachable, - .Void => unreachable, - .NoReturn => unreachable, - .Undefined => unreachable, - .Null => unreachable, - .Opaque => unreachable, - - else => {}, - } - - return self.lowerUnnamedConst(typed_value); + }; + return mcv; } const CallMCValues = struct { From 5b3ea49806f5d0b9034e3eacbef9e19428a5db8a Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 3 Mar 2023 18:53:13 +0100 Subject: [PATCH 006/294] riscv64: use common implementation of genTypedValue --- src/arch/riscv64/CodeGen.zig | 158 +++++------------------------------ 1 file changed, 20 insertions(+), 138 deletions(-) diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index fad5482cbc..c7191145f9 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -23,6 +23,7 @@ const log = std.log.scoped(.codegen); const build_options = @import("build_options"); const codegen = @import("../../codegen.zig"); +const CodeGenError = codegen.CodeGenError; const Result = codegen.Result; const DebugInfoOutput = codegen.DebugInfoOutput; @@ -35,7 +36,7 @@ const Instruction = abi.Instruction; const callee_preserved_regs = abi.callee_preserved_regs; const gp = abi.RegisterClass.gp; -const InnerError = codegen.CodeGenError || error{OutOfRegisters}; +const InnerError = CodeGenError || error{OutOfRegisters}; gpa: Allocator, air: Air, @@ -221,7 +222,7 @@ pub fn generate( liveness: Liveness, code: *std.ArrayList(u8), debug_output: DebugInfoOutput, -) codegen.CodeGenError!Result { +) CodeGenError!Result { if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) { @panic("Attempted to compile for architecture that was disabled by build configuration"); } @@ -2548,145 +2549,26 @@ fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue { } } -fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) InnerError!MCValue { - const ptr_bits = self.target.cpu.arch.ptrBitWidth(); - const ptr_bytes: u64 = @divExact(ptr_bits, 8); - const mod = self.bin_file.options.module.?; - const decl = mod.declPtr(decl_index); - mod.markDeclAlive(decl); - if (self.bin_file.cast(link.File.Elf)) |elf_file| { - const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index); - const atom = elf_file.getAtom(atom_index); - return MCValue{ .memory = atom.getOffsetTableAddress(elf_file) }; - } else if (self.bin_file.cast(link.File.MachO)) |_| { - unreachable; - } else if (self.bin_file.cast(link.File.Coff)) |_| { - return self.fail("TODO codegen COFF const Decl pointer", .{}); - } else if (self.bin_file.cast(link.File.Plan9)) |p9| { - const decl_block_index = try p9.seeDecl(decl_index); - const decl_block = p9.getDeclBlock(decl_block_index); - const got_addr = p9.bases.data + decl_block.got_index.? * ptr_bytes; - return MCValue{ .memory = got_addr }; - } else { - return self.fail("TODO codegen non-ELF const Decl pointer", .{}); - } - _ = tv; -} - fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { - if (typed_value.val.isUndef()) - return MCValue{ .undef = {} }; - - if (typed_value.val.castTag(.decl_ref)) |payload| { - return self.lowerDeclRef(typed_value, payload.data); - } - if (typed_value.val.castTag(.decl_ref_mut)) |payload| { - return self.lowerDeclRef(typed_value, payload.data.decl_index); - } - const target = self.target.*; - const ptr_bits = self.target.cpu.arch.ptrBitWidth(); - switch (typed_value.ty.zigTypeTag()) { - .Pointer => switch (typed_value.ty.ptrSize()) { - .Slice => { - var buf: Type.SlicePtrFieldTypeBuffer = undefined; - const ptr_type = typed_value.ty.slicePtrFieldType(&buf); - const ptr_mcv = try self.genTypedValue(.{ .ty = ptr_type, .val = typed_value.val }); - const mod = self.bin_file.options.module.?; - const slice_len = typed_value.val.sliceLen(mod); - // Codegen can't handle some kinds of indirection. If the wrong union field is accessed here it may mean - // the Sema code needs to use anonymous Decls or alloca instructions to store data. - const ptr_imm = ptr_mcv.memory; - _ = slice_len; - _ = ptr_imm; - // We need more general support for const data being stored in memory to make this work. - return self.fail("TODO codegen for const slices", .{}); - }, - else => { - if (typed_value.val.tag() == .int_u64) { - return MCValue{ .immediate = typed_value.val.toUnsignedInt(target) }; - } - return self.fail("TODO codegen more kinds of const pointers", .{}); - }, + const mcv: MCValue = switch (try codegen.genTypedValue( + self.bin_file, + self.src_loc, + typed_value, + self.mod_fn.owner_decl, + )) { + .mcv => |mcv| switch (mcv) { + .none => .none, + .undef => .undef, + .linker_load => unreachable, // TODO + .immediate => |imm| .{ .immediate = imm }, + .memory => |addr| .{ .memory = addr }, }, - .Int => { - const info = typed_value.ty.intInfo(self.target.*); - if (info.bits > ptr_bits or info.signedness == .signed) { - return self.fail("TODO const int bigger than ptr and signed int", .{}); - } - return MCValue{ .immediate = typed_value.val.toUnsignedInt(target) }; + .fail => |msg| { + self.err_msg = msg; + return error.CodegenFail; }, - .Bool => { - return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) }; - }, - .ComptimeInt => unreachable, // semantic analysis prevents this - .ComptimeFloat => unreachable, // semantic analysis prevents this - .Optional => { - if (typed_value.ty.isPtrLikeOptional()) { - if (typed_value.val.isNull()) - return MCValue{ .immediate = 0 }; - - var buf: Type.Payload.ElemType = undefined; - return self.genTypedValue(.{ - .ty = typed_value.ty.optionalChild(&buf), - .val = typed_value.val, - }); - } else if (typed_value.ty.abiSize(self.target.*) == 1) { - return MCValue{ .immediate = @boolToInt(typed_value.val.isNull()) }; - } - return self.fail("TODO non pointer optionals", .{}); - }, - .Enum => { - if (typed_value.val.castTag(.enum_field_index)) |field_index| { - switch (typed_value.ty.tag()) { - .enum_simple => { - return MCValue{ .immediate = field_index.data }; - }, - .enum_full, .enum_nonexhaustive => { - const enum_full = typed_value.ty.cast(Type.Payload.EnumFull).?.data; - if (enum_full.values.count() != 0) { - const tag_val = enum_full.values.keys()[field_index.data]; - return self.genTypedValue(.{ .ty = enum_full.tag_ty, .val = tag_val }); - } else { - return MCValue{ .immediate = field_index.data }; - } - }, - else => unreachable, - } - } else { - var int_tag_buffer: Type.Payload.Bits = undefined; - const int_tag_ty = typed_value.ty.intTagType(&int_tag_buffer); - return self.genTypedValue(.{ .ty = int_tag_ty, .val = typed_value.val }); - } - }, - .ErrorSet => { - switch (typed_value.val.tag()) { - .@"error" => { - const err_name = typed_value.val.castTag(.@"error").?.data.name; - const module = self.bin_file.options.module.?; - const global_error_set = module.global_error_set; - const error_index = global_error_set.get(err_name).?; - return MCValue{ .immediate = error_index }; - }, - else => { - // In this case we are rendering an error union which has a 0 bits payload. - return MCValue{ .immediate = 0 }; - }, - } - }, - .ErrorUnion => { - const error_type = typed_value.ty.errorUnionSet(); - const payload_type = typed_value.ty.errorUnionPayload(); - const sub_val = typed_value.val.castTag(.eu_payload).?.data; - - if (!payload_type.hasRuntimeBits()) { - // We use the error type directly as the type. - return self.genTypedValue(.{ .ty = error_type, .val = sub_val }); - } - - return self.fail("TODO implement error union const of type '{}'", .{typed_value.ty.fmtDebug()}); - }, - else => return self.fail("TODO implement const of type '{}'", .{typed_value.ty.fmtDebug()}), - } + }; + return mcv; } const CallMCValues = struct { From f6eeb6c8ce83af392dc075e3f80846aefc791f42 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 3 Mar 2023 18:53:30 +0100 Subject: [PATCH 007/294] sparc64: use common implementation of genTypedValue --- src/arch/sparc64/CodeGen.zig | 170 +++++------------------------------ 1 file changed, 20 insertions(+), 150 deletions(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 5a108eca85..dc1a450e9a 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -19,7 +19,7 @@ const Mir = @import("Mir.zig"); const Emit = @import("Emit.zig"); const Liveness = @import("../../Liveness.zig"); const Type = @import("../../type.zig").Type; -const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError; +const CodeGenError = codegen.CodeGenError; const Result = @import("../../codegen.zig").Result; const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; @@ -38,7 +38,7 @@ const gp = abi.RegisterClass.gp; const Self = @This(); -const InnerError = codegen.CodeGenError || error{OutOfRegisters}; +const InnerError = CodeGenError || error{OutOfRegisters}; const RegisterView = enum(u1) { caller, @@ -261,7 +261,7 @@ pub fn generate( liveness: Liveness, code: *std.ArrayList(u8), debug_output: DebugInfoOutput, -) GenerateSymbolError!Result { +) CodeGenError!Result { if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) { @panic("Attempted to compile for architecture that was disabled by build configuration"); } @@ -3894,133 +3894,25 @@ fn genStore(self: *Self, value_reg: Register, addr_reg: Register, comptime off_t } fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { - var tv = typed_value; - log.debug("genTypedValue: ty = {}, val = {}", .{ tv.ty.fmtDebug(), tv.val.fmtDebug() }); - - if (tv.val.castTag(.runtime_value)) |rt| { - tv.val = rt.data; - } - - if (tv.val.isUndef()) - return MCValue{ .undef = {} }; - - if (tv.val.castTag(.decl_ref)) |payload| { - return self.lowerDeclRef(tv, payload.data); - } - if (tv.val.castTag(.decl_ref_mut)) |payload| { - return self.lowerDeclRef(tv, payload.data.decl_index); - } - const target = self.target.*; - - switch (tv.ty.zigTypeTag()) { - .Pointer => switch (tv.ty.ptrSize()) { - .Slice => {}, - else => { - switch (tv.val.tag()) { - .int_u64 => { - return MCValue{ .immediate = tv.val.toUnsignedInt(target) }; - }, - else => {}, - } - }, + const mcv: MCValue = switch (try codegen.genTypedValue( + self.bin_file, + self.src_loc, + typed_value, + self.mod_fn.owner_decl, + )) { + .mcv => |mcv| switch (mcv) { + .none => .none, + .undef => .undef, + .linker_load => unreachable, // TODO + .immediate => |imm| .{ .immediate = imm }, + .memory => |addr| .{ .memory = addr }, }, - .Bool => { - return MCValue{ .immediate = @boolToInt(tv.val.toBool()) }; + .fail => |msg| { + self.err_msg = msg; + return error.CodegenFail; }, - .Int => { - const info = tv.ty.intInfo(self.target.*); - if (info.bits <= 64) { - const unsigned = switch (info.signedness) { - .signed => blk: { - const signed = tv.val.toSignedInt(target); - break :blk @bitCast(u64, signed); - }, - .unsigned => tv.val.toUnsignedInt(target), - }; - - return MCValue{ .immediate = unsigned }; - } else { - return self.fail("TODO implement int genTypedValue of > 64 bits", .{}); - } - }, - .Optional => { - if (tv.ty.isPtrLikeOptional()) { - if (tv.val.isNull()) - return MCValue{ .immediate = 0 }; - - var buf: Type.Payload.ElemType = undefined; - return self.genTypedValue(.{ - .ty = tv.ty.optionalChild(&buf), - .val = tv.val, - }); - } else if (tv.ty.abiSize(self.target.*) == 1) { - return MCValue{ .immediate = @boolToInt(tv.val.isNull()) }; - } - }, - .Enum => { - if (tv.val.castTag(.enum_field_index)) |field_index| { - switch (tv.ty.tag()) { - .enum_simple => { - return MCValue{ .immediate = field_index.data }; - }, - .enum_full, .enum_nonexhaustive => { - const enum_full = tv.ty.cast(Type.Payload.EnumFull).?.data; - if (enum_full.values.count() != 0) { - const tag_val = enum_full.values.keys()[field_index.data]; - return self.genTypedValue(.{ .ty = enum_full.tag_ty, .val = tag_val }); - } else { - return MCValue{ .immediate = field_index.data }; - } - }, - else => unreachable, - } - } else { - var int_tag_buffer: Type.Payload.Bits = undefined; - const int_tag_ty = tv.ty.intTagType(&int_tag_buffer); - return self.genTypedValue(.{ .ty = int_tag_ty, .val = tv.val }); - } - }, - .ErrorSet => { - const err_name = tv.val.castTag(.@"error").?.data.name; - const module = self.bin_file.options.module.?; - const global_error_set = module.global_error_set; - const error_index = global_error_set.get(err_name).?; - return MCValue{ .immediate = error_index }; - }, - .ErrorUnion => { - const error_type = tv.ty.errorUnionSet(); - const payload_type = tv.ty.errorUnionPayload(); - - if (tv.val.castTag(.eu_payload)) |pl| { - if (!payload_type.hasRuntimeBits()) { - // We use the error type directly as the type. - return MCValue{ .immediate = 0 }; - } - - _ = pl; - return self.fail("TODO implement error union const of type '{}' (non-error)", .{tv.ty.fmtDebug()}); - } else { - if (!payload_type.hasRuntimeBits()) { - // We use the error type directly as the type. - return self.genTypedValue(.{ .ty = error_type, .val = tv.val }); - } - - return self.fail("TODO implement error union const of type '{}' (error)", .{tv.ty.fmtDebug()}); - } - }, - .ComptimeInt => unreachable, // semantic analysis prevents this - .ComptimeFloat => unreachable, // semantic analysis prevents this - .Type => unreachable, - .EnumLiteral => unreachable, - .Void => unreachable, - .NoReturn => unreachable, - .Undefined => unreachable, - .Null => unreachable, - .Opaque => unreachable, - else => {}, - } - - return self.fail("TODO implement const of type '{}'", .{tv.ty.fmtDebug()}); + }; + return mcv; } fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue { @@ -4196,28 +4088,6 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo } } -fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) InnerError!MCValue { - // TODO this feels clunky. Perhaps we should check for it in `genTypedValue`? - if (tv.ty.zigTypeTag() == .Pointer) blk: { - if (tv.ty.castPtrToFn()) |_| break :blk; - if (!tv.ty.elemType2().hasRuntimeBits()) { - return MCValue.none; - } - } - - const mod = self.bin_file.options.module.?; - const decl = mod.declPtr(decl_index); - - mod.markDeclAlive(decl); - if (self.bin_file.cast(link.File.Elf)) |elf_file| { - const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index); - const atom = elf_file.getAtom(atom_index); - return MCValue{ .memory = atom.getOffsetTableAddress(elf_file) }; - } else { - return self.fail("TODO codegen non-ELF const Decl pointer", .{}); - } -} - fn minMax( self: *Self, tag: Air.Inst.Tag, From d23472747eb288e4c2332e03f6185c69e864f67d Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 3 Mar 2023 18:53:47 +0100 Subject: [PATCH 008/294] elf: fully zero out symbol when appending to freelist --- src/link/Elf.zig | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index f499a9952a..a91722d072 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -2097,9 +2097,16 @@ fn freeAtom(self: *Elf, atom_index: Atom.Index) void { // Appending to free lists is allowed to fail because the free lists are heuristics based anyway. const local_sym_index = atom.getSymbolIndex().?; + log.debug("adding %{d} to local symbols free list", .{local_sym_index}); self.local_symbol_free_list.append(gpa, local_sym_index) catch {}; - self.local_symbols.items[local_sym_index].st_info = 0; - self.local_symbols.items[local_sym_index].st_shndx = 0; + self.local_symbols.items[local_sym_index] = .{ + .st_name = 0, + .st_info = 0, + .st_other = 0, + .st_shndx = 0, + .st_value = 0, + .st_size = 0, + }; _ = self.atom_by_index_table.remove(local_sym_index); self.getAtomPtr(atom_index).local_sym_index = 0; From dc709fbf48798ae74d5c7763cf99dffeb8143795 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 3 Mar 2023 18:56:57 +0100 Subject: [PATCH 009/294] codegen: rename GenerateSymbolError to CodeGenError --- src/arch/wasm/CodeGen.zig | 2 +- src/codegen.zig | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 511a10769e..5cd6c95690 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1162,7 +1162,7 @@ pub fn generate( liveness: Liveness, code: *std.ArrayList(u8), debug_output: codegen.DebugInfoOutput, -) codegen.GenerateSymbolError!codegen.Result { +) codegen.CodeGenError!codegen.Result { _ = src_loc; var code_gen: CodeGen = .{ .gpa = bin_file.allocator, diff --git a/src/codegen.zig b/src/codegen.zig index 7e7f34f992..a91795841c 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -35,8 +35,6 @@ pub const CodeGenError = error{ CodegenFail, }; -pub const GenerateSymbolError = CodeGenError; - pub const DebugInfoOutput = union(enum) { dwarf: *link.File.Dwarf.DeclState, /// the plan9 debuginfo output is a bytecode with 4 opcodes From d6bd00e85500fa1a7909695ae5943be438f7521d Mon Sep 17 00:00:00 2001 From: r00ster91 Date: Fri, 3 Mar 2023 17:30:18 +0100 Subject: [PATCH 010/294] Zir: move set_cold from Inst.Tag to Inst.Extended If I could mark a builtin function as cold, I would mark @setCold as cold. We have run out of `Zir.Inst.Tag`s so I had to move a tag from Zir.Inst.Tag to Zir.Inst.Extended. This is because a new noreturn builtin will be added and noreturn builtins cannot be part of Inst.Tag: ``` /// `noreturn` instructions may not go here; they must be part of the main `Tag` enum. pub const Extended = enum(u16) { ``` Here's another reason I went for @setCold: ``` $ git grep setRuntimeSafety | wc -l 322 $ git grep setCold | wc -l 79 $ git grep setEvalBranchQuota | wc -l 82 ``` This also simply removes @setCold from Autodoc and the docs frontend because as far as I could understand it, builtins represented using Zir extended instructions are not yet supported because I couldn't find @setStackAlign or @setFloatMode there, either. --- lib/docs/main.js | 4 ---- src/AstGen.zig | 13 ++++++++++--- src/Autodoc.zig | 1 - src/Sema.zig | 18 +++++++++--------- src/Zir.zig | 10 ++++------ src/print_zir.zig | 2 +- 6 files changed, 24 insertions(+), 24 deletions(-) diff --git a/lib/docs/main.js b/lib/docs/main.js index a0647bbe61..fc99b2f861 100644 --- a/lib/docs/main.js +++ b/lib/docs/main.js @@ -1187,10 +1187,6 @@ const NAV_MODES = { payloadHtml += "panic"; break; } - case "set_cold": { - payloadHtml += "setCold"; - break; - } case "set_runtime_safety": { payloadHtml += "setRuntimeSafety"; break; diff --git a/src/AstGen.zig b/src/AstGen.zig index 41a8ccadb2..679fc2df0c 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -2609,8 +2609,9 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As .extended => switch (gz.astgen.instructions.items(.data)[inst].extended.opcode) { .breakpoint, .fence, - .set_align_stack, .set_float_mode, + .set_align_stack, + .set_cold, => break :b true, else => break :b false, }, @@ -2658,7 +2659,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As .validate_struct_init_comptime, .validate_array_init, .validate_array_init_comptime, - .set_cold, .set_runtime_safety, .closure_capture, .memcpy, @@ -8078,6 +8078,14 @@ fn builtinCall( }); return rvalue(gz, ri, result, node); }, + .set_cold => { + const order = try expr(gz, scope, ri, params[0]); + const result = try gz.addExtendedPayload(.set_cold, Zir.Inst.UnNode{ + .node = gz.nodeIndexToRelative(node), + .operand = order, + }); + return rvalue(gz, ri, result, node); + }, .src => { const token_starts = tree.tokens.items(.start); @@ -8111,7 +8119,6 @@ fn builtinCall( .bool_to_int => return simpleUnOp(gz, scope, ri, node, bool_ri, params[0], .bool_to_int), .embed_file => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .ty = .const_slice_u8_type } }, params[0], .embed_file), .error_name => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .ty = .anyerror_type } }, params[0], .error_name), - .set_cold => return simpleUnOp(gz, scope, ri, node, bool_ri, params[0], .set_cold), .set_runtime_safety => return simpleUnOp(gz, scope, ri, node, bool_ri, params[0], .set_runtime_safety), .sqrt => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .sqrt), .sin => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .sin), diff --git a/src/Autodoc.zig b/src/Autodoc.zig index 3cf3fff4c0..15d90b104b 100644 --- a/src/Autodoc.zig +++ b/src/Autodoc.zig @@ -1338,7 +1338,6 @@ fn walkInstruction( .embed_file, .error_name, .panic, - .set_cold, // @check .set_runtime_safety, // @check .sqrt, .sin, diff --git a/src/Sema.zig b/src/Sema.zig index f9a6f39867..4702d10688 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1167,6 +1167,11 @@ fn analyzeBodyInner( i += 1; continue; }, + .set_cold => { + try sema.zirSetCold(block, extended); + i += 1; + continue; + }, .breakpoint => { if (!block.is_comptime) { _ = try block.addNoOp(.breakpoint); @@ -1304,11 +1309,6 @@ fn analyzeBodyInner( i += 1; continue; }, - .set_cold => { - try sema.zirSetCold(block, inst); - i += 1; - continue; - }, .set_runtime_safety => { try sema.zirSetRuntimeSafety(block, inst); i += 1; @@ -5721,10 +5721,10 @@ fn zirSetAlignStack(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.Inst gop.value_ptr.* = .{ .alignment = alignment, .src = src }; } -fn zirSetCold(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { - const inst_data = sema.code.instructions.items(.data)[inst].un_node; - const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; - const is_cold = try sema.resolveConstBool(block, operand_src, inst_data.operand, "operand to @setCold must be comptime-known"); +fn zirSetCold(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void { + const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; + const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; + const is_cold = try sema.resolveConstBool(block, operand_src, extra.operand, "operand to @setCold must be comptime-known"); const func = sema.func orelse return; // does nothing outside a function func.is_cold = is_cold; } diff --git a/src/Zir.zig b/src/Zir.zig index 4dd2386c51..c7f2141dcc 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -808,8 +808,6 @@ pub const Inst = struct { panic, /// Same as `panic` but forces comptime. panic_comptime, - /// Implement builtin `@setCold`. Uses `un_node`. - set_cold, /// Implement builtin `@setRuntimeSafety`. Uses `un_node`. set_runtime_safety, /// Implement builtin `@sqrt`. Uses `un_node`. @@ -1187,7 +1185,6 @@ pub const Inst = struct { .bool_to_int, .embed_file, .error_name, - .set_cold, .set_runtime_safety, .sqrt, .sin, @@ -1323,7 +1320,6 @@ pub const Inst = struct { .validate_deref, .@"export", .export_value, - .set_cold, .set_runtime_safety, .memcpy, .memset, @@ -1561,7 +1557,7 @@ pub const Inst = struct { => false, .extended => switch (data.extended.opcode) { - .breakpoint, .fence => true, + .fence, .set_cold, .breakpoint => true, else => false, }, }; @@ -1750,7 +1746,6 @@ pub const Inst = struct { .error_name = .un_node, .panic = .un_node, .panic_comptime = .un_node, - .set_cold = .un_node, .set_runtime_safety = .un_node, .sqrt = .un_node, .sin = .un_node, @@ -1979,6 +1974,9 @@ pub const Inst = struct { /// Implement builtin `@setAlignStack`. /// `operand` is payload index to `UnNode`. set_align_stack, + /// Implements `@setCold`. + /// `operand` is payload index to `UnNode`. + set_cold, /// Implements the `@errSetCast` builtin. /// `operand` is payload index to `BinNode`. `lhs` is dest type, `rhs` is operand. err_set_cast, diff --git a/src/print_zir.zig b/src/print_zir.zig index fb9031296d..5ec9fbcdfc 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -196,7 +196,6 @@ const Writer = struct { .error_name, .panic, .panic_comptime, - .set_cold, .set_runtime_safety, .sqrt, .sin, @@ -503,6 +502,7 @@ const Writer = struct { .fence, .set_float_mode, .set_align_stack, + .set_cold, .wasm_memory_size, .error_to_int, .int_to_error, From e0d390463865340adc8055d1e34c0bc7acf4e4c3 Mon Sep 17 00:00:00 2001 From: r00ster91 Date: Fri, 3 Mar 2023 09:42:34 +0100 Subject: [PATCH 011/294] Ast: properly handle sentinel-terminated slices in tuple Co-authored-by: Veikka Tuominen --- lib/std/zig/Ast.zig | 9 ++++++--- test/behavior/tuple.zig | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/lib/std/zig/Ast.zig b/lib/std/zig/Ast.zig index f99d58aafa..cb86696e13 100644 --- a/lib/std/zig/Ast.zig +++ b/lib/std/zig/Ast.zig @@ -1407,7 +1407,8 @@ pub fn containerField(tree: Ast, node: Node.Index) full.ContainerField { .type_expr = data.lhs, .value_expr = extra.value_expr, .align_expr = extra.align_expr, - .tuple_like = tree.tokens.items(.tag)[main_token + 1] != .colon, + .tuple_like = tree.tokens.items(.tag)[main_token] != .identifier or + tree.tokens.items(.tag)[main_token + 1] != .colon, }); } @@ -1420,7 +1421,8 @@ pub fn containerFieldInit(tree: Ast, node: Node.Index) full.ContainerField { .type_expr = data.lhs, .value_expr = data.rhs, .align_expr = 0, - .tuple_like = tree.tokens.items(.tag)[main_token + 1] != .colon, + .tuple_like = tree.tokens.items(.tag)[main_token] != .identifier or + tree.tokens.items(.tag)[main_token + 1] != .colon, }); } @@ -1433,7 +1435,8 @@ pub fn containerFieldAlign(tree: Ast, node: Node.Index) full.ContainerField { .type_expr = data.lhs, .value_expr = 0, .align_expr = data.rhs, - .tuple_like = tree.tokens.items(.tag)[main_token + 1] != .colon, + .tuple_like = tree.tokens.items(.tag)[main_token] != .identifier or + tree.tokens.items(.tag)[main_token + 1] != .colon, }); } diff --git a/test/behavior/tuple.zig b/test/behavior/tuple.zig index 13b02b40e8..f7860be34e 100644 --- a/test/behavior/tuple.zig +++ b/test/behavior/tuple.zig @@ -397,3 +397,22 @@ test "nested runtime conditionals in tuple initializer" { }; try expectEqualStrings("up", x[0]); } + +test "sentinel slice in tuple with other fields" { + const S = struct { + a: u32, + b: u32, + }; + + const Submission = union(enum) { + open: struct { *S, [:0]const u8, u32 }, + }; + + _ = Submission; +} + +test "sentinel slice in tuple" { + const S = struct { [:0]const u8 }; + + _ = S; +} From 653814f76ba5d678ebad91f140417cd5829c6aad Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 3 Mar 2023 16:17:23 -0700 Subject: [PATCH 012/294] std.Build.addModule: return the created module --- lib/std/Build.zig | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/lib/std/Build.zig b/lib/std/Build.zig index 26919962e3..120196f972 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -550,17 +550,13 @@ pub fn addAssembly(b: *Build, options: AssemblyOptions) *CompileStep { return obj_step; } -pub const AddModuleOptions = struct { - name: []const u8, - source_file: FileSource, - dependencies: []const ModuleDependency = &.{}, -}; - -pub fn addModule(b: *Build, options: AddModuleOptions) void { - b.modules.put(b.dupe(options.name), b.createModule(.{ - .source_file = options.source_file, - .dependencies = options.dependencies, - })) catch @panic("OOM"); +/// This function creates a module and adds it to the package's module set, making +/// it available to other packages which depend on this one. +/// `createModule` can be used instead to create a private module. +pub fn addModule(b: *Build, name: []const u8, options: CreateModuleOptions) *Module { + const module = b.createModule(options); + b.modules.put(b.dupe(name), module) catch @panic("OOM"); + return module; } pub const ModuleDependency = struct { @@ -573,8 +569,9 @@ pub const CreateModuleOptions = struct { dependencies: []const ModuleDependency = &.{}, }; -/// Prefer to use `addModule` which will make the module available to other -/// packages which depend on this package. +/// This function creates a private module, to be used by the current package, +/// but not exposed to other packages depending on this one. +/// `addModule` can be used instead to create a public module. pub fn createModule(b: *Build, options: CreateModuleOptions) *Module { const module = b.allocator.create(Module) catch @panic("OOM"); module.* = .{ From 65368683ad92b858d0a391cb29d37c0476784b40 Mon Sep 17 00:00:00 2001 From: r00ster91 Date: Fri, 3 Mar 2023 18:35:03 +0100 Subject: [PATCH 013/294] add @trap builtin This introduces a new builtin function that compiles down to something that results in an illegal instruction exception/interrupt. It can be used to exit a program abnormally. This implements the builtin for all backends. --- doc/langref.html.in | 17 ++++++++++++++++- lib/zig.h | 10 ++++++++-- src/Air.zig | 10 +++++++++- src/AstGen.zig | 8 +++++++- src/BuiltinFn.zig | 8 ++++++++ src/Liveness.zig | 2 ++ src/Sema.zig | 9 +++++++++ src/Zir.zig | 11 +++++++++-- src/arch/aarch64/CodeGen.zig | 11 ++++++++++- src/arch/arm/CodeGen.zig | 9 +++++++++ src/arch/arm/Emit.zig | 9 +++++++-- src/arch/arm/Mir.zig | 2 ++ src/arch/arm/bits.zig | 11 +++++++++++ src/arch/riscv64/CodeGen.zig | 9 +++++++++ src/arch/riscv64/Emit.zig | 2 ++ src/arch/riscv64/Mir.zig | 1 + src/arch/riscv64/bits.zig | 1 + src/arch/sparc64/CodeGen.zig | 16 ++++++++++++++++ src/arch/wasm/CodeGen.zig | 6 ++++++ src/arch/x86_64/CodeGen.zig | 10 ++++++++++ src/arch/x86_64/Emit.zig | 7 +++++++ src/arch/x86_64/Mir.zig | 3 +++ src/codegen/c.zig | 6 ++++++ src/codegen/llvm.zig | 8 ++++++++ src/print_air.zig | 1 + src/print_zir.zig | 1 + 26 files changed, 178 insertions(+), 10 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index e016ef13f8..0290d3acd6 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -7818,12 +7818,14 @@ comptime {

This function inserts a platform-specific debug trap instruction which causes debuggers to break there. + Unlike for {#syntax#}@trap(){#endsyntax#}, execution may continue after this point if the program is resumed.

This function is only valid within function scope.

- + {#see_also|@trap#} {#header_close#} + {#header_open|@mulAdd#}
{#syntax#}@mulAdd(comptime T: type, a: T, b: T, c: T) T{#endsyntax#}

@@ -9393,6 +9395,19 @@ fn List(comptime T: type) type {

{#header_close#} + {#header_open|@trap#} +
{#syntax#}@trap() noreturn{#endsyntax#}
+

+ This function inserts a platform-specific trap/jam instruction which can be used to exit the program abnormally. + This may be implemented by explicitly emitting an invalid instruction which may cause an illegal instruction exception of some sort. + Unlike for {#syntax#}@breakpoint(){#endsyntax#}, execution does not continue after this point. +

+

+ This function is only valid within function scope. +

+ {#see_also|@breakpoint#} + {#header_close#} + {#header_open|@truncate#}
{#syntax#}@truncate(comptime T: type, integer: anytype) T{#endsyntax#}

diff --git a/lib/zig.h b/lib/zig.h index c10720d1bd..f3ad7db8a1 100644 --- a/lib/zig.h +++ b/lib/zig.h @@ -180,10 +180,16 @@ typedef char bool; #define zig_export(sig, symbol, name) __asm(name " = " symbol) #endif +#if zig_has_builtin(trap) +#define zig_trap() __builtin_trap() +#elif defined(__i386__) || defined(__x86_64__) +#define zig_trap() __asm__ volatile("ud2"); +#else +#define zig_trap() raise(SIGILL) +#endif + #if zig_has_builtin(debugtrap) #define zig_breakpoint() __builtin_debugtrap() -#elif zig_has_builtin(trap) || defined(zig_gnuc) -#define zig_breakpoint() __builtin_trap() #elif defined(_MSC_VER) || defined(__MINGW32__) || defined(__MINGW64__) #define zig_breakpoint() __debugbreak() #elif defined(__i386__) || defined(__x86_64__) diff --git a/src/Air.zig b/src/Air.zig index 3ebdd319de..4646dcc89e 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -232,7 +232,14 @@ pub const Inst = struct { /// Result type is always noreturn; no instructions in a block follow this one. /// Uses the `br` field. br, - /// Lowers to a hardware trap instruction, or the next best thing. + /// Lowers to a trap/jam instruction causing program abortion. + /// This may lower to an instruction known to be invalid. + /// Sometimes, for the lack of a better instruction, `trap` and `breakpoint` may compile down to the same code. + /// Result type is always noreturn; no instructions in a block follow this one. + trap, + /// Lowers to a trap instruction causing debuggers to break here, or the next best thing. + /// The debugger or something else may allow the program to resume after this point. + /// Sometimes, for the lack of a better instruction, `trap` and `breakpoint` may compile down to the same code. /// Result type is always void. breakpoint, /// Yields the return address of the current function. @@ -1186,6 +1193,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { .ret, .ret_load, .unreach, + .trap, => return Type.initTag(.noreturn), .breakpoint, diff --git a/src/AstGen.zig b/src/AstGen.zig index 679fc2df0c..fd51e73cf9 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -2631,6 +2631,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As .repeat_inline, .panic, .panic_comptime, + .trap, .check_comptime_control_flow, => { noreturn_src_node = statement; @@ -8105,7 +8106,7 @@ fn builtinCall( .error_return_trace => return rvalue(gz, ri, try gz.addNodeExtended(.error_return_trace, node), node), .frame => return rvalue(gz, ri, try gz.addNodeExtended(.frame, node), node), .frame_address => return rvalue(gz, ri, try gz.addNodeExtended(.frame_address, node), node), - .breakpoint => return rvalue(gz, ri, try gz.addNodeExtended(.breakpoint, node), node), + .breakpoint => return rvalue(gz, ri, try gz.addNodeExtended(.breakpoint, node), node), .type_info => return simpleUnOpType(gz, scope, ri, node, params[0], .type_info), .size_of => return simpleUnOpType(gz, scope, ri, node, params[0], .size_of), @@ -8178,6 +8179,11 @@ fn builtinCall( try emitDbgNode(gz, node); return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .ty = .const_slice_u8_type } }, params[0], if (gz.force_comptime) .panic_comptime else .panic); }, + .trap => { + try emitDbgNode(gz, node); + _ = try gz.addNode(.trap, node); + return rvalue(gz, ri, .void_value, node); + }, .error_to_int => { const operand = try expr(gz, scope, .{ .rl = .none }, params[0]); const result = try gz.addExtendedPayload(.error_to_int, Zir.Inst.UnNode{ diff --git a/src/BuiltinFn.zig b/src/BuiltinFn.zig index 20edbabe47..79c6617483 100644 --- a/src/BuiltinFn.zig +++ b/src/BuiltinFn.zig @@ -109,6 +109,7 @@ pub const Tag = enum { sub_with_overflow, tag_name, This, + trap, truncate, Type, type_info, @@ -915,6 +916,13 @@ pub const list = list: { .param_count = 0, }, }, + .{ + "@trap", + .{ + .tag = .trap, + .param_count = 0, + }, + }, .{ "@truncate", .{ diff --git a/src/Liveness.zig b/src/Liveness.zig index 481cf25d04..8dc81aa165 100644 --- a/src/Liveness.zig +++ b/src/Liveness.zig @@ -226,6 +226,7 @@ pub fn categorizeOperand( .ret_ptr, .constant, .const_ty, + .trap, .breakpoint, .dbg_stmt, .dbg_inline_begin, @@ -848,6 +849,7 @@ fn analyzeInst( .ret_ptr, .constant, .const_ty, + .trap, .breakpoint, .dbg_stmt, .dbg_inline_begin, diff --git a/src/Sema.zig b/src/Sema.zig index 4702d10688..8940527bc0 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1101,6 +1101,7 @@ fn analyzeBodyInner( .@"unreachable" => break sema.zirUnreachable(block, inst), .panic => break sema.zirPanic(block, inst, false), .panic_comptime => break sema.zirPanic(block, inst, true), + .trap => break sema.zirTrap(block, inst), // zig fmt: on .extended => ext: { @@ -5144,6 +5145,14 @@ fn zirPanic(sema: *Sema, block: *Block, inst: Zir.Inst.Index, force_comptime: bo return always_noreturn; } +fn zirTrap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index { + const src_node = sema.code.instructions.items(.data)[inst].node; + const src = LazySrcLoc.nodeOffset(src_node); + sema.src = src; + _ = try block.addNoOp(.trap); + return always_noreturn; +} + fn zirLoop(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const tracy = trace(@src()); defer tracy.end(); diff --git a/src/Zir.zig b/src/Zir.zig index c7f2141dcc..b8ea2ea295 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -617,7 +617,7 @@ pub const Inst = struct { /// Uses the `un_node` field. typeof_log2_int_type, /// Asserts control-flow will not reach this instruction (`unreachable`). - /// Uses the `unreachable` union field. + /// Uses the `@"unreachable"` union field. @"unreachable", /// Bitwise XOR. `^` /// Uses the `pl_node` union field. Payload is `Bin`. @@ -808,6 +808,9 @@ pub const Inst = struct { panic, /// Same as `panic` but forces comptime. panic_comptime, + /// Implements `@trap`. + /// Uses the `node` field. + trap, /// Implement builtin `@setRuntimeSafety`. Uses `un_node`. set_runtime_safety, /// Implement builtin `@sqrt`. Uses `un_node`. @@ -1274,6 +1277,7 @@ pub const Inst = struct { .repeat_inline, .panic, .panic_comptime, + .trap, .check_comptime_control_flow, => true, }; @@ -1549,6 +1553,7 @@ pub const Inst = struct { .repeat_inline, .panic, .panic_comptime, + .trap, .for_len, .@"try", .try_ptr, @@ -1746,6 +1751,7 @@ pub const Inst = struct { .error_name = .un_node, .panic = .un_node, .panic_comptime = .un_node, + .trap = .node, .set_runtime_safety = .un_node, .sqrt = .un_node, .sin = .un_node, @@ -1982,6 +1988,7 @@ pub const Inst = struct { err_set_cast, /// `operand` is payload index to `UnNode`. await_nosuspend, + /// Implements `@breakpoint`. /// `operand` is `src_node: i32`. breakpoint, /// Implements the `@select` builtin. @@ -1995,7 +2002,7 @@ pub const Inst = struct { int_to_error, /// Implement builtin `@Type`. /// `operand` is payload index to `UnNode`. - /// `small` contains `NameStrategy + /// `small` contains `NameStrategy`. reify, /// Implements the `@asyncCall` builtin. /// `operand` is payload index to `AsyncCall`. diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 818b04f890..a42d0539f2 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -737,6 +737,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .bitcast => try self.airBitCast(inst), .block => try self.airBlock(inst), .br => try self.airBr(inst), + .trap => try self.airTrap(), .breakpoint => try self.airBreakpoint(), .ret_addr => try self.airRetAddr(inst), .frame_addr => try self.airFrameAddress(inst), @@ -4198,10 +4199,18 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ .none, .none, .none }); } +fn airTrap(self: *Self) !void { + _ = try self.addInst(.{ + .tag = .brk, + .data = .{ .imm16 = 0x0001 }, + }); + return self.finishAirBookkeeping(); +} + fn airBreakpoint(self: *Self) !void { _ = try self.addInst(.{ .tag = .brk, - .data = .{ .imm16 = 1 }, + .data = .{ .imm16 = 0xf000 }, }); return self.finishAirBookkeeping(); } diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index ceabe70438..cecda8fd4a 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -721,6 +721,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .bitcast => try self.airBitCast(inst), .block => try self.airBlock(inst), .br => try self.airBr(inst), + .trap => try self.airTrap(), .breakpoint => try self.airBreakpoint(), .ret_addr => try self.airRetAddr(inst), .frame_addr => try self.airFrameAddress(inst), @@ -4146,6 +4147,14 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ .none, .none, .none }); } +fn airTrap(self: *Self) !void { + _ = try self.addInst(.{ + .tag = .undefined_instruction, + .data = .{ .nop = {} }, + }); + return self.finishAirBookkeeping(); +} + fn airBreakpoint(self: *Self) !void { _ = try self.addInst(.{ .tag = .bkpt, diff --git a/src/arch/arm/Emit.zig b/src/arch/arm/Emit.zig index 17540f0968..17415318de 100644 --- a/src/arch/arm/Emit.zig +++ b/src/arch/arm/Emit.zig @@ -1,4 +1,4 @@ -//! This file contains the functionality for lowering AArch64 MIR into +//! This file contains the functionality for lowering AArch32 MIR into //! machine code const Emit = @This(); @@ -15,7 +15,7 @@ const Target = std.Target; const assert = std.debug.assert; const Instruction = bits.Instruction; const Register = bits.Register; -const log = std.log.scoped(.aarch64_emit); +const log = std.log.scoped(.aarch32_emit); const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; const CodeGen = @import("CodeGen.zig"); @@ -100,6 +100,7 @@ pub fn emitMir( .b => try emit.mirBranch(inst), + .undefined_instruction => try emit.mirUndefinedInstruction(), .bkpt => try emit.mirExceptionGeneration(inst), .blx => try emit.mirBranchExchange(inst), @@ -494,6 +495,10 @@ fn mirBranch(emit: *Emit, inst: Mir.Inst.Index) !void { } } +fn mirUndefinedInstruction(emit: *Emit) !void { + try emit.writeInstruction(Instruction.undefinedInstruction()); +} + fn mirExceptionGeneration(emit: *Emit, inst: Mir.Inst.Index) !void { const tag = emit.mir.instructions.items(.tag)[inst]; const imm16 = emit.mir.instructions.items(.data)[inst].imm16; diff --git a/src/arch/arm/Mir.zig b/src/arch/arm/Mir.zig index 07a8384c2c..736d0574bb 100644 --- a/src/arch/arm/Mir.zig +++ b/src/arch/arm/Mir.zig @@ -35,6 +35,8 @@ pub const Inst = struct { asr, /// Branch b, + /// Undefined instruction + undefined_instruction, /// Breakpoint bkpt, /// Branch with Link and Exchange diff --git a/src/arch/arm/bits.zig b/src/arch/arm/bits.zig index 8e76ae9409..185c4ed921 100644 --- a/src/arch/arm/bits.zig +++ b/src/arch/arm/bits.zig @@ -307,6 +307,9 @@ pub const Instruction = union(enum) { fixed: u4 = 0b1111, cond: u4, }, + undefined_instruction: packed struct { + imm32: u32 = 0xe7ffdefe, + }, breakpoint: packed struct { imm4: u4, fixed_1: u4 = 0b0111, @@ -613,6 +616,7 @@ pub const Instruction = union(enum) { .branch => |v| @bitCast(u32, v), .branch_exchange => |v| @bitCast(u32, v), .supervisor_call => |v| @bitCast(u32, v), + .undefined_instruction => |v| v.imm32, .breakpoint => |v| @intCast(u32, v.imm4) | (@intCast(u32, v.fixed_1) << 4) | (@intCast(u32, v.imm12) << 8) | (@intCast(u32, v.fixed_2_and_cond) << 20), }; } @@ -890,6 +894,13 @@ pub const Instruction = union(enum) { }; } + // This instruction has no official mnemonic equivalent so it is public as-is. + pub fn undefinedInstruction() Instruction { + return Instruction{ + .undefined_instruction = .{}, + }; + } + fn breakpoint(imm: u16) Instruction { return Instruction{ .breakpoint = .{ diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index afcf4b0bb7..0b45982fb3 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -550,6 +550,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .bitcast => try self.airBitCast(inst), .block => try self.airBlock(inst), .br => try self.airBr(inst), + .trap => try self.airTrap(), .breakpoint => try self.airBreakpoint(), .ret_addr => try self.airRetAddr(inst), .frame_addr => try self.airFrameAddress(inst), @@ -1652,6 +1653,14 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, mcv, .{ .none, .none, .none }); } +fn airTrap(self: *Self) !void { + _ = try self.addInst(.{ + .tag = .unimp, + .data = .{ .nop = {} }, + }); + return self.finishAirBookkeeping(); +} + fn airBreakpoint(self: *Self) !void { _ = try self.addInst(.{ .tag = .ebreak, diff --git a/src/arch/riscv64/Emit.zig b/src/arch/riscv64/Emit.zig index 387c735896..3b330cbd3f 100644 --- a/src/arch/riscv64/Emit.zig +++ b/src/arch/riscv64/Emit.zig @@ -51,6 +51,7 @@ pub fn emitMir( .ebreak => try emit.mirSystem(inst), .ecall => try emit.mirSystem(inst), + .unimp => try emit.mirSystem(inst), .dbg_line => try emit.mirDbgLine(inst), @@ -153,6 +154,7 @@ fn mirSystem(emit: *Emit, inst: Mir.Inst.Index) !void { switch (tag) { .ebreak => try emit.writeInstruction(Instruction.ebreak), .ecall => try emit.writeInstruction(Instruction.ecall), + .unimp => try emit.writeInstruction(Instruction.unimp), else => unreachable, } } diff --git a/src/arch/riscv64/Mir.zig b/src/arch/riscv64/Mir.zig index 97accb7642..8905b24c3c 100644 --- a/src/arch/riscv64/Mir.zig +++ b/src/arch/riscv64/Mir.zig @@ -32,6 +32,7 @@ pub const Inst = struct { dbg_epilogue_begin, /// Pseudo-instruction: Update debug line dbg_line, + unimp, ebreak, ecall, jalr, diff --git a/src/arch/riscv64/bits.zig b/src/arch/riscv64/bits.zig index 6b94927df8..7b3ff0bfe9 100644 --- a/src/arch/riscv64/bits.zig +++ b/src/arch/riscv64/bits.zig @@ -380,6 +380,7 @@ pub const Instruction = union(enum) { pub const ecall = iType(0b1110011, 0b000, .zero, .zero, 0x000); pub const ebreak = iType(0b1110011, 0b000, .zero, .zero, 0x001); + pub const unimp = iType(0, 0, .zero, .zero, 0); }; pub const Register = enum(u6) { diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index c8f77fe702..1b7290ddce 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -566,6 +566,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .bitcast => try self.airBitCast(inst), .block => try self.airBlock(inst), .br => try self.airBr(inst), + .trap => try self.airTrap(), .breakpoint => try self.airBreakpoint(), .ret_addr => @panic("TODO try self.airRetAddr(inst)"), .frame_addr => @panic("TODO try self.airFrameAddress(inst)"), @@ -1160,6 +1161,21 @@ fn airBr(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, .dead, .{ branch.operand, .none, .none }); } +fn airTrap(self: *Self) !void { + // ta 0x05 + _ = try self.addInst(.{ + .tag = .tcc, + .data = .{ + .trap = .{ + .is_imm = true, + .cond = .al, + .rs2_or_imm = .{ .imm = 0x05 }, + }, + }, + }); + return self.finishAirBookkeeping(); +} + fn airBreakpoint(self: *Self) !void { // ta 0x01 _ = try self.addInst(.{ diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 2f191fd834..d388bc8fab 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1829,6 +1829,7 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { .arg => func.airArg(inst), .bitcast => func.airBitcast(inst), .block => func.airBlock(inst), + .trap => func.airTrap(inst), .breakpoint => func.airBreakpoint(inst), .br => func.airBr(inst), .bool_to_int => func.airBoolToInt(inst), @@ -3289,6 +3290,11 @@ fn airNot(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { func.finishAir(inst, result, &.{ty_op.operand}); } +fn airTrap(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { + try func.addTag(.@"unreachable"); + func.finishAir(inst, .none, &.{}); +} + fn airBreakpoint(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { // unsupported by wasm itfunc. Can be implemented once we support DWARF // for wasm diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 53d38f520a..70b51e50fd 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -638,6 +638,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .bitcast => try self.airBitCast(inst), .block => try self.airBlock(inst), .br => try self.airBr(inst), + .trap => try self.airTrap(), .breakpoint => try self.airBreakpoint(), .ret_addr => try self.airRetAddr(inst), .frame_addr => try self.airFrameAddress(inst), @@ -3917,6 +3918,15 @@ fn genVarDbgInfo( } } +fn airTrap(self: *Self) !void { + _ = try self.addInst(.{ + .tag = .ud, + .ops = Mir.Inst.Ops.encode(.{}), + .data = undefined, + }); + return self.finishAirBookkeeping(); +} + fn airBreakpoint(self: *Self) !void { _ = try self.addInst(.{ .tag = .interrupt, diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index 12c19915c6..e521de4bd4 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -166,6 +166,7 @@ pub fn lowerMir(emit: *Emit) InnerError!void { .@"test" => try emit.mirTest(inst), + .ud => try emit.mirUndefinedInstruction(), .interrupt => try emit.mirInterrupt(inst), .nop => {}, // just skip it @@ -234,6 +235,10 @@ fn fixupRelocs(emit: *Emit) InnerError!void { } } +fn mirUndefinedInstruction(emit: *Emit) InnerError!void { + return lowerToZoEnc(.ud2, emit.code); +} + fn mirInterrupt(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const tag = emit.mir.instructions.items(.tag)[inst]; assert(tag == .interrupt); @@ -1279,6 +1284,7 @@ const Tag = enum { push, pop, @"test", + ud2, int3, nop, imul, @@ -1571,6 +1577,7 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) OpCode { .zo => return switch (tag) { .ret_near => OpCode.init(&.{0xc3}), .ret_far => OpCode.init(&.{0xcb}), + .ud2 => OpCode.init(&.{ 0x0F, 0x0B }), .int3 => OpCode.init(&.{0xcc}), .nop => OpCode.init(&.{0x90}), .syscall => OpCode.init(&.{ 0x0f, 0x05 }), diff --git a/src/arch/x86_64/Mir.zig b/src/arch/x86_64/Mir.zig index 112d9a5982..ba71f4cddd 100644 --- a/src/arch/x86_64/Mir.zig +++ b/src/arch/x86_64/Mir.zig @@ -329,6 +329,9 @@ pub const Inst = struct { /// TODO handle more cases @"test", + /// Undefined Instruction + ud, + /// Breakpoint form: /// 0b00 int3 interrupt, diff --git a/src/codegen/c.zig b/src/codegen/c.zig index cf428d4bd6..c0585c3a4a 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -2741,6 +2741,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, .const_ty => unreachable, // excluded from function bodies .arg => try airArg(f, inst), + .trap => try airTrap(f.object.writer()), .breakpoint => try airBreakpoint(f.object.writer()), .ret_addr => try airRetAddr(f, inst), .frame_addr => try airFrameAddress(f, inst), @@ -4428,6 +4429,11 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue { return local; } +fn airTrap(writer: anytype) !CValue { + try writer.writeAll("zig_trap();\n"); + return .none; +} + fn airBreakpoint(writer: anytype) !CValue { try writer.writeAll("zig_breakpoint();\n"); return .none; diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 6f240b88f5..1f8473ac32 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -4590,6 +4590,7 @@ pub const FuncGen = struct { .block => try self.airBlock(inst), .br => try self.airBr(inst), .switch_br => try self.airSwitchBr(inst), + .trap => try self.airTrap(inst), .breakpoint => try self.airBreakpoint(inst), .ret_addr => try self.airRetAddr(inst), .frame_addr => try self.airFrameAddress(inst), @@ -8256,6 +8257,13 @@ pub const FuncGen = struct { return fg.load(ptr, ptr_ty); } + fn airTrap(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { + _ = inst; + const llvm_fn = self.getIntrinsic("llvm.trap", &.{}); + _ = self.builder.buildCall(llvm_fn.globalGetValueType(), llvm_fn, undefined, 0, .Cold, .Auto, ""); + return null; + } + fn airBreakpoint(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { _ = inst; const llvm_fn = self.getIntrinsic("llvm.debugtrap", &.{}); diff --git a/src/print_air.zig b/src/print_air.zig index 447af5a9c7..f5c06daae2 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -194,6 +194,7 @@ const Writer = struct { .c_va_end, => try w.writeUnOp(s, inst), + .trap, .breakpoint, .unreach, .ret_addr, diff --git a/src/print_zir.zig b/src/print_zir.zig index 5ec9fbcdfc..5e7d0d45de 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -410,6 +410,7 @@ const Writer = struct { .alloc_inferred_comptime_mut, .ret_ptr, .ret_type, + .trap, => try self.writeNode(stream, inst), .error_value, From 4eb3f50fcf6fcfb6b8013571be00b9eeeb909833 Mon Sep 17 00:00:00 2001 From: r00ster91 Date: Fri, 3 Mar 2023 19:59:18 +0100 Subject: [PATCH 014/294] Wasm @breakpoint: emit unreachable This should improve the developer debugging experience. --- src/arch/wasm/CodeGen.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index d388bc8fab..dbabb436c8 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -3298,6 +3298,7 @@ fn airTrap(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { fn airBreakpoint(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { // unsupported by wasm itfunc. Can be implemented once we support DWARF // for wasm + try func.addTag(.@"unreachable"); func.finishAir(inst, .none, &.{}); } From 2cf27c571880a607401dca181f8103e855d0c46d Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sat, 4 Mar 2023 02:11:04 -0500 Subject: [PATCH 015/294] llvm: fix incorrectly annotated DIType Closes #14715 Closes #14783 --- src/codegen/llvm.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 6f240b88f5..937c1cf120 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -1773,7 +1773,7 @@ pub const Object = struct { if (ty.optionalReprIsPayload()) { const ptr_di_ty = try o.lowerDebugType(child_ty, resolve); // The recursive call to `lowerDebugType` means we can't use `gop` anymore. - try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(ptr_di_ty), .{ .mod = o.module }); + try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.init(ptr_di_ty, resolve), .{ .mod = o.module }); return ptr_di_ty; } From 010596c93054543c3c218e7d4b045d5e46384dab Mon Sep 17 00:00:00 2001 From: r00ster91 Date: Sat, 4 Mar 2023 12:51:16 +0100 Subject: [PATCH 016/294] AstGen: compile-error on primitive value export Fixes #14778 Co-authored-by: Veikka Tuominen --- src/AstGen.zig | 5 +++- .../exporting_primitive_values.zig | 29 +++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 test/cases/compile_errors/exporting_primitive_values.zig diff --git a/src/AstGen.zig b/src/AstGen.zig index 41a8ccadb2..8e3f11df76 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -7976,6 +7976,9 @@ fn builtinCall( switch (node_tags[params[0]]) { .identifier => { const ident_token = main_tokens[params[0]]; + if (isPrimitive(tree.tokenSlice(ident_token))) { + return astgen.failTok(ident_token, "unable to export primitive value", .{}); + } decl_name = try astgen.identAsString(ident_token); var s = scope; @@ -8988,7 +8991,7 @@ const primitive_instrs = std.ComptimeStringMap(Zir.Inst.Ref, .{ }); comptime { - // These checks ensure that std.zig.primitives stays in synce with the primitive->Zir map. + // These checks ensure that std.zig.primitives stays in sync with the primitive->Zir map. const primitives = std.zig.primitives; for (primitive_instrs.kvs) |kv| { if (!primitives.isPrimitive(kv.key)) { diff --git a/test/cases/compile_errors/exporting_primitive_values.zig b/test/cases/compile_errors/exporting_primitive_values.zig new file mode 100644 index 0000000000..bf3c38a553 --- /dev/null +++ b/test/cases/compile_errors/exporting_primitive_values.zig @@ -0,0 +1,29 @@ +pub export fn entry1() void { + @export(u100, .{ .name = "a" }); +} +pub export fn entry3() void { + @export(undefined, .{ .name = "b" }); +} +pub export fn entry4() void { + @export(null, .{ .name = "c" }); +} +pub export fn entry5() void { + @export(false, .{ .name = "d" }); +} +pub export fn entry6() void { + @export(u8, .{ .name = "e" }); +} +pub export fn entry7() void { + @export(u65535, .{ .name = "f" }); +} + +// error +// backend=llvm +// target=native +// +// :2:13: error: unable to export primitive value +// :5:13: error: unable to export primitive value +// :8:13: error: unable to export primitive value +// :11:13: error: unable to export primitive value +// :14:13: error: unable to export primitive value +// :17:13: error: unable to export primitive value From 16302578d5a0ca226c7db76bc8e39574dea1dc1d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 4 Mar 2023 14:04:58 -0700 Subject: [PATCH 017/294] add behavior test case for previous commit --- test/behavior/slice.zig | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test/behavior/slice.zig b/test/behavior/slice.zig index 435e1887bb..ed5e2a721d 100644 --- a/test/behavior/slice.zig +++ b/test/behavior/slice.zig @@ -747,3 +747,18 @@ test "slice decays to many pointer" { const p: [*:0]const u8 = buf[0..7 :0]; try expectEqualStrings(buf[0..7], std.mem.span(p)); } + +test "write through pointer to optional slice arg" { + const S = struct { + fn bar(foo: *?[]const u8) !void { + foo.* = try baz(); + } + + fn baz() ![]const u8 { + return "ok"; + } + }; + var foo: ?[]const u8 = null; + try S.bar(&foo); + try expectEqualStrings(foo.?, "ok"); +} From c9d990d79083f117564837f762c3e225d7fbc5cf Mon Sep 17 00:00:00 2001 From: tranquillity-codes Date: Sat, 4 Mar 2023 10:10:07 +0100 Subject: [PATCH 018/294] fix doc Build Mode --- doc/langref.html.in | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index e016ef13f8..71d99b3aae 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -9565,9 +9565,10 @@ pub fn build(b: *std.Build) void { This causes these options to be available:

-
-Drelease-safe=[bool]
Optimizations on and safety on
-
-Drelease-fast=[bool]
Optimizations on and safety off
-
-Drelease-small=[bool]
Size optimizations on and safety off
+
-Doptimize=Debug
Optimizations off and safety on (default)
+
-Doptimize=ReleaseSafe
Optimizations on and safety on
+
-Doptimize=ReleaseFast
Optimizations on and safety off
+
-Doptimize=ReleaseSmall
Size optimizations on and safety off
{#header_open|Debug#} {#shell_samp#}$ zig build-exe example.zig{#end_shell_samp#} From 874ae81f1b2ae76cea6f5c79203f4baa68263163 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Fri, 3 Mar 2023 00:18:34 -0500 Subject: [PATCH 019/294] CBE: implement big integer literals --- lib/std/math/big/int.zig | 1 + lib/zig.h | 12 +- src/codegen/c.zig | 361 +++++++++++++++++++------------------- src/codegen/c/type.zig | 348 ++++++++++++++++++++++++++++++++---- test/behavior/bitcast.zig | 1 - 5 files changed, 502 insertions(+), 221 deletions(-) diff --git a/lib/std/math/big/int.zig b/lib/std/math/big/int.zig index b7725b9ae9..4e4e7c489e 100644 --- a/lib/std/math/big/int.zig +++ b/lib/std/math/big/int.zig @@ -1674,6 +1674,7 @@ pub const Mutable = struct { /// If a is positive, this passes through to truncate. /// If a is negative, then r is set to positive with the bit pattern ~(a - 1). + /// r may alias a. /// /// Asserts `r` has enough storage to store the result. /// The upper bound is `calcTwosCompLimbCount(a.len)`. diff --git a/lib/zig.h b/lib/zig.h index f3ad7db8a1..7353ea935d 100644 --- a/lib/zig.h +++ b/lib/zig.h @@ -1360,8 +1360,8 @@ typedef signed __int128 zig_i128; #define zig_make_u128(hi, lo) ((zig_u128)(hi)<<64|(lo)) #define zig_make_i128(hi, lo) ((zig_i128)zig_make_u128(hi, lo)) -#define zig_make_constant_u128(hi, lo) zig_make_u128(hi, lo) -#define zig_make_constant_i128(hi, lo) zig_make_i128(hi, lo) +#define zig_init_u128(hi, lo) zig_make_u128(hi, lo) +#define zig_init_i128(hi, lo) zig_make_i128(hi, lo) #define zig_hi_u128(val) ((uint64_t)((val) >> 64)) #define zig_lo_u128(val) ((uint64_t)((val) >> 0)) #define zig_hi_i128(val) (( int64_t)((val) >> 64)) @@ -1391,11 +1391,11 @@ typedef struct { zig_align(16) int64_t hi; uint64_t lo; } zig_i128; #define zig_make_i128(hi, lo) ((zig_i128){ .h##i = (hi), .l##o = (lo) }) #if _MSC_VER /* MSVC doesn't allow struct literals in constant expressions */ -#define zig_make_constant_u128(hi, lo) { .h##i = (hi), .l##o = (lo) } -#define zig_make_constant_i128(hi, lo) { .h##i = (hi), .l##o = (lo) } +#define zig_init_u128(hi, lo) { .h##i = (hi), .l##o = (lo) } +#define zig_init_i128(hi, lo) { .h##i = (hi), .l##o = (lo) } #else /* But non-MSVC doesn't like the unprotected commas */ -#define zig_make_constant_u128(hi, lo) zig_make_u128(hi, lo) -#define zig_make_constant_i128(hi, lo) zig_make_i128(hi, lo) +#define zig_init_u128(hi, lo) zig_make_u128(hi, lo) +#define zig_init_i128(hi, lo) zig_make_i128(hi, lo) #endif #define zig_hi_u128(val) ((val).hi) #define zig_lo_u128(val) ((val).lo) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index c0585c3a4a..addd3c8332 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -449,7 +449,7 @@ pub const Function = struct { } fn fmtIntLiteral(f: *Function, ty: Type, val: Value) !std.fmt.Formatter(formatIntLiteral) { - return f.object.dg.fmtIntLiteral(ty, val); + return f.object.dg.fmtIntLiteral(ty, val, .Other); } fn getLazyFnName(f: *Function, key: LazyFnKey, data: LazyFnValue.Data) ![]const u8 { @@ -574,9 +574,9 @@ pub const DeclGen = struct { const len_val = Value.initPayload(&len_pl.base); if (location == .StaticInitializer) { - return writer.print(", {} }}", .{try dg.fmtIntLiteral(Type.usize, len_val)}); + return writer.print(", {} }}", .{try dg.fmtIntLiteral(Type.usize, len_val, .Other)}); } else { - return writer.print(", .len = {} }}", .{try dg.fmtIntLiteral(Type.usize, len_val)}); + return writer.print(", .len = {} }}", .{try dg.fmtIntLiteral(Type.usize, len_val, .Other)}); } } @@ -606,7 +606,7 @@ pub const DeclGen = struct { try writer.writeByte(')'); } switch (ptr_val.tag()) { - .int_u64, .one => try writer.print("{x}", .{try dg.fmtIntLiteral(Type.usize, ptr_val)}), + .int_u64, .one => try writer.print("{x}", .{try dg.fmtIntLiteral(Type.usize, ptr_val, .Other)}), .decl_ref_mut, .decl_ref, .variable => { const decl_index = switch (ptr_val.tag()) { .decl_ref => ptr_val.castTag(.decl_ref).?.data, @@ -670,7 +670,9 @@ pub const DeclGen = struct { container_ptr_ty, location, ); - try writer.print(" + {})", .{try dg.fmtIntLiteral(Type.usize, byte_offset_val)}); + try writer.print(" + {})", .{ + try dg.fmtIntLiteral(Type.usize, byte_offset_val, .Other), + }); }, .end => { try writer.writeAll("(("); @@ -680,7 +682,9 @@ pub const DeclGen = struct { container_ptr_ty, location, ); - try writer.print(") + {})", .{try dg.fmtIntLiteral(Type.usize, Value.one)}); + try writer.print(") + {})", .{ + try dg.fmtIntLiteral(Type.usize, Value.one, .Other), + }); }, } }, @@ -746,7 +750,7 @@ pub const DeclGen = struct { return writer.writeAll("false"); } }, - .Int, .Enum, .ErrorSet => return writer.print("{x}", .{try dg.fmtIntLiteralLoc(ty, val, location)}), + .Int, .Enum, .ErrorSet => return writer.print("{x}", .{try dg.fmtIntLiteral(ty, val, location)}), .Float => { const bits = ty.floatBits(target); var int_pl = Type.Payload.Bits{ .base = .{ .tag = .int_signed }, .data = bits }; @@ -780,11 +784,11 @@ pub const DeclGen = struct { var buf: Type.SlicePtrFieldTypeBuffer = undefined; const ptr_ty = ty.slicePtrFieldType(&buf); try dg.renderType(writer, ptr_ty); - return writer.print("){x}, {0x}}}", .{try dg.fmtIntLiteral(Type.usize, val)}); + return writer.print("){x}, {0x}}}", .{try dg.fmtIntLiteral(Type.usize, val, .Other)}); } else { try writer.writeAll("(("); try dg.renderType(writer, ty); - return writer.print("){x})", .{try dg.fmtIntLiteral(Type.usize, val)}); + return writer.print("){x})", .{try dg.fmtIntLiteral(Type.usize, val, .Other)}); }, .Optional => { var opt_buf: Type.Payload.ElemType = undefined; @@ -831,7 +835,7 @@ pub const DeclGen = struct { return writer.writeByte('}'); }, - .Packed => return writer.print("{x}", .{try dg.fmtIntLiteral(ty, Value.undef)}), + .Packed => return writer.print("{x}", .{try dg.fmtIntLiteral(ty, Value.undef, .Other)}), }, .Union => { if (!location.isInitializer()) { @@ -854,7 +858,7 @@ pub const DeclGen = struct { if (!field.ty.hasRuntimeBits()) continue; try dg.renderValue(writer, field.ty, val, initializer_type); break; - } else try writer.print("{x}", .{try dg.fmtIntLiteral(Type.u8, Value.undef)}); + } else try writer.print("{x}", .{try dg.fmtIntLiteral(Type.u8, Value.undef, .Other)}); if (ty.unionTagTypeSafety()) |_| try writer.writeByte('}'); return writer.writeByte('}'); }, @@ -868,7 +872,7 @@ pub const DeclGen = struct { try writer.writeAll("{ .payload = "); try dg.renderValue(writer, ty.errorUnionPayload(), val, initializer_type); return writer.print(", .error = {x} }}", .{ - try dg.fmtIntLiteral(ty.errorUnionSet(), val), + try dg.fmtIntLiteral(ty.errorUnionSet(), val, .Other), }); }, .Array, .Vector => { @@ -927,7 +931,7 @@ pub const DeclGen = struct { .decl_ref_mut, .decl_ref, => try dg.renderParentPtr(writer, val, ty, location), - else => try writer.print("{}", .{try dg.fmtIntLiteralLoc(ty, val, location)}), + else => try writer.print("{}", .{try dg.fmtIntLiteral(ty, val, location)}), }, .Float => { const bits = ty.floatBits(target); @@ -1020,7 +1024,7 @@ pub const DeclGen = struct { try writer.writeAll(", "); empty = false; } - try writer.print("{x}", .{try dg.fmtIntLiteralLoc(int_ty, int_val, location)}); + try writer.print("{x}", .{try dg.fmtIntLiteral(int_ty, int_val, location)}); if (!empty) try writer.writeByte(')'); return; }, @@ -1069,7 +1073,7 @@ pub const DeclGen = struct { .int_u64, .one => { try writer.writeAll("(("); try dg.renderType(writer, ty); - return writer.print("){x})", .{try dg.fmtIntLiteral(Type.usize, val)}); + return writer.print("){x})", .{try dg.fmtIntLiteral(Type.usize, val, .Other)}); }, .field_ptr, .elem_ptr, @@ -1889,11 +1893,11 @@ pub const DeclGen = struct { const int_info = ty.intInfo(target); if (int_info.signedness == .signed) { const min_val = try ty.minInt(stack.get(), target); - try writer.print(", {x}", .{try dg.fmtIntLiteral(ty, min_val)}); + try writer.print(", {x}", .{try dg.fmtIntLiteral(ty, min_val, .Other)}); } const max_val = try ty.maxInt(stack.get(), target); - try writer.print(", {x}", .{try dg.fmtIntLiteral(ty, max_val)}); + try writer.print(", {x}", .{try dg.fmtIntLiteral(ty, max_val, .Other)}); }, .Bits => { var bits_pl = Value.Payload.U64{ @@ -1901,7 +1905,7 @@ pub const DeclGen = struct { .data = ty.bitSize(target), }; const bits_val = Value.initPayload(&bits_pl.base); - try writer.print(", {}", .{try dg.fmtIntLiteral(Type.u8, bits_val)}); + try writer.print(", {}", .{try dg.fmtIntLiteral(Type.u8, bits_val, .Other)}); }, } } @@ -1910,30 +1914,21 @@ pub const DeclGen = struct { dg: *DeclGen, ty: Type, val: Value, + loc: ValueRenderLocation, ) !std.fmt.Formatter(formatIntLiteral) { - const int_info = ty.intInfo(dg.module.getTarget()); - const c_bits = toCIntBits(int_info.bits); - if (c_bits == null or c_bits.? > 128) - return dg.fail("TODO implement integer constants larger than 128 bits", .{}); + const kind: CType.Kind = switch (loc) { + .FunctionArgument => .parameter, + .Initializer, .Other => .complete, + .StaticInitializer => .global, + }; return std.fmt.Formatter(formatIntLiteral){ .data = .{ - .ty = ty, + .dg = dg, + .int_info = ty.intInfo(dg.module.getTarget()), + .kind = kind, + .cty = try dg.typeToCType(ty, kind), .val = val, - .mod = dg.module, } }; } - - fn fmtIntLiteralLoc( - dg: *DeclGen, - ty: Type, - val: Value, - location: ValueRenderLocation, // TODO: Instead add this as optional arg to fmtIntLiteral - ) !std.fmt.Formatter(formatIntLiteral) { - const int_info = ty.intInfo(dg.module.getTarget()); - const c_bits = toCIntBits(int_info.bits); - if (c_bits == null or c_bits.? > 128) - return dg.fail("TODO implement integer constants larger than 128 bits", .{}); - return std.fmt.Formatter(formatIntLiteral){ .data = .{ .ty = ty, .val = val, .mod = dg.module, .location = location } }; - } }; const CTypeFix = enum { prefix, suffix }; @@ -2450,7 +2445,7 @@ pub fn genErrDecls(o: *Object) !void { const len_val = Value.initPayload(&len_pl.base); try writer.print("{{" ++ name_prefix ++ "{}, {}}}", .{ - fmtIdent(name), try o.dg.fmtIntLiteral(Type.usize, len_val), + fmtIdent(name), try o.dg.fmtIntLiteral(Type.usize, len_val, .Other), }); } try writer.writeAll("};\n"); @@ -2501,7 +2496,10 @@ pub fn genLazyFn(o: *Object, lazy_fn: LazyFnMap.Entry) !void { var int_pl: Value.Payload.U64 = undefined; const int_val = tag_val.enumToInt(enum_ty, &int_pl); - var name_ty_pl = Type.Payload.Len{ .base = .{ .tag = .array_u8_sentinel_0 }, .data = name.len }; + var name_ty_pl = Type.Payload.Len{ + .base = .{ .tag = .array_u8_sentinel_0 }, + .data = name.len, + }; const name_ty = Type.initPayload(&name_ty_pl.base); var name_pl = Value.Payload.Bytes{ .base = .{ .tag = .bytes }, .data = name }; @@ -2510,14 +2508,16 @@ pub fn genLazyFn(o: *Object, lazy_fn: LazyFnMap.Entry) !void { var len_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, .data = name.len }; const len_val = Value.initPayload(&len_pl.base); - try w.print(" case {}: {{\n static ", .{try o.dg.fmtIntLiteral(enum_ty, int_val)}); + try w.print(" case {}: {{\n static ", .{ + try o.dg.fmtIntLiteral(enum_ty, int_val, .Other), + }); try o.dg.renderTypeAndName(w, name_ty, .{ .identifier = "name" }, Const, 0, .complete); try w.writeAll(" = "); try o.dg.renderValue(w, name_ty, name_val, .Initializer); try w.writeAll(";\n return ("); try o.dg.renderType(w, name_slice_ty); try w.print("){{{}, {}}};\n", .{ - fmtIdent("name"), try o.dg.fmtIntLiteral(Type.usize, len_val), + fmtIdent("name"), try o.dg.fmtIntLiteral(Type.usize, len_val, .Other), }); try w.writeAll(" }\n"); @@ -2535,7 +2535,12 @@ pub fn genLazyFn(o: *Object, lazy_fn: LazyFnMap.Entry) !void { const fwd_decl_writer = o.dg.fwd_decl.writer(); try fwd_decl_writer.print("static zig_{s} ", .{@tagName(key)}); - try o.dg.renderFunctionSignature(fwd_decl_writer, fn_decl_index, .forward, .{ .string = fn_name }); + try o.dg.renderFunctionSignature( + fwd_decl_writer, + fn_decl_index, + .forward, + .{ .string = fn_name }, + ); try fwd_decl_writer.writeAll(";\n"); try w.print("static zig_{s} ", .{@tagName(key)}); @@ -7177,30 +7182,33 @@ fn undefPattern(comptime IntType: type) IntType { return @bitCast(IntType, @as(UnsignedType, (1 << (int_info.bits | 1)) / 3)); } -const FormatIntLiteralContext = struct { ty: Type, val: Value, mod: *Module, location: ?ValueRenderLocation = null }; +const FormatIntLiteralContext = struct { + dg: *DeclGen, + int_info: std.builtin.Type.Int, + kind: CType.Kind, + cty: CType, + val: Value, +}; fn formatIntLiteral( data: FormatIntLiteralContext, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype, ) @TypeOf(writer).Error!void { - const target = data.mod.getTarget(); - const int_info = data.ty.intInfo(target); + const target = data.dg.module.getTarget(); const ExpectedContents = struct { const base = 10; - const limbs_count_128 = BigInt.calcTwosCompLimbCount(128); - const expected_needed_limbs_count = BigInt.calcToStringLimbsBufferLen(limbs_count_128, base); - const worst_case_int = BigInt.Const{ - .limbs = &([1]BigIntLimb{std.math.maxInt(BigIntLimb)} ** expected_needed_limbs_count), - .positive = false, - }; + const bits = 128; + const limbs_count = BigInt.calcTwosCompLimbCount(bits); - undef_limbs: [limbs_count_128]BigIntLimb, - wrap_limbs: [limbs_count_128]BigIntLimb, + undef_limbs: [limbs_count]BigIntLimb, + wrap_limbs: [limbs_count]BigIntLimb, + to_string_buf: [bits]u8, + to_string_limbs: [BigInt.calcToStringLimbsBufferLen(limbs_count, base)]BigIntLimb, }; var stack align(@alignOf(ExpectedContents)) = - std.heap.stackFallback(@sizeOf(ExpectedContents), data.mod.gpa); + std.heap.stackFallback(@sizeOf(ExpectedContents), data.dg.gpa); const allocator = stack.get(); var undef_limbs: []BigIntLimb = &.{}; @@ -7208,7 +7216,7 @@ fn formatIntLiteral( var int_buf: Value.BigIntSpace = undefined; const int = if (data.val.isUndefDeep()) blk: { - undef_limbs = try allocator.alloc(BigIntLimb, BigInt.calcTwosCompLimbCount(int_info.bits)); + undef_limbs = try allocator.alloc(BigIntLimb, BigInt.calcTwosCompLimbCount(data.int_info.bits)); std.mem.set(BigIntLimb, undef_limbs, undefPattern(BigIntLimb)); var undef_int = BigInt.Mutable{ @@ -7216,163 +7224,150 @@ fn formatIntLiteral( .len = undef_limbs.len, .positive = true, }; - undef_int.truncate(undef_int.toConst(), int_info.signedness, int_info.bits); + undef_int.truncate(undef_int.toConst(), data.int_info.signedness, data.int_info.bits); break :blk undef_int.toConst(); } else data.val.toBigInt(&int_buf, target); - assert(int.fitsInTwosComp(int_info.signedness, int_info.bits)); + assert(int.fitsInTwosComp(data.int_info.signedness, data.int_info.bits)); - const c_bits = toCIntBits(int_info.bits) orelse unreachable; + const c_bits = @intCast(usize, data.cty.byteSize(data.dg.ctypes.set, target) * 8); var one_limbs: [BigInt.calcLimbLen(1)]BigIntLimb = undefined; const one = BigInt.Mutable.init(&one_limbs, 1).toConst(); - const wrap_limbs = try allocator.alloc(BigIntLimb, BigInt.calcTwosCompLimbCount(c_bits)); - defer allocator.free(wrap_limbs); - var wrap = BigInt.Mutable{ .limbs = wrap_limbs, .len = undefined, .positive = undefined }; - if (wrap.addWrap(int, one, int_info.signedness, c_bits) or - int_info.signedness == .signed and wrap.subWrap(int, one, int_info.signedness, c_bits)) - { - const abbrev = switch (data.ty.tag()) { - .c_short, .c_ushort => "SHRT", - .c_int, .c_uint => "INT", - .c_long, .c_ulong => "LONG", - .c_longlong, .c_ulonglong => "LLONG", - .isize, .usize => "INTPTR", - else => return writer.print("zig_{s}Int_{c}{d}", .{ - if (int.positive) "max" else "min", signAbbrev(int_info.signedness), c_bits, + var wrap = BigInt.Mutable{ + .limbs = try allocator.alloc(BigIntLimb, BigInt.calcTwosCompLimbCount(c_bits)), + .len = undefined, + .positive = undefined, + }; + defer allocator.free(wrap.limbs); + if (wrap.addWrap(int, one, data.int_info.signedness, c_bits) or + data.int_info.signedness == .signed and wrap.subWrap(int, one, data.int_info.signedness, c_bits)) + return writer.print("{s}_{s}", .{ + data.cty.getStandardDefineAbbrev() orelse return writer.print("zig_{s}Int_{c}{d}", .{ + if (int.positive) "max" else "min", signAbbrev(data.int_info.signedness), c_bits, }), - }; - if (int_info.signedness == .unsigned) try writer.writeByte('U'); - return writer.print("{s}_{s}", .{ abbrev, if (int.positive) "MAX" else "MIN" }); - } + if (int.positive) "MAX" else "MIN", + }); - var use_twos_comp = false; - if (!int.positive) { - if (c_bits > 64) { - // TODO: Can this be done for decimal literals as well? - if (fmt.len == 1 and fmt[0] != 'd') { - use_twos_comp = true; - } else { - // TODO: Use fmtIntLiteral for 0? - try writer.print("zig_sub_{c}{d}(zig_make_{c}{d}(0, 0), ", .{ signAbbrev(int_info.signedness), c_bits, signAbbrev(int_info.signedness), c_bits }); - } - } else { - try writer.writeByte('-'); - } - } - - switch (data.ty.tag()) { - .c_short, .c_ushort, .c_int, .c_uint, .c_long, .c_ulong, .c_longlong, .c_ulonglong => {}, - else => { - if (int_info.bits <= 64) { - try writer.print("{s}INT{d}_C(", .{ switch (int_info.signedness) { - .signed => "", - .unsigned => "U", - }, c_bits }); - } else if (data.location != null and data.location.? == .StaticInitializer) { - // MSVC treats casting the struct initializer as not constant (C2099), so an alternate form is used in global initializers - try writer.print("zig_make_constant_{c}{d}(", .{ signAbbrev(int_info.signedness), c_bits }); - } else { - try writer.print("zig_make_{c}{d}(", .{ signAbbrev(int_info.signedness), c_bits }); - } + const c_limb_info: struct { + cty: CType, + count: usize, + endian: std.builtin.Endian, + homogeneous: bool, + } = switch (data.cty.tag()) { + else => .{ + .cty = CType.initTag(.void), + .count = 1, + .endian = .Little, + .homogeneous = true, }, - } + .zig_u128, .zig_i128 => .{ + .cty = CType.initTag(.uint64_t), + .count = 2, + .endian = .Big, + .homogeneous = false, + }, + .array => info: { + const array_data = data.cty.castTag(.array).?.data; + break :info .{ + .cty = data.dg.indexToCType(array_data.elem_type), + .count = @intCast(usize, array_data.len), + .endian = target.cpu.arch.endian(), + .homogeneous = true, + }; + }, + }; + if (c_limb_info.count == 1) { + if (!int.positive) try writer.writeByte('-'); + try data.cty.renderLiteralPrefix(writer, data.kind); - const limbs_count_64 = @divExact(64, @bitSizeOf(BigIntLimb)); - if (c_bits <= 64) { - var base: u8 = undefined; - var case: std.fmt.Case = undefined; - switch (fmt.len) { - 0 => base = 10, + const style: struct { base: u8, case: std.fmt.Case = undefined } = switch (fmt.len) { + 0 => .{ .base = 10 }, 1 => switch (fmt[0]) { - 'b' => { - base = 2; + 'b' => style: { try writer.writeAll("0b"); + break :style .{ .base = 2 }; }, - 'o' => { - base = 8; + 'o' => style: { try writer.writeByte('0'); + break :style .{ .base = 8 }; }, - 'd' => base = 10, - 'x' => { - base = 16; - case = .lower; - try writer.writeAll("0x"); - }, - 'X' => { - base = 16; - case = .upper; + 'd' => .{ .base = 10 }, + 'x', 'X' => |base| style: { try writer.writeAll("0x"); + break :style .{ .base = 16, .case = switch (base) { + 'x' => .lower, + 'X' => .upper, + else => unreachable, + } }; }, else => @compileError("Invalid fmt: " ++ fmt), }, else => @compileError("Invalid fmt: " ++ fmt), - } + }; - var str: [64]u8 = undefined; - var limbs_buf: [BigInt.calcToStringLimbsBufferLen(limbs_count_64, 10)]BigIntLimb = undefined; - try writer.writeAll(str[0..int.abs().toString(&str, base, case, &limbs_buf)]); + const string = try int.abs().toStringAlloc(allocator, style.base, style.case); + defer allocator.free(string); + try writer.writeAll(string); } else { - assert(c_bits == 128); - const split = std.math.min(int.limbs.len, limbs_count_64); - var twos_comp_limbs: [BigInt.calcTwosCompLimbCount(128)]BigIntLimb = undefined; + try data.cty.renderLiteralPrefix(writer, data.kind); + wrap.convertToTwosComplement(int, .unsigned, data.int_info.bits); + std.mem.set(BigIntLimb, wrap.limbs[wrap.len..], 0); + wrap.len = wrap.limbs.len; + const limbs_per_c_limb = @divExact(wrap.len, c_limb_info.count); - // Adding a negation in the C code before the doesn't work in all cases: - // - struct versions would require an extra zig_sub_ call to negate, which wouldn't work in constant expressions - // - negating the f80 int representation (i128) doesn't make sense - // Instead we write out the literal as a negative number in twos complement - var limbs = int.limbs; + var c_limb_int_info = std.builtin.Type.Int{ + .signedness = undefined, + .bits = @intCast(u16, @divExact(c_bits, c_limb_info.count)), + }; + var c_limb_cty: CType = undefined; - if (use_twos_comp) { - var twos_comp = BigInt.Mutable{ - .limbs = &twos_comp_limbs, - .positive = undefined, + var limb_offset: usize = 0; + const most_significant_limb_i = wrap.len - limbs_per_c_limb; + while (limb_offset < wrap.len) : (limb_offset += limbs_per_c_limb) { + const limb_i = switch (c_limb_info.endian) { + .Little => limb_offset, + .Big => most_significant_limb_i - limb_offset, + }; + var c_limb_mut = BigInt.Mutable{ + .limbs = wrap.limbs[limb_i..][0..limbs_per_c_limb], .len = undefined, + .positive = true, + }; + c_limb_mut.normalize(limbs_per_c_limb); + + if (limb_i == most_significant_limb_i and + !c_limb_info.homogeneous and data.int_info.signedness == .signed) + { + // most significant limb is actually signed + c_limb_int_info.signedness = .signed; + c_limb_cty = c_limb_info.cty.toSigned(); + + c_limb_mut.positive = wrap.positive; + c_limb_mut.convertToTwosComplement( + c_limb_mut.toConst(), + .signed, + data.int_info.bits - limb_i * @bitSizeOf(BigIntLimb), + ); + } else { + c_limb_int_info.signedness = .unsigned; + c_limb_cty = c_limb_info.cty; + } + var c_limb_val_pl = Value.Payload.BigInt{ + .base = .{ .tag = if (c_limb_mut.positive) .int_big_positive else .int_big_negative }, + .data = c_limb_mut.limbs[0..c_limb_mut.len], }; - twos_comp.convertToTwosComplement(int, .signed, int_info.bits); - limbs = twos_comp.limbs; + if (limb_offset > 0) try writer.writeAll(", "); + try formatIntLiteral(.{ + .dg = data.dg, + .int_info = c_limb_int_info, + .kind = data.kind, + .cty = c_limb_cty, + .val = Value.initPayload(&c_limb_val_pl.base), + }, fmt, options, writer); } - - var upper_pl = Value.Payload.BigInt{ - .base = .{ .tag = .int_big_positive }, - .data = limbs[split..], - }; - const upper_val = Value.initPayload(&upper_pl.base); - try formatIntLiteral(.{ - .ty = switch (int_info.signedness) { - .unsigned => Type.u64, - .signed => if (use_twos_comp) Type.u64 else Type.i64, - }, - .val = upper_val, - .mod = data.mod, - }, fmt, options, writer); - - try writer.writeAll(", "); - - var lower_pl = Value.Payload.BigInt{ - .base = .{ .tag = .int_big_positive }, - .data = limbs[0..split], - }; - const lower_val = Value.initPayload(&lower_pl.base); - try formatIntLiteral(.{ - .ty = Type.u64, - .val = lower_val, - .mod = data.mod, - }, fmt, options, writer); - - if (!int.positive and c_bits > 64 and !use_twos_comp) try writer.writeByte(')'); - return writer.writeByte(')'); - } - - switch (data.ty.tag()) { - .c_short, .c_ushort, .c_int => {}, - .c_uint => try writer.writeAll("u"), - .c_long => try writer.writeAll("l"), - .c_ulong => try writer.writeAll("ul"), - .c_longlong => try writer.writeAll("ll"), - .c_ulonglong => try writer.writeAll("ull"), - else => try writer.writeByte(')'), } + try data.cty.renderLiteralSuffix(writer); } fn isByRef(ty: Type) bool { diff --git a/src/codegen/c/type.zig b/src/codegen/c/type.zig index 1f1a220cd2..a1b11df315 100644 --- a/src/codegen/c/type.zig +++ b/src/codegen/c/type.zig @@ -496,6 +496,296 @@ pub const CType = extern union { } }; + pub fn toSigned(self: CType) CType { + return CType.initTag(switch (self.tag()) { + .char, .@"signed char", .@"unsigned char" => .@"signed char", + .short, .@"unsigned short" => .short, + .int, .@"unsigned int" => .int, + .long, .@"unsigned long" => .long, + .@"long long", .@"unsigned long long" => .@"long long", + .size_t, .ptrdiff_t => .ptrdiff_t, + .uint8_t, .int8_t => .int8_t, + .uint16_t, .int16_t => .int16_t, + .uint32_t, .int32_t => .int32_t, + .uint64_t, .int64_t => .int64_t, + .uintptr_t, .intptr_t => .intptr_t, + .zig_u128, .zig_i128 => .zig_i128, + .float, + .double, + .@"long double", + .zig_f16, + .zig_f32, + .zig_f80, + .zig_f128, + .zig_c_longdouble, + => |t| t, + else => unreachable, + }); + } + + pub fn toUnsigned(self: CType) CType { + return CType.initTag(switch (self.tag()) { + .char, .@"signed char", .@"unsigned char" => .@"unsigned char", + .short, .@"unsigned short" => .@"unsigned short", + .int, .@"unsigned int" => .@"unsigned int", + .long, .@"unsigned long" => .@"unsigned long", + .@"long long", .@"unsigned long long" => .@"unsigned long long", + .size_t, .ptrdiff_t => .size_t, + .uint8_t, .int8_t => .uint8_t, + .uint16_t, .int16_t => .uint16_t, + .uint32_t, .int32_t => .uint32_t, + .uint64_t, .int64_t => .uint64_t, + .uintptr_t, .intptr_t => .uintptr_t, + .zig_u128, .zig_i128 => .zig_u128, + else => unreachable, + }); + } + + pub fn getStandardDefineAbbrev(self: CType) ?[]const u8 { + return switch (self.tag()) { + .char => "CHAR", + .@"signed char" => "SCHAR", + .short => "SHRT", + .int => "INT", + .long => "LONG", + .@"long long" => "LLONG", + .@"unsigned char" => "UCHAR", + .@"unsigned short" => "USHRT", + .@"unsigned int" => "UINT", + .@"unsigned long" => "ULONG", + .@"unsigned long long" => "ULLONG", + .float => "FLT", + .double => "DBL", + .@"long double" => "LDBL", + .size_t => "SIZE", + .ptrdiff_t => "PTRDIFF", + .uint8_t => "UINT8", + .int8_t => "INT8", + .uint16_t => "UINT16", + .int16_t => "INT16", + .uint32_t => "UINT32", + .int32_t => "INT32", + .uint64_t => "UINT64", + .int64_t => "INT64", + .uintptr_t => "UINTPTR", + .intptr_t => "INTPTR", + else => null, + }; + } + + pub fn renderLiteralPrefix(self: CType, writer: anytype, kind: Kind) @TypeOf(writer).Error!void { + switch (self.tag()) { + .void => unreachable, + ._Bool, + .char, + .@"signed char", + .short, + .@"unsigned short", + .bool, + .size_t, + .ptrdiff_t, + .uintptr_t, + .intptr_t, + => |t| switch (kind) { + else => try writer.print("({s})", .{@tagName(t)}), + .global => {}, + }, + .int, + .long, + .@"long long", + .@"unsigned char", + .@"unsigned int", + .@"unsigned long", + .@"unsigned long long", + .float, + .double, + .@"long double", + => {}, + .uint8_t, + .int8_t, + .uint16_t, + .int16_t, + .uint32_t, + .int32_t, + .uint64_t, + .int64_t, + => try writer.print("{s}_C(", .{self.getStandardDefineAbbrev().?}), + .zig_u128, + .zig_i128, + .zig_f16, + .zig_f32, + .zig_f64, + .zig_f80, + .zig_f128, + .zig_c_longdouble, + => |t| try writer.print("zig_{s}_{s}(", .{ + switch (kind) { + else => "make", + .global => "init", + }, + @tagName(t)["zig_".len..], + }), + .pointer, + .pointer_const, + .pointer_volatile, + .pointer_const_volatile, + => unreachable, + .array, + .vector, + => try writer.writeByte('{'), + .fwd_anon_struct, + .fwd_anon_union, + .fwd_struct, + .fwd_union, + .unnamed_struct, + .unnamed_union, + .packed_unnamed_struct, + .packed_unnamed_union, + .anon_struct, + .anon_union, + .@"struct", + .@"union", + .packed_struct, + .packed_union, + .function, + .varargs_function, + => unreachable, + } + } + + pub fn renderLiteralSuffix(self: CType, writer: anytype) @TypeOf(writer).Error!void { + switch (self.tag()) { + .void => unreachable, + ._Bool => {}, + .char, + .@"signed char", + .short, + .int, + => {}, + .long => try writer.writeByte('l'), + .@"long long" => try writer.writeAll("ll"), + .@"unsigned char", + .@"unsigned short", + .@"unsigned int", + => try writer.writeByte('u'), + .@"unsigned long", + .size_t, + .uintptr_t, + => try writer.writeAll("ul"), + .@"unsigned long long" => try writer.writeAll("ull"), + .float => try writer.writeByte('f'), + .double => {}, + .@"long double" => try writer.writeByte('l'), + .bool, + .ptrdiff_t, + .intptr_t, + => {}, + .uint8_t, + .int8_t, + .uint16_t, + .int16_t, + .uint32_t, + .int32_t, + .uint64_t, + .int64_t, + .zig_u128, + .zig_i128, + .zig_f16, + .zig_f32, + .zig_f64, + .zig_f80, + .zig_f128, + .zig_c_longdouble, + => try writer.writeByte(')'), + .pointer, + .pointer_const, + .pointer_volatile, + .pointer_const_volatile, + => unreachable, + .array, + .vector, + => try writer.writeByte('}'), + .fwd_anon_struct, + .fwd_anon_union, + .fwd_struct, + .fwd_union, + .unnamed_struct, + .unnamed_union, + .packed_unnamed_struct, + .packed_unnamed_union, + .anon_struct, + .anon_union, + .@"struct", + .@"union", + .packed_struct, + .packed_union, + .function, + .varargs_function, + => unreachable, + } + } + + pub fn byteSize(self: CType, store: Store.Set, target: Target) u64 { + return switch (self.tag()) { + .void => 0, + .char, .@"signed char", ._Bool, .@"unsigned char", .bool, .uint8_t, .int8_t => 1, + .short => target.c_type_byte_size(.short), + .int => target.c_type_byte_size(.int), + .long => target.c_type_byte_size(.long), + .@"long long" => target.c_type_byte_size(.longlong), + .@"unsigned short" => target.c_type_byte_size(.ushort), + .@"unsigned int" => target.c_type_byte_size(.uint), + .@"unsigned long" => target.c_type_byte_size(.ulong), + .@"unsigned long long" => target.c_type_byte_size(.ulonglong), + .float => target.c_type_byte_size(.float), + .double => target.c_type_byte_size(.double), + .@"long double" => target.c_type_byte_size(.longdouble), + .size_t, + .ptrdiff_t, + .uintptr_t, + .intptr_t, + .pointer, + .pointer_const, + .pointer_volatile, + .pointer_const_volatile, + => @divExact(target.cpu.arch.ptrBitWidth(), 8), + .uint16_t, .int16_t, .zig_f16 => 2, + .uint32_t, .int32_t, .zig_f32 => 4, + .uint64_t, .int64_t, .zig_f64 => 8, + .zig_u128, .zig_i128, .zig_f128 => 16, + .zig_f80 => if (target.c_type_bit_size(.longdouble) == 80) + target.c_type_byte_size(.longdouble) + else + 16, + .zig_c_longdouble => target.c_type_byte_size(.longdouble), + + .array, + .vector, + => { + const data = self.cast(Payload.Sequence).?.data; + return data.len * store.indexToCType(data.elem_type).byteSize(store, target); + }, + + .fwd_anon_struct, + .fwd_anon_union, + .fwd_struct, + .fwd_union, + .unnamed_struct, + .unnamed_union, + .packed_unnamed_struct, + .packed_unnamed_union, + .anon_struct, + .anon_union, + .@"struct", + .@"union", + .packed_struct, + .packed_union, + .function, + .varargs_function, + => unreachable, + }; + } + pub fn isPacked(self: CType) bool { return switch (self.tag()) { else => false, @@ -787,26 +1077,26 @@ pub const CType = extern union { }; } - fn tagFromIntInfo(signedness: std.builtin.Signedness, bits: u16) Tag { - return switch (bits) { + fn tagFromIntInfo(int_info: std.builtin.Type.Int) Tag { + return switch (int_info.bits) { 0 => .void, - 1...8 => switch (signedness) { + 1...8 => switch (int_info.signedness) { .unsigned => .uint8_t, .signed => .int8_t, }, - 9...16 => switch (signedness) { + 9...16 => switch (int_info.signedness) { .unsigned => .uint16_t, .signed => .int16_t, }, - 17...32 => switch (signedness) { + 17...32 => switch (int_info.signedness) { .unsigned => .uint32_t, .signed => .int32_t, }, - 33...64 => switch (signedness) { + 33...64 => switch (int_info.signedness) { .unsigned => .uint64_t, .signed => .int64_t, }, - 65...128 => switch (signedness) { + 65...128 => switch (int_info.signedness) { .unsigned => .zig_u128, .signed => .zig_i128, }, @@ -945,31 +1235,27 @@ pub const CType = extern union { .c_ulong => self.init(.@"unsigned long"), .c_longlong => self.init(.@"long long"), .c_ulonglong => self.init(.@"unsigned long long"), - else => { - const info = ty.intInfo(target); - const t = tagFromIntInfo(info.signedness, info.bits); - switch (t) { - .void => unreachable, - else => self.init(t), - .array => switch (kind) { - .forward, .complete, .global => { - const abi_size = ty.abiSize(target); - const abi_align = ty.abiAlignment(target); - self.storage = .{ .seq = .{ .base = .{ .tag = .array }, .data = .{ - .len = @divExact(abi_size, abi_align), - .elem_type = tagFromIntInfo( - .unsigned, - @intCast(u16, abi_align * 8), - ).toIndex(), - } } }; - self.value = .{ .cty = initPayload(&self.storage.seq) }; - }, - .forward_parameter, - .parameter, - => try self.initArrayParameter(ty, kind, lookup), - .payload => unreachable, + else => switch (tagFromIntInfo(ty.intInfo(target))) { + .void => unreachable, + else => |t| self.init(t), + .array => switch (kind) { + .forward, .complete, .global => { + const abi_size = ty.abiSize(target); + const abi_align = ty.abiAlignment(target); + self.storage = .{ .seq = .{ .base = .{ .tag = .array }, .data = .{ + .len = @divExact(abi_size, abi_align), + .elem_type = tagFromIntInfo(.{ + .signedness = .unsigned, + .bits = @intCast(u16, abi_align * 8), + }).toIndex(), + } } }; + self.value = .{ .cty = initPayload(&self.storage.seq) }; }, - } + .forward_parameter, + .parameter, + => try self.initArrayParameter(ty, kind, lookup), + .payload => unreachable, + }, }, } else switch (ty.zigTypeTag()) { .Frame => unreachable, diff --git a/test/behavior/bitcast.zig b/test/behavior/bitcast.zig index f8a1928dd1..70ac38d6fa 100644 --- a/test/behavior/bitcast.zig +++ b/test/behavior/bitcast.zig @@ -368,7 +368,6 @@ test "comptime @bitCast packed struct to int and back" { } test "comptime bitcast with fields following f80" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; From a8f4ac2b94e7945a5a1623547f258f5f32f12674 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Fri, 3 Mar 2023 00:18:35 -0500 Subject: [PATCH 020/294] CBE: implement big integer and vector comparisons --- lib/zig.h | 313 +++++++++++++++++++++++++++------- src/codegen/c.zig | 342 ++++++++++++++++++++++++-------------- src/codegen/c/type.zig | 124 ++++++++++++++ src/type.zig | 2 +- test/behavior/bitcast.zig | 2 - test/behavior/math.zig | 1 - test/behavior/vector.zig | 2 - 7 files changed, 595 insertions(+), 191 deletions(-) diff --git a/lib/zig.h b/lib/zig.h index 7353ea935d..c39cffee24 100644 --- a/lib/zig.h +++ b/lib/zig.h @@ -37,6 +37,14 @@ typedef char bool; #define zig_has_attribute(attribute) 0 #endif +#if __LITTLE_ENDIAN__ || _MSC_VER +#define zig_little_endian 1 +#define zig_big_endian 0 +#else +#define zig_little_endian 0 +#define zig_big_endian 1 +#endif + #if __STDC_VERSION__ >= 201112L #define zig_threadlocal _Thread_local #elif defined(__GNUC__) @@ -1379,7 +1387,7 @@ typedef signed __int128 zig_i128; #else /* zig_has_int128 */ -#if __LITTLE_ENDIAN__ || _MSC_VER +#if zig_little_endian typedef struct { zig_align(16) uint64_t lo; uint64_t hi; } zig_u128; typedef struct { zig_align(16) uint64_t lo; int64_t hi; } zig_i128; #else @@ -1909,6 +1917,177 @@ static inline zig_i128 zig_bit_reverse_i128(zig_i128 val, uint8_t bits) { return zig_bitcast_i128(zig_bit_reverse_u128(zig_bitcast_u128(val), bits)); } +/* ========================== Big Integer Support =========================== */ + +static inline uint16_t zig_big_bytes(uint16_t bits) { + uint16_t bytes = (bits + CHAR_BIT - 1) / CHAR_BIT; + uint16_t alignment = 16; + while (alignment / 2 >= bytes) alignment /= 2; + return (bytes + alignment - 1) / alignment * alignment; +} + +static inline int32_t zig_cmp_big(const void *lhs, const void *rhs, bool is_signed, uint16_t bits) { + const uint8_t *lhs_bytes = lhs; + const uint8_t *rhs_bytes = rhs; + uint16_t byte_offset = 0; + bool do_signed = is_signed; + uint16_t remaining_bytes = zig_big_bytes(bits); + +#if zig_little_endian + byte_offset = remaining_bytes; +#endif + + while (remaining_bytes >= 128 / CHAR_BIT) { + int32_t limb_cmp; + +#if zig_little_endian + byte_offset -= 128 / CHAR_BIT; +#endif + + if (do_signed) { + zig_i128 lhs_limb; + zig_i128 rhs_limb; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + limb_cmp = zig_cmp_i128(lhs_limb, rhs_limb); + do_signed = false; + } else { + zig_u128 lhs_limb; + zig_u128 rhs_limb; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + limb_cmp = zig_cmp_u128(lhs_limb, rhs_limb); + } + + if (limb_cmp != 0) return limb_cmp; + remaining_bytes -= 128 / CHAR_BIT; + +#if zig_big_endian + byte_offset -= 128 / CHAR_BIT; +#endif + } + + while (remaining_bytes >= 64 / CHAR_BIT) { +#if zig_little_endian + byte_offset -= 64 / CHAR_BIT; +#endif + + if (do_signed) { + int64_t lhs_limb; + int64_t rhs_limb; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + if (lhs_limb != rhs_limb) return (lhs_limb > rhs_limb) - (lhs_limb < rhs_limb); + do_signed = false; + } else { + uint64_t lhs_limb; + uint64_t rhs_limb; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + if (lhs_limb != rhs_limb) return (lhs_limb > rhs_limb) - (lhs_limb < rhs_limb); + } + + remaining_bytes -= 64 / CHAR_BIT; + +#if zig_big_endian + byte_offset -= 64 / CHAR_BIT; +#endif + } + + while (remaining_bytes >= 32 / CHAR_BIT) { +#if zig_little_endian + byte_offset -= 32 / CHAR_BIT; +#endif + + if (do_signed) { + int32_t lhs_limb; + int32_t rhs_limb; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + if (lhs_limb != rhs_limb) return (lhs_limb > rhs_limb) - (lhs_limb < rhs_limb); + do_signed = false; + } else { + uint32_t lhs_limb; + uint32_t rhs_limb; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + if (lhs_limb != rhs_limb) return (lhs_limb > rhs_limb) - (lhs_limb < rhs_limb); + } + + remaining_bytes -= 32 / CHAR_BIT; + +#if zig_big_endian + byte_offset -= 32 / CHAR_BIT; +#endif + } + + while (remaining_bytes >= 16 / CHAR_BIT) { +#if zig_little_endian + byte_offset -= 16 / CHAR_BIT; +#endif + + if (do_signed) { + int16_t lhs_limb; + int16_t rhs_limb; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + if (lhs_limb != rhs_limb) return (lhs_limb > rhs_limb) - (lhs_limb < rhs_limb); + do_signed = false; + } else { + uint16_t lhs_limb; + uint16_t rhs_limb; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + if (lhs_limb != rhs_limb) return (lhs_limb > rhs_limb) - (lhs_limb < rhs_limb); + } + + remaining_bytes -= 16 / CHAR_BIT; + +#if zig_big_endian + byte_offset -= 16 / CHAR_BIT; +#endif + } + + while (remaining_bytes >= 8 / CHAR_BIT) { +#if zig_little_endian + byte_offset -= 8 / CHAR_BIT; +#endif + + if (do_signed) { + int8_t lhs_limb; + int8_t rhs_limb; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + if (lhs_limb != rhs_limb) return (lhs_limb > rhs_limb) - (lhs_limb < rhs_limb); + do_signed = false; + } else { + uint8_t lhs_limb; + uint8_t rhs_limb; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + if (lhs_limb != rhs_limb) return (lhs_limb > rhs_limb) - (lhs_limb < rhs_limb); + } + + remaining_bytes -= 8 / CHAR_BIT; + +#if zig_big_endian + byte_offset -= 8 / CHAR_BIT; +#endif + } + + return 0; +} + /* ========================= Floating Point Support ========================= */ #if _MSC_VER @@ -1933,7 +2112,6 @@ static inline zig_i128 zig_bit_reverse_i128(zig_i128 val, uint8_t bits) { #define zig_make_special_f64(sign, name, arg, repr) sign zig_make_f64(__builtin_##name, )(arg) #define zig_make_special_f80(sign, name, arg, repr) sign zig_make_f80(__builtin_##name, )(arg) #define zig_make_special_f128(sign, name, arg, repr) sign zig_make_f128(__builtin_##name, )(arg) -#define zig_make_special_c_longdouble(sign, name, arg, repr) sign zig_make_c_longdouble(__builtin_##name, )(arg) #else #define zig_has_float_builtins 0 #define zig_make_special_f16(sign, name, arg, repr) zig_float_from_repr_f16(repr) @@ -1941,13 +2119,13 @@ static inline zig_i128 zig_bit_reverse_i128(zig_i128 val, uint8_t bits) { #define zig_make_special_f64(sign, name, arg, repr) zig_float_from_repr_f64(repr) #define zig_make_special_f80(sign, name, arg, repr) zig_float_from_repr_f80(repr) #define zig_make_special_f128(sign, name, arg, repr) zig_float_from_repr_f128(repr) -#define zig_make_special_c_longdouble(sign, name, arg, repr) zig_float_from_repr_c_longdouble(repr) #endif #define zig_has_f16 1 #define zig_bitSizeOf_f16 16 +typedef int16_t zig_repr_f16; #define zig_libc_name_f16(name) __##name##h -#define zig_make_special_constant_f16(sign, name, arg, repr) zig_make_special_f16(sign, name, arg, repr) +#define zig_init_special_f16(sign, name, arg, repr) zig_make_special_f16(sign, name, arg, repr) #if FLT_MANT_DIG == 11 typedef float zig_f16; #define zig_make_f16(fp, repr) fp##f @@ -1956,7 +2134,9 @@ typedef double zig_f16; #define zig_make_f16(fp, repr) fp #elif LDBL_MANT_DIG == 11 #define zig_bitSizeOf_c_longdouble 16 -typedef uint16_t zig_repr_c_longdouble; +#ifndef ZIG_TARGET_ABI_MSVC +typedef zig_repr_f16 zig_repr_c_longdouble; +#endif typedef long double zig_f16; #define zig_make_f16(fp, repr) fp##l #elif FLT16_MANT_DIG == 11 && (zig_has_builtin(inff16) || defined(zig_gnuc)) @@ -1973,17 +2153,18 @@ typedef int16_t zig_f16; #define zig_make_f16(fp, repr) repr #undef zig_make_special_f16 #define zig_make_special_f16(sign, name, arg, repr) repr -#undef zig_make_special_constant_f16 -#define zig_make_special_constant_f16(sign, name, arg, repr) repr +#undef zig_init_special_f16 +#define zig_init_special_f16(sign, name, arg, repr) repr #endif #define zig_has_f32 1 #define zig_bitSizeOf_f32 32 +typedef int32_t zig_repr_f32; #define zig_libc_name_f32(name) name##f #if _MSC_VER -#define zig_make_special_constant_f32(sign, name, arg, repr) sign zig_make_f32(zig_msvc_flt_##name, ) +#define zig_init_special_f32(sign, name, arg, repr) sign zig_make_f32(zig_msvc_flt_##name, ) #else -#define zig_make_special_constant_f32(sign, name, arg, repr) zig_make_special_f32(sign, name, arg, repr) +#define zig_init_special_f32(sign, name, arg, repr) zig_make_special_f32(sign, name, arg, repr) #endif #if FLT_MANT_DIG == 24 typedef float zig_f32; @@ -1993,7 +2174,9 @@ typedef double zig_f32; #define zig_make_f32(fp, repr) fp #elif LDBL_MANT_DIG == 24 #define zig_bitSizeOf_c_longdouble 32 -typedef uint32_t zig_repr_c_longdouble; +#ifndef ZIG_TARGET_ABI_MSVC +typedef zig_repr_f32 zig_repr_c_longdouble; +#endif typedef long double zig_f32; #define zig_make_f32(fp, repr) fp##l #elif FLT32_MANT_DIG == 24 @@ -2007,21 +2190,24 @@ typedef int32_t zig_f32; #define zig_make_f32(fp, repr) repr #undef zig_make_special_f32 #define zig_make_special_f32(sign, name, arg, repr) repr -#undef zig_make_special_constant_f32 -#define zig_make_special_constant_f32(sign, name, arg, repr) repr +#undef zig_init_special_f32 +#define zig_init_special_f32(sign, name, arg, repr) repr #endif #define zig_has_f64 1 #define zig_bitSizeOf_f64 64 +typedef int64_t zig_repr_f64; #define zig_libc_name_f64(name) name #if _MSC_VER #ifdef ZIG_TARGET_ABI_MSVC #define zig_bitSizeOf_c_longdouble 64 -typedef uint64_t zig_repr_c_longdouble; +#ifndef ZIG_TARGET_ABI_MSVC +typedef zig_repr_f64 zig_repr_c_longdouble; #endif -#define zig_make_special_constant_f64(sign, name, arg, repr) sign zig_make_f64(zig_msvc_flt_##name, ) +#endif +#define zig_init_special_f64(sign, name, arg, repr) sign zig_make_f64(zig_msvc_flt_##name, ) #else /* _MSC_VER */ -#define zig_make_special_constant_f64(sign, name, arg, repr) zig_make_special_f64(sign, name, arg, repr) +#define zig_init_special_f64(sign, name, arg, repr) zig_make_special_f64(sign, name, arg, repr) #endif /* _MSC_VER */ #if FLT_MANT_DIG == 53 typedef float zig_f64; @@ -2031,7 +2217,9 @@ typedef double zig_f64; #define zig_make_f64(fp, repr) fp #elif LDBL_MANT_DIG == 53 #define zig_bitSizeOf_c_longdouble 64 -typedef uint64_t zig_repr_c_longdouble; +#ifndef ZIG_TARGET_ABI_MSVC +typedef zig_repr_f64 zig_repr_c_longdouble; +#endif typedef long double zig_f64; #define zig_make_f64(fp, repr) fp##l #elif FLT64_MANT_DIG == 53 @@ -2048,14 +2236,15 @@ typedef int64_t zig_f64; #define zig_make_f64(fp, repr) repr #undef zig_make_special_f64 #define zig_make_special_f64(sign, name, arg, repr) repr -#undef zig_make_special_constant_f64 -#define zig_make_special_constant_f64(sign, name, arg, repr) repr +#undef zig_init_special_f64 +#define zig_init_special_f64(sign, name, arg, repr) repr #endif #define zig_has_f80 1 #define zig_bitSizeOf_f80 80 +typedef zig_i128 zig_repr_f80; #define zig_libc_name_f80(name) __##name##x -#define zig_make_special_constant_f80(sign, name, arg, repr) zig_make_special_f80(sign, name, arg, repr) +#define zig_init_special_f80(sign, name, arg, repr) zig_make_special_f80(sign, name, arg, repr) #if FLT_MANT_DIG == 64 typedef float zig_f80; #define zig_make_f80(fp, repr) fp##f @@ -2064,7 +2253,9 @@ typedef double zig_f80; #define zig_make_f80(fp, repr) fp #elif LDBL_MANT_DIG == 64 #define zig_bitSizeOf_c_longdouble 80 -typedef zig_u128 zig_repr_c_longdouble; +#ifndef ZIG_TARGET_ABI_MSVC +typedef zig_repr_f80 zig_repr_c_longdouble; +#endif typedef long double zig_f80; #define zig_make_f80(fp, repr) fp##l #elif FLT80_MANT_DIG == 64 @@ -2084,14 +2275,15 @@ typedef zig_i128 zig_f80; #define zig_make_f80(fp, repr) repr #undef zig_make_special_f80 #define zig_make_special_f80(sign, name, arg, repr) repr -#undef zig_make_special_constant_f80 -#define zig_make_special_constant_f80(sign, name, arg, repr) repr +#undef zig_init_special_f80 +#define zig_init_special_f80(sign, name, arg, repr) repr #endif #define zig_has_f128 1 #define zig_bitSizeOf_f128 128 +typedef zig_i128 zig_repr_f128; #define zig_libc_name_f128(name) name##q -#define zig_make_special_constant_f128(sign, name, arg, repr) zig_make_special_f128(sign, name, arg, repr) +#define zig_init_special_f128(sign, name, arg, repr) zig_make_special_f128(sign, name, arg, repr) #if FLT_MANT_DIG == 113 typedef float zig_f128; #define zig_make_f128(fp, repr) fp##f @@ -2100,7 +2292,9 @@ typedef double zig_f128; #define zig_make_f128(fp, repr) fp #elif LDBL_MANT_DIG == 113 #define zig_bitSizeOf_c_longdouble 128 -typedef zig_u128 zig_repr_c_longdouble; +#ifndef ZIG_TARGET_ABI_MSVC +typedef zig_repr_f128 zig_repr_c_longdouble; +#endif typedef long double zig_f128; #define zig_make_f128(fp, repr) fp##l #elif FLT128_MANT_DIG == 113 @@ -2122,63 +2316,44 @@ typedef zig_i128 zig_f128; #define zig_make_f128(fp, repr) repr #undef zig_make_special_f128 #define zig_make_special_f128(sign, name, arg, repr) repr -#undef zig_make_special_constant_f128 -#define zig_make_special_constant_f128(sign, name, arg, repr) repr +#undef zig_init_special_f128 +#define zig_init_special_f128(sign, name, arg, repr) repr #endif -#define zig_has_c_longdouble 1 - -#ifdef ZIG_TARGET_ABI_MSVC -#define zig_libc_name_c_longdouble(name) name -#else -#define zig_libc_name_c_longdouble(name) name##l -#endif - -#define zig_make_special_constant_c_longdouble(sign, name, arg, repr) zig_make_special_c_longdouble(sign, name, arg, repr) #ifdef zig_bitSizeOf_c_longdouble +#define zig_has_c_longdouble 1 #ifdef ZIG_TARGET_ABI_MSVC #undef zig_bitSizeOf_c_longdouble #define zig_bitSizeOf_c_longdouble 64 -typedef uint64_t zig_repr_c_longdouble; typedef zig_f64 zig_c_longdouble; -#define zig_make_c_longdouble(fp, repr) fp +typedef zig_repr_f64 zig_repr_c_longdouble; #else typedef long double zig_c_longdouble; -#define zig_make_c_longdouble(fp, repr) fp##l #endif #else /* zig_bitSizeOf_c_longdouble */ -#undef zig_has_c_longdouble #define zig_has_c_longdouble 0 -#define zig_bitSizeOf_c_longdouble 80 -typedef zig_u128 zig_repr_c_longdouble; -#define zig_compiler_rt_abbrev_c_longdouble zig_compiler_rt_abbrev_f80 #define zig_bitSizeOf_repr_c_longdouble 128 -typedef zig_i128 zig_c_longdouble; -#define zig_make_c_longdouble(fp, repr) repr -#undef zig_make_special_c_longdouble -#define zig_make_special_c_longdouble(sign, name, arg, repr) repr -#undef zig_make_special_constant_c_longdouble -#define zig_make_special_constant_c_longdouble(sign, name, arg, repr) repr +typedef zig_f128 zig_c_longdouble; +typedef zig_repr_f128 zig_repr_c_longdouble; #endif /* zig_bitSizeOf_c_longdouble */ #if !zig_has_float_builtins -#define zig_float_from_repr(Type, ReprType) \ - static inline zig_##Type zig_float_from_repr_##Type(ReprType repr) { \ +#define zig_float_from_repr(Type) \ + static inline zig_##Type zig_float_from_repr_##Type(zig_repr_##Type repr) { \ zig_##Type result; \ memcpy(&result, &repr, sizeof(result)); \ return result; \ } -zig_float_from_repr(f16, uint16_t) -zig_float_from_repr(f32, uint32_t) -zig_float_from_repr(f64, uint64_t) -zig_float_from_repr(f80, zig_u128) -zig_float_from_repr(f128, zig_u128) -zig_float_from_repr(c_longdouble, zig_repr_c_longdouble) +zig_float_from_repr(f16) +zig_float_from_repr(f32) +zig_float_from_repr(f64) +zig_float_from_repr(f80) +zig_float_from_repr(f128) #endif #define zig_cast_f16 (zig_f16) @@ -2187,11 +2362,9 @@ zig_float_from_repr(c_longdouble, zig_repr_c_longdouble) #if _MSC_VER && !zig_has_f128 #define zig_cast_f80 -#define zig_cast_c_longdouble #define zig_cast_f128 #else #define zig_cast_f80 (zig_f80) -#define zig_cast_c_longdouble (zig_c_longdouble) #define zig_cast_f128 (zig_f128) #endif @@ -2320,7 +2493,6 @@ zig_float_builtins(f32) zig_float_builtins(f64) zig_float_builtins(f80) zig_float_builtins(f128) -zig_float_builtins(c_longdouble) #if _MSC_VER && (_M_IX86 || _M_X64) @@ -2563,6 +2735,29 @@ zig_msvc_atomics_128op(u128, max) #endif /* _MSC_VER && (_M_IX86 || _M_X64) */ +/* ============================= Vector Support ============================= */ + +#define zig_cmp_vec(operation, operator) \ + static inline void zig_##operation##_vec(bool *result, const void *lhs, const void *rhs, uint32_t len, bool is_signed, uint16_t elem_bits) { \ + uint32_t index = 0; \ + const uint8_t *lhs_ptr = lhs; \ + const uint8_t *rhs_ptr = rhs; \ + uint16_t elem_bytes = zig_big_bytes(elem_bits); \ + \ + while (index < len) { \ + result[index] = zig_cmp_big(lhs_ptr, rhs_ptr, is_signed, elem_bits) operator 0; \ + lhs_ptr += elem_bytes; \ + rhs_ptr += elem_bytes; \ + index += 1; \ + } \ + } +zig_cmp_vec(eq, ==) +zig_cmp_vec(ne, !=) +zig_cmp_vec(lt, < ) +zig_cmp_vec(le, <=) +zig_cmp_vec(gt, > ) +zig_cmp_vec(ge, >=) + /* ======================== Special Case Intrinsics ========================= */ #if (_MSC_VER && _M_X64) || defined(__x86_64__) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index addd3c8332..f4a817cecd 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -112,11 +112,7 @@ const ValueRenderLocation = enum { } }; -const BuiltinInfo = enum { - None, - Range, - Bits, -}; +const BuiltinInfo = enum { none, bits }; const reserved_idents = std.ComptimeStringMap(void, .{ // C language @@ -440,6 +436,10 @@ pub const Function = struct { return f.object.dg.typeToCType(ty, kind); } + fn byteSize(f: *Function, cty: CType) u64 { + return f.object.dg.byteSize(cty); + } + fn renderType(f: *Function, w: anytype, t: Type) !void { return f.object.dg.renderType(w, t); } @@ -1003,8 +1003,9 @@ pub const DeclGen = struct { // return dg.fail("Only quiet nans are supported in global variable initializers", .{}); } - try writer.writeAll("zig_make_special_"); - if (location == .StaticInitializer) try writer.writeAll("constant_"); + try writer.writeAll("zig_"); + try writer.writeAll(if (location == .StaticInitializer) "init" else "make"); + try writer.writeAll("_special_"); try dg.renderTypeForBuiltinFnName(writer, ty); try writer.writeByte('('); if (std.math.signbit(f128_val)) try writer.writeByte('-'); @@ -1565,6 +1566,10 @@ pub const DeclGen = struct { return dg.ctypes.typeToCType(dg.gpa, ty, dg.module, kind); } + fn byteSize(dg: *DeclGen, cty: CType) u64 { + return cty.byteSize(dg.ctypes.set, dg.module.getTarget()); + } + /// Renders a type as a single identifier, generating intermediate typedefs /// if necessary. /// @@ -1861,51 +1866,64 @@ pub const DeclGen = struct { } fn renderTypeForBuiltinFnName(dg: *DeclGen, writer: anytype, ty: Type) !void { - const target = dg.module.getTarget(); - if (ty.isAbiInt()) { - const int_info = ty.intInfo(target); - const c_bits = toCIntBits(int_info.bits) orelse - return dg.fail("TODO: C backend: implement integer types larger than 128 bits", .{}); - try writer.print("{c}{d}", .{ signAbbrev(int_info.signedness), c_bits }); - } else if (ty.isRuntimeFloat()) { - try ty.print(writer, dg.module); - } else if (ty.isPtrAtRuntime()) { - try writer.print("p{d}", .{ty.bitSize(target)}); - } else if (ty.zigTypeTag() == .Bool) { - try writer.print("u8", .{}); - } else return dg.fail("TODO: CBE: implement renderTypeForBuiltinFnName for type {}", .{ - ty.fmt(dg.module), - }); + try dg.renderCTypeForBuiltinFnName(writer, try dg.typeToCType(ty, .complete)); + } + + fn renderCTypeForBuiltinFnName(dg: *DeclGen, writer: anytype, cty: CType) !void { + switch (cty.tag()) { + else => try writer.print("{c}{d}", .{ + if (cty.isBool()) + signAbbrev(.unsigned) + else if (cty.isInteger()) + signAbbrev(cty.signedness() orelse .unsigned) + else if (cty.isFloat()) + @as(u8, 'f') + else if (cty.isPointer()) + @as(u8, 'p') + else + return dg.fail("TODO: CBE: implement renderTypeForBuiltinFnName for type {}", .{ + cty.tag(), + }), + if (cty.isFloat()) cty.floatActiveBits(dg.module.getTarget()) else dg.byteSize(cty) * 8, + }), + .array => try writer.writeAll("big"), + .vector => try writer.writeAll("vec"), + } } fn renderBuiltinInfo(dg: *DeclGen, writer: anytype, ty: Type, info: BuiltinInfo) !void { - const target = dg.module.getTarget(); switch (info) { - .None => {}, - .Range => { - var arena = std.heap.ArenaAllocator.init(dg.gpa); - defer arena.deinit(); - - const ExpectedContents = union { u: Value.Payload.U64, i: Value.Payload.I64 }; - var stack align(@alignOf(ExpectedContents)) = - std.heap.stackFallback(@sizeOf(ExpectedContents), arena.allocator()); - - const int_info = ty.intInfo(target); - if (int_info.signedness == .signed) { - const min_val = try ty.minInt(stack.get(), target); - try writer.print(", {x}", .{try dg.fmtIntLiteral(ty, min_val, .Other)}); + .none => {}, + .bits => { + const cty = try dg.typeToCType(ty, .complete); + if (cty.castTag(.vector)) |pl| { + var len_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, .data = pl.data.len }; + try writer.print(", {}", .{try dg.fmtIntLiteral( + Type.u32, + Value.initPayload(&len_pl.base), + .FunctionArgument, + )}); } - const max_val = try ty.maxInt(stack.get(), target); - try writer.print(", {x}", .{try dg.fmtIntLiteral(ty, max_val, .Other)}); - }, - .Bits => { - var bits_pl = Value.Payload.U64{ - .base = .{ .tag = .int_u64 }, - .data = ty.bitSize(target), - }; - const bits_val = Value.initPayload(&bits_pl.base); - try writer.print(", {}", .{try dg.fmtIntLiteral(Type.u8, bits_val, .Other)}); + const target = dg.module.getTarget(); + const elem_ty = ty.shallowElemType(); + const elem_info = if (elem_ty.isAbiInt()) + elem_ty.intInfo(target) + else + std.builtin.Type.Int{ + .signedness = .unsigned, + .bits = @intCast(u16, elem_ty.bitSize(target)), + }; + switch (cty.tag()) { + else => {}, + .array, .vector => try writer.print(", {}", .{elem_info.signedness == .signed}), + } + + var bits_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, .data = elem_info.bits }; + try writer.print(", {}", .{try dg.fmtIntLiteral(switch (cty.tag()) { + else => Type.u8, + .array, .vector => Type.u16, + }, Value.initPayload(&bits_pl.base), .FunctionArgument)}); }, } } @@ -2758,35 +2776,35 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, // TODO use a different strategy for add, sub, mul, div // that communicates to the optimizer that wrapping is UB. - .add => try airBinOp(f, inst, "+", "add", .None), - .sub => try airBinOp(f, inst, "-", "sub", .None), - .mul => try airBinOp(f, inst, "*", "mul", .None), + .add => try airBinOp(f, inst, "+", "add", .none), + .sub => try airBinOp(f, inst, "-", "sub", .none), + .mul => try airBinOp(f, inst, "*", "mul", .none), .neg => try airFloatNeg(f, inst), - .div_float => try airBinBuiltinCall(f, inst, "div", .None), + .div_float => try airBinBuiltinCall(f, inst, "div", .none), - .div_trunc, .div_exact => try airBinOp(f, inst, "/", "div_trunc", .None), + .div_trunc, .div_exact => try airBinOp(f, inst, "/", "div_trunc", .none), .rem => blk: { const bin_op = f.air.instructions.items(.data)[inst].bin_op; const lhs_ty = f.air.typeOf(bin_op.lhs); // For binary operations @TypeOf(lhs)==@TypeOf(rhs), // so we only check one. break :blk if (lhs_ty.isInt()) - try airBinOp(f, inst, "%", "rem", .None) + try airBinOp(f, inst, "%", "rem", .none) else try airBinFloatOp(f, inst, "fmod"); }, - .div_floor => try airBinBuiltinCall(f, inst, "div_floor", .None), - .mod => try airBinBuiltinCall(f, inst, "mod", .None), + .div_floor => try airBinBuiltinCall(f, inst, "div_floor", .none), + .mod => try airBinBuiltinCall(f, inst, "mod", .none), - .addwrap => try airBinBuiltinCall(f, inst, "addw", .Bits), - .subwrap => try airBinBuiltinCall(f, inst, "subw", .Bits), - .mulwrap => try airBinBuiltinCall(f, inst, "mulw", .Bits), + .addwrap => try airBinBuiltinCall(f, inst, "addw", .bits), + .subwrap => try airBinBuiltinCall(f, inst, "subw", .bits), + .mulwrap => try airBinBuiltinCall(f, inst, "mulw", .bits), - .add_sat => try airBinBuiltinCall(f, inst, "adds", .Bits), - .sub_sat => try airBinBuiltinCall(f, inst, "subs", .Bits), - .mul_sat => try airBinBuiltinCall(f, inst, "muls", .Bits), - .shl_sat => try airBinBuiltinCall(f, inst, "shls", .Bits), + .add_sat => try airBinBuiltinCall(f, inst, "adds", .bits), + .sub_sat => try airBinBuiltinCall(f, inst, "subs", .bits), + .mul_sat => try airBinBuiltinCall(f, inst, "muls", .bits), + .shl_sat => try airBinBuiltinCall(f, inst, "shls", .bits), .sqrt => try airUnFloatOp(f, inst, "sqrt"), .sin => try airUnFloatOp(f, inst, "sin"), @@ -2805,34 +2823,38 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, .mul_add => try airMulAdd(f, inst), - .add_with_overflow => try airOverflow(f, inst, "add", .Bits), - .sub_with_overflow => try airOverflow(f, inst, "sub", .Bits), - .mul_with_overflow => try airOverflow(f, inst, "mul", .Bits), - .shl_with_overflow => try airOverflow(f, inst, "shl", .Bits), + .add_with_overflow => try airOverflow(f, inst, "add", .bits), + .sub_with_overflow => try airOverflow(f, inst, "sub", .bits), + .mul_with_overflow => try airOverflow(f, inst, "mul", .bits), + .shl_with_overflow => try airOverflow(f, inst, "shl", .bits), .min => try airMinMax(f, inst, '<', "fmin"), .max => try airMinMax(f, inst, '>', "fmax"), .slice => try airSlice(f, inst), - .cmp_gt => try airCmpOp(f, inst, ">", "gt"), - .cmp_gte => try airCmpOp(f, inst, ">=", "ge"), - .cmp_lt => try airCmpOp(f, inst, "<", "lt"), - .cmp_lte => try airCmpOp(f, inst, "<=", "le"), + .cmp_gt => try airCmpOp(f, inst, .gt), + .cmp_gte => try airCmpOp(f, inst, .gte), + .cmp_lt => try airCmpOp(f, inst, .lt), + .cmp_lte => try airCmpOp(f, inst, .lte), - .cmp_eq => try airEquality(f, inst, "((", "==", "eq"), - .cmp_neq => try airEquality(f, inst, "!((", "!=", "ne"), + .cmp_eq => try airEquality(f, inst, .eq), + .cmp_neq => try airEquality(f, inst, .neq), - .cmp_vector => return f.fail("TODO: C backend: implement cmp_vector", .{}), + .cmp_vector => blk: { + const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; + const extra = f.air.extraData(Air.VectorCmp, ty_pl.payload).data; + break :blk try cmpBuiltinCall(f, inst, extra, extra.compareOperator(), .operator, .bits); + }, .cmp_lt_errors_len => try airCmpLtErrorsLen(f, inst), // bool_and and bool_or are non-short-circuit operations - .bool_and, .bit_and => try airBinOp(f, inst, "&", "and", .None), - .bool_or, .bit_or => try airBinOp(f, inst, "|", "or", .None), - .xor => try airBinOp(f, inst, "^", "xor", .None), - .shr, .shr_exact => try airBinBuiltinCall(f, inst, "shr", .None), - .shl, => try airBinBuiltinCall(f, inst, "shlw", .Bits), - .shl_exact => try airBinOp(f, inst, "<<", "shl", .None), + .bool_and, .bit_and => try airBinOp(f, inst, "&", "and", .none), + .bool_or, .bit_or => try airBinOp(f, inst, "|", "or", .none), + .xor => try airBinOp(f, inst, "^", "xor", .none), + .shr, .shr_exact => try airBinBuiltinCall(f, inst, "shr", .none), + .shl, => try airBinBuiltinCall(f, inst, "shlw", .bits), + .shl_exact => try airBinOp(f, inst, "<<", "shl", .none), .not => try airNot (f, inst), .optional_payload => try airOptionalPayload(f, inst), @@ -2877,11 +2899,11 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, .memcpy => try airMemcpy(f, inst), .set_union_tag => try airSetUnionTag(f, inst), .get_union_tag => try airGetUnionTag(f, inst), - .clz => try airUnBuiltinCall(f, inst, "clz", .Bits), - .ctz => try airUnBuiltinCall(f, inst, "ctz", .Bits), - .popcount => try airUnBuiltinCall(f, inst, "popcount", .Bits), - .byte_swap => try airUnBuiltinCall(f, inst, "byte_swap", .Bits), - .bit_reverse => try airUnBuiltinCall(f, inst, "bit_reverse", .Bits), + .clz => try airUnBuiltinCall(f, inst, "clz", .bits), + .ctz => try airUnBuiltinCall(f, inst, "ctz", .bits), + .popcount => try airUnBuiltinCall(f, inst, "popcount", .bits), + .byte_swap => try airUnBuiltinCall(f, inst, "byte_swap", .bits), + .bit_reverse => try airUnBuiltinCall(f, inst, "bit_reverse", .bits), .tag_name => try airTagName(f, inst), .error_name => try airErrorName(f, inst), .splat => try airSplat(f, inst), @@ -3349,7 +3371,7 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { try f.writeCValueDeref(writer, operand); try writer.print(", {})", .{try f.fmtIntLiteral(bit_offset_ty, bit_offset_val)}); if (cant_cast) try writer.writeByte(')'); - try f.object.dg.renderBuiltinInfo(writer, field_ty, .Bits); + try f.object.dg.renderBuiltinInfo(writer, field_ty, .bits); try writer.writeByte(')'); } else { try f.writeCValue(writer, local, .Other); @@ -3744,7 +3766,7 @@ fn airOverflow(f: *Function, inst: Air.Inst.Index, operation: []const u8, info: fn airNot(f: *Function, inst: Air.Inst.Index) !CValue { const inst_ty = f.air.typeOfIndex(inst); if (inst_ty.tag() != .bool) - return try airUnBuiltinCall(f, inst, "not", .Bits); + return try airUnBuiltinCall(f, inst, "not", .bits); const ty_op = f.air.instructions.items(.data)[inst].ty_op; @@ -3803,7 +3825,7 @@ fn airBinOp( return local; } -fn airCmpOp(f: *Function, inst: Air.Inst.Index, operator: []const u8, operation: []const u8) !CValue { +fn airCmpOp(f: *Function, inst: Air.Inst.Index, operator: std.math.CompareOperator) !CValue { const bin_op = f.air.instructions.items(.data)[inst].bin_op; if (f.liveness.isUnused(inst)) { @@ -3813,10 +3835,11 @@ fn airCmpOp(f: *Function, inst: Air.Inst.Index, operator: []const u8, operation: const operand_ty = f.air.typeOf(bin_op.lhs); const target = f.object.dg.module.getTarget(); - if (operand_ty.isInt() and operand_ty.bitSize(target) > 64) - return try cmpBuiltinCall(f, inst, operator, "cmp"); + const operand_bits = operand_ty.bitSize(target); + if (operand_ty.isInt() and operand_bits > 64) + return cmpBuiltinCall(f, inst, bin_op, operator, .cmp, if (operand_bits > 128) .bits else .none); if (operand_ty.isRuntimeFloat()) - return try cmpBuiltinCall(f, inst, operator, operation); + return cmpBuiltinCall(f, inst, bin_op, operator, .operator, .none); const inst_ty = f.air.typeOfIndex(inst); const lhs = try f.resolveInst(bin_op.lhs); @@ -3829,7 +3852,7 @@ fn airCmpOp(f: *Function, inst: Air.Inst.Index, operator: []const u8, operation: try writer.writeAll(" = "); try f.writeCValue(writer, lhs, .Other); try writer.writeByte(' '); - try writer.writeAll(operator); + try writer.writeAll(compareOperatorC(operator)); try writer.writeByte(' '); try f.writeCValue(writer, rhs, .Other); try writer.writeAll(";\n"); @@ -3840,9 +3863,7 @@ fn airCmpOp(f: *Function, inst: Air.Inst.Index, operator: []const u8, operation: fn airEquality( f: *Function, inst: Air.Inst.Index, - negate_prefix: []const u8, - operator: []const u8, - operation: []const u8, + operator: std.math.CompareOperator, ) !CValue { const bin_op = f.air.instructions.items(.data)[inst].bin_op; @@ -3853,10 +3874,11 @@ fn airEquality( const operand_ty = f.air.typeOf(bin_op.lhs); const target = f.object.dg.module.getTarget(); - if (operand_ty.isInt() and operand_ty.bitSize(target) > 64) - return try cmpBuiltinCall(f, inst, operator, "cmp"); + const operand_bits = operand_ty.bitSize(target); + if (operand_ty.isInt() and operand_bits > 64) + return cmpBuiltinCall(f, inst, bin_op, operator, .cmp, if (operand_bits > 128) .bits else .none); if (operand_ty.isRuntimeFloat()) - return try cmpBuiltinCall(f, inst, operator, operation); + return cmpBuiltinCall(f, inst, bin_op, operator, .operator, .none); const lhs = try f.resolveInst(bin_op.lhs); const rhs = try f.resolveInst(bin_op.rhs); @@ -3872,7 +3894,12 @@ fn airEquality( // (A && B) || (C && (A == B)) // A = lhs.is_null ; B = rhs.is_null ; C = rhs.payload == lhs.payload - try writer.writeAll(negate_prefix); + switch (operator) { + .eq => {}, + .neq => try writer.writeByte('!'), + else => unreachable, + } + try writer.writeAll("(("); try f.writeCValue(writer, lhs, .Other); try writer.writeAll(".is_null && "); try f.writeCValue(writer, rhs, .Other); @@ -3891,7 +3918,7 @@ fn airEquality( try f.writeCValue(writer, lhs, .Other); try writer.writeByte(' '); - try writer.writeAll(operator); + try writer.writeAll(compareOperatorC(operator)); try writer.writeByte(' '); try f.writeCValue(writer, rhs, .Other); try writer.writeAll(";\n"); @@ -3972,7 +3999,7 @@ fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: u8, operation: []cons const inst_ty = f.air.typeOfIndex(inst); const target = f.object.dg.module.getTarget(); if (inst_ty.isInt() and inst_ty.bitSize(target) > 64) - return try airBinBuiltinCall(f, inst, operation[1..], .None); + return try airBinBuiltinCall(f, inst, operation[1..], .none); if (inst_ty.isRuntimeFloat()) return try airBinFloatOp(f, inst, operation); @@ -4418,12 +4445,35 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue { // Ensure padding bits have the expected value. if (dest_ty.isAbiInt()) { + const dest_cty = try f.typeToCType(dest_ty, .complete); + const dest_info = dest_ty.intInfo(target); + var wrap_ty_pl = Type.Payload.Bits{ .base = .{ .tag = switch (dest_info.signedness) { + .unsigned => .int_unsigned, + .signed => .int_signed, + } }, .data = dest_info.bits }; + try f.writeCValue(writer, local, .Other); + if (dest_cty.castTag(.array)) |pl| { + try writer.print("[{d}]", .{switch (target.cpu.arch.endian()) { + .Little => pl.data.len - 1, + .Big => 0, + }}); + wrap_ty_pl.data -= 1; + wrap_ty_pl.data %= @intCast(u16, f.byteSize(f.indexToCType(pl.data.elem_type)) * 8); + wrap_ty_pl.data += 1; + } + const wrap_ty = Type.initPayload(&wrap_ty_pl.base); try writer.writeAll(" = zig_wrap_"); - try f.object.dg.renderTypeForBuiltinFnName(writer, dest_ty); + try f.object.dg.renderTypeForBuiltinFnName(writer, wrap_ty); try writer.writeByte('('); try f.writeCValue(writer, local, .Other); - try f.object.dg.renderBuiltinInfo(writer, dest_ty, .Bits); + if (dest_cty.castTag(.array)) |pl| { + try writer.print("[{d}]", .{switch (target.cpu.arch.endian()) { + .Little => pl.data.len - 1, + .Big => 0, + }}); + } + try f.object.dg.renderBuiltinInfo(writer, wrap_ty, .bits); try writer.writeAll(");\n"); } @@ -5438,7 +5488,7 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { try f.object.dg.renderValue(writer, bit_offset_ty, bit_offset_val, .FunctionArgument); try writer.writeByte(')'); if (cant_cast) try writer.writeByte(')'); - try f.object.dg.renderBuiltinInfo(writer, field_int_ty, .Bits); + try f.object.dg.renderBuiltinInfo(writer, field_int_ty, .bits); try writer.writeAll(");\n"); if (inst_ty.eql(field_int_ty, f.object.dg.module)) return temp_local; @@ -5871,7 +5921,7 @@ fn airFloatCast(f: *Function, inst: Air.Inst.Index) !CValue { try f.writeCValue(writer, operand, .FunctionArgument); try writer.writeByte(')'); if (inst_ty.isInt() and operand_ty.isRuntimeFloat()) { - try f.object.dg.renderBuiltinInfo(writer, inst_ty, .Bits); + try f.object.dg.renderBuiltinInfo(writer, inst_ty, .bits); try writer.writeByte(')'); } try writer.writeAll(";\n"); @@ -5972,29 +6022,46 @@ fn airBinBuiltinCall( fn cmpBuiltinCall( f: *Function, inst: Air.Inst.Index, - operator: []const u8, - operation: []const u8, + data: anytype, + operator: std.math.CompareOperator, + operation: enum { cmp, operator }, + info: BuiltinInfo, ) !CValue { const inst_ty = f.air.typeOfIndex(inst); - const bin_op = f.air.instructions.items(.data)[inst].bin_op; - const operand_ty = f.air.typeOf(bin_op.lhs); + const operand_ty = f.air.typeOf(data.lhs); - const lhs = try f.resolveInst(bin_op.lhs); - const rhs = try f.resolveInst(bin_op.rhs); - try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); + const lhs = try f.resolveInst(data.lhs); + const rhs = try f.resolveInst(data.rhs); + try reap(f, inst, &.{ data.lhs, data.rhs }); + + const ref_ret = inst_ty.tag() != .bool; const writer = f.object.writer(); const local = try f.allocLocal(inst, inst_ty); - try f.writeCValue(writer, local, .Other); - try writer.writeAll(" = zig_"); - try writer.writeAll(operation); - try writer.writeByte('_'); + if (!ref_ret) { + try f.writeCValue(writer, local, .Other); + try writer.writeAll(" = "); + } + try writer.print("zig_{s}_", .{switch (operation) { + else => @tagName(operation), + .operator => compareOperatorAbbrev(operator), + }}); try f.object.dg.renderTypeForBuiltinFnName(writer, operand_ty); try writer.writeByte('('); + if (ref_ret) { + try f.writeCValue(writer, local, .FunctionArgument); + try writer.writeAll(", "); + } try f.writeCValue(writer, lhs, .FunctionArgument); try writer.writeAll(", "); try f.writeCValue(writer, rhs, .FunctionArgument); - try writer.print(") {s} {};\n", .{ operator, try f.fmtIntLiteral(Type.initTag(.i32), Value.zero) }); + try f.object.dg.renderBuiltinInfo(writer, operand_ty, info); + try writer.writeByte(')'); + if (!ref_ret) try writer.print(" {s} {}", .{ + compareOperatorC(operator), + try f.fmtIntLiteral(Type.initTag(.i32), Value.zero), + }); + try writer.writeAll(";\n"); return local; } @@ -6675,7 +6742,7 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(", "); try f.object.dg.renderValue(writer, bit_offset_ty, bit_offset_val, .FunctionArgument); - try f.object.dg.renderBuiltinInfo(writer, inst_ty, .Bits); + try f.object.dg.renderBuiltinInfo(writer, inst_ty, .bits); try writer.writeByte(')'); if (!empty) try writer.writeByte(')'); @@ -7094,6 +7161,28 @@ fn compilerRtAbbrev(ty: Type, target: std.Target) []const u8 { } else unreachable; } +fn compareOperatorAbbrev(operator: std.math.CompareOperator) []const u8 { + return switch (operator) { + .lt => "lt", + .lte => "le", + .eq => "eq", + .gte => "ge", + .gt => "gt", + .neq => "ne", + }; +} + +fn compareOperatorC(operator: std.math.CompareOperator) []const u8 { + return switch (operator) { + .lt => "<", + .lte => "<=", + .eq => "==", + .gte => ">=", + .gt => ">", + .neq => "!=", + }; +} + fn StringLiteral(comptime WriterType: type) type { // MSVC has a length limit of 16380 per string literal (before concatenation) const max_char_len = 4; @@ -7239,14 +7328,6 @@ fn formatIntLiteral( .positive = undefined, }; defer allocator.free(wrap.limbs); - if (wrap.addWrap(int, one, data.int_info.signedness, c_bits) or - data.int_info.signedness == .signed and wrap.subWrap(int, one, data.int_info.signedness, c_bits)) - return writer.print("{s}_{s}", .{ - data.cty.getStandardDefineAbbrev() orelse return writer.print("zig_{s}Int_{c}{d}", .{ - if (int.positive) "max" else "min", signAbbrev(data.int_info.signedness), c_bits, - }), - if (int.positive) "MAX" else "MIN", - }); const c_limb_info: struct { cty: CType, @@ -7277,6 +7358,15 @@ fn formatIntLiteral( }, }; if (c_limb_info.count == 1) { + if (wrap.addWrap(int, one, data.int_info.signedness, c_bits) or + data.int_info.signedness == .signed and wrap.subWrap(int, one, data.int_info.signedness, c_bits)) + return writer.print("{s}_{s}", .{ + data.cty.getStandardDefineAbbrev() orelse return writer.print("zig_{s}Int_{c}{d}", .{ + if (int.positive) "max" else "min", signAbbrev(data.int_info.signedness), c_bits, + }), + if (int.positive) "MAX" else "MIN", + }); + if (!int.positive) try writer.writeByte('-'); try data.cty.renderLiteralPrefix(writer, data.kind); @@ -7310,7 +7400,7 @@ fn formatIntLiteral( try writer.writeAll(string); } else { try data.cty.renderLiteralPrefix(writer, data.kind); - wrap.convertToTwosComplement(int, .unsigned, data.int_info.bits); + wrap.convertToTwosComplement(int, data.int_info.signedness, c_bits); std.mem.set(BigIntLimb, wrap.limbs[wrap.len..], 0); wrap.len = wrap.limbs.len; const limbs_per_c_limb = @divExact(wrap.len, c_limb_info.count); @@ -7343,7 +7433,7 @@ fn formatIntLiteral( c_limb_cty = c_limb_info.cty.toSigned(); c_limb_mut.positive = wrap.positive; - c_limb_mut.convertToTwosComplement( + c_limb_mut.truncate( c_limb_mut.toConst(), .signed, data.int_info.bits - limb_i * @bitSizeOf(BigIntLimb), diff --git a/src/codegen/c/type.zig b/src/codegen/c/type.zig index a1b11df315..85e4cc9840 100644 --- a/src/codegen/c/type.zig +++ b/src/codegen/c/type.zig @@ -496,6 +496,116 @@ pub const CType = extern union { } }; + pub fn isBool(self: CType) bool { + return switch (self.tag()) { + ._Bool, + .bool, + => true, + else => false, + }; + } + + pub fn isInteger(self: CType) bool { + return switch (self.tag()) { + .char, + .@"signed char", + .short, + .int, + .long, + .@"long long", + .@"unsigned char", + .@"unsigned short", + .@"unsigned int", + .@"unsigned long", + .@"unsigned long long", + .size_t, + .ptrdiff_t, + .uint8_t, + .int8_t, + .uint16_t, + .int16_t, + .uint32_t, + .int32_t, + .uint64_t, + .int64_t, + .uintptr_t, + .intptr_t, + .zig_u128, + .zig_i128, + => true, + else => false, + }; + } + + pub fn signedness(self: CType) ?std.builtin.Signedness { + return switch (self.tag()) { + .char => null, // unknown signedness + .@"signed char", + .short, + .int, + .long, + .@"long long", + .ptrdiff_t, + .int8_t, + .int16_t, + .int32_t, + .int64_t, + .intptr_t, + .zig_i128, + => .signed, + .@"unsigned char", + .@"unsigned short", + .@"unsigned int", + .@"unsigned long", + .@"unsigned long long", + .size_t, + .uint8_t, + .uint16_t, + .uint32_t, + .uint64_t, + .uintptr_t, + .zig_u128, + => .unsigned, + else => unreachable, + }; + } + + pub fn isFloat(self: CType) bool { + return switch (self.tag()) { + .float, + .double, + .@"long double", + .zig_f16, + .zig_f32, + .zig_f64, + .zig_f80, + .zig_f128, + .zig_c_longdouble, + => true, + else => false, + }; + } + + pub fn isPointer(self: CType) bool { + return switch (self.tag()) { + .pointer, + .pointer_const, + .pointer_volatile, + .pointer_const_volatile, + => true, + else => false, + }; + } + + pub fn isFunction(self: CType) bool { + return switch (self.tag()) { + .function, + .varargs_function, + => true, + else => false, + }; + } + pub fn toSigned(self: CType) CType { return CType.initTag(switch (self.tag()) { .char, .@"signed char", .@"unsigned char" => .@"signed char", @@ -725,6 +835,20 @@ pub const CType = extern union { } } + pub fn floatActiveBits(self: CType, target: Target) u16 { + return switch (self.tag()) { + .float => target.c_type_bit_size(.float), + .double => target.c_type_bit_size(.double), + .@"long double", .zig_c_longdouble => target.c_type_bit_size(.longdouble), + .zig_f16 => 16, + .zig_f32 => 32, + .zig_f64 => 64, + .zig_f80 => 80, + .zig_f128 => 128, + else => unreachable, + }; + } + pub fn byteSize(self: CType, store: Store.Set, target: Target) u64 { return switch (self.tag()) { .void => 0, diff --git a/src/type.zig b/src/type.zig index 15525f14eb..9e501d893c 100644 --- a/src/type.zig +++ b/src/type.zig @@ -4213,7 +4213,7 @@ pub const Type = extern union { }; } - fn shallowElemType(child_ty: Type) Type { + pub fn shallowElemType(child_ty: Type) Type { return switch (child_ty.zigTypeTag()) { .Array, .Vector => child_ty.childType(), else => child_ty, diff --git a/test/behavior/bitcast.zig b/test/behavior/bitcast.zig index 70ac38d6fa..552080c836 100644 --- a/test/behavior/bitcast.zig +++ b/test/behavior/bitcast.zig @@ -34,7 +34,6 @@ test "@bitCast iX -> uX (8, 16, 128)" { test "@bitCast iX -> uX exotic integers" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; @@ -81,7 +80,6 @@ fn conv_uN(comptime N: usize, x: std.meta.Int(.unsigned, N)) std.meta.Int(.signe test "bitcast uX to bytes" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 54263e1daf..9ebeca8541 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -1526,7 +1526,6 @@ fn testNanEqNan(comptime F: type) !void { } test "vector comparison" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO 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 diff --git a/test/behavior/vector.zig b/test/behavior/vector.zig index 50fef7f646..d885a7fabc 100644 --- a/test/behavior/vector.zig +++ b/test/behavior/vector.zig @@ -48,7 +48,6 @@ test "vector wrap operators" { test "vector bin compares with mem.eql" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -403,7 +402,6 @@ test "initialize vector which is a struct field" { test "vector comparison operators" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO From 93d696e84ef17a32d5c2f1520a295ebcda968e91 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Fri, 3 Mar 2023 01:18:23 -0500 Subject: [PATCH 021/294] CBE: implement some big integer and vector unary operations --- lib/zig.h | 422 ++++++++++++++++++++++++++++++++++- src/codegen/c.zig | 51 ++++- test/behavior/bugs/10147.zig | 1 - test/behavior/math.zig | 8 +- test/behavior/popcount.zig | 1 - 5 files changed, 460 insertions(+), 23 deletions(-) diff --git a/lib/zig.h b/lib/zig.h index c39cffee24..e5cb421c6f 100644 --- a/lib/zig.h +++ b/lib/zig.h @@ -1919,7 +1919,7 @@ static inline zig_i128 zig_bit_reverse_i128(zig_i128 val, uint8_t bits) { /* ========================== Big Integer Support =========================== */ -static inline uint16_t zig_big_bytes(uint16_t bits) { +static inline uint16_t zig_int_bytes(uint16_t bits) { uint16_t bytes = (bits + CHAR_BIT - 1) / CHAR_BIT; uint16_t alignment = 16; while (alignment / 2 >= bytes) alignment /= 2; @@ -1931,7 +1931,7 @@ static inline int32_t zig_cmp_big(const void *lhs, const void *rhs, bool is_sign const uint8_t *rhs_bytes = rhs; uint16_t byte_offset = 0; bool do_signed = is_signed; - uint16_t remaining_bytes = zig_big_bytes(bits); + uint16_t remaining_bytes = zig_int_bytes(bits); #if zig_little_endian byte_offset = remaining_bytes; @@ -1965,7 +1965,7 @@ static inline int32_t zig_cmp_big(const void *lhs, const void *rhs, bool is_sign remaining_bytes -= 128 / CHAR_BIT; #if zig_big_endian - byte_offset -= 128 / CHAR_BIT; + byte_offset += 128 / CHAR_BIT; #endif } @@ -1994,7 +1994,7 @@ static inline int32_t zig_cmp_big(const void *lhs, const void *rhs, bool is_sign remaining_bytes -= 64 / CHAR_BIT; #if zig_big_endian - byte_offset -= 64 / CHAR_BIT; + byte_offset += 64 / CHAR_BIT; #endif } @@ -2023,7 +2023,7 @@ static inline int32_t zig_cmp_big(const void *lhs, const void *rhs, bool is_sign remaining_bytes -= 32 / CHAR_BIT; #if zig_big_endian - byte_offset -= 32 / CHAR_BIT; + byte_offset += 32 / CHAR_BIT; #endif } @@ -2052,7 +2052,7 @@ static inline int32_t zig_cmp_big(const void *lhs, const void *rhs, bool is_sign remaining_bytes -= 16 / CHAR_BIT; #if zig_big_endian - byte_offset -= 16 / CHAR_BIT; + byte_offset += 16 / CHAR_BIT; #endif } @@ -2081,13 +2081,368 @@ static inline int32_t zig_cmp_big(const void *lhs, const void *rhs, bool is_sign remaining_bytes -= 8 / CHAR_BIT; #if zig_big_endian - byte_offset -= 8 / CHAR_BIT; + byte_offset += 8 / CHAR_BIT; #endif } return 0; } +static inline uint16_t zig_clz_big(const void *val, bool is_signed, uint16_t bits) { + const uint8_t *val_bytes = val; + uint16_t byte_offset = 0; + uint16_t remaining_bytes = zig_int_bytes(bits); + uint16_t skip_bits = remaining_bytes * 8 - bits; + uint16_t total_lz = 0; + uint16_t limb_lz; + (void)is_signed; + +#if zig_little_endian + byte_offset = remaining_bytes; +#endif + + while (remaining_bytes >= 128 / CHAR_BIT) { +#if zig_little_endian + byte_offset -= 128 / CHAR_BIT; +#endif + + { + zig_u128 val_limb; + + memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb)); + limb_lz = zig_clz_u128(val_limb, 128 - skip_bits); + } + + total_lz += limb_lz; + if (limb_lz < 128 - skip_bits) return total_lz; + skip_bits = 0; + remaining_bytes -= 128 / CHAR_BIT; + +#if zig_big_endian + byte_offset += 128 / CHAR_BIT; +#endif + } + + while (remaining_bytes >= 64 / CHAR_BIT) { +#if zig_little_endian + byte_offset -= 64 / CHAR_BIT; +#endif + + { + uint64_t val_limb; + + memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb)); + limb_lz = zig_clz_u64(val_limb, 64 - skip_bits); + } + + total_lz += limb_lz; + if (limb_lz < 64 - skip_bits) return total_lz; + skip_bits = 0; + remaining_bytes -= 64 / CHAR_BIT; + +#if zig_big_endian + byte_offset += 64 / CHAR_BIT; +#endif + } + + while (remaining_bytes >= 32 / CHAR_BIT) { +#if zig_little_endian + byte_offset -= 32 / CHAR_BIT; +#endif + + { + uint32_t val_limb; + + memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb)); + limb_lz = zig_clz_u32(val_limb, 32 - skip_bits); + } + + total_lz += limb_lz; + if (limb_lz < 32 - skip_bits) return total_lz; + skip_bits = 0; + remaining_bytes -= 32 / CHAR_BIT; + +#if zig_big_endian + byte_offset += 32 / CHAR_BIT; +#endif + } + + while (remaining_bytes >= 16 / CHAR_BIT) { +#if zig_little_endian + byte_offset -= 16 / CHAR_BIT; +#endif + + { + uint16_t val_limb; + + memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb)); + limb_lz = zig_clz_u16(val_limb, 16 - skip_bits); + } + + total_lz += limb_lz; + if (limb_lz < 16 - skip_bits) return total_lz; + skip_bits = 0; + remaining_bytes -= 16 / CHAR_BIT; + +#if zig_big_endian + byte_offset += 16 / CHAR_BIT; +#endif + } + + while (remaining_bytes >= 8 / CHAR_BIT) { +#if zig_little_endian + byte_offset -= 8 / CHAR_BIT; +#endif + + { + uint8_t val_limb; + + memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb)); + limb_lz = zig_clz_u8(val_limb, 8 - skip_bits); + } + + total_lz += limb_lz; + if (limb_lz < 8 - skip_bits) return total_lz; + skip_bits = 0; + remaining_bytes -= 8 / CHAR_BIT; + +#if zig_big_endian + byte_offset += 8 / CHAR_BIT; +#endif + } + + return total_lz; +} + +static inline uint16_t zig_ctz_big(const void *val, bool is_signed, uint16_t bits) { + const uint8_t *val_bytes = val; + uint16_t byte_offset = 0; + uint16_t remaining_bytes = zig_int_bytes(bits); + uint16_t total_tz = 0; + uint16_t limb_tz; + (void)is_signed; + +#if zig_big_endian + byte_offset = remaining_bytes; +#endif + + while (remaining_bytes >= 128 / CHAR_BIT) { +#if zig_big_endian + byte_offset -= 128 / CHAR_BIT; +#endif + + { + zig_u128 val_limb; + + memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb)); + limb_tz = zig_ctz_u128(val_limb, 128); + } + + total_tz += limb_tz; + if (limb_tz < 128) return total_tz; + remaining_bytes -= 128 / CHAR_BIT; + +#if zig_little_endian + byte_offset += 128 / CHAR_BIT; +#endif + } + + while (remaining_bytes >= 64 / CHAR_BIT) { +#if zig_big_endian + byte_offset -= 64 / CHAR_BIT; +#endif + + { + uint64_t val_limb; + + memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb)); + limb_tz = zig_ctz_u64(val_limb, 64); + } + + total_tz += limb_tz; + if (limb_tz < 64) return total_tz; + remaining_bytes -= 64 / CHAR_BIT; + +#if zig_little_endian + byte_offset += 64 / CHAR_BIT; +#endif + } + + while (remaining_bytes >= 32 / CHAR_BIT) { +#if zig_big_endian + byte_offset -= 32 / CHAR_BIT; +#endif + + { + uint32_t val_limb; + + memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb)); + limb_tz = zig_ctz_u32(val_limb, 32); + } + + total_tz += limb_tz; + if (limb_tz < 32) return total_tz; + remaining_bytes -= 32 / CHAR_BIT; + +#if zig_little_endian + byte_offset += 32 / CHAR_BIT; +#endif + } + + while (remaining_bytes >= 16 / CHAR_BIT) { +#if zig_big_endian + byte_offset -= 16 / CHAR_BIT; +#endif + + { + uint16_t val_limb; + + memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb)); + limb_tz = zig_ctz_u16(val_limb, 16); + } + + total_tz += limb_tz; + if (limb_tz < 16) return total_tz; + remaining_bytes -= 16 / CHAR_BIT; + +#if zig_little_endian + byte_offset += 16 / CHAR_BIT; +#endif + } + + while (remaining_bytes >= 8 / CHAR_BIT) { +#if zig_big_endian + byte_offset -= 8 / CHAR_BIT; +#endif + + { + uint8_t val_limb; + + memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb)); + limb_tz = zig_ctz_u8(val_limb, 8); + } + + total_tz += limb_tz; + if (limb_tz < 8) return total_tz; + remaining_bytes -= 8 / CHAR_BIT; + +#if zig_little_endian + byte_offset += 8 / CHAR_BIT; +#endif + } + + return total_tz; +} + +static inline uint16_t zig_popcount_big(const void *val, bool is_signed, uint16_t bits) { + const uint8_t *val_bytes = val; + uint16_t byte_offset = 0; + uint16_t remaining_bytes = zig_int_bytes(bits); + uint16_t total_pc = 0; + (void)is_signed; + +#if zig_big_endian + byte_offset = remaining_bytes; +#endif + + while (remaining_bytes >= 128 / CHAR_BIT) { +#if zig_big_endian + byte_offset -= 128 / CHAR_BIT; +#endif + + { + zig_u128 val_limb; + + memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb)); + total_pc += zig_popcount_u128(val_limb, 128); + } + + remaining_bytes -= 128 / CHAR_BIT; + +#if zig_little_endian + byte_offset += 128 / CHAR_BIT; +#endif + } + + while (remaining_bytes >= 64 / CHAR_BIT) { +#if zig_big_endian + byte_offset -= 64 / CHAR_BIT; +#endif + + { + uint64_t val_limb; + + memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb)); + total_pc += zig_popcount_u64(val_limb, 64); + } + + remaining_bytes -= 64 / CHAR_BIT; + +#if zig_little_endian + byte_offset += 64 / CHAR_BIT; +#endif + } + + while (remaining_bytes >= 32 / CHAR_BIT) { +#if zig_big_endian + byte_offset -= 32 / CHAR_BIT; +#endif + + { + uint32_t val_limb; + + memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb)); + total_pc += zig_popcount_u32(val_limb, 32); + } + + remaining_bytes -= 32 / CHAR_BIT; + +#if zig_little_endian + byte_offset += 32 / CHAR_BIT; +#endif + } + + while (remaining_bytes >= 16 / CHAR_BIT) { +#if zig_big_endian + byte_offset -= 16 / CHAR_BIT; +#endif + + { + uint16_t val_limb; + + memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb)); + total_pc = zig_popcount_u16(val_limb, 16); + } + + remaining_bytes -= 16 / CHAR_BIT; + +#if zig_little_endian + byte_offset += 16 / CHAR_BIT; +#endif + } + + while (remaining_bytes >= 8 / CHAR_BIT) { +#if zig_big_endian + byte_offset -= 8 / CHAR_BIT; +#endif + + { + uint8_t val_limb; + + memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb)); + total_pc = zig_popcount_u8(val_limb, 8); + } + + remaining_bytes -= 8 / CHAR_BIT; + +#if zig_little_endian + byte_offset += 8 / CHAR_BIT; +#endif + } + + return total_pc; +} + /* ========================= Floating Point Support ========================= */ #if _MSC_VER @@ -2742,7 +3097,7 @@ zig_msvc_atomics_128op(u128, max) uint32_t index = 0; \ const uint8_t *lhs_ptr = lhs; \ const uint8_t *rhs_ptr = rhs; \ - uint16_t elem_bytes = zig_big_bytes(elem_bits); \ + uint16_t elem_bytes = zig_int_bytes(elem_bits); \ \ while (index < len) { \ result[index] = zig_cmp_big(lhs_ptr, rhs_ptr, is_signed, elem_bits) operator 0; \ @@ -2758,6 +3113,57 @@ zig_cmp_vec(le, <=) zig_cmp_vec(gt, > ) zig_cmp_vec(ge, >=) +static inline void zig_clz_vec(void *result, const void *val, uint32_t len, bool is_signed, uint16_t elem_bits) { + uint32_t index = 0; + const uint8_t *val_ptr = val; + uint16_t elem_bytes = zig_int_bytes(elem_bits); + + while (index < len) { + uint16_t lz = zig_clz_big(val_ptr, is_signed, elem_bits); + if (elem_bits <= 128) { + ((uint8_t *)result)[index] = (uint8_t)lz; + } else { + ((uint16_t *)result)[index] = lz; + } + val_ptr += elem_bytes; + index += 1; + } +} + +static inline void zig_ctz_vec(void *result, const void *val, uint32_t len, bool is_signed, uint16_t elem_bits) { + uint32_t index = 0; + const uint8_t *val_ptr = val; + uint16_t elem_bytes = zig_int_bytes(elem_bits); + + while (index < len) { + uint16_t tz = zig_ctz_big(val_ptr, is_signed, elem_bits); + if (elem_bits <= 128) { + ((uint8_t *)result)[index] = (uint8_t)tz; + } else { + ((uint16_t *)result)[index] = tz; + } + val_ptr += elem_bytes; + index += 1; + } +} + +static inline void zig_popcount_vec(void *result, const void *val, uint32_t len, bool is_signed, uint16_t elem_bits) { + uint32_t index = 0; + const uint8_t *val_ptr = val; + uint16_t elem_bytes = zig_int_bytes(elem_bits); + + while (index < len) { + uint16_t pc = zig_popcount_big(val_ptr, is_signed, elem_bits); + if (elem_bits <= 128) { + ((uint8_t *)result)[index] = (uint8_t)pc; + } else { + ((uint16_t *)result)[index] = pc; + } + val_ptr += elem_bytes; + index += 1; + } +} + /* ======================== Special Case Intrinsics ========================= */ #if (_MSC_VER && _M_X64) || defined(__x86_64__) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index f4a817cecd..4d3e71e78a 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -2844,7 +2844,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, .cmp_vector => blk: { const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; const extra = f.air.extraData(Air.VectorCmp, ty_pl.payload).data; - break :blk try cmpBuiltinCall(f, inst, extra, extra.compareOperator(), .operator, .bits); + break :blk try airCmpBuiltinCall(f, inst, extra, extra.compareOperator(), .operator, .bits,); }, .cmp_lt_errors_len => try airCmpLtErrorsLen(f, inst), @@ -3837,9 +3837,16 @@ fn airCmpOp(f: *Function, inst: Air.Inst.Index, operator: std.math.CompareOperat const target = f.object.dg.module.getTarget(); const operand_bits = operand_ty.bitSize(target); if (operand_ty.isInt() and operand_bits > 64) - return cmpBuiltinCall(f, inst, bin_op, operator, .cmp, if (operand_bits > 128) .bits else .none); + return airCmpBuiltinCall( + f, + inst, + bin_op, + operator, + .cmp, + if (operand_bits > 128) .bits else .none, + ); if (operand_ty.isRuntimeFloat()) - return cmpBuiltinCall(f, inst, bin_op, operator, .operator, .none); + return airCmpBuiltinCall(f, inst, bin_op, operator, .operator, .none); const inst_ty = f.air.typeOfIndex(inst); const lhs = try f.resolveInst(bin_op.lhs); @@ -3876,9 +3883,16 @@ fn airEquality( const target = f.object.dg.module.getTarget(); const operand_bits = operand_ty.bitSize(target); if (operand_ty.isInt() and operand_bits > 64) - return cmpBuiltinCall(f, inst, bin_op, operator, .cmp, if (operand_bits > 128) .bits else .none); + return airCmpBuiltinCall( + f, + inst, + bin_op, + operator, + .cmp, + if (operand_bits > 128) .bits else .none, + ); if (operand_ty.isRuntimeFloat()) - return cmpBuiltinCall(f, inst, bin_op, operator, .operator, .none); + return airCmpBuiltinCall(f, inst, bin_op, operator, .operator, .none); const lhs = try f.resolveInst(bin_op.lhs); const rhs = try f.resolveInst(bin_op.rhs); @@ -5969,14 +5983,25 @@ fn airUnBuiltinCall( const inst_ty = f.air.typeOfIndex(inst); const operand_ty = f.air.typeOf(ty_op.operand); + const inst_cty = try f.typeToCType(inst_ty, .complete); + const ref_ret = switch (inst_cty.tag()) { + else => false, + .array, .vector => true, + }; + const writer = f.object.writer(); const local = try f.allocLocal(inst, inst_ty); - try f.writeCValue(writer, local, .Other); - try writer.writeAll(" = zig_"); - try writer.writeAll(operation); - try writer.writeByte('_'); + if (!ref_ret) { + try f.writeCValue(writer, local, .Other); + try writer.writeAll(" = "); + } + try writer.print("zig_{s}_", .{operation}); try f.object.dg.renderTypeForBuiltinFnName(writer, operand_ty); try writer.writeByte('('); + if (ref_ret) { + try f.writeCValue(writer, local, .FunctionArgument); + try writer.writeAll(", "); + } try f.writeCValue(writer, operand, .FunctionArgument); try f.object.dg.renderBuiltinInfo(writer, operand_ty, info); try writer.writeAll(");\n"); @@ -6019,7 +6044,7 @@ fn airBinBuiltinCall( return local; } -fn cmpBuiltinCall( +fn airCmpBuiltinCall( f: *Function, inst: Air.Inst.Index, data: anytype, @@ -6034,7 +6059,11 @@ fn cmpBuiltinCall( const rhs = try f.resolveInst(data.rhs); try reap(f, inst, &.{ data.lhs, data.rhs }); - const ref_ret = inst_ty.tag() != .bool; + const inst_cty = try f.typeToCType(inst_ty, .complete); + const ref_ret = switch (inst_cty.tag()) { + else => false, + .array, .vector => true, + }; const writer = f.object.writer(); const local = try f.allocLocal(inst, inst_ty); diff --git a/test/behavior/bugs/10147.zig b/test/behavior/bugs/10147.zig index 3ca9085805..77c513caa6 100644 --- a/test/behavior/bugs/10147.zig +++ b/test/behavior/bugs/10147.zig @@ -6,7 +6,6 @@ test "test calling @clz on both vector and scalar inputs" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO var x: u32 = 0x1; diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 9ebeca8541..d7b8e4764b 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -100,7 +100,6 @@ test "@clz vectors" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO try testClzVectors(); @@ -163,7 +162,6 @@ test "@ctz vectors" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .aarch64) { @@ -1562,6 +1560,12 @@ test "signed zeros are represented properly" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.os.tag == .windows and builtin.cpu.arch == .aarch64 and + builtin.zig_backend == .stage2_c) + { + return error.SkipZigTest; + } + const S = struct { fn doTheTest() !void { try testOne(f16); diff --git a/test/behavior/popcount.zig b/test/behavior/popcount.zig index b27d5d77d3..9dce5820cd 100644 --- a/test/behavior/popcount.zig +++ b/test/behavior/popcount.zig @@ -67,7 +67,6 @@ fn testPopCountIntegers() !void { } test "@popCount vectors" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO From e96a0fd0a1a05fe8c3b4d87df03d78ae99b7dbcb Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Fri, 3 Mar 2023 01:48:13 -0500 Subject: [PATCH 022/294] CBE: "compute" max int alignment the lazy way --- lib/zig.h | 2 +- src/link/C.zig | 22 ++++++++++++++-------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/lib/zig.h b/lib/zig.h index e5cb421c6f..5d77c76c8f 100644 --- a/lib/zig.h +++ b/lib/zig.h @@ -1921,7 +1921,7 @@ static inline zig_i128 zig_bit_reverse_i128(zig_i128 val, uint8_t bits) { static inline uint16_t zig_int_bytes(uint16_t bits) { uint16_t bytes = (bits + CHAR_BIT - 1) / CHAR_BIT; - uint16_t alignment = 16; + uint16_t alignment = ZIG_TARGET_MAX_INT_ALIGNMENT; while (alignment / 2 >= bytes) alignment /= 2; return (bytes + alignment - 1) / alignment * alignment; } diff --git a/src/link/C.zig b/src/link/C.zig index 5663ba71e2..7e3ad2eddd 100644 --- a/src/link/C.zig +++ b/src/link/C.zig @@ -221,14 +221,19 @@ pub fn flush(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) !void return self.flushModule(comp, prog_node); } -fn abiDefine(comp: *Compilation) ?[]const u8 { - return switch (comp.getTarget().abi) { - .msvc => "#define ZIG_TARGET_ABI_MSVC\n", - else => null, - }; +fn abiDefines(self: *C, target: std.Target) !std.ArrayList(u8) { + var defines = std.ArrayList(u8).init(self.base.allocator); + errdefer defines.deinit(); + const writer = defines.writer(); + switch (target.abi) { + .msvc => try writer.writeAll("#define ZIG_TARGET_ABI_MSVC\n"), + else => {}, + } + try writer.print("#define ZIG_TARGET_MAX_INT_ALIGNMENT {d}\n", .{target.maxIntAlignment()}); + return defines; } -pub fn flushModule(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) !void { +pub fn flushModule(self: *C, _: *Compilation, prog_node: *std.Progress.Node) !void { const tracy = trace(@src()); defer tracy.end(); @@ -245,12 +250,13 @@ pub fn flushModule(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) var f: Flush = .{}; defer f.deinit(gpa); - const abi_define = abiDefine(comp); + const abi_defines = try self.abiDefines(module.getTarget()); + defer abi_defines.deinit(); // Covers defines, zig.h, ctypes, asm, lazy fwd. try f.all_buffers.ensureUnusedCapacity(gpa, 5); - if (abi_define) |buf| f.appendBufAssumeCapacity(buf); + f.appendBufAssumeCapacity(abi_defines.items); f.appendBufAssumeCapacity(zig_h); const ctypes_index = f.all_buffers.items.len; From 9e3a5ecd39227aff3b2821d0c0b489eb9713b146 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sat, 4 Mar 2023 15:18:05 -0500 Subject: [PATCH 023/294] CBE: fix behavior test failures on msvc --- lib/zig.h | 4 +++- src/codegen/c.zig | 37 +++++++++++++++++++++++++++++-------- src/codegen/c/type.zig | 7 +++++++ 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/lib/zig.h b/lib/zig.h index 5d77c76c8f..6b95ba3358 100644 --- a/lib/zig.h +++ b/lib/zig.h @@ -1646,7 +1646,9 @@ static inline zig_u128 zig_wrap_u128(zig_u128 val, uint8_t bits) { } static inline zig_i128 zig_wrap_i128(zig_i128 val, uint8_t bits) { - return zig_make_i128(zig_wrap_i64(zig_hi_i128(val), bits - UINT8_C(64)), zig_lo_i128(val)); + if (bits > UINT8_C(64)) return zig_make_i128(zig_wrap_i64(zig_hi_i128(val), bits - UINT8_C(64)), zig_lo_i128(val)); + int64_t lo = zig_wrap_i64((int64_t)zig_lo_i128(val), bits); + return zig_make_i128(zig_shr_i64(lo, 63), (uint64_t)lo); } static inline zig_u128 zig_shlw_u128(zig_u128 lhs, uint8_t rhs, uint8_t bits) { diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 4d3e71e78a..b8606b1a17 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -4461,10 +4461,12 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue { if (dest_ty.isAbiInt()) { const dest_cty = try f.typeToCType(dest_ty, .complete); const dest_info = dest_ty.intInfo(target); - var wrap_ty_pl = Type.Payload.Bits{ .base = .{ .tag = switch (dest_info.signedness) { + var info_ty_pl = Type.Payload.Bits{ .base = .{ .tag = switch (dest_info.signedness) { .unsigned => .int_unsigned, .signed => .int_signed, } }, .data = dest_info.bits }; + var wrap_cty: ?CType = null; + var need_bitcasts = false; try f.writeCValue(writer, local, .Other); if (dest_cty.castTag(.array)) |pl| { @@ -4472,14 +4474,31 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue { .Little => pl.data.len - 1, .Big => 0, }}); - wrap_ty_pl.data -= 1; - wrap_ty_pl.data %= @intCast(u16, f.byteSize(f.indexToCType(pl.data.elem_type)) * 8); - wrap_ty_pl.data += 1; + const elem_cty = f.indexToCType(pl.data.elem_type); + wrap_cty = elem_cty.toSignedness(dest_info.signedness); + need_bitcasts = wrap_cty.?.tag() == .zig_i128; + info_ty_pl.data -= 1; + info_ty_pl.data %= @intCast(u16, f.byteSize(elem_cty) * 8); + info_ty_pl.data += 1; } - const wrap_ty = Type.initPayload(&wrap_ty_pl.base); - try writer.writeAll(" = zig_wrap_"); - try f.object.dg.renderTypeForBuiltinFnName(writer, wrap_ty); + try writer.writeAll(" = "); + if (need_bitcasts) { + try writer.writeAll("zig_bitcast_"); + try f.object.dg.renderCTypeForBuiltinFnName(writer, wrap_cty.?.toUnsigned()); + try writer.writeByte('('); + } + try writer.writeAll("zig_wrap_"); + const info_ty = Type.initPayload(&info_ty_pl.base); + if (wrap_cty) |cty| + try f.object.dg.renderCTypeForBuiltinFnName(writer, cty) + else + try f.object.dg.renderTypeForBuiltinFnName(writer, info_ty); try writer.writeByte('('); + if (need_bitcasts) { + try writer.writeAll("zig_bitcast_"); + try f.object.dg.renderCTypeForBuiltinFnName(writer, wrap_cty.?); + try writer.writeByte('('); + } try f.writeCValue(writer, local, .Other); if (dest_cty.castTag(.array)) |pl| { try writer.print("[{d}]", .{switch (target.cpu.arch.endian()) { @@ -4487,7 +4506,9 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue { .Big => 0, }}); } - try f.object.dg.renderBuiltinInfo(writer, wrap_ty, .bits); + if (need_bitcasts) try writer.writeByte(')'); + try f.object.dg.renderBuiltinInfo(writer, info_ty, .bits); + if (need_bitcasts) try writer.writeByte(')'); try writer.writeAll(");\n"); } diff --git a/src/codegen/c/type.zig b/src/codegen/c/type.zig index 85e4cc9840..313fcc130c 100644 --- a/src/codegen/c/type.zig +++ b/src/codegen/c/type.zig @@ -651,6 +651,13 @@ pub const CType = extern union { }); } + pub fn toSignedness(self: CType, s: std.builtin.Signedness) CType { + return switch (s) { + .unsigned => self.toUnsigned(), + .signed => self.toSigned(), + }; + } + pub fn getStandardDefineAbbrev(self: CType) ?[]const u8 { return switch (self.tag()) { .char => "CHAR", From b2e9c0d0ff1dc6799fe3b5fdbecd53af176f37b7 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sat, 4 Mar 2023 19:02:42 -0500 Subject: [PATCH 024/294] Sema: fix cmp_vector type --- src/Sema.zig | 40 +++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 8940527bc0..8c6e3cf05c 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -574,11 +574,13 @@ pub const Block = struct { }); } - fn addCmpVector(block: *Block, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref, cmp_op: std.math.CompareOperator, vector_ty: Air.Inst.Ref) !Air.Inst.Ref { + fn addCmpVector(block: *Block, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref, cmp_op: std.math.CompareOperator) !Air.Inst.Ref { return block.addInst(.{ .tag = if (block.float_mode == .Optimized) .cmp_vector_optimized else .cmp_vector, .data = .{ .ty_pl = .{ - .ty = vector_ty, + .ty = try block.sema.addType( + try Type.vector(block.sema.arena, block.sema.typeOf(lhs).vectorLen(), Type.bool), + ), .payload = try block.sema.addExtra(Air.VectorCmp{ .lhs = lhs, .rhs = rhs, @@ -9412,7 +9414,7 @@ fn intCast( const ok = if (is_vector) ok: { const zeros = try Value.Tag.repeated.create(sema.arena, Value.zero); const zero_inst = try sema.addConstant(sema.typeOf(operand), zeros); - const is_in_range = try block.addCmpVector(operand, zero_inst, .eq, try sema.addType(operand_ty)); + const is_in_range = try block.addCmpVector(operand, zero_inst, .eq); const all_in_range = try block.addInst(.{ .tag = .reduce, .data = .{ .reduce = .{ .operand = is_in_range, .operation = .And } }, @@ -9466,7 +9468,7 @@ fn intCast( const dest_range = try sema.addConstant(unsigned_operand_ty, dest_range_val); const ok = if (is_vector) ok: { - const is_in_range = try block.addCmpVector(diff_unsigned, dest_range, .lte, try sema.addType(operand_ty)); + const is_in_range = try block.addCmpVector(diff_unsigned, dest_range, .lte); const all_in_range = try block.addInst(.{ .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce, .data = .{ .reduce = .{ @@ -9483,7 +9485,7 @@ fn intCast( try sema.addSafetyCheck(block, ok, .cast_truncated_data); } else { const ok = if (is_vector) ok: { - const is_in_range = try block.addCmpVector(diff, dest_max, .lte, try sema.addType(operand_ty)); + const is_in_range = try block.addCmpVector(diff, dest_max, .lte); const all_in_range = try block.addInst(.{ .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce, .data = .{ .reduce = .{ @@ -9504,7 +9506,7 @@ fn intCast( const ok = if (is_vector) ok: { const zero_val = try Value.Tag.repeated.create(sema.arena, Value.zero); const zero_inst = try sema.addConstant(operand_ty, zero_val); - const is_in_range = try block.addCmpVector(operand, zero_inst, .gte, try sema.addType(operand_ty)); + const is_in_range = try block.addCmpVector(operand, zero_inst, .gte); const all_in_range = try block.addInst(.{ .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce, .data = .{ .reduce = .{ @@ -12016,7 +12018,7 @@ fn zirShl( const ok = if (rhs_ty.zigTypeTag() == .Vector) ok: { const bit_count_inst = try sema.addConstant(rhs_ty, try Value.Tag.repeated.create(sema.arena, bit_count_val)); - const lt = try block.addCmpVector(rhs, bit_count_inst, .lt, try sema.addType(rhs_ty)); + const lt = try block.addCmpVector(rhs, bit_count_inst, .lt); break :ok try block.addInst(.{ .tag = .reduce, .data = .{ .reduce = .{ @@ -12172,7 +12174,7 @@ fn zirShr( const ok = if (rhs_ty.zigTypeTag() == .Vector) ok: { const bit_count_inst = try sema.addConstant(rhs_ty, try Value.Tag.repeated.create(sema.arena, bit_count_val)); - const lt = try block.addCmpVector(rhs, bit_count_inst, .lt, try sema.addType(rhs_ty)); + const lt = try block.addCmpVector(rhs, bit_count_inst, .lt); break :ok try block.addInst(.{ .tag = .reduce, .data = .{ .reduce = .{ @@ -12191,7 +12193,7 @@ fn zirShr( const back = try block.addBinOp(.shl, result, rhs); const ok = if (rhs_ty.zigTypeTag() == .Vector) ok: { - const eql = try block.addCmpVector(lhs, back, .eq, try sema.addType(rhs_ty)); + const eql = try block.addCmpVector(lhs, back, .eq); break :ok try block.addInst(.{ .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce, .data = .{ .reduce = .{ @@ -13192,7 +13194,7 @@ fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const floored = try block.addUnOp(.floor, result); if (resolved_type.zigTypeTag() == .Vector) { - const eql = try block.addCmpVector(result, floored, .eq, try sema.addType(resolved_type)); + const eql = try block.addCmpVector(result, floored, .eq); break :ok try block.addInst(.{ .tag = switch (block.float_mode) { .Strict => .reduce, @@ -13216,7 +13218,7 @@ fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai if (resolved_type.zigTypeTag() == .Vector) { const zero_val = try Value.Tag.repeated.create(sema.arena, Value.zero); const zero = try sema.addConstant(resolved_type, zero_val); - const eql = try block.addCmpVector(remainder, zero, .eq, try sema.addType(resolved_type)); + const eql = try block.addCmpVector(remainder, zero, .eq); break :ok try block.addInst(.{ .tag = .reduce, .data = .{ .reduce = .{ @@ -13514,14 +13516,13 @@ fn addDivIntOverflowSafety( var ok: Air.Inst.Ref = .none; if (resolved_type.zigTypeTag() == .Vector) { - const vector_ty_ref = try sema.addType(resolved_type); if (maybe_lhs_val == null) { const min_int_ref = try sema.addConstant(resolved_type, min_int); - ok = try block.addCmpVector(casted_lhs, min_int_ref, .neq, vector_ty_ref); + ok = try block.addCmpVector(casted_lhs, min_int_ref, .neq); } if (maybe_rhs_val == null) { const neg_one_ref = try sema.addConstant(resolved_type, neg_one); - const rhs_ok = try block.addCmpVector(casted_rhs, neg_one_ref, .neq, vector_ty_ref); + const rhs_ok = try block.addCmpVector(casted_rhs, neg_one_ref, .neq); if (ok == .none) { ok = rhs_ok; } else { @@ -13573,7 +13574,7 @@ fn addDivByZeroSafety( const ok = if (resolved_type.zigTypeTag() == .Vector) ok: { const zero_val = try Value.Tag.repeated.create(sema.arena, Value.zero); const zero = try sema.addConstant(resolved_type, zero_val); - const ok = try block.addCmpVector(casted_rhs, zero, .neq, try sema.addType(resolved_type)); + const ok = try block.addCmpVector(casted_rhs, zero, .neq); break :ok try block.addInst(.{ .tag = if (is_int) .reduce else .reduce_optimized, .data = .{ .reduce = .{ @@ -15202,9 +15203,7 @@ fn cmpSelf( }; try sema.requireRuntimeBlock(block, src, runtime_src); if (resolved_type.zigTypeTag() == .Vector) { - const result_ty = try Type.vector(sema.arena, resolved_type.vectorLen(), Type.bool); - const result_ty_ref = try sema.addType(result_ty); - return block.addCmpVector(casted_lhs, casted_rhs, op, result_ty_ref); + return block.addCmpVector(casted_lhs, casted_rhs, op); } const tag = Air.Inst.Tag.fromCmpOp(op, block.float_mode == .Optimized); return block.addBinOp(tag, casted_lhs, casted_rhs); @@ -23035,7 +23034,7 @@ fn panicSentinelMismatch( const ok = if (sentinel_ty.zigTypeTag() == .Vector) ok: { const eql = - try parent_block.addCmpVector(expected_sentinel, actual_sentinel, .eq, try sema.addType(sentinel_ty)); + try parent_block.addCmpVector(expected_sentinel, actual_sentinel, .eq); break :ok try parent_block.addInst(.{ .tag = .reduce, .data = .{ .reduce = .{ @@ -29368,8 +29367,7 @@ fn cmpVector( }; try sema.requireRuntimeBlock(block, src, runtime_src); - const result_ty_inst = try sema.addType(result_ty); - return block.addCmpVector(lhs, rhs, op, result_ty_inst); + return block.addCmpVector(lhs, rhs, op); } fn wrapOptional( From c478c7609e4529267d1ce030577777e836ffc10b Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sun, 5 Mar 2023 00:01:15 -0500 Subject: [PATCH 025/294] CBE: implement vector operations Also, bigint add and sub which is all I was actually trying to do. --- lib/zig.h | 660 ++++++++++++++++++++---------- src/codegen/c.zig | 620 +++++++++++++++++----------- src/type.zig | 2 +- src/value.zig | 2 +- test/behavior/bitreverse.zig | 3 - test/behavior/byteswap.zig | 3 - test/behavior/cast.zig | 1 - test/behavior/floatop.zig | 12 - test/behavior/maximum_minimum.zig | 2 - test/behavior/muladd.zig | 5 - test/behavior/vector.zig | 30 +- 11 files changed, 835 insertions(+), 505 deletions(-) diff --git a/lib/zig.h b/lib/zig.h index 6b95ba3358..22a9dbbb9e 100644 --- a/lib/zig.h +++ b/lib/zig.h @@ -612,12 +612,6 @@ static inline bool zig_addo_u32(uint32_t *res, uint32_t lhs, uint32_t rhs, uint8 #endif } -static inline void zig_vaddo_u32(uint8_t *ov, uint32_t *res, int n, - const uint32_t *lhs, const uint32_t *rhs, uint8_t bits) -{ - for (int i = 0; i < n; ++i) ov[i] = zig_addo_u32(&res[i], lhs[i], rhs[i], bits); -} - zig_extern int32_t __addosi4(int32_t lhs, int32_t rhs, int *overflow); static inline bool zig_addo_i32(int32_t *res, int32_t lhs, int32_t rhs, uint8_t bits) { #if zig_has_builtin(add_overflow) || defined(zig_gnuc) @@ -632,12 +626,6 @@ static inline bool zig_addo_i32(int32_t *res, int32_t lhs, int32_t rhs, uint8_t return overflow || full_res < zig_minInt_i(32, bits) || full_res > zig_maxInt_i(32, bits); } -static inline void zig_vaddo_i32(uint8_t *ov, int32_t *res, int n, - const int32_t *lhs, const int32_t *rhs, uint8_t bits) -{ - for (int i = 0; i < n; ++i) ov[i] = zig_addo_i32(&res[i], lhs[i], rhs[i], bits); -} - static inline bool zig_addo_u64(uint64_t *res, uint64_t lhs, uint64_t rhs, uint8_t bits) { #if zig_has_builtin(add_overflow) || defined(zig_gnuc) uint64_t full_res; @@ -650,12 +638,6 @@ static inline bool zig_addo_u64(uint64_t *res, uint64_t lhs, uint64_t rhs, uint8 #endif } -static inline void zig_vaddo_u64(uint8_t *ov, uint64_t *res, int n, - const uint64_t *lhs, const uint64_t *rhs, uint8_t bits) -{ - for (int i = 0; i < n; ++i) ov[i] = zig_addo_u64(&res[i], lhs[i], rhs[i], bits); -} - zig_extern int64_t __addodi4(int64_t lhs, int64_t rhs, int *overflow); static inline bool zig_addo_i64(int64_t *res, int64_t lhs, int64_t rhs, uint8_t bits) { #if zig_has_builtin(add_overflow) || defined(zig_gnuc) @@ -670,12 +652,6 @@ static inline bool zig_addo_i64(int64_t *res, int64_t lhs, int64_t rhs, uint8_t return overflow || full_res < zig_minInt_i(64, bits) || full_res > zig_maxInt_i(64, bits); } -static inline void zig_vaddo_i64(uint8_t *ov, int64_t *res, int n, - const int64_t *lhs, const int64_t *rhs, uint8_t bits) -{ - for (int i = 0; i < n; ++i) ov[i] = zig_addo_i64(&res[i], lhs[i], rhs[i], bits); -} - static inline bool zig_addo_u8(uint8_t *res, uint8_t lhs, uint8_t rhs, uint8_t bits) { #if zig_has_builtin(add_overflow) || defined(zig_gnuc) uint8_t full_res; @@ -690,12 +666,6 @@ static inline bool zig_addo_u8(uint8_t *res, uint8_t lhs, uint8_t rhs, uint8_t b #endif } -static inline void zig_vaddo_u8(uint8_t *ov, uint8_t *res, int n, - const uint8_t *lhs, const uint8_t *rhs, uint8_t bits) -{ - for (int i = 0; i < n; ++i) ov[i] = zig_addo_u8(&res[i], lhs[i], rhs[i], bits); -} - static inline bool zig_addo_i8(int8_t *res, int8_t lhs, int8_t rhs, uint8_t bits) { #if zig_has_builtin(add_overflow) || defined(zig_gnuc) int8_t full_res; @@ -710,12 +680,6 @@ static inline bool zig_addo_i8(int8_t *res, int8_t lhs, int8_t rhs, uint8_t bits #endif } -static inline void zig_vaddo_i8(uint8_t *ov, int8_t *res, int n, - const int8_t *lhs, const int8_t *rhs, uint8_t bits) -{ - for (int i = 0; i < n; ++i) ov[i] = zig_addo_i8(&res[i], lhs[i], rhs[i], bits); -} - static inline bool zig_addo_u16(uint16_t *res, uint16_t lhs, uint16_t rhs, uint8_t bits) { #if zig_has_builtin(add_overflow) || defined(zig_gnuc) uint16_t full_res; @@ -730,12 +694,6 @@ static inline bool zig_addo_u16(uint16_t *res, uint16_t lhs, uint16_t rhs, uint8 #endif } -static inline void zig_vaddo_u16(uint8_t *ov, uint16_t *res, int n, - const uint16_t *lhs, const uint16_t *rhs, uint8_t bits) -{ - for (int i = 0; i < n; ++i) ov[i] = zig_addo_u16(&res[i], lhs[i], rhs[i], bits); -} - static inline bool zig_addo_i16(int16_t *res, int16_t lhs, int16_t rhs, uint8_t bits) { #if zig_has_builtin(add_overflow) || defined(zig_gnuc) int16_t full_res; @@ -750,12 +708,6 @@ static inline bool zig_addo_i16(int16_t *res, int16_t lhs, int16_t rhs, uint8_t #endif } -static inline void zig_vaddo_i16(uint8_t *ov, int16_t *res, int n, - const int16_t *lhs, const int16_t *rhs, uint8_t bits) -{ - for (int i = 0; i < n; ++i) ov[i] = zig_addo_i16(&res[i], lhs[i], rhs[i], bits); -} - static inline bool zig_subo_u32(uint32_t *res, uint32_t lhs, uint32_t rhs, uint8_t bits) { #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) uint32_t full_res; @@ -768,12 +720,6 @@ static inline bool zig_subo_u32(uint32_t *res, uint32_t lhs, uint32_t rhs, uint8 #endif } -static inline void zig_vsubo_u32(uint8_t *ov, uint32_t *res, int n, - const uint32_t *lhs, const uint32_t *rhs, uint8_t bits) -{ - for (int i = 0; i < n; ++i) ov[i] = zig_subo_u32(&res[i], lhs[i], rhs[i], bits); -} - zig_extern int32_t __subosi4(int32_t lhs, int32_t rhs, int *overflow); static inline bool zig_subo_i32(int32_t *res, int32_t lhs, int32_t rhs, uint8_t bits) { #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) @@ -788,12 +734,6 @@ static inline bool zig_subo_i32(int32_t *res, int32_t lhs, int32_t rhs, uint8_t return overflow || full_res < zig_minInt_i(32, bits) || full_res > zig_maxInt_i(32, bits); } -static inline void zig_vsubo_i32(uint8_t *ov, int32_t *res, int n, - const int32_t *lhs, const int32_t *rhs, uint8_t bits) -{ - for (int i = 0; i < n; ++i) ov[i] = zig_subo_i32(&res[i], lhs[i], rhs[i], bits); -} - static inline bool zig_subo_u64(uint64_t *res, uint64_t lhs, uint64_t rhs, uint8_t bits) { #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) uint64_t full_res; @@ -806,12 +746,6 @@ static inline bool zig_subo_u64(uint64_t *res, uint64_t lhs, uint64_t rhs, uint8 #endif } -static inline void zig_vsubo_u64(uint8_t *ov, uint64_t *res, int n, - const uint64_t *lhs, const uint64_t *rhs, uint8_t bits) -{ - for (int i = 0; i < n; ++i) ov[i] = zig_subo_u64(&res[i], lhs[i], rhs[i], bits); -} - zig_extern int64_t __subodi4(int64_t lhs, int64_t rhs, int *overflow); static inline bool zig_subo_i64(int64_t *res, int64_t lhs, int64_t rhs, uint8_t bits) { #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) @@ -826,12 +760,6 @@ static inline bool zig_subo_i64(int64_t *res, int64_t lhs, int64_t rhs, uint8_t return overflow || full_res < zig_minInt_i(64, bits) || full_res > zig_maxInt_i(64, bits); } -static inline void zig_vsubo_i64(uint8_t *ov, int64_t *res, int n, - const int64_t *lhs, const int64_t *rhs, uint8_t bits) -{ - for (int i = 0; i < n; ++i) ov[i] = zig_subo_i64(&res[i], lhs[i], rhs[i], bits); -} - static inline bool zig_subo_u8(uint8_t *res, uint8_t lhs, uint8_t rhs, uint8_t bits) { #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) uint8_t full_res; @@ -846,12 +774,6 @@ static inline bool zig_subo_u8(uint8_t *res, uint8_t lhs, uint8_t rhs, uint8_t b #endif } -static inline void zig_vsubo_u8(uint8_t *ov, uint8_t *res, int n, - const uint8_t *lhs, const uint8_t *rhs, uint8_t bits) -{ - for (int i = 0; i < n; ++i) ov[i] = zig_subo_u8(&res[i], lhs[i], rhs[i], bits); -} - static inline bool zig_subo_i8(int8_t *res, int8_t lhs, int8_t rhs, uint8_t bits) { #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) int8_t full_res; @@ -866,13 +788,6 @@ static inline bool zig_subo_i8(int8_t *res, int8_t lhs, int8_t rhs, uint8_t bits #endif } -static inline void zig_vsubo_i8(uint8_t *ov, int8_t *res, int n, - const int8_t *lhs, const int8_t *rhs, uint8_t bits) -{ - for (int i = 0; i < n; ++i) ov[i] = zig_subo_i8(&res[i], lhs[i], rhs[i], bits); -} - - static inline bool zig_subo_u16(uint16_t *res, uint16_t lhs, uint16_t rhs, uint8_t bits) { #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) uint16_t full_res; @@ -887,13 +802,6 @@ static inline bool zig_subo_u16(uint16_t *res, uint16_t lhs, uint16_t rhs, uint8 #endif } -static inline void zig_vsubo_u16(uint8_t *ov, uint16_t *res, int n, - const uint16_t *lhs, const uint16_t *rhs, uint8_t bits) -{ - for (int i = 0; i < n; ++i) ov[i] = zig_subo_u16(&res[i], lhs[i], rhs[i], bits); -} - - static inline bool zig_subo_i16(int16_t *res, int16_t lhs, int16_t rhs, uint8_t bits) { #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) int16_t full_res; @@ -908,12 +816,6 @@ static inline bool zig_subo_i16(int16_t *res, int16_t lhs, int16_t rhs, uint8_t #endif } -static inline void zig_vsubo_i16(uint8_t *ov, int16_t *res, int n, - const int16_t *lhs, const int16_t *rhs, uint8_t bits) -{ - for (int i = 0; i < n; ++i) ov[i] = zig_subo_i16(&res[i], lhs[i], rhs[i], bits); -} - static inline bool zig_mulo_u32(uint32_t *res, uint32_t lhs, uint32_t rhs, uint8_t bits) { #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) uint32_t full_res; @@ -926,12 +828,6 @@ static inline bool zig_mulo_u32(uint32_t *res, uint32_t lhs, uint32_t rhs, uint8 #endif } -static inline void zig_vmulo_u32(uint8_t *ov, uint32_t *res, int n, - const uint32_t *lhs, const uint32_t *rhs, uint8_t bits) -{ - for (int i = 0; i < n; ++i) ov[i] = zig_mulo_u32(&res[i], lhs[i], rhs[i], bits); -} - zig_extern int32_t __mulosi4(int32_t lhs, int32_t rhs, int *overflow); static inline bool zig_mulo_i32(int32_t *res, int32_t lhs, int32_t rhs, uint8_t bits) { #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) @@ -946,12 +842,6 @@ static inline bool zig_mulo_i32(int32_t *res, int32_t lhs, int32_t rhs, uint8_t return overflow || full_res < zig_minInt_i(32, bits) || full_res > zig_maxInt_i(32, bits); } -static inline void zig_vmulo_i32(uint8_t *ov, int32_t *res, int n, - const int32_t *lhs, const int32_t *rhs, uint8_t bits) -{ - for (int i = 0; i < n; ++i) ov[i] = zig_mulo_i32(&res[i], lhs[i], rhs[i], bits); -} - static inline bool zig_mulo_u64(uint64_t *res, uint64_t lhs, uint64_t rhs, uint8_t bits) { #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) uint64_t full_res; @@ -964,12 +854,6 @@ static inline bool zig_mulo_u64(uint64_t *res, uint64_t lhs, uint64_t rhs, uint8 #endif } -static inline void zig_vmulo_u64(uint8_t *ov, uint64_t *res, int n, - const uint64_t *lhs, const uint64_t *rhs, uint8_t bits) -{ - for (int i = 0; i < n; ++i) ov[i] = zig_mulo_u64(&res[i], lhs[i], rhs[i], bits); -} - zig_extern int64_t __mulodi4(int64_t lhs, int64_t rhs, int *overflow); static inline bool zig_mulo_i64(int64_t *res, int64_t lhs, int64_t rhs, uint8_t bits) { #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) @@ -984,12 +868,6 @@ static inline bool zig_mulo_i64(int64_t *res, int64_t lhs, int64_t rhs, uint8_t return overflow || full_res < zig_minInt_i(64, bits) || full_res > zig_maxInt_i(64, bits); } -static inline void zig_vmulo_i64(uint8_t *ov, int64_t *res, int n, - const int64_t *lhs, const int64_t *rhs, uint8_t bits) -{ - for (int i = 0; i < n; ++i) ov[i] = zig_mulo_i64(&res[i], lhs[i], rhs[i], bits); -} - static inline bool zig_mulo_u8(uint8_t *res, uint8_t lhs, uint8_t rhs, uint8_t bits) { #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) uint8_t full_res; @@ -1004,12 +882,6 @@ static inline bool zig_mulo_u8(uint8_t *res, uint8_t lhs, uint8_t rhs, uint8_t b #endif } -static inline void zig_vmulo_u8(uint8_t *ov, uint8_t *res, int n, - const uint8_t *lhs, const uint8_t *rhs, uint8_t bits) -{ - for (int i = 0; i < n; ++i) ov[i] = zig_mulo_u8(&res[i], lhs[i], rhs[i], bits); -} - static inline bool zig_mulo_i8(int8_t *res, int8_t lhs, int8_t rhs, uint8_t bits) { #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) int8_t full_res; @@ -1024,12 +896,6 @@ static inline bool zig_mulo_i8(int8_t *res, int8_t lhs, int8_t rhs, uint8_t bits #endif } -static inline void zig_vmulo_i8(uint8_t *ov, int8_t *res, int n, - const int8_t *lhs, const int8_t *rhs, uint8_t bits) -{ - for (int i = 0; i < n; ++i) ov[i] = zig_mulo_i8(&res[i], lhs[i], rhs[i], bits); -} - static inline bool zig_mulo_u16(uint16_t *res, uint16_t lhs, uint16_t rhs, uint8_t bits) { #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) uint16_t full_res; @@ -1044,12 +910,6 @@ static inline bool zig_mulo_u16(uint16_t *res, uint16_t lhs, uint16_t rhs, uint8 #endif } -static inline void zig_vmulo_u16(uint8_t *ov, uint16_t *res, int n, - const uint16_t *lhs, const uint16_t *rhs, uint8_t bits) -{ - for (int i = 0; i < n; ++i) ov[i] = zig_mulo_u16(&res[i], lhs[i], rhs[i], bits); -} - static inline bool zig_mulo_i16(int16_t *res, int16_t lhs, int16_t rhs, uint8_t bits) { #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) int16_t full_res; @@ -1064,12 +924,6 @@ static inline bool zig_mulo_i16(int16_t *res, int16_t lhs, int16_t rhs, uint8_t #endif } -static inline void zig_vmulo_i16(uint8_t *ov, int16_t *res, int n, - const int16_t *lhs, const int16_t *rhs, uint8_t bits) -{ - for (int i = 0; i < n; ++i) ov[i] = zig_mulo_i16(&res[i], lhs[i], rhs[i], bits); -} - #define zig_int_builtins(w) \ static inline bool zig_shlo_u##w(uint##w##_t *res, uint##w##_t lhs, uint8_t rhs, uint8_t bits) { \ *res = zig_shlw_u##w(lhs, rhs, bits); \ @@ -2090,6 +1944,446 @@ static inline int32_t zig_cmp_big(const void *lhs, const void *rhs, bool is_sign return 0; } +static inline bool zig_addo_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) { + uint8_t *res_bytes = res; + const uint8_t *lhs_bytes = lhs; + const uint8_t *rhs_bytes = rhs; + uint16_t byte_offset = 0; + uint16_t remaining_bytes = zig_int_bytes(bits); + uint16_t top_bits = remaining_bytes * 8 - bits; + bool overflow = false; + +#if zig_big_endian + byte_offset = remaining_bytes; +#endif + + while (remaining_bytes >= 128 / CHAR_BIT) { + uint16_t limb_bits = 128 - (remaining_bytes == 128 / CHAR_BIT ? top_bits : 0); + +#if zig_big_endian + byte_offset -= 128 / CHAR_BIT; +#endif + + if (remaining_bytes == 128 / CHAR_BIT && is_signed) { + zig_i128 res_limb; + zig_i128 tmp_limb; + zig_i128 lhs_limb; + zig_i128 rhs_limb; + bool limb_overflow; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + limb_overflow = zig_addo_i128(&tmp_limb, lhs_limb, rhs_limb, limb_bits); + overflow = limb_overflow ^ zig_addo_i128(&res_limb, tmp_limb, zig_make_i128(INT64_C(0), overflow ? UINT64_C(1) : UINT64_C(0)), limb_bits); + memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb)); + } else { + zig_u128 res_limb; + zig_u128 tmp_limb; + zig_u128 lhs_limb; + zig_u128 rhs_limb; + bool limb_overflow; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + limb_overflow = zig_addo_u128(&tmp_limb, lhs_limb, rhs_limb, limb_bits); + overflow = limb_overflow ^ zig_addo_u128(&res_limb, tmp_limb, zig_make_u128(UINT64_C(0), overflow ? UINT64_C(1) : UINT64_C(0)), limb_bits); + memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb)); + } + + remaining_bytes -= 128 / CHAR_BIT; + +#if zig_little_endian + byte_offset += 128 / CHAR_BIT; +#endif + } + + while (remaining_bytes >= 64 / CHAR_BIT) { + uint16_t limb_bits = 64 - (remaining_bytes == 64 / CHAR_BIT ? top_bits : 0); + +#if zig_big_endian + byte_offset -= 64 / CHAR_BIT; +#endif + + if (remaining_bytes == 64 / CHAR_BIT && is_signed) { + int64_t res_limb; + int64_t tmp_limb; + int64_t lhs_limb; + int64_t rhs_limb; + bool limb_overflow; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + limb_overflow = zig_addo_i64(&tmp_limb, lhs_limb, rhs_limb, limb_bits); + overflow = limb_overflow ^ zig_addo_i64(&res_limb, tmp_limb, overflow ? INT64_C(1) : INT64_C(0), limb_bits); + memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb)); + } else { + uint64_t res_limb; + uint64_t tmp_limb; + uint64_t lhs_limb; + uint64_t rhs_limb; + bool limb_overflow; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + limb_overflow = zig_addo_u64(&tmp_limb, lhs_limb, rhs_limb, limb_bits); + overflow = limb_overflow ^ zig_addo_u64(&res_limb, tmp_limb, overflow ? UINT64_C(1) : UINT64_C(0), limb_bits); + memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb)); + } + + remaining_bytes -= 64 / CHAR_BIT; + +#if zig_little_endian + byte_offset += 64 / CHAR_BIT; +#endif + } + + while (remaining_bytes >= 32 / CHAR_BIT) { + uint16_t limb_bits = 32 - (remaining_bytes == 32 / CHAR_BIT ? top_bits : 0); + +#if zig_big_endian + byte_offset -= 32 / CHAR_BIT; +#endif + + if (remaining_bytes == 32 / CHAR_BIT && is_signed) { + int32_t res_limb; + int32_t tmp_limb; + int32_t lhs_limb; + int32_t rhs_limb; + bool limb_overflow; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + limb_overflow = zig_addo_i32(&tmp_limb, lhs_limb, rhs_limb, limb_bits); + overflow = limb_overflow ^ zig_addo_i32(&res_limb, tmp_limb, overflow ? INT32_C(1) : INT32_C(0), limb_bits); + memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb)); + } else { + uint32_t res_limb; + uint32_t tmp_limb; + uint32_t lhs_limb; + uint32_t rhs_limb; + bool limb_overflow; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + limb_overflow = zig_addo_u32(&tmp_limb, lhs_limb, rhs_limb, limb_bits); + overflow = limb_overflow ^ zig_addo_u32(&res_limb, tmp_limb, overflow ? UINT32_C(1) : UINT32_C(0), limb_bits); + memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb)); + } + + remaining_bytes -= 32 / CHAR_BIT; + +#if zig_little_endian + byte_offset += 32 / CHAR_BIT; +#endif + } + + while (remaining_bytes >= 16 / CHAR_BIT) { + uint16_t limb_bits = 16 - (remaining_bytes == 16 / CHAR_BIT ? top_bits : 0); + +#if zig_big_endian + byte_offset -= 16 / CHAR_BIT; +#endif + + if (remaining_bytes == 16 / CHAR_BIT && is_signed) { + int16_t res_limb; + int16_t tmp_limb; + int16_t lhs_limb; + int16_t rhs_limb; + bool limb_overflow; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + limb_overflow = zig_addo_i16(&tmp_limb, lhs_limb, rhs_limb, limb_bits); + overflow = limb_overflow ^ zig_addo_i16(&res_limb, tmp_limb, overflow ? INT16_C(1) : INT16_C(0), limb_bits); + memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb)); + } else { + uint16_t res_limb; + uint16_t tmp_limb; + uint16_t lhs_limb; + uint16_t rhs_limb; + bool limb_overflow; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + limb_overflow = zig_addo_u16(&tmp_limb, lhs_limb, rhs_limb, limb_bits); + overflow = limb_overflow ^ zig_addo_u16(&res_limb, tmp_limb, overflow ? UINT16_C(1) : UINT16_C(0), limb_bits); + memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb)); + } + + remaining_bytes -= 16 / CHAR_BIT; + +#if zig_little_endian + byte_offset += 16 / CHAR_BIT; +#endif + } + + while (remaining_bytes >= 8 / CHAR_BIT) { + uint16_t limb_bits = 8 - (remaining_bytes == 8 / CHAR_BIT ? top_bits : 0); + +#if zig_big_endian + byte_offset -= 8 / CHAR_BIT; +#endif + + if (remaining_bytes == 8 / CHAR_BIT && is_signed) { + int8_t res_limb; + int8_t tmp_limb; + int8_t lhs_limb; + int8_t rhs_limb; + bool limb_overflow; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + limb_overflow = zig_addo_i8(&tmp_limb, lhs_limb, rhs_limb, limb_bits); + overflow = limb_overflow ^ zig_addo_i8(&res_limb, tmp_limb, overflow ? INT8_C(1) : INT8_C(0), limb_bits); + memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb)); + } else { + uint8_t res_limb; + uint8_t tmp_limb; + uint8_t lhs_limb; + uint8_t rhs_limb; + bool limb_overflow; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + limb_overflow = zig_addo_u8(&tmp_limb, lhs_limb, rhs_limb, limb_bits); + overflow = limb_overflow ^ zig_addo_u8(&res_limb, tmp_limb, overflow ? UINT8_C(1) : UINT8_C(0), limb_bits); + memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb)); + } + + remaining_bytes -= 8 / CHAR_BIT; + +#if zig_little_endian + byte_offset += 8 / CHAR_BIT; +#endif + } + + return overflow; +} + +static inline bool zig_subo_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) { + uint8_t *res_bytes = res; + const uint8_t *lhs_bytes = lhs; + const uint8_t *rhs_bytes = rhs; + uint16_t byte_offset = 0; + uint16_t remaining_bytes = zig_int_bytes(bits); + uint16_t top_bits = remaining_bytes * 8 - bits; + bool overflow = false; + +#if zig_big_endian + byte_offset = remaining_bytes; +#endif + + while (remaining_bytes >= 128 / CHAR_BIT) { + uint16_t limb_bits = 128 - (remaining_bytes == 128 / CHAR_BIT ? top_bits : 0); + +#if zig_big_endian + byte_offset -= 128 / CHAR_BIT; +#endif + + if (remaining_bytes == 128 / CHAR_BIT && is_signed) { + zig_i128 res_limb; + zig_i128 tmp_limb; + zig_i128 lhs_limb; + zig_i128 rhs_limb; + bool limb_overflow; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + limb_overflow = zig_subo_i128(&tmp_limb, lhs_limb, rhs_limb, limb_bits); + overflow = limb_overflow ^ zig_subo_i128(&res_limb, tmp_limb, zig_make_i128(INT64_C(0), overflow ? UINT64_C(1) : UINT64_C(0)), limb_bits); + memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb)); + } else { + zig_u128 res_limb; + zig_u128 tmp_limb; + zig_u128 lhs_limb; + zig_u128 rhs_limb; + bool limb_overflow; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + limb_overflow = zig_subo_u128(&tmp_limb, lhs_limb, rhs_limb, limb_bits); + overflow = limb_overflow ^ zig_subo_u128(&res_limb, tmp_limb, zig_make_u128(UINT64_C(0), overflow ? UINT64_C(1) : UINT64_C(0)), limb_bits); + memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb)); + } + + remaining_bytes -= 128 / CHAR_BIT; + +#if zig_little_endian + byte_offset += 128 / CHAR_BIT; +#endif + } + + while (remaining_bytes >= 64 / CHAR_BIT) { + uint16_t limb_bits = 64 - (remaining_bytes == 64 / CHAR_BIT ? top_bits : 0); + +#if zig_big_endian + byte_offset -= 64 / CHAR_BIT; +#endif + + if (remaining_bytes == 64 / CHAR_BIT && is_signed) { + int64_t res_limb; + int64_t tmp_limb; + int64_t lhs_limb; + int64_t rhs_limb; + bool limb_overflow; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + limb_overflow = zig_subo_i64(&tmp_limb, lhs_limb, rhs_limb, limb_bits); + overflow = limb_overflow ^ zig_subo_i64(&res_limb, tmp_limb, overflow ? INT64_C(1) : INT64_C(0), limb_bits); + memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb)); + } else { + uint64_t res_limb; + uint64_t tmp_limb; + uint64_t lhs_limb; + uint64_t rhs_limb; + bool limb_overflow; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + limb_overflow = zig_subo_u64(&tmp_limb, lhs_limb, rhs_limb, limb_bits); + overflow = limb_overflow ^ zig_subo_u64(&res_limb, tmp_limb, overflow ? UINT64_C(1) : UINT64_C(0), limb_bits); + memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb)); + } + + remaining_bytes -= 64 / CHAR_BIT; + +#if zig_little_endian + byte_offset += 64 / CHAR_BIT; +#endif + } + + while (remaining_bytes >= 32 / CHAR_BIT) { + uint16_t limb_bits = 32 - (remaining_bytes == 32 / CHAR_BIT ? top_bits : 0); + +#if zig_big_endian + byte_offset -= 32 / CHAR_BIT; +#endif + + if (remaining_bytes == 32 / CHAR_BIT && is_signed) { + int32_t res_limb; + int32_t tmp_limb; + int32_t lhs_limb; + int32_t rhs_limb; + bool limb_overflow; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + limb_overflow = zig_subo_i32(&tmp_limb, lhs_limb, rhs_limb, limb_bits); + overflow = limb_overflow ^ zig_subo_i32(&res_limb, tmp_limb, overflow ? INT32_C(1) : INT32_C(0), limb_bits); + memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb)); + } else { + uint32_t res_limb; + uint32_t tmp_limb; + uint32_t lhs_limb; + uint32_t rhs_limb; + bool limb_overflow; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + limb_overflow = zig_subo_u32(&tmp_limb, lhs_limb, rhs_limb, limb_bits); + overflow = limb_overflow ^ zig_subo_u32(&res_limb, tmp_limb, overflow ? UINT32_C(1) : UINT32_C(0), limb_bits); + memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb)); + } + + remaining_bytes -= 32 / CHAR_BIT; + +#if zig_little_endian + byte_offset += 32 / CHAR_BIT; +#endif + } + + while (remaining_bytes >= 16 / CHAR_BIT) { + uint16_t limb_bits = 16 - (remaining_bytes == 16 / CHAR_BIT ? top_bits : 0); + +#if zig_big_endian + byte_offset -= 16 / CHAR_BIT; +#endif + + if (remaining_bytes == 16 / CHAR_BIT && is_signed) { + int16_t res_limb; + int16_t tmp_limb; + int16_t lhs_limb; + int16_t rhs_limb; + bool limb_overflow; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + limb_overflow = zig_subo_i16(&tmp_limb, lhs_limb, rhs_limb, limb_bits); + overflow = limb_overflow ^ zig_subo_i16(&res_limb, tmp_limb, overflow ? INT16_C(1) : INT16_C(0), limb_bits); + memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb)); + } else { + uint16_t res_limb; + uint16_t tmp_limb; + uint16_t lhs_limb; + uint16_t rhs_limb; + bool limb_overflow; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + limb_overflow = zig_subo_u16(&tmp_limb, lhs_limb, rhs_limb, limb_bits); + overflow = limb_overflow ^ zig_subo_u16(&res_limb, tmp_limb, overflow ? UINT16_C(1) : UINT16_C(0), limb_bits); + memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb)); + } + + remaining_bytes -= 16 / CHAR_BIT; + +#if zig_little_endian + byte_offset += 16 / CHAR_BIT; +#endif + } + + while (remaining_bytes >= 8 / CHAR_BIT) { + uint16_t limb_bits = 8 - (remaining_bytes == 8 / CHAR_BIT ? top_bits : 0); + +#if zig_big_endian + byte_offset -= 8 / CHAR_BIT; +#endif + + if (remaining_bytes == 8 / CHAR_BIT && is_signed) { + int8_t res_limb; + int8_t tmp_limb; + int8_t lhs_limb; + int8_t rhs_limb; + bool limb_overflow; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + limb_overflow = zig_subo_i8(&tmp_limb, lhs_limb, rhs_limb, limb_bits); + overflow = limb_overflow ^ zig_subo_i8(&res_limb, tmp_limb, overflow ? INT8_C(1) : INT8_C(0), limb_bits); + memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb)); + } else { + uint8_t res_limb; + uint8_t tmp_limb; + uint8_t lhs_limb; + uint8_t rhs_limb; + bool limb_overflow; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + limb_overflow = zig_subo_u8(&tmp_limb, lhs_limb, rhs_limb, limb_bits); + overflow = limb_overflow ^ zig_subo_u8(&res_limb, tmp_limb, overflow ? UINT8_C(1) : UINT8_C(0), limb_bits); + memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb)); + } + + remaining_bytes -= 8 / CHAR_BIT; + +#if zig_little_endian + byte_offset += 8 / CHAR_BIT; +#endif + } + + return overflow; +} + +static inline void zig_addw_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) { + (void)zig_addo_big(res, lhs, rhs, is_signed, bits); +} + +static inline void zig_subw_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) { + (void)zig_subo_big(res, lhs, rhs, is_signed, bits); +} + static inline uint16_t zig_clz_big(const void *val, bool is_signed, uint16_t bits) { const uint8_t *val_bytes = val; uint16_t byte_offset = 0; @@ -3092,80 +3386,6 @@ zig_msvc_atomics_128op(u128, max) #endif /* _MSC_VER && (_M_IX86 || _M_X64) */ -/* ============================= Vector Support ============================= */ - -#define zig_cmp_vec(operation, operator) \ - static inline void zig_##operation##_vec(bool *result, const void *lhs, const void *rhs, uint32_t len, bool is_signed, uint16_t elem_bits) { \ - uint32_t index = 0; \ - const uint8_t *lhs_ptr = lhs; \ - const uint8_t *rhs_ptr = rhs; \ - uint16_t elem_bytes = zig_int_bytes(elem_bits); \ - \ - while (index < len) { \ - result[index] = zig_cmp_big(lhs_ptr, rhs_ptr, is_signed, elem_bits) operator 0; \ - lhs_ptr += elem_bytes; \ - rhs_ptr += elem_bytes; \ - index += 1; \ - } \ - } -zig_cmp_vec(eq, ==) -zig_cmp_vec(ne, !=) -zig_cmp_vec(lt, < ) -zig_cmp_vec(le, <=) -zig_cmp_vec(gt, > ) -zig_cmp_vec(ge, >=) - -static inline void zig_clz_vec(void *result, const void *val, uint32_t len, bool is_signed, uint16_t elem_bits) { - uint32_t index = 0; - const uint8_t *val_ptr = val; - uint16_t elem_bytes = zig_int_bytes(elem_bits); - - while (index < len) { - uint16_t lz = zig_clz_big(val_ptr, is_signed, elem_bits); - if (elem_bits <= 128) { - ((uint8_t *)result)[index] = (uint8_t)lz; - } else { - ((uint16_t *)result)[index] = lz; - } - val_ptr += elem_bytes; - index += 1; - } -} - -static inline void zig_ctz_vec(void *result, const void *val, uint32_t len, bool is_signed, uint16_t elem_bits) { - uint32_t index = 0; - const uint8_t *val_ptr = val; - uint16_t elem_bytes = zig_int_bytes(elem_bits); - - while (index < len) { - uint16_t tz = zig_ctz_big(val_ptr, is_signed, elem_bits); - if (elem_bits <= 128) { - ((uint8_t *)result)[index] = (uint8_t)tz; - } else { - ((uint16_t *)result)[index] = tz; - } - val_ptr += elem_bytes; - index += 1; - } -} - -static inline void zig_popcount_vec(void *result, const void *val, uint32_t len, bool is_signed, uint16_t elem_bits) { - uint32_t index = 0; - const uint8_t *val_ptr = val; - uint16_t elem_bytes = zig_int_bytes(elem_bits); - - while (index < len) { - uint16_t pc = zig_popcount_big(val_ptr, is_signed, elem_bits); - if (elem_bits <= 128) { - ((uint8_t *)result)[index] = (uint8_t)pc; - } else { - ((uint16_t *)result)[index] = pc; - } - val_ptr += elem_bytes; - index += 1; - } -} - /* ======================== Special Case Intrinsics ========================= */ #if (_MSC_VER && _M_X64) || defined(__x86_64__) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index b8606b1a17..5e92a6f76c 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -444,8 +444,8 @@ pub const Function = struct { return f.object.dg.renderType(w, t); } - fn renderIntCast(f: *Function, w: anytype, dest_ty: Type, src: CValue, src_ty: Type, location: ValueRenderLocation) !void { - return f.object.dg.renderIntCast(w, dest_ty, .{ .c_value = .{ .f = f, .value = src } }, src_ty, location); + fn renderIntCast(f: *Function, w: anytype, dest_ty: Type, src: CValue, v: Vectorizer, src_ty: Type, location: ValueRenderLocation) !void { + return f.object.dg.renderIntCast(w, dest_ty, .{ .c_value = .{ .f = f, .value = src, .v = v } }, src_ty, location); } fn fmtIntLiteral(f: *Function, ty: Type, val: Value) !std.fmt.Formatter(formatIntLiteral) { @@ -1593,6 +1593,7 @@ pub const DeclGen = struct { c_value: struct { f: *Function, value: CValue, + v: Vectorizer, }, value: struct { value: Value, @@ -1602,6 +1603,7 @@ pub const DeclGen = struct { switch (self.*) { .c_value => |v| { try v.f.writeCValue(w, v.value, location); + try v.v.elem(v.f, w); }, .value => |v| { try dg.renderValue(w, value_ty, v.value, location); @@ -1887,7 +1889,6 @@ pub const DeclGen = struct { if (cty.isFloat()) cty.floatActiveBits(dg.module.getTarget()) else dg.byteSize(cty) * 8, }), .array => try writer.writeAll("big"), - .vector => try writer.writeAll("vec"), } } @@ -1895,34 +1896,19 @@ pub const DeclGen = struct { switch (info) { .none => {}, .bits => { - const cty = try dg.typeToCType(ty, .complete); - if (cty.castTag(.vector)) |pl| { - var len_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, .data = pl.data.len }; - try writer.print(", {}", .{try dg.fmtIntLiteral( - Type.u32, - Value.initPayload(&len_pl.base), - .FunctionArgument, - )}); - } - const target = dg.module.getTarget(); - const elem_ty = ty.shallowElemType(); - const elem_info = if (elem_ty.isAbiInt()) - elem_ty.intInfo(target) - else - std.builtin.Type.Int{ - .signedness = .unsigned, - .bits = @intCast(u16, elem_ty.bitSize(target)), - }; - switch (cty.tag()) { - else => {}, - .array, .vector => try writer.print(", {}", .{elem_info.signedness == .signed}), - } + const int_info = if (ty.isAbiInt()) ty.intInfo(target) else std.builtin.Type.Int{ + .signedness = .unsigned, + .bits = @intCast(u16, ty.bitSize(target)), + }; - var bits_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, .data = elem_info.bits }; + const cty = try dg.typeToCType(ty, .complete); + if (cty.tag() == .array) try writer.print(", {}", .{int_info.signedness == .signed}); + + var bits_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, .data = int_info.bits }; try writer.print(", {}", .{try dg.fmtIntLiteral(switch (cty.tag()) { else => Type.u8, - .array, .vector => Type.u16, + .array => Type.u16, }, Value.initPayload(&bits_pl.base), .FunctionArgument)}); }, } @@ -2786,10 +2772,10 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, .div_trunc, .div_exact => try airBinOp(f, inst, "/", "div_trunc", .none), .rem => blk: { const bin_op = f.air.instructions.items(.data)[inst].bin_op; - const lhs_ty = f.air.typeOf(bin_op.lhs); + const lhs_scalar_ty = f.air.typeOf(bin_op.lhs).scalarType(); // For binary operations @TypeOf(lhs)==@TypeOf(rhs), // so we only check one. - break :blk if (lhs_ty.isInt()) + break :blk if (lhs_scalar_ty.isInt()) try airBinOp(f, inst, "%", "rem", .none) else try airBinFloatOp(f, inst, "fmod"); @@ -2833,10 +2819,10 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, .slice => try airSlice(f, inst), - .cmp_gt => try airCmpOp(f, inst, .gt), - .cmp_gte => try airCmpOp(f, inst, .gte), - .cmp_lt => try airCmpOp(f, inst, .lt), - .cmp_lte => try airCmpOp(f, inst, .lte), + .cmp_gt => try airCmpOp(f, inst, f.air.instructions.items(.data)[inst].bin_op, .gt), + .cmp_gte => try airCmpOp(f, inst, f.air.instructions.items(.data)[inst].bin_op, .gte), + .cmp_lt => try airCmpOp(f, inst, f.air.instructions.items(.data)[inst].bin_op, .lt), + .cmp_lte => try airCmpOp(f, inst, f.air.instructions.items(.data)[inst].bin_op, .lte), .cmp_eq => try airEquality(f, inst, .eq), .cmp_neq => try airEquality(f, inst, .neq), @@ -2844,7 +2830,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, .cmp_vector => blk: { const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; const extra = f.air.extraData(Air.VectorCmp, ty_pl.payload).data; - break :blk try airCmpBuiltinCall(f, inst, extra, extra.compareOperator(), .operator, .bits,); + break :blk try airCmpOp(f, inst, extra, extra.compareOperator()); }, .cmp_lt_errors_len => try airCmpLtErrorsLen(f, inst), @@ -3294,7 +3280,10 @@ fn airArg(f: *Function, inst: Air.Inst.Index) !CValue { fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { const ty_op = f.air.instructions.items(.data)[inst].ty_op; - const ptr_info = f.air.typeOf(ty_op.operand).ptrInfo().data; + + const ptr_ty = f.air.typeOf(ty_op.operand); + const ptr_scalar_ty = ptr_ty.scalarType(); + const ptr_info = ptr_scalar_ty.ptrInfo().data; const src_ty = ptr_info.pointee_type; if (!src_ty.hasRuntimeBitsIgnoreComptime() or @@ -3312,16 +3301,19 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { const is_aligned = ptr_info.@"align" == 0 or ptr_info.@"align" >= src_ty.abiAlignment(target); const is_array = lowersToArray(src_ty, target); const need_memcpy = !is_aligned or is_array; - const writer = f.object.writer(); + const writer = f.object.writer(); const local = try f.allocLocal(inst, src_ty); + const v = try Vectorizer.start(f, inst, writer, ptr_ty); if (need_memcpy) { try writer.writeAll("memcpy("); if (!is_array) try writer.writeByte('&'); - try f.writeCValue(writer, local, .FunctionArgument); + try f.writeCValue(writer, local, .Other); + try v.elem(f, writer); try writer.writeAll(", (const char *)"); try f.writeCValue(writer, operand, .Other); + try v.elem(f, writer); try writer.writeAll(", sizeof("); try f.renderType(writer, src_ty); try writer.writeAll("))"); @@ -3351,6 +3343,7 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { const field_ty = Type.initPayload(&field_pl.base); try f.writeCValue(writer, local, .Other); + try v.elem(f, writer); try writer.writeAll(" = ("); try f.renderType(writer, src_ty); try writer.writeAll(")zig_wrap_"); @@ -3369,16 +3362,21 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { try f.object.dg.renderTypeForBuiltinFnName(writer, host_ty); try writer.writeByte('('); try f.writeCValueDeref(writer, operand); + try v.elem(f, writer); try writer.print(", {})", .{try f.fmtIntLiteral(bit_offset_ty, bit_offset_val)}); if (cant_cast) try writer.writeByte(')'); try f.object.dg.renderBuiltinInfo(writer, field_ty, .bits); try writer.writeByte(')'); } else { try f.writeCValue(writer, local, .Other); + try v.elem(f, writer); try writer.writeAll(" = "); try f.writeCValueDeref(writer, operand); + try v.elem(f, writer); } try writer.writeAll(";\n"); + try v.end(f, inst, writer); + return local; } @@ -3444,15 +3442,22 @@ fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue { const operand = try f.resolveInst(ty_op.operand); try reap(f, inst, &.{ty_op.operand}); - const writer = f.object.writer(); - const inst_ty = f.air.typeOfIndex(inst); - const local = try f.allocLocal(inst, inst_ty); - const operand_ty = f.air.typeOf(ty_op.operand); + const inst_ty = f.air.typeOfIndex(inst); + const inst_scalar_ty = inst_ty.scalarType(); + const operand_ty = f.air.typeOf(ty_op.operand); + const scalar_ty = operand_ty.scalarType(); + + const writer = f.object.writer(); + const local = try f.allocLocal(inst, inst_ty); + const v = try Vectorizer.start(f, inst, writer, operand_ty); try f.writeCValue(writer, local, .Other); + try v.elem(f, writer); try writer.writeAll(" = "); - try f.renderIntCast(writer, inst_ty, operand, operand_ty, .Other); + try f.renderIntCast(writer, inst_scalar_ty, operand, v, scalar_ty, .Other); try writer.writeAll(";\n"); + try v.end(f, inst, writer); + return local; } @@ -3578,7 +3583,10 @@ fn storeUndefined(f: *Function, lhs_child_ty: Type, dest_ptr: CValue) !CValue { fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { // *a = b; const bin_op = f.air.instructions.items(.data)[inst].bin_op; - const ptr_info = f.air.typeOf(bin_op.lhs).ptrInfo().data; + + const ptr_ty = f.air.typeOf(bin_op.lhs); + const ptr_scalar_ty = ptr_ty.scalarType(); + const ptr_info = ptr_scalar_ty.ptrInfo().data; if (!ptr_info.pointee_type.hasRuntimeBitsIgnoreComptime()) { try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); return .none; @@ -3601,11 +3609,13 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { ptr_info.@"align" >= ptr_info.pointee_type.abiAlignment(target); const is_array = lowersToArray(ptr_info.pointee_type, target); const need_memcpy = !is_aligned or is_array; - const writer = f.object.writer(); const src_val = try f.resolveInst(bin_op.rhs); try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); + const writer = f.object.writer(); + const v = try Vectorizer.start(f, inst, writer, ptr_ty); + if (need_memcpy) { // For this memcpy to safely work we need the rhs to have the same // underlying type as the lhs (i.e. they must both be arrays of the same underlying type). @@ -3626,9 +3636,11 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll("memcpy((char *)"); try f.writeCValue(writer, ptr_val, .FunctionArgument); + try v.elem(f, writer); try writer.writeAll(", "); if (!is_array) try writer.writeByte('&'); try f.writeCValue(writer, array_src, .FunctionArgument); + try v.elem(f, writer); try writer.writeAll(", sizeof("); try f.renderType(writer, src_ty); try writer.writeAll("))"); @@ -3672,12 +3684,14 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { const mask_val = Value.initPayload(&mask_pl.base); try f.writeCValueDeref(writer, ptr_val); + try v.elem(f, writer); try writer.writeAll(" = zig_or_"); try f.object.dg.renderTypeForBuiltinFnName(writer, host_ty); try writer.writeAll("(zig_and_"); try f.object.dg.renderTypeForBuiltinFnName(writer, host_ty); try writer.writeByte('('); try f.writeCValueDeref(writer, ptr_val); + try v.elem(f, writer); try writer.print(", {x}), zig_shl_", .{try f.fmtIntLiteral(host_ty, mask_val)}); try f.object.dg.renderTypeForBuiltinFnName(writer, host_ty); try writer.writeByte('('); @@ -3699,14 +3713,19 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeByte(')'); } try f.writeCValue(writer, src_val, .Other); + try v.elem(f, writer); if (cant_cast) try writer.writeByte(')'); try writer.print(", {}))", .{try f.fmtIntLiteral(bit_offset_ty, bit_offset_val)}); } else { try f.writeCValueDeref(writer, ptr_val); + try v.elem(f, writer); try writer.writeAll(" = "); try f.writeCValue(writer, src_val, .Other); + try v.elem(f, writer); } try writer.writeAll(";\n"); + try v.end(f, inst, writer); + return .none; } @@ -3724,51 +3743,39 @@ fn airOverflow(f: *Function, inst: Air.Inst.Index, operation: []const u8, info: try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); const inst_ty = f.air.typeOfIndex(inst); - const vector_ty = f.air.typeOf(bin_op.lhs); - const scalar_ty = vector_ty.scalarType(); + const operand_ty = f.air.typeOf(bin_op.lhs); + const scalar_ty = operand_ty.scalarType(); + const w = f.object.writer(); - const local = try f.allocLocal(inst, inst_ty); - - switch (vector_ty.zigTypeTag()) { - .Vector => { - try w.writeAll("zig_v"); - try w.writeAll(operation); - try w.writeAll("o_"); - try f.object.dg.renderTypeForBuiltinFnName(w, scalar_ty); - try w.writeAll("("); - try f.writeCValueMember(w, local, .{ .field = 1 }); - try w.writeAll(", "); - try f.writeCValueMember(w, local, .{ .field = 0 }); - try w.print(", {d}, ", .{vector_ty.vectorLen()}); - }, - else => { - try f.writeCValueMember(w, local, .{ .field = 1 }); - try w.writeAll(" = zig_"); - try w.writeAll(operation); - try w.writeAll("o_"); - try f.object.dg.renderTypeForBuiltinFnName(w, scalar_ty); - try w.writeAll("(&"); - try f.writeCValueMember(w, local, .{ .field = 0 }); - try w.writeAll(", "); - }, - } - + const v = try Vectorizer.start(f, inst, w, operand_ty); + try f.writeCValueMember(w, local, .{ .field = 1 }); + try v.elem(f, w); + try w.writeAll(" = zig_"); + try w.writeAll(operation); + try w.writeAll("o_"); + try f.object.dg.renderTypeForBuiltinFnName(w, scalar_ty); + try w.writeAll("(&"); + try f.writeCValueMember(w, local, .{ .field = 0 }); + try v.elem(f, w); + try w.writeAll(", "); try f.writeCValue(w, lhs, .FunctionArgument); + try v.elem(f, w); try w.writeAll(", "); try f.writeCValue(w, rhs, .FunctionArgument); + try v.elem(f, w); try f.object.dg.renderBuiltinInfo(w, scalar_ty, info); try w.writeAll(");\n"); + try v.end(f, inst, w); return local; } fn airNot(f: *Function, inst: Air.Inst.Index) !CValue { - const inst_ty = f.air.typeOfIndex(inst); - if (inst_ty.tag() != .bool) - return try airUnBuiltinCall(f, inst, "not", .bits); - const ty_op = f.air.instructions.items(.data)[inst].ty_op; + const operand_ty = f.air.typeOf(ty_op.operand); + const scalar_ty = operand_ty.scalarType(); + if (scalar_ty.tag() != .bool) return try airUnBuiltinCall(f, inst, "not", .bits); if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ty_op.operand}); @@ -3778,14 +3785,20 @@ fn airNot(f: *Function, inst: Air.Inst.Index) !CValue { const op = try f.resolveInst(ty_op.operand); try reap(f, inst, &.{ty_op.operand}); + const inst_ty = f.air.typeOfIndex(inst); + const writer = f.object.writer(); const local = try f.allocLocal(inst, inst_ty); - + const v = try Vectorizer.start(f, inst, writer, operand_ty); try f.writeCValue(writer, local, .Other); + try v.elem(f, writer); try writer.writeAll(" = "); try writer.writeByte('!'); try f.writeCValue(writer, op, .Other); + try v.elem(f, writer); try writer.writeAll(";\n"); + try v.end(f, inst, writer); + return local; } @@ -3798,71 +3811,89 @@ fn airBinOp( ) !CValue { const bin_op = f.air.instructions.items(.data)[inst].bin_op; const operand_ty = f.air.typeOf(bin_op.lhs); + const scalar_ty = operand_ty.scalarType(); const target = f.object.dg.module.getTarget(); - if ((operand_ty.isInt() and operand_ty.bitSize(target) > 64) or operand_ty.isRuntimeFloat()) + if ((scalar_ty.isInt() and scalar_ty.bitSize(target) > 64) or scalar_ty.isRuntimeFloat()) return try airBinBuiltinCall(f, inst, operation, info); - const lhs = try f.resolveInst(bin_op.lhs); - const rhs = try f.resolveInst(bin_op.rhs); - - try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - - if (f.liveness.isUnused(inst)) return .none; - - const inst_ty = f.air.typeOfIndex(inst); - - const writer = f.object.writer(); - const local = try f.allocLocal(inst, inst_ty); - try f.writeCValue(writer, local, .Other); - try writer.writeAll(" = "); - try f.writeCValue(writer, lhs, .Other); - try writer.writeByte(' '); - try writer.writeAll(operator); - try writer.writeByte(' '); - try f.writeCValue(writer, rhs, .Other); - try writer.writeAll(";\n"); - - return local; -} - -fn airCmpOp(f: *Function, inst: Air.Inst.Index, operator: std.math.CompareOperator) !CValue { - const bin_op = f.air.instructions.items(.data)[inst].bin_op; - if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); return .none; } - const operand_ty = f.air.typeOf(bin_op.lhs); - const target = f.object.dg.module.getTarget(); - const operand_bits = operand_ty.bitSize(target); - if (operand_ty.isInt() and operand_bits > 64) - return airCmpBuiltinCall( - f, - inst, - bin_op, - operator, - .cmp, - if (operand_bits > 128) .bits else .none, - ); - if (operand_ty.isRuntimeFloat()) - return airCmpBuiltinCall(f, inst, bin_op, operator, .operator, .none); - - const inst_ty = f.air.typeOfIndex(inst); const lhs = try f.resolveInst(bin_op.lhs); const rhs = try f.resolveInst(bin_op.rhs); try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); + const inst_ty = f.air.typeOfIndex(inst); + const writer = f.object.writer(); const local = try f.allocLocal(inst, inst_ty); + const v = try Vectorizer.start(f, inst, writer, operand_ty); try f.writeCValue(writer, local, .Other); + try v.elem(f, writer); try writer.writeAll(" = "); try f.writeCValue(writer, lhs, .Other); + try v.elem(f, writer); + try writer.writeByte(' '); + try writer.writeAll(operator); + try writer.writeByte(' '); + try f.writeCValue(writer, rhs, .Other); + try v.elem(f, writer); + try writer.writeAll(";\n"); + try v.end(f, inst, writer); + + return local; +} + +fn airCmpOp( + f: *Function, + inst: Air.Inst.Index, + data: anytype, + operator: std.math.CompareOperator, +) !CValue { + if (f.liveness.isUnused(inst)) { + try reap(f, inst, &.{ data.lhs, data.rhs }); + return .none; + } + + const operand_ty = f.air.typeOf(data.lhs); + const scalar_ty = operand_ty.scalarType(); + + const target = f.object.dg.module.getTarget(); + const scalar_bits = scalar_ty.bitSize(target); + if (scalar_ty.isInt() and scalar_bits > 64) + return airCmpBuiltinCall( + f, + inst, + data, + operator, + .cmp, + if (scalar_bits > 128) .bits else .none, + ); + if (scalar_ty.isRuntimeFloat()) + return airCmpBuiltinCall(f, inst, data, operator, .operator, .none); + + const inst_ty = f.air.typeOfIndex(inst); + const lhs = try f.resolveInst(data.lhs); + const rhs = try f.resolveInst(data.rhs); + try reap(f, inst, &.{ data.lhs, data.rhs }); + + const writer = f.object.writer(); + const local = try f.allocLocal(inst, inst_ty); + const v = try Vectorizer.start(f, inst, writer, operand_ty); + try f.writeCValue(writer, local, .Other); + try v.elem(f, writer); + try writer.writeAll(" = "); + try f.writeCValue(writer, lhs, .Other); + try v.elem(f, writer); try writer.writeByte(' '); try writer.writeAll(compareOperatorC(operator)); try writer.writeByte(' '); try f.writeCValue(writer, rhs, .Other); + try v.elem(f, writer); try writer.writeAll(";\n"); + try v.end(f, inst, writer); return local; } @@ -3974,11 +4005,14 @@ fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue { try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); const inst_ty = f.air.typeOfIndex(inst); - const elem_ty = inst_ty.elemType2(); + const inst_scalar_ty = inst_ty.scalarType(); + const elem_ty = inst_scalar_ty.elemType2(); const local = try f.allocLocal(inst, inst_ty); const writer = f.object.writer(); + const v = try Vectorizer.start(f, inst, writer, inst_ty); try f.writeCValue(writer, local, .Other); + try v.elem(f, writer); try writer.writeAll(" = "); if (elem_ty.hasRuntimeBitsIgnoreComptime()) { @@ -3986,19 +4020,26 @@ fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue { // results in a NULL pointer, or if LHS is NULL. The operation is only UB // if the result is NULL and then dereferenced. try writer.writeByte('('); - try f.renderType(writer, inst_ty); + try f.renderType(writer, inst_scalar_ty); try writer.writeAll(")(((uintptr_t)"); try f.writeCValue(writer, lhs, .Other); + try v.elem(f, writer); try writer.writeAll(") "); try writer.writeByte(operator); try writer.writeAll(" ("); try f.writeCValue(writer, rhs, .Other); + try v.elem(f, writer); try writer.writeAll("*sizeof("); try f.renderType(writer, elem_ty); try writer.writeAll(")))"); - } else try f.writeCValue(writer, lhs, .Initializer); + } else { + try f.writeCValue(writer, lhs, .Other); + try v.elem(f, writer); + } try writer.writeAll(";\n"); + try v.end(f, inst, writer); + return local; } @@ -4011,10 +4052,12 @@ fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: u8, operation: []cons } const inst_ty = f.air.typeOfIndex(inst); + const inst_scalar_ty = inst_ty.scalarType(); + const target = f.object.dg.module.getTarget(); - if (inst_ty.isInt() and inst_ty.bitSize(target) > 64) + if (inst_scalar_ty.isInt() and inst_scalar_ty.bitSize(target) > 64) return try airBinBuiltinCall(f, inst, operation[1..], .none); - if (inst_ty.isRuntimeFloat()) + if (inst_scalar_ty.isRuntimeFloat()) return try airBinFloatOp(f, inst, operation); const lhs = try f.resolveInst(bin_op.lhs); @@ -4023,19 +4066,26 @@ fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: u8, operation: []cons const writer = f.object.writer(); const local = try f.allocLocal(inst, inst_ty); + const v = try Vectorizer.start(f, inst, writer, inst_ty); try f.writeCValue(writer, local, .Other); + try v.elem(f, writer); // (lhs <> rhs) ? lhs : rhs try writer.writeAll(" = ("); try f.writeCValue(writer, lhs, .Other); + try v.elem(f, writer); try writer.writeByte(' '); try writer.writeByte(operator); try writer.writeByte(' '); try f.writeCValue(writer, rhs, .Other); + try v.elem(f, writer); try writer.writeAll(") ? "); try f.writeCValue(writer, lhs, .Other); + try v.elem(f, writer); try writer.writeAll(" : "); try f.writeCValue(writer, rhs, .Other); + try v.elem(f, writer); try writer.writeAll(";\n"); + try v.end(f, inst, writer); return local; } @@ -6002,30 +6052,35 @@ fn airUnBuiltinCall( const operand = try f.resolveInst(ty_op.operand); try reap(f, inst, &.{ty_op.operand}); const inst_ty = f.air.typeOfIndex(inst); + const inst_scalar_ty = inst_ty.scalarType(); const operand_ty = f.air.typeOf(ty_op.operand); + const scalar_ty = operand_ty.scalarType(); - const inst_cty = try f.typeToCType(inst_ty, .complete); - const ref_ret = switch (inst_cty.tag()) { - else => false, - .array, .vector => true, - }; + const inst_scalar_cty = try f.typeToCType(inst_scalar_ty, .complete); + const ref_ret = inst_scalar_cty.tag() == .array; const writer = f.object.writer(); const local = try f.allocLocal(inst, inst_ty); + const v = try Vectorizer.start(f, inst, writer, operand_ty); if (!ref_ret) { try f.writeCValue(writer, local, .Other); + try v.elem(f, writer); try writer.writeAll(" = "); } try writer.print("zig_{s}_", .{operation}); - try f.object.dg.renderTypeForBuiltinFnName(writer, operand_ty); + try f.object.dg.renderTypeForBuiltinFnName(writer, scalar_ty); try writer.writeByte('('); if (ref_ret) { try f.writeCValue(writer, local, .FunctionArgument); + try v.elem(f, writer); try writer.writeAll(", "); } try f.writeCValue(writer, operand, .FunctionArgument); - try f.object.dg.renderBuiltinInfo(writer, operand_ty, info); + try v.elem(f, writer); + try f.object.dg.renderBuiltinInfo(writer, scalar_ty, info); try writer.writeAll(");\n"); + try v.end(f, inst, writer); + return local; } @@ -6047,21 +6102,38 @@ fn airBinBuiltinCall( try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); const inst_ty = f.air.typeOfIndex(inst); + const inst_scalar_ty = inst_ty.scalarType(); const operand_ty = f.air.typeOf(bin_op.lhs); + const scalar_ty = operand_ty.scalarType(); + + const inst_scalar_cty = try f.typeToCType(inst_scalar_ty, .complete); + const ref_ret = inst_scalar_cty.tag() == .array; const writer = f.object.writer(); const local = try f.allocLocal(inst, inst_ty); - try f.writeCValue(writer, local, .Other); - try writer.writeAll(" = zig_"); - try writer.writeAll(operation); - try writer.writeByte('_'); - try f.object.dg.renderTypeForBuiltinFnName(writer, operand_ty); + const v = try Vectorizer.start(f, inst, writer, operand_ty); + if (!ref_ret) { + try f.writeCValue(writer, local, .Other); + try v.elem(f, writer); + try writer.writeAll(" = "); + } + try writer.print("zig_{s}_", .{operation}); + try f.object.dg.renderTypeForBuiltinFnName(writer, scalar_ty); try writer.writeByte('('); + if (ref_ret) { + try f.writeCValue(writer, local, .FunctionArgument); + try v.elem(f, writer); + try writer.writeAll(", "); + } try f.writeCValue(writer, lhs, .FunctionArgument); + try v.elem(f, writer); try writer.writeAll(", "); try f.writeCValue(writer, rhs, .FunctionArgument); - try f.object.dg.renderBuiltinInfo(writer, operand_ty, info); + try v.elem(f, writer); + try f.object.dg.renderBuiltinInfo(writer, scalar_ty, info); try writer.writeAll(");\n"); + try v.end(f, inst, writer); + return local; } @@ -6073,45 +6145,56 @@ fn airCmpBuiltinCall( operation: enum { cmp, operator }, info: BuiltinInfo, ) !CValue { - const inst_ty = f.air.typeOfIndex(inst); - const operand_ty = f.air.typeOf(data.lhs); + if (f.liveness.isUnused(inst)) { + try reap(f, inst, &.{ data.lhs, data.rhs }); + return .none; + } const lhs = try f.resolveInst(data.lhs); const rhs = try f.resolveInst(data.rhs); try reap(f, inst, &.{ data.lhs, data.rhs }); - const inst_cty = try f.typeToCType(inst_ty, .complete); - const ref_ret = switch (inst_cty.tag()) { - else => false, - .array, .vector => true, - }; + const inst_ty = f.air.typeOfIndex(inst); + const inst_scalar_ty = inst_ty.scalarType(); + const operand_ty = f.air.typeOf(data.lhs); + const scalar_ty = operand_ty.scalarType(); + + const inst_scalar_cty = try f.typeToCType(inst_scalar_ty, .complete); + const ref_ret = inst_scalar_cty.tag() == .array; const writer = f.object.writer(); const local = try f.allocLocal(inst, inst_ty); + const v = try Vectorizer.start(f, inst, writer, operand_ty); if (!ref_ret) { try f.writeCValue(writer, local, .Other); + try v.elem(f, writer); try writer.writeAll(" = "); } try writer.print("zig_{s}_", .{switch (operation) { else => @tagName(operation), .operator => compareOperatorAbbrev(operator), }}); - try f.object.dg.renderTypeForBuiltinFnName(writer, operand_ty); + try f.object.dg.renderTypeForBuiltinFnName(writer, scalar_ty); try writer.writeByte('('); if (ref_ret) { try f.writeCValue(writer, local, .FunctionArgument); + try v.elem(f, writer); try writer.writeAll(", "); } try f.writeCValue(writer, lhs, .FunctionArgument); + try v.elem(f, writer); try writer.writeAll(", "); try f.writeCValue(writer, rhs, .FunctionArgument); - try f.object.dg.renderBuiltinInfo(writer, operand_ty, info); + try v.elem(f, writer); + try f.object.dg.renderBuiltinInfo(writer, scalar_ty, info); try writer.writeByte(')'); if (!ref_ret) try writer.print(" {s} {}", .{ compareOperatorC(operator), try f.fmtIntLiteral(Type.initTag(.i32), Value.zero), }); try writer.writeAll(";\n"); + try v.end(f, inst, writer); + return local; } @@ -6498,65 +6581,35 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue { const operand = try f.resolveInst(reduce.operand); try reap(f, inst, &.{reduce.operand}); const operand_ty = f.air.typeOf(reduce.operand); - const vector_len = operand_ty.vectorLen(); const writer = f.object.writer(); - const Op = union(enum) { - call_fn: []const u8, + const op: union(enum) { + float_op: []const u8, + builtin: []const u8, infix: []const u8, ternary: []const u8, - }; - var fn_name_buf: [64]u8 = undefined; - const op: Op = switch (reduce.operation) { + } = switch (reduce.operation) { .And => .{ .infix = " &= " }, .Or => .{ .infix = " |= " }, .Xor => .{ .infix = " ^= " }, .Min => switch (scalar_ty.zigTypeTag()) { - .Int => Op{ .ternary = " < " }, - .Float => op: { - const float_bits = scalar_ty.floatBits(target); - break :op Op{ - .call_fn = std.fmt.bufPrintZ(&fn_name_buf, "{s}fmin{s}", .{ - libcFloatPrefix(float_bits), libcFloatSuffix(float_bits), - }) catch unreachable, - }; - }, + .Int => .{ .ternary = " < " }, + .Float => .{ .float_op = "fmin" }, else => unreachable, }, .Max => switch (scalar_ty.zigTypeTag()) { - .Int => Op{ .ternary = " > " }, - .Float => op: { - const float_bits = scalar_ty.floatBits(target); - break :op Op{ - .call_fn = std.fmt.bufPrintZ(&fn_name_buf, "{s}fmax{s}", .{ - libcFloatPrefix(float_bits), libcFloatSuffix(float_bits), - }) catch unreachable, - }; - }, + .Int => .{ .ternary = " > " }, + .Float => .{ .float_op = "fmax" }, else => unreachable, }, .Add => switch (scalar_ty.zigTypeTag()) { - .Int => Op{ .infix = " += " }, - .Float => op: { - const float_bits = scalar_ty.floatBits(target); - break :op Op{ - .call_fn = std.fmt.bufPrintZ(&fn_name_buf, "__add{s}f3", .{ - compilerRtFloatAbbrev(float_bits), - }) catch unreachable, - }; - }, + .Int => .{ .infix = " += " }, + .Float => .{ .builtin = "add" }, else => unreachable, }, .Mul => switch (scalar_ty.zigTypeTag()) { - .Int => Op{ .infix = " *= " }, - .Float => op: { - const float_bits = scalar_ty.floatBits(target); - break :op Op{ - .call_fn = std.fmt.bufPrintZ(&fn_name_buf, "__mul{s}f3", .{ - compilerRtFloatAbbrev(float_bits), - }) catch unreachable, - }; - }, + .Int => .{ .infix = " *= " }, + .Float => .{ .builtin = "mul" }, else => unreachable, }, }; @@ -6572,75 +6625,94 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue { // } // break :reduce accum; // } - const it = try f.allocLocal(inst, Type.usize); - try f.writeCValue(writer, it, .Other); - try writer.writeAll(" = 0;\n"); const accum = try f.allocLocal(inst, scalar_ty); try f.writeCValue(writer, accum, .Other); try writer.writeAll(" = "); - const init_val = switch (reduce.operation) { - .And, .Or, .Xor, .Add => "0", + var arena = std.heap.ArenaAllocator.init(f.object.dg.gpa); + defer arena.deinit(); + + const ExpectedContents = union { + u: Value.Payload.U64, + i: Value.Payload.I64, + f16: Value.Payload.Float_16, + f32: Value.Payload.Float_32, + f64: Value.Payload.Float_64, + f80: Value.Payload.Float_80, + f128: Value.Payload.Float_128, + }; + var stack align(@alignOf(ExpectedContents)) = + std.heap.stackFallback(@sizeOf(ExpectedContents), arena.allocator()); + + try f.object.dg.renderValue(writer, scalar_ty, switch (reduce.operation) { + .Or, .Xor, .Add => Value.zero, + .And => switch (scalar_ty.zigTypeTag()) { + .Bool => Value.one, + else => switch (scalar_ty.intInfo(target).signedness) { + .unsigned => try scalar_ty.maxInt(stack.get(), target), + .signed => Value.negative_one, + }, + }, .Min => switch (scalar_ty.zigTypeTag()) { - .Int => "TODO_intmax", - .Float => "TODO_nan", + .Bool => Value.one, + .Int => try scalar_ty.maxInt(stack.get(), target), + .Float => try Value.floatToValue(std.math.nan(f128), stack.get(), scalar_ty, target), else => unreachable, }, .Max => switch (scalar_ty.zigTypeTag()) { - .Int => "TODO_intmin", - .Float => "TODO_nan", + .Bool => Value.zero, + .Int => try scalar_ty.minInt(stack.get(), target), + .Float => try Value.floatToValue(std.math.nan(f128), stack.get(), scalar_ty, target), else => unreachable, }, - .Mul => "1", - }; - try writer.writeAll(init_val); - try writer.writeAll(";"); - try f.object.indent_writer.insertNewline(); - try writer.writeAll("for (;"); - try f.writeCValue(writer, it, .Other); - try writer.print("<{d};++", .{vector_len}); - try f.writeCValue(writer, it, .Other); - try writer.writeAll(") "); - try f.writeCValue(writer, accum, .Other); + .Mul => Value.one, + }, .Initializer); + try writer.writeAll(";\n"); + const v = try Vectorizer.start(f, inst, writer, operand_ty); + try f.writeCValue(writer, accum, .Other); switch (op) { - .call_fn => |fn_name| { - try writer.print(" = {s}(", .{fn_name}); + .float_op => |operation| { + try writer.writeAll(" = zig_libc_name_"); + try f.object.dg.renderTypeForBuiltinFnName(writer, scalar_ty); + try writer.print("({s})(", .{operation}); try f.writeCValue(writer, accum, .FunctionArgument); try writer.writeAll(", "); try f.writeCValue(writer, operand, .Other); - try writer.writeAll("["); - try f.writeCValue(writer, it, .Other); - try writer.writeAll("])"); + try v.elem(f, writer); + try writer.writeByte(')'); + }, + .builtin => |operation| { + try writer.print(" = zig_{s}_", .{operation}); + try f.object.dg.renderTypeForBuiltinFnName(writer, scalar_ty); + try writer.writeByte('('); + try f.writeCValue(writer, accum, .FunctionArgument); + try writer.writeAll(", "); + try f.writeCValue(writer, operand, .Other); + try v.elem(f, writer); + try writer.writeByte(')'); }, .infix => |ass| { try writer.writeAll(ass); try f.writeCValue(writer, operand, .Other); - try writer.writeAll("["); - try f.writeCValue(writer, it, .Other); - try writer.writeAll("]"); + try v.elem(f, writer); }, .ternary => |cmp| { try writer.writeAll(" = "); try f.writeCValue(writer, accum, .Other); try writer.writeAll(cmp); try f.writeCValue(writer, operand, .Other); - try writer.writeAll("["); - try f.writeCValue(writer, it, .Other); - try writer.writeAll("] ? "); + try v.elem(f, writer); + try writer.writeAll(" ? "); try f.writeCValue(writer, accum, .Other); try writer.writeAll(" : "); try f.writeCValue(writer, operand, .Other); - try writer.writeAll("["); - try f.writeCValue(writer, it, .Other); - try writer.writeAll("]"); + try v.elem(f, writer); }, } - try writer.writeAll(";\n"); - - try freeLocal(f, inst, it.new_local, 0); + try v.end(f, inst, writer); return accum; } @@ -6774,7 +6846,7 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeByte('('); if (inst_ty.isAbiInt() and (field_ty.isAbiInt() or field_ty.isPtrAtRuntime())) { - try f.renderIntCast(writer, inst_ty, element, field_ty, .FunctionArgument); + try f.renderIntCast(writer, inst_ty, element, .{}, field_ty, .FunctionArgument); } else { try writer.writeByte('('); try f.renderType(writer, inst_ty); @@ -6916,7 +6988,6 @@ fn airWasmMemoryGrow(f: *Function, inst: Air.Inst.Index) !CValue { } fn airFloatNeg(f: *Function, inst: Air.Inst.Index) !CValue { - const inst_ty = f.air.typeOfIndex(inst); const un_op = f.air.instructions.items(.data)[inst].un_op; if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{un_op}); @@ -6925,16 +6996,23 @@ fn airFloatNeg(f: *Function, inst: Air.Inst.Index) !CValue { const operand = try f.resolveInst(un_op); try reap(f, inst, &.{un_op}); + const operand_ty = f.air.typeOf(un_op); + const scalar_ty = operand_ty.scalarType(); const writer = f.object.writer(); - const local = try f.allocLocal(inst, inst_ty); + const local = try f.allocLocal(inst, operand_ty); + const v = try Vectorizer.start(f, inst, writer, operand_ty); try f.writeCValue(writer, local, .Other); + try v.elem(f, writer); try writer.writeAll(" = zig_neg_"); - try f.object.dg.renderTypeForBuiltinFnName(writer, operand_ty); + try f.object.dg.renderTypeForBuiltinFnName(writer, scalar_ty); try writer.writeByte('('); try f.writeCValue(writer, operand, .FunctionArgument); + try v.elem(f, writer); try writer.writeAll(");\n"); + try v.end(f, inst, writer); + return local; } @@ -6944,19 +7022,28 @@ fn airUnFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CVal try reap(f, inst, &.{un_op}); return .none; } + const operand = try f.resolveInst(un_op); try reap(f, inst, &.{un_op}); - const writer = f.object.writer(); + const inst_ty = f.air.typeOfIndex(inst); + const inst_scalar_ty = inst_ty.scalarType(); + + const writer = f.object.writer(); const local = try f.allocLocal(inst, inst_ty); + const v = try Vectorizer.start(f, inst, writer, inst_ty); try f.writeCValue(writer, local, .Other); + try v.elem(f, writer); try writer.writeAll(" = zig_libc_name_"); - try f.object.dg.renderTypeForBuiltinFnName(writer, inst_ty); + try f.object.dg.renderTypeForBuiltinFnName(writer, inst_scalar_ty); try writer.writeByte('('); try writer.writeAll(operation); try writer.writeAll(")("); try f.writeCValue(writer, operand, .FunctionArgument); + try v.elem(f, writer); try writer.writeAll(");\n"); + try v.end(f, inst, writer); + return local; } @@ -6966,23 +7053,32 @@ fn airBinFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CVa try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); return .none; } + const lhs = try f.resolveInst(bin_op.lhs); const rhs = try f.resolveInst(bin_op.rhs); try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - const writer = f.object.writer(); const inst_ty = f.air.typeOfIndex(inst); + const inst_scalar_ty = inst_ty.scalarType(); + + const writer = f.object.writer(); const local = try f.allocLocal(inst, inst_ty); + const v = try Vectorizer.start(f, inst, writer, inst_ty); try f.writeCValue(writer, local, .Other); + try v.elem(f, writer); try writer.writeAll(" = zig_libc_name_"); - try f.object.dg.renderTypeForBuiltinFnName(writer, inst_ty); + try f.object.dg.renderTypeForBuiltinFnName(writer, inst_scalar_ty); try writer.writeByte('('); try writer.writeAll(operation); try writer.writeAll(")("); try f.writeCValue(writer, lhs, .FunctionArgument); + try v.elem(f, writer); try writer.writeAll(", "); try f.writeCValue(writer, rhs, .FunctionArgument); + try v.elem(f, writer); try writer.writeAll(");\n"); + try v.end(f, inst, writer); + return local; } @@ -6993,23 +7089,34 @@ fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue { try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs, pl_op.operand }); return .none; } - const inst_ty = f.air.typeOfIndex(inst); + const mulend1 = try f.resolveInst(bin_op.lhs); const mulend2 = try f.resolveInst(bin_op.rhs); const addend = try f.resolveInst(pl_op.operand); try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs, pl_op.operand }); + + const inst_ty = f.air.typeOfIndex(inst); + const inst_scalar_ty = inst_ty.scalarType(); + const writer = f.object.writer(); const local = try f.allocLocal(inst, inst_ty); + const v = try Vectorizer.start(f, inst, writer, inst_ty); try f.writeCValue(writer, local, .Other); + try v.elem(f, writer); try writer.writeAll(" = zig_libc_name_"); - try f.object.dg.renderTypeForBuiltinFnName(writer, inst_ty); + try f.object.dg.renderTypeForBuiltinFnName(writer, inst_scalar_ty); try writer.writeAll("(fma)("); try f.writeCValue(writer, mulend1, .FunctionArgument); + try v.elem(f, writer); try writer.writeAll(", "); try f.writeCValue(writer, mulend2, .FunctionArgument); + try v.elem(f, writer); try writer.writeAll(", "); try f.writeCValue(writer, addend, .FunctionArgument); + try v.elem(f, writer); try writer.writeAll(");\n"); + try v.end(f, inst, writer); + return local; } @@ -7510,6 +7617,47 @@ fn formatIntLiteral( try data.cty.renderLiteralSuffix(writer); } +const Vectorizer = struct { + index: CValue = .none, + + pub fn start(f: *Function, inst: Air.Inst.Index, writer: anytype, ty: Type) !Vectorizer { + return if (ty.zigTypeTag() == .Vector) index: { + var len_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, .data = ty.vectorLen() }; + + const local = try f.allocLocal(inst, Type.usize); + + try writer.writeAll("for ("); + try f.writeCValue(writer, local, .Other); + try writer.print(" = {d}; ", .{try f.fmtIntLiteral(Type.usize, Value.zero)}); + try f.writeCValue(writer, local, .Other); + try writer.print(" < {d}; ", .{ + try f.fmtIntLiteral(Type.usize, Value.initPayload(&len_pl.base)), + }); + try f.writeCValue(writer, local, .Other); + try writer.print(" += {d}) {{\n", .{try f.fmtIntLiteral(Type.usize, Value.one)}); + f.object.indent_writer.pushIndent(); + + break :index .{ .index = local }; + } else .{}; + } + + pub fn elem(self: Vectorizer, f: *Function, writer: anytype) !void { + if (self.index != .none) { + try writer.writeByte('['); + try f.writeCValue(writer, self.index, .Other); + try writer.writeByte(']'); + } + } + + pub fn end(self: Vectorizer, f: *Function, inst: Air.Inst.Index, writer: anytype) !void { + if (self.index != .none) { + f.object.indent_writer.popIndent(); + try writer.writeAll("}\n"); + try freeLocal(f, inst, self.index.new_local, 0); + } + } +}; + fn isByRef(ty: Type) bool { _ = ty; return false; diff --git a/src/type.zig b/src/type.zig index 9e501d893c..15525f14eb 100644 --- a/src/type.zig +++ b/src/type.zig @@ -4213,7 +4213,7 @@ pub const Type = extern union { }; } - pub fn shallowElemType(child_ty: Type) Type { + fn shallowElemType(child_ty: Type) Type { return switch (child_ty.zigTypeTag()) { .Array, .Vector => child_ty.childType(), else => child_ty, diff --git a/src/value.zig b/src/value.zig index 4a5683df36..00bf59ca38 100644 --- a/src/value.zig +++ b/src/value.zig @@ -3319,7 +3319,7 @@ pub const Value = extern union { } } - fn floatToValue(float: f128, arena: Allocator, dest_ty: Type, target: Target) !Value { + pub fn floatToValue(float: f128, arena: Allocator, dest_ty: Type, target: Target) !Value { switch (dest_ty.floatBits(target)) { 16 => return Value.Tag.float_16.create(arena, @floatCast(f16, float)), 32 => return Value.Tag.float_32.create(arena, @floatCast(f32, float)), diff --git a/test/behavior/bitreverse.zig b/test/behavior/bitreverse.zig index aa830144d1..80167b9a17 100644 --- a/test/behavior/bitreverse.zig +++ b/test/behavior/bitreverse.zig @@ -96,7 +96,6 @@ fn vector8() !void { test "bitReverse vectors u8" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; @@ -115,7 +114,6 @@ fn vector16() !void { test "bitReverse vectors u16" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; @@ -134,7 +132,6 @@ fn vector24() !void { test "bitReverse vectors u24" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; diff --git a/test/behavior/byteswap.zig b/test/behavior/byteswap.zig index fc385e0443..d173c13275 100644 --- a/test/behavior/byteswap.zig +++ b/test/behavior/byteswap.zig @@ -62,7 +62,6 @@ fn vector8() !void { test "@byteSwap vectors u8" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; @@ -81,7 +80,6 @@ fn vector16() !void { test "@byteSwap vectors u16" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; @@ -100,7 +98,6 @@ fn vector24() !void { test "@byteSwap vectors u24" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index 927caa965b..f179cbe525 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -598,7 +598,6 @@ test "cast *[1][*]const u8 to [*]const ?[*]const u8" { test "vector casts" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO diff --git a/test/behavior/floatop.zig b/test/behavior/floatop.zig index 7befa41380..f05901f7d9 100644 --- a/test/behavior/floatop.zig +++ b/test/behavior/floatop.zig @@ -141,7 +141,6 @@ fn testSqrt() !void { test "@sqrt with vectors" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO @@ -234,7 +233,6 @@ fn testSin() !void { test "@sin with vectors" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO @@ -275,7 +273,6 @@ fn testCos() !void { test "@cos with vectors" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO @@ -315,7 +312,6 @@ fn testExp() !void { test "@exp with vectors" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO @@ -355,7 +351,6 @@ fn testExp2() !void { test "@exp2" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO @@ -409,7 +404,6 @@ test "@log with @vectors" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO { @@ -447,7 +441,6 @@ test "@log2 with vectors" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO // https://github.com/ziglang/zig/issues/13681 if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .aarch64 and @@ -491,7 +484,6 @@ test "@log10 with vectors" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO comptime try testLog10WithVectors(); try testLog10WithVectors(); @@ -537,7 +529,6 @@ fn testFabs() !void { test "@fabs with vectors" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO @@ -660,7 +651,6 @@ fn testFloor() !void { test "@floor with vectors" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO @@ -754,7 +744,6 @@ fn testCeil() !void { test "@ceil with vectors" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO @@ -848,7 +837,6 @@ fn testTrunc() !void { test "@trunc with vectors" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO diff --git a/test/behavior/maximum_minimum.zig b/test/behavior/maximum_minimum.zig index 133a543d42..34a7d0976a 100644 --- a/test/behavior/maximum_minimum.zig +++ b/test/behavior/maximum_minimum.zig @@ -25,7 +25,6 @@ test "@max" { test "@max on vectors" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -75,7 +74,6 @@ test "@min for vectors" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const S = struct { diff --git a/test/behavior/muladd.zig b/test/behavior/muladd.zig index a2d9e6d16d..218edc5a2d 100644 --- a/test/behavior/muladd.zig +++ b/test/behavior/muladd.zig @@ -100,7 +100,6 @@ fn vector16() !void { } test "vector f16" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -124,7 +123,6 @@ fn vector32() !void { } test "vector f32" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -148,7 +146,6 @@ fn vector64() !void { } test "vector f64" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -171,7 +168,6 @@ fn vector80() !void { } test "vector f80" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -195,7 +191,6 @@ fn vector128() !void { } test "vector f128" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO diff --git a/test/behavior/vector.zig b/test/behavior/vector.zig index d885a7fabc..e74bcdad86 100644 --- a/test/behavior/vector.zig +++ b/test/behavior/vector.zig @@ -25,7 +25,6 @@ test "implicit cast vector to array - bool" { test "vector wrap operators" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -116,7 +115,6 @@ test "vector float operators" { test "vector bit operators" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -442,7 +440,6 @@ test "vector comparison operators" { test "vector division operators" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -525,7 +522,6 @@ test "vector division operators" { test "vector bitwise not operator" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -557,7 +553,6 @@ test "vector bitwise not operator" { test "vector shift operators" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -651,7 +646,6 @@ test "vector shift operators" { test "vector reduce operation" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -707,7 +701,7 @@ test "vector reduce operation" { // LLVM 11 ERROR: Cannot select type // https://github.com/ziglang/zig/issues/7138 - if (builtin.target.cpu.arch != .aarch64) { + if (builtin.zig_backend != .stage2_llvm or builtin.target.cpu.arch != .aarch64) { try testReduce(.Min, [4]i64{ 1234567, -386, 0, 3 }, @as(i64, -386)); try testReduce(.Min, [4]u64{ 99, 9999, 9, 99999 }, @as(u64, 9)); } @@ -725,7 +719,7 @@ test "vector reduce operation" { // LLVM 11 ERROR: Cannot select type // https://github.com/ziglang/zig/issues/7138 - if (builtin.target.cpu.arch != .aarch64) { + if (builtin.zig_backend != .stage2_llvm or builtin.target.cpu.arch != .aarch64) { try testReduce(.Max, [4]i64{ 1234567, -386, 0, 3 }, @as(i64, 1234567)); try testReduce(.Max, [4]u64{ 99, 9999, 9, 99999 }, @as(u64, 99999)); } @@ -773,14 +767,14 @@ test "vector reduce operation" { // LLVM 11 ERROR: Cannot select type // https://github.com/ziglang/zig/issues/7138 - if (false) { - try testReduce(.Min, [4]f16{ -1.9, 5.1, f16_nan, 100.0 }, f16_nan); - try testReduce(.Min, [4]f32{ -1.9, 5.1, f32_nan, 100.0 }, f32_nan); - try testReduce(.Min, [4]f64{ -1.9, 5.1, f64_nan, 100.0 }, f64_nan); + if (builtin.zig_backend != .stage2_llvm) { + try testReduce(.Min, [4]f16{ -1.9, 5.1, f16_nan, 100.0 }, @as(f16, -1.9)); + try testReduce(.Min, [4]f32{ -1.9, 5.1, f32_nan, 100.0 }, @as(f32, -1.9)); + try testReduce(.Min, [4]f64{ -1.9, 5.1, f64_nan, 100.0 }, @as(f64, -1.9)); - try testReduce(.Max, [4]f16{ -1.9, 5.1, f16_nan, 100.0 }, f16_nan); - try testReduce(.Max, [4]f32{ -1.9, 5.1, f32_nan, 100.0 }, f32_nan); - try testReduce(.Max, [4]f64{ -1.9, 5.1, f64_nan, 100.0 }, f64_nan); + try testReduce(.Max, [4]f16{ -1.9, 5.1, f16_nan, 100.0 }, @as(f16, 100.0)); + try testReduce(.Max, [4]f32{ -1.9, 5.1, f32_nan, 100.0 }, @as(f32, 100.0)); + try testReduce(.Max, [4]f64{ -1.9, 5.1, f64_nan, 100.0 }, @as(f64, 100.0)); } try testReduce(.Mul, [4]f16{ -1.9, 5.1, f16_nan, 100.0 }, f16_nan); @@ -831,7 +825,6 @@ test "mask parameter of @shuffle is comptime scope" { test "saturating add" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -863,7 +856,6 @@ test "saturating add" { test "saturating subtraction" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -886,7 +878,6 @@ test "saturating subtraction" { test "saturating multiplication" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -913,7 +904,6 @@ test "saturating multiplication" { test "saturating shift-left" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -1047,7 +1037,6 @@ test "@mulWithOverflow" { } test "@shlWithOverflow" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO @@ -1202,7 +1191,6 @@ test "zero multiplicand" { test "@intCast to u0" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO From 8f6da78fb1bfc9d5e8b3d5affd33cf6a62f5e8c7 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sun, 5 Mar 2023 00:30:55 -0500 Subject: [PATCH 026/294] CBE: implement vector element pointers --- src/codegen/c.zig | 10 ++-------- src/codegen/c/type.zig | 2 +- test/behavior/vector.zig | 3 --- 3 files changed, 3 insertions(+), 12 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 5e92a6f76c..60f93311a4 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -17,12 +17,6 @@ const LazySrcLoc = Module.LazySrcLoc; const Air = @import("../Air.zig"); const Liveness = @import("../Liveness.zig"); -const target_util = @import("../target.zig"); -const libcFloatPrefix = target_util.libcFloatPrefix; -const libcFloatSuffix = target_util.libcFloatSuffix; -const compilerRtFloatAbbrev = target_util.compilerRtFloatAbbrev; -const compilerRtIntAbbrev = target_util.compilerRtIntAbbrev; - const BigIntLimb = std.math.big.Limb; const BigInt = std.math.big.int; @@ -3317,7 +3311,7 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(", sizeof("); try f.renderType(writer, src_ty); try writer.writeAll("))"); - } else if (ptr_info.host_size != 0) { + } else if (ptr_info.host_size > 0 and ptr_info.vector_index == .none) { var host_pl = Type.Payload.Bits{ .base = .{ .tag = .int_unsigned }, .data = ptr_info.host_size * 8, @@ -3647,7 +3641,7 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { if (src_val == .constant) { try freeLocal(f, inst, array_src.new_local, 0); } - } else if (ptr_info.host_size != 0) { + } else if (ptr_info.host_size > 0 and ptr_info.vector_index == .none) { const host_bits = ptr_info.host_size * 8; var host_pl = Type.Payload.Bits{ .base = .{ .tag = .int_unsigned }, .data = host_bits }; const host_ty = Type.initPayload(&host_pl.base); diff --git a/src/codegen/c/type.zig b/src/codegen/c/type.zig index 313fcc130c..038f53f186 100644 --- a/src/codegen/c/type.zig +++ b/src/codegen/c/type.zig @@ -1465,7 +1465,7 @@ pub const CType = extern union { .base = .{ .tag = .int_unsigned }, .data = info.host_size * 8, }; - const pointee_ty = if (info.host_size > 0) + const pointee_ty = if (info.host_size > 0 and info.vector_index == .none) Type.initPayload(&host_int_pl.base) else info.pointee_type; diff --git a/test/behavior/vector.zig b/test/behavior/vector.zig index e74bcdad86..42befa9c0f 100644 --- a/test/behavior/vector.zig +++ b/test/behavior/vector.zig @@ -1118,7 +1118,6 @@ test "byte vector initialized in inline function" { } test "byte vector initialized in inline function" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO @@ -1233,7 +1232,6 @@ test "load packed vector element" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO var x: @Vector(2, u15) = .{ 1, 4 }; try expect((&x[0]).* == 1); @@ -1246,7 +1244,6 @@ test "store packed vector element" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO var v = @Vector(4, u1){ 1, 1, 1, 1 }; try expectEqual(@Vector(4, u1){ 1, 1, 1, 1 }, v); From ba69ee488baec677d6e206eb0670240b1c2167a6 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sun, 5 Mar 2023 00:44:27 -0500 Subject: [PATCH 027/294] CBE: implement vector truncate --- src/codegen/c.zig | 34 ++++++++++++++++++++++------------ test/behavior/truncate.zig | 1 - 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 60f93311a4..3fea7c2ef2 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -3465,34 +3465,40 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue { const operand = try f.resolveInst(ty_op.operand); try reap(f, inst, &.{ty_op.operand}); const inst_ty = f.air.typeOfIndex(inst); - const writer = f.object.writer(); - const local = try f.allocLocal(inst, inst_ty); + const inst_scalar_ty = inst_ty.scalarType(); const target = f.object.dg.module.getTarget(); - const dest_int_info = inst_ty.intInfo(target); + const dest_int_info = inst_scalar_ty.intInfo(target); const dest_bits = dest_int_info.bits; const dest_c_bits = toCIntBits(dest_int_info.bits) orelse return f.fail("TODO: C backend: implement integer types larger than 128 bits", .{}); const operand_ty = f.air.typeOf(ty_op.operand); - const operand_int_info = operand_ty.intInfo(target); + const scalar_ty = operand_ty.scalarType(); + const scalar_int_info = scalar_ty.intInfo(target); + + const writer = f.object.writer(); + const local = try f.allocLocal(inst, inst_ty); + const v = try Vectorizer.start(f, inst, writer, operand_ty); try f.writeCValue(writer, local, .Other); + try v.elem(f, writer); try writer.writeAll(" = "); if (dest_c_bits < 64) { try writer.writeByte('('); - try f.renderType(writer, inst_ty); + try f.renderType(writer, inst_scalar_ty); try writer.writeByte(')'); } - const needs_lo = operand_int_info.bits > 64 and dest_bits <= 64; + const needs_lo = scalar_int_info.bits > 64 and dest_bits <= 64; if (needs_lo) { try writer.writeAll("zig_lo_"); - try f.object.dg.renderTypeForBuiltinFnName(writer, operand_ty); + try f.object.dg.renderTypeForBuiltinFnName(writer, scalar_ty); try writer.writeByte('('); } if (dest_bits >= 8 and std.math.isPowerOfTwo(dest_bits)) { try f.writeCValue(writer, operand, .Other); + try v.elem(f, writer); } else switch (dest_int_info.signedness) { .unsigned => { var arena = std.heap.ArenaAllocator.init(f.object.dg.gpa); @@ -3502,15 +3508,16 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue { var stack align(@alignOf(ExpectedContents)) = std.heap.stackFallback(@sizeOf(ExpectedContents), arena.allocator()); - const mask_val = try inst_ty.maxInt(stack.get(), target); + const mask_val = try inst_scalar_ty.maxInt(stack.get(), target); try writer.writeAll("zig_and_"); - try f.object.dg.renderTypeForBuiltinFnName(writer, operand_ty); + try f.object.dg.renderTypeForBuiltinFnName(writer, scalar_ty); try writer.writeByte('('); try f.writeCValue(writer, operand, .FunctionArgument); - try writer.print(", {x})", .{try f.fmtIntLiteral(operand_ty, mask_val)}); + try v.elem(f, writer); + try writer.print(", {x})", .{try f.fmtIntLiteral(scalar_ty, mask_val)}); }, .signed => { - const c_bits = toCIntBits(operand_int_info.bits) orelse + const c_bits = toCIntBits(scalar_int_info.bits) orelse return f.fail("TODO: C backend: implement integer types larger than 128 bits", .{}); var shift_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, @@ -3519,7 +3526,7 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue { const shift_val = Value.initPayload(&shift_pl.base); try writer.writeAll("zig_shr_"); - try f.object.dg.renderTypeForBuiltinFnName(writer, operand_ty); + try f.object.dg.renderTypeForBuiltinFnName(writer, scalar_ty); if (c_bits == 128) { try writer.print("(zig_bitcast_i{d}(", .{c_bits}); } else { @@ -3532,6 +3539,7 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue { try writer.print("(uint{d}_t)", .{c_bits}); } try f.writeCValue(writer, operand, .FunctionArgument); + try v.elem(f, writer); if (c_bits == 128) try writer.writeByte(')'); try writer.print(", {})", .{try f.fmtIntLiteral(Type.u8, shift_val)}); if (c_bits == 128) try writer.writeByte(')'); @@ -3541,6 +3549,8 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue { if (needs_lo) try writer.writeByte(')'); try writer.writeAll(";\n"); + try v.end(f, inst, writer); + return local; } diff --git a/test/behavior/truncate.zig b/test/behavior/truncate.zig index c81abebe68..e70d33eea2 100644 --- a/test/behavior/truncate.zig +++ b/test/behavior/truncate.zig @@ -60,7 +60,6 @@ test "truncate on comptime integer" { } test "truncate on vectors" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; From aac47079026d0daf4d5acac08b7d0ad1150002d0 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sun, 5 Mar 2023 01:23:21 -0500 Subject: [PATCH 028/294] CBE: implement splat --- src/codegen/c.zig | 33 ++++++++++++++++++++++++++++----- test/behavior/vector.zig | 1 - 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 3fea7c2ef2..f5309918bf 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -438,6 +438,10 @@ pub const Function = struct { return f.object.dg.renderType(w, t); } + fn renderCType(f: *Function, w: anytype, t: CType.Index) !void { + return f.object.dg.renderCType(w, t); + } + fn renderIntCast(f: *Function, w: anytype, dest_ty: Type, src: CValue, v: Vectorizer, src_ty: Type, location: ValueRenderLocation) !void { return f.object.dg.renderIntCast(w, dest_ty, .{ .c_value = .{ .f = f, .value = src, .v = v } }, src_ty, location); } @@ -1576,9 +1580,12 @@ pub const DeclGen = struct { /// | `renderType` | "uint8_t *" | "uint8_t *[10]" | /// fn renderType(dg: *DeclGen, w: anytype, t: Type) error{ OutOfMemory, AnalysisFail }!void { + try dg.renderCType(w, try dg.typeToIndex(t, .complete)); + } + + fn renderCType(dg: *DeclGen, w: anytype, idx: CType.Index) error{ OutOfMemory, AnalysisFail }!void { const store = &dg.ctypes.set; const module = dg.module; - const idx = try dg.typeToIndex(t, .complete); _ = try renderTypePrefix(dg.decl_index, store.*, module, w, idx, .suffix, .{}); try renderTypeSuffix(dg.decl_index, store.*, module, w, idx, .suffix, .{}); } @@ -6543,21 +6550,37 @@ fn airErrorName(f: *Function, inst: Air.Inst.Index) !CValue { fn airSplat(f: *Function, inst: Air.Inst.Index) !CValue { const ty_op = f.air.instructions.items(.data)[inst].ty_op; + if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ty_op.operand}); return .none; } - const inst_ty = f.air.typeOfIndex(inst); const operand = try f.resolveInst(ty_op.operand); try reap(f, inst, &.{ty_op.operand}); + + const inst_ty = f.air.typeOfIndex(inst); + const inst_scalar_ty = inst_ty.scalarType(); + const inst_scalar_cty = try f.typeToIndex(inst_scalar_ty, .complete); + const need_memcpy = f.indexToCType(inst_scalar_cty).tag() == .array; + const writer = f.object.writer(); const local = try f.allocLocal(inst, inst_ty); + const v = try Vectorizer.start(f, inst, writer, inst_ty); + if (need_memcpy) try writer.writeAll("memcpy(&"); try f.writeCValue(writer, local, .Other); - try writer.writeAll(" = "); + try v.elem(f, writer); + try writer.writeAll(if (need_memcpy) ", &" else " = "); + try f.writeCValue(writer, operand, .Other); + if (need_memcpy) { + try writer.writeAll(", sizeof("); + try f.renderCType(writer, inst_scalar_cty); + try writer.writeAll("))"); + } + try writer.writeAll(";\n"); + try v.end(f, inst, writer); - _ = operand; - return f.fail("TODO: C backend: implement airSplat", .{}); + return local; } fn airSelect(f: *Function, inst: Air.Inst.Index) !CValue { diff --git a/test/behavior/vector.zig b/test/behavior/vector.zig index 42befa9c0f..5d569bd815 100644 --- a/test/behavior/vector.zig +++ b/test/behavior/vector.zig @@ -234,7 +234,6 @@ test "vector casts of sizes not divisible by 8" { } test "vector @splat" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO From 0b0298aff27a31a7f45828d96d95adfdde61a085 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sun, 5 Mar 2023 02:06:53 -0500 Subject: [PATCH 029/294] CBE: implement select and shuffle --- src/codegen/c.zig | 79 +++++++++++++++++++++++++++++++++++++-- test/behavior/select.zig | 2 - test/behavior/shuffle.zig | 2 - test/behavior/vector.zig | 2 - 4 files changed, 75 insertions(+), 10 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index f5309918bf..5e64823a0d 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -6584,15 +6584,86 @@ fn airSplat(f: *Function, inst: Air.Inst.Index) !CValue { } fn airSelect(f: *Function, inst: Air.Inst.Index) !CValue { - if (f.liveness.isUnused(inst)) return .none; + const pl_op = f.air.instructions.items(.data)[inst].pl_op; + const extra = f.air.extraData(Air.Bin, pl_op.payload).data; - return f.fail("TODO: C backend: implement airSelect", .{}); + if (f.liveness.isUnused(inst)) { + try reap(f, inst, &.{ pl_op.operand, extra.lhs, extra.rhs }); + return .none; + } + + const pred = try f.resolveInst(pl_op.operand); + const lhs = try f.resolveInst(extra.lhs); + const rhs = try f.resolveInst(extra.rhs); + try reap(f, inst, &.{ pl_op.operand, extra.lhs, extra.rhs }); + + const inst_ty = f.air.typeOfIndex(inst); + + const writer = f.object.writer(); + const local = try f.allocLocal(inst, inst_ty); + const v = try Vectorizer.start(f, inst, writer, inst_ty); + try f.writeCValue(writer, local, .Other); + try v.elem(f, writer); + try writer.writeAll(" = "); + try f.writeCValue(writer, pred, .Other); + try v.elem(f, writer); + try writer.writeAll(" ? "); + try f.writeCValue(writer, lhs, .Other); + try v.elem(f, writer); + try writer.writeAll(" : "); + try f.writeCValue(writer, rhs, .Other); + try v.elem(f, writer); + try writer.writeAll(";\n"); + try v.end(f, inst, writer); + + return local; } fn airShuffle(f: *Function, inst: Air.Inst.Index) !CValue { - if (f.liveness.isUnused(inst)) return .none; + const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; + const extra = f.air.extraData(Air.Shuffle, ty_pl.payload).data; - return f.fail("TODO: C backend: implement airShuffle", .{}); + if (f.liveness.isUnused(inst)) { + try reap(f, inst, &.{ extra.a, extra.b }); + return .none; + } + + const mask = f.air.values[extra.mask]; + const lhs = try f.resolveInst(extra.a); + const rhs = try f.resolveInst(extra.b); + + const module = f.object.dg.module; + const target = module.getTarget(); + const inst_ty = f.air.typeOfIndex(inst); + + const writer = f.object.writer(); + const local = try f.allocLocal(inst, inst_ty); + try reap(f, inst, &.{ extra.a, extra.b }); // local cannot alias operands + for (0..extra.mask_len) |index| { + var dst_pl = Value.Payload.U64{ + .base = .{ .tag = .int_u64 }, + .data = @intCast(u64, index), + }; + + try f.writeCValue(writer, local, .Other); + try writer.writeByte('['); + try f.object.dg.renderValue(writer, Type.usize, Value.initPayload(&dst_pl.base), .Other); + try writer.writeAll("] = "); + + var buf: Value.ElemValueBuffer = undefined; + const mask_elem = mask.elemValueBuffer(module, index, &buf).toSignedInt(target); + var src_pl = Value.Payload.U64{ + .base = .{ .tag = .int_u64 }, + .data = @intCast(u64, mask_elem ^ mask_elem >> 63), + }; + + try f.writeCValue(writer, if (mask_elem >= 0) lhs else rhs, .Other); + try writer.writeByte('['); + try f.object.dg.renderValue(writer, Type.usize, Value.initPayload(&src_pl.base), .Other); + try writer.writeAll("];\n"); + } + + return local; } fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue { diff --git a/test/behavior/select.zig b/test/behavior/select.zig index d09683b67c..73d69c6530 100644 --- a/test/behavior/select.zig +++ b/test/behavior/select.zig @@ -4,7 +4,6 @@ const mem = std.mem; const expect = std.testing.expect; test "@select vectors" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -33,7 +32,6 @@ fn selectVectors() !void { } test "@select arrays" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO diff --git a/test/behavior/shuffle.zig b/test/behavior/shuffle.zig index bcc4618aee..b591aee2e2 100644 --- a/test/behavior/shuffle.zig +++ b/test/behavior/shuffle.zig @@ -8,7 +8,6 @@ test "@shuffle int" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const S = struct { @@ -50,7 +49,6 @@ test "@shuffle bool 1" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const S = struct { diff --git a/test/behavior/vector.zig b/test/behavior/vector.zig index 5d569bd815..816bd6c23a 100644 --- a/test/behavior/vector.zig +++ b/test/behavior/vector.zig @@ -804,7 +804,6 @@ test "vector @reduce comptime" { test "mask parameter of @shuffle is comptime scope" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -1212,7 +1211,6 @@ test "modRem with zero divisor" { test "array operands to shuffle are coerced to vectors" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO From 33fa25ba4470bf000280a94f0376988b05918b75 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sun, 5 Mar 2023 02:35:32 -0500 Subject: [PATCH 030/294] CBE: ensure uniqueness of more internal identifiers --- src/codegen/c.zig | 35 +++++++++++++---------------------- test/behavior/vector.zig | 1 - 2 files changed, 13 insertions(+), 23 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 5e64823a0d..f1761ed80d 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1841,30 +1841,21 @@ pub const DeclGen = struct { dg.module.markDeclAlive(decl); if (dg.module.decl_exports.get(decl_index)) |exports| { - return writer.writeAll(exports.items[export_index].options.name); + try writer.writeAll(exports.items[export_index].options.name); } else if (decl.isExtern()) { - return writer.writeAll(mem.sliceTo(decl.name, 0)); - } else if (dg.module.test_functions.get(decl_index)) |_| { - const gpa = dg.gpa; - const name = try decl.getFullyQualifiedName(dg.module); - defer gpa.free(name); - return writer.print("{}_{d}", .{ fmtIdent(name), @enumToInt(decl_index) }); + try writer.writeAll(mem.sliceTo(decl.name, 0)); } else { - const gpa = dg.gpa; - const name = try decl.getFullyQualifiedName(dg.module); - defer gpa.free(name); - - // MSVC has a limit of 4095 character token length limit, and fmtIdent can (worst case), expand - // to 3x the length of its input - if (name.len > 1365) { - var hash = ident_hasher_init; - hash.update(name); - const ident_hash = hash.finalInt(); - try writer.writeAll("zig_D_"); - return std.fmt.formatIntValue(ident_hash, "x", .{}, writer); - } else { - return writer.print("{}", .{fmtIdent(name)}); - } + // MSVC has a limit of 4095 character token length limit, and fmtIdent can (worst case), + // expand to 3x the length of its input, but let's cut it off at a much shorter limit. + var name: [100]u8 = undefined; + var name_stream = std.io.fixedBufferStream(&name); + decl.renderFullyQualifiedName(dg.module, name_stream.writer()) catch |err| switch (err) { + error.NoSpaceLeft => {}, + }; + try writer.print("{}__{d}", .{ + fmtIdent(name_stream.getWritten()), + @enumToInt(decl_index), + }); } } diff --git a/test/behavior/vector.zig b/test/behavior/vector.zig index 816bd6c23a..0215572f8f 100644 --- a/test/behavior/vector.zig +++ b/test/behavior/vector.zig @@ -91,7 +91,6 @@ test "vector int operators" { test "vector float operators" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO From 7352d461cff72d92b07cf2d2b7ee17714005b9cf Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sun, 5 Mar 2023 03:29:50 -0500 Subject: [PATCH 031/294] behavior: fix comptime issue and disable failing test --- test/behavior/muladd.zig | 7 +++++++ test/behavior/shuffle.zig | 3 +-- test/behavior/vector.zig | 7 +++++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/test/behavior/muladd.zig b/test/behavior/muladd.zig index 218edc5a2d..25ed3641b8 100644 --- a/test/behavior/muladd.zig +++ b/test/behavior/muladd.zig @@ -197,6 +197,13 @@ test "vector f128" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.os.tag == .windows and builtin.cpu.arch == .aarch64 and + builtin.zig_backend == .stage2_c) + { + // https://github.com/ziglang/zig/issues/13876 + return error.SkipZigTest; + } + comptime try vector128(); try vector128(); } diff --git a/test/behavior/shuffle.zig b/test/behavior/shuffle.zig index b591aee2e2..97223cc263 100644 --- a/test/behavior/shuffle.zig +++ b/test/behavior/shuffle.zig @@ -69,7 +69,6 @@ test "@shuffle bool 2" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_llvm) { @@ -81,7 +80,7 @@ test "@shuffle bool 2" { fn doTheTest() !void { var x: @Vector(3, bool) = [3]bool{ false, true, false }; var v: @Vector(2, bool) = [2]bool{ true, false }; - const mask: @Vector(4, i32) = [4]i32{ 0, ~@as(i32, 1), 1, 2 }; + const mask = [4]i32{ 0, ~@as(i32, 1), 1, 2 }; var res = @shuffle(bool, x, v, mask); try expect(mem.eql(bool, &@as([4]bool, res), &[4]bool{ false, false, true, false })); } diff --git a/test/behavior/vector.zig b/test/behavior/vector.zig index 0215572f8f..1d9d517a96 100644 --- a/test/behavior/vector.zig +++ b/test/behavior/vector.zig @@ -96,6 +96,13 @@ test "vector float operators" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.os.tag == .windows and builtin.cpu.arch == .aarch64 and + builtin.zig_backend == .stage2_c) + { + // https://github.com/ziglang/zig/issues/13876 + return error.SkipZigTest; + } + inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| { const S = struct { fn doTheTest() !void { From 8ea1c1932e7bd869ec77a161da7876d171d4ef1d Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sun, 5 Mar 2023 04:25:04 -0500 Subject: [PATCH 032/294] behavior: disable failing tests --- test/behavior/slice.zig | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/behavior/slice.zig b/test/behavior/slice.zig index ed5e2a721d..6239de2d76 100644 --- a/test/behavior/slice.zig +++ b/test/behavior/slice.zig @@ -749,6 +749,11 @@ test "slice decays to many pointer" { } test "write through pointer to optional slice arg" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + const S = struct { fn bar(foo: *?[]const u8) !void { foo.* = try baz(); From 1efd36cd5c9a1128ae702b081d60ee32f21bc258 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sun, 5 Mar 2023 06:32:23 -0500 Subject: [PATCH 033/294] CBE: fix reduce of emulated integers --- src/codegen/c.zig | 46 +++++++++++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index f1761ed80d..3d059adc15 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -6672,33 +6672,43 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue { const operand_ty = f.air.typeOf(reduce.operand); const writer = f.object.writer(); + const use_operator = scalar_ty.bitSize(target) <= 64; const op: union(enum) { - float_op: []const u8, - builtin: []const u8, + const Func = struct { operation: []const u8, info: BuiltinInfo = .none }; + float_op: Func, + builtin: Func, infix: []const u8, ternary: []const u8, } = switch (reduce.operation) { - .And => .{ .infix = " &= " }, - .Or => .{ .infix = " |= " }, - .Xor => .{ .infix = " ^= " }, + .And => if (use_operator) .{ .infix = " &= " } else .{ .builtin = .{ .operation = "and" } }, + .Or => if (use_operator) .{ .infix = " |= " } else .{ .builtin = .{ .operation = "or" } }, + .Xor => if (use_operator) .{ .infix = " ^= " } else .{ .builtin = .{ .operation = "xor" } }, .Min => switch (scalar_ty.zigTypeTag()) { - .Int => .{ .ternary = " < " }, - .Float => .{ .float_op = "fmin" }, + .Int => if (use_operator) .{ .ternary = " < " } else .{ + .builtin = .{ .operation = "min" }, + }, + .Float => .{ .float_op = .{ .operation = "fmin" } }, else => unreachable, }, .Max => switch (scalar_ty.zigTypeTag()) { - .Int => .{ .ternary = " > " }, - .Float => .{ .float_op = "fmax" }, + .Int => if (use_operator) .{ .ternary = " > " } else .{ + .builtin = .{ .operation = "max" }, + }, + .Float => .{ .float_op = .{ .operation = "fmax" } }, else => unreachable, }, .Add => switch (scalar_ty.zigTypeTag()) { - .Int => .{ .infix = " += " }, - .Float => .{ .builtin = "add" }, + .Int => if (use_operator) .{ .infix = " += " } else .{ + .builtin = .{ .operation = "addw", .info = .bits }, + }, + .Float => .{ .builtin = .{ .operation = "add" } }, else => unreachable, }, .Mul => switch (scalar_ty.zigTypeTag()) { - .Int => .{ .infix = " *= " }, - .Float => .{ .builtin = "mul" }, + .Int => if (use_operator) .{ .infix = " *= " } else .{ + .builtin = .{ .operation = "mulw", .info = .bits }, + }, + .Float => .{ .builtin = .{ .operation = "mul" } }, else => unreachable, }, }; @@ -6762,24 +6772,26 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue { const v = try Vectorizer.start(f, inst, writer, operand_ty); try f.writeCValue(writer, accum, .Other); switch (op) { - .float_op => |operation| { + .float_op => |func| { try writer.writeAll(" = zig_libc_name_"); try f.object.dg.renderTypeForBuiltinFnName(writer, scalar_ty); - try writer.print("({s})(", .{operation}); + try writer.print("({s})(", .{func.operation}); try f.writeCValue(writer, accum, .FunctionArgument); try writer.writeAll(", "); try f.writeCValue(writer, operand, .Other); try v.elem(f, writer); + try f.object.dg.renderBuiltinInfo(writer, scalar_ty, func.info); try writer.writeByte(')'); }, - .builtin => |operation| { - try writer.print(" = zig_{s}_", .{operation}); + .builtin => |func| { + try writer.print(" = zig_{s}_", .{func.operation}); try f.object.dg.renderTypeForBuiltinFnName(writer, scalar_ty); try writer.writeByte('('); try f.writeCValue(writer, accum, .FunctionArgument); try writer.writeAll(", "); try f.writeCValue(writer, operand, .Other); try v.elem(f, writer); + try f.object.dg.renderBuiltinInfo(writer, scalar_ty, func.info); try writer.writeByte(')'); }, .infix => |ass| { From a63134a4a56e8683aeee292b641b4e943cbfb999 Mon Sep 17 00:00:00 2001 From: jim price Date: Sat, 4 Mar 2023 18:03:37 -0800 Subject: [PATCH 034/294] std.os: Add DeviceBusy as a possible write error In Linux when writing to various files in the virtual file system, for example /sys/fs/cgroup, if you write an invalid value to a file you'll get errno 16. This change allows for these specific cases to be caught instead of being lumped together in UnexpectedError. --- lib/std/os.zig | 5 +++++ src/link.zig | 1 + 2 files changed, 6 insertions(+) diff --git a/lib/std/os.zig b/lib/std/os.zig index fe664302a7..3a3433d819 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -1036,6 +1036,7 @@ pub const WriteError = error{ FileTooBig, InputOutput, NoSpaceLeft, + DeviceBusy, /// In WASI, this error may occur when the file descriptor does /// not hold the required rights to write to it. @@ -1134,6 +1135,7 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize { .PERM => return error.AccessDenied, .PIPE => return error.BrokenPipe, .CONNRESET => return error.ConnectionResetByPeer, + .BUSY => return error.DeviceBusy, else => |err| return unexpectedErrno(err), } } @@ -1203,6 +1205,7 @@ pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!usize { .PERM => return error.AccessDenied, .PIPE => return error.BrokenPipe, .CONNRESET => return error.ConnectionResetByPeer, + .BUSY => return error.DeviceBusy, else => |err| return unexpectedErrno(err), } } @@ -1299,6 +1302,7 @@ pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize { .NXIO => return error.Unseekable, .SPIPE => return error.Unseekable, .OVERFLOW => return error.Unseekable, + .BUSY => return error.DeviceBusy, else => |err| return unexpectedErrno(err), } } @@ -1388,6 +1392,7 @@ pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) PWriteError!usiz .NXIO => return error.Unseekable, .SPIPE => return error.Unseekable, .OVERFLOW => return error.Unseekable, + .BUSY => return error.DeviceBusy, else => |err| return unexpectedErrno(err), } } diff --git a/src/link.zig b/src/link.zig index 4c4915441d..24cc0a3861 100644 --- a/src/link.zig +++ b/src/link.zig @@ -460,6 +460,7 @@ pub const File = struct { CurrentWorkingDirectoryUnlinked, LockViolation, NetNameDeleted, + DeviceBusy, }; /// Called from within the CodeGen to lower a local variable instantion as an unnamed From 29c56a8aa74d1b1a19bece5ba5d738af1e3c9f6d Mon Sep 17 00:00:00 2001 From: jiacai2050 Date: Sat, 4 Mar 2023 10:47:25 +0800 Subject: [PATCH 035/294] fix package redeclaration when cache is not found --- src/Package.zig | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Package.zig b/src/Package.zig index 2aa5e85294..ed93500980 100644 --- a/src/Package.zig +++ b/src/Package.zig @@ -432,6 +432,12 @@ fn fetchAndUnpack( const build_root = try global_cache_directory.join(gpa, &.{pkg_dir_sub_path}); errdefer gpa.free(build_root); + var pkg_dir = global_cache_directory.handle.openDir(pkg_dir_sub_path, .{}) catch |err| switch (err) { + error.FileNotFound => break :cached, + else => |e| return e, + }; + errdefer pkg_dir.close(); + try build_roots_source.writer().print(" pub const {s} = \"{}\";\n", .{ std.zig.fmtId(fqn), std.zig.fmtEscapes(build_root), }); @@ -444,12 +450,6 @@ fn fetchAndUnpack( return gop.value_ptr.*; } - var pkg_dir = global_cache_directory.handle.openDir(pkg_dir_sub_path, .{}) catch |err| switch (err) { - error.FileNotFound => break :cached, - else => |e| return e, - }; - errdefer pkg_dir.close(); - const ptr = try gpa.create(Package); errdefer gpa.destroy(ptr); From f1ae688d371f49fdbf65f952d655905c74871fdb Mon Sep 17 00:00:00 2001 From: r00ster91 Date: Sun, 5 Mar 2023 15:45:23 +0100 Subject: [PATCH 036/294] AstGen: ensure certain builtin functions return void Fixes #14779 Co-authored-by: Veikka Tuominen --- src/AstGen.zig | 32 +++++++++---------- ...n_functions_returning_void_or_noreturn.zig | 32 +++++++++++++++++++ 2 files changed, 48 insertions(+), 16 deletions(-) create mode 100644 test/behavior/builtin_functions_returning_void_or_noreturn.zig diff --git a/src/AstGen.zig b/src/AstGen.zig index 587b574a01..20f4fb6df3 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -8060,35 +8060,35 @@ fn builtinCall( }, .fence => { const order = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .atomic_order_type } }, params[0]); - const result = try gz.addExtendedPayload(.fence, Zir.Inst.UnNode{ + _ = try gz.addExtendedPayload(.fence, Zir.Inst.UnNode{ .node = gz.nodeIndexToRelative(node), .operand = order, }); - return rvalue(gz, ri, result, node); + return rvalue(gz, ri, .void_value, node); }, .set_float_mode => { const order = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .float_mode_type } }, params[0]); - const result = try gz.addExtendedPayload(.set_float_mode, Zir.Inst.UnNode{ + _ = try gz.addExtendedPayload(.set_float_mode, Zir.Inst.UnNode{ .node = gz.nodeIndexToRelative(node), .operand = order, }); - return rvalue(gz, ri, result, node); + return rvalue(gz, ri, .void_value, node); }, .set_align_stack => { const order = try expr(gz, scope, align_ri, params[0]); - const result = try gz.addExtendedPayload(.set_align_stack, Zir.Inst.UnNode{ + _ = try gz.addExtendedPayload(.set_align_stack, Zir.Inst.UnNode{ .node = gz.nodeIndexToRelative(node), .operand = order, }); - return rvalue(gz, ri, result, node); + return rvalue(gz, ri, .void_value, node); }, .set_cold => { const order = try expr(gz, scope, ri, params[0]); - const result = try gz.addExtendedPayload(.set_cold, Zir.Inst.UnNode{ + _ = try gz.addExtendedPayload(.set_cold, Zir.Inst.UnNode{ .node = gz.nodeIndexToRelative(node), .operand = order, }); - return rvalue(gz, ri, result, node); + return rvalue(gz, ri, .void_value, node); }, .src => { @@ -8373,14 +8373,14 @@ fn builtinCall( }, .atomic_store => { const int_type = try typeExpr(gz, scope, params[0]); - const result = try gz.addPlNode(.atomic_store, node, Zir.Inst.AtomicStore{ + _ = try gz.addPlNode(.atomic_store, node, Zir.Inst.AtomicStore{ // zig fmt: off .ptr = try expr(gz, scope, .{ .rl = .none }, params[1]), .operand = try expr(gz, scope, .{ .rl = .{ .ty = int_type } }, params[2]), .ordering = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .atomic_order_type } }, params[3]), // zig fmt: on }); - return rvalue(gz, ri, result, node); + return rvalue(gz, ri, .void_value, node); }, .mul_add => { const float_type = try typeExpr(gz, scope, params[0]); @@ -8421,20 +8421,20 @@ fn builtinCall( return rvalue(gz, ri, result, node); }, .memcpy => { - const result = try gz.addPlNode(.memcpy, node, Zir.Inst.Memcpy{ + _ = try gz.addPlNode(.memcpy, node, Zir.Inst.Memcpy{ .dest = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .manyptr_u8_type } }, params[0]), .source = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .manyptr_const_u8_type } }, params[1]), .byte_count = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, params[2]), }); - return rvalue(gz, ri, result, node); + return rvalue(gz, ri, .void_value, node); }, .memset => { - const result = try gz.addPlNode(.memset, node, Zir.Inst.Memset{ + _ = try gz.addPlNode(.memset, node, Zir.Inst.Memset{ .dest = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .manyptr_u8_type } }, params[0]), .byte = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .u8_type } }, params[1]), .byte_count = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, params[2]), }); - return rvalue(gz, ri, result, node); + return rvalue(gz, ri, .void_value, node); }, .shuffle => { const result = try gz.addPlNode(.shuffle, node, Zir.Inst.Shuffle{ @@ -8475,12 +8475,12 @@ fn builtinCall( .prefetch => { const ptr = try expr(gz, scope, .{ .rl = .none }, params[0]); const options = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = .prefetch_options_type } }, params[1]); - const result = try gz.addExtendedPayload(.prefetch, Zir.Inst.BinNode{ + _ = try gz.addExtendedPayload(.prefetch, Zir.Inst.BinNode{ .node = gz.nodeIndexToRelative(node), .lhs = ptr, .rhs = options, }); - return rvalue(gz, ri, result, node); + return rvalue(gz, ri, .void_value, node); }, .c_va_arg => { if (astgen.fn_block == null) { diff --git a/test/behavior/builtin_functions_returning_void_or_noreturn.zig b/test/behavior/builtin_functions_returning_void_or_noreturn.zig new file mode 100644 index 0000000000..072f5576cc --- /dev/null +++ b/test/behavior/builtin_functions_returning_void_or_noreturn.zig @@ -0,0 +1,32 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const testing = std.testing; + +var x: u8 = 1; + +// This excludes builtin functions that return void or noreturn that cannot be tested. +test { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO + + var val: u8 = undefined; + try testing.expectEqual({}, @atomicStore(u8, &val, 0, .Unordered)); + try testing.expectEqual(void, @TypeOf(@breakpoint())); + try testing.expectEqual({}, @export(x, .{ .name = "x" })); + try testing.expectEqual({}, @fence(.Acquire)); + try testing.expectEqual({}, @memcpy(@intToPtr([*]u8, 1), @intToPtr([*]u8, 1), 0)); + try testing.expectEqual({}, @memset(@intToPtr([*]u8, 1), undefined, 0)); + try testing.expectEqual(noreturn, @TypeOf(if (true) @panic("") else {})); + try testing.expectEqual({}, @prefetch(&val, .{})); + try testing.expectEqual({}, @setAlignStack(16)); + try testing.expectEqual({}, @setCold(true)); + try testing.expectEqual({}, @setEvalBranchQuota(0)); + try testing.expectEqual({}, @setFloatMode(.Optimized)); + try testing.expectEqual({}, @setRuntimeSafety(true)); + try testing.expectEqual(noreturn, @TypeOf(if (true) @trap() else {})); +} From 34a23db664e0fe50fb21c892f33b0aec8a7a2f7f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 4 Mar 2023 14:21:57 -0700 Subject: [PATCH 037/294] zig.h: lower trap to SIGTRAP instead of SIGILL --- lib/zig.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/zig.h b/lib/zig.h index 22a9dbbb9e..65fb21f99a 100644 --- a/lib/zig.h +++ b/lib/zig.h @@ -193,7 +193,7 @@ typedef char bool; #elif defined(__i386__) || defined(__x86_64__) #define zig_trap() __asm__ volatile("ud2"); #else -#define zig_trap() raise(SIGILL) +#define zig_trap() raise(SIGTRAP) #endif #if zig_has_builtin(debugtrap) From fb04ff45cd1b4eca5c56e0295bbbe961557ef820 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 4 Mar 2023 14:22:46 -0700 Subject: [PATCH 038/294] langref: small clarification to `@trap` --- doc/langref.html.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index a413c3aab5..7044fe977f 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -9403,7 +9403,7 @@ fn List(comptime T: type) type { Unlike for {#syntax#}@breakpoint(){#endsyntax#}, execution does not continue after this point.

- This function is only valid within function scope. + Outside function scope, this builtin causes a compile error.

{#see_also|@breakpoint#} {#header_close#} From 48e72960a496edc86b231d45bfa39d618b6adfaf Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 4 Mar 2023 14:48:31 -0700 Subject: [PATCH 039/294] llvm: fix lowering of `@trap` It needed an unreachable instruction after it. --- src/codegen/llvm.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index baeaeee58f..85a82f4eda 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -8261,6 +8261,7 @@ pub const FuncGen = struct { _ = inst; const llvm_fn = self.getIntrinsic("llvm.trap", &.{}); _ = self.builder.buildCall(llvm_fn.globalGetValueType(), llvm_fn, undefined, 0, .Cold, .Auto, ""); + _ = self.builder.buildUnreachable(); return null; } From c839c180ef1686794c039fc6d3c20a8716e87357 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 5 Mar 2023 12:46:12 -0700 Subject: [PATCH 040/294] stage2: add zig_backend to ZIR cache namespace --- src/Module.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Module.zig b/src/Module.zig index a2502d36d3..7ea69a0a2e 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -3528,6 +3528,7 @@ pub fn astGenFile(mod: *Module, file: *File) !void { const digest = hash: { var path_hash: Cache.HashHelper = .{}; path_hash.addBytes(build_options.version); + path_hash.add(builtin.zig_backend); if (!want_local_cache) { path_hash.addOptionalBytes(file.pkg.root_src_directory.path); } From cdb9cc8f6bda4b4faa270278e3b67c4ef9246a84 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 4 Mar 2023 14:41:12 -0700 Subject: [PATCH 041/294] update zig1.wasm --- stage1/zig.h | 2759 +++++++++++++++++++++++++++++++--------------- stage1/zig1.wasm | Bin 2408069 -> 2412111 bytes 2 files changed, 1858 insertions(+), 901 deletions(-) diff --git a/stage1/zig.h b/stage1/zig.h index 0756d9f731..65fb21f99a 100644 --- a/stage1/zig.h +++ b/stage1/zig.h @@ -1,8 +1,11 @@ #undef linux +#ifndef __STDC_WANT_IEC_60559_TYPES_EXT__ #define __STDC_WANT_IEC_60559_TYPES_EXT__ +#endif #include #include +#include #include #include @@ -34,6 +37,14 @@ typedef char bool; #define zig_has_attribute(attribute) 0 #endif +#if __LITTLE_ENDIAN__ || _MSC_VER +#define zig_little_endian 1 +#define zig_big_endian 0 +#else +#define zig_little_endian 0 +#define zig_big_endian 1 +#endif + #if __STDC_VERSION__ >= 201112L #define zig_threadlocal _Thread_local #elif defined(__GNUC__) @@ -75,6 +86,32 @@ typedef char bool; #define zig_cold #endif +#if zig_has_attribute(flatten) +#define zig_maybe_flatten __attribute__((flatten)) +#else +#define zig_maybe_flatten +#endif + +#if zig_has_attribute(noinline) +#define zig_never_inline __attribute__((noinline)) zig_maybe_flatten +#elif defined(_MSC_VER) +#define zig_never_inline __declspec(noinline) zig_maybe_flatten +#else +#define zig_never_inline zig_never_inline_unavailable +#endif + +#if zig_has_attribute(not_tail_called) +#define zig_never_tail __attribute__((not_tail_called)) zig_never_inline +#else +#define zig_never_tail zig_never_tail_unavailable +#endif + +#if zig_has_attribute(always_inline) +#define zig_always_tail __attribute__((musttail)) +#else +#define zig_always_tail zig_always_tail_unavailable +#endif + #if __STDC_VERSION__ >= 199901L #define zig_restrict restrict #elif defined(__GNUC__) @@ -151,10 +188,16 @@ typedef char bool; #define zig_export(sig, symbol, name) __asm(name " = " symbol) #endif +#if zig_has_builtin(trap) +#define zig_trap() __builtin_trap() +#elif defined(__i386__) || defined(__x86_64__) +#define zig_trap() __asm__ volatile("ud2"); +#else +#define zig_trap() raise(SIGTRAP) +#endif + #if zig_has_builtin(debugtrap) #define zig_breakpoint() __builtin_debugtrap() -#elif zig_has_builtin(trap) || defined(zig_gnuc) -#define zig_breakpoint() __builtin_trap() #elif defined(_MSC_VER) || defined(__MINGW32__) || defined(__MINGW64__) #define zig_breakpoint() __debugbreak() #elif defined(__i386__) || defined(__x86_64__) @@ -286,701 +329,656 @@ typedef char bool; #endif #if __STDC_VERSION__ >= 201112L -#define zig_noreturn _Noreturn void +#define zig_noreturn _Noreturn #elif zig_has_attribute(noreturn) || defined(zig_gnuc) -#define zig_noreturn __attribute__((noreturn)) void +#define zig_noreturn __attribute__((noreturn)) #elif _MSC_VER -#define zig_noreturn __declspec(noreturn) void +#define zig_noreturn __declspec(noreturn) #else -#define zig_noreturn void +#define zig_noreturn #endif #define zig_bitSizeOf(T) (CHAR_BIT * sizeof(T)) -typedef uintptr_t zig_usize; -typedef intptr_t zig_isize; -typedef signed short int zig_c_short; -typedef unsigned short int zig_c_ushort; -typedef signed int zig_c_int; -typedef unsigned int zig_c_uint; -typedef signed long int zig_c_long; -typedef unsigned long int zig_c_ulong; -typedef signed long long int zig_c_longlong; -typedef unsigned long long int zig_c_ulonglong; +#define zig_compiler_rt_abbrev_uint32_t si +#define zig_compiler_rt_abbrev_int32_t si +#define zig_compiler_rt_abbrev_uint64_t di +#define zig_compiler_rt_abbrev_int64_t di +#define zig_compiler_rt_abbrev_zig_u128 ti +#define zig_compiler_rt_abbrev_zig_i128 ti +#define zig_compiler_rt_abbrev_zig_f16 hf +#define zig_compiler_rt_abbrev_zig_f32 sf +#define zig_compiler_rt_abbrev_zig_f64 df +#define zig_compiler_rt_abbrev_zig_f80 xf +#define zig_compiler_rt_abbrev_zig_f128 tf -typedef uint8_t zig_u8; -typedef int8_t zig_i8; -typedef uint16_t zig_u16; -typedef int16_t zig_i16; -typedef uint32_t zig_u32; -typedef int32_t zig_i32; -typedef uint64_t zig_u64; -typedef int64_t zig_i64; +zig_extern void *memcpy (void *zig_restrict, void const *zig_restrict, size_t); +zig_extern void *memset (void *, int, size_t); -#define zig_as_u8(val) UINT8_C(val) -#define zig_as_i8(val) INT8_C(val) -#define zig_as_u16(val) UINT16_C(val) -#define zig_as_i16(val) INT16_C(val) -#define zig_as_u32(val) UINT32_C(val) -#define zig_as_i32(val) INT32_C(val) -#define zig_as_u64(val) UINT64_C(val) -#define zig_as_i64(val) INT64_C(val) +/* ===================== 8/16/32/64-bit Integer Support ===================== */ + +#if __STDC_VERSION__ >= 199901L || _MSC_VER +#include +#else + +#if SCHAR_MIN == ~0x7F && SCHAR_MAX == 0x7F && UCHAR_MAX == 0xFF +typedef unsigned char uint8_t; +typedef signed char int8_t; +#define INT8_C(c) c +#define UINT8_C(c) c##U +#elif SHRT_MIN == ~0x7F && SHRT_MAX == 0x7F && USHRT_MAX == 0xFF +typedef unsigned short uint8_t; +typedef signed short int8_t; +#define INT8_C(c) c +#define UINT8_C(c) c##U +#elif INT_MIN == ~0x7F && INT_MAX == 0x7F && UINT_MAX == 0xFF +typedef unsigned int uint8_t; +typedef signed int int8_t; +#define INT8_C(c) c +#define UINT8_C(c) c##U +#elif LONG_MIN == ~0x7F && LONG_MAX == 0x7F && ULONG_MAX == 0xFF +typedef unsigned long uint8_t; +typedef signed long int8_t; +#define INT8_C(c) c##L +#define UINT8_C(c) c##LU +#elif LLONG_MIN == ~0x7F && LLONG_MAX == 0x7F && ULLONG_MAX == 0xFF +typedef unsigned long long uint8_t; +typedef signed long long int8_t; +#define INT8_C(c) c##LL +#define UINT8_C(c) c##LLU +#endif +#define INT8_MIN (~INT8_C(0x7F)) +#define INT8_MAX ( INT8_C(0x7F)) +#define UINT8_MAX ( INT8_C(0xFF)) + +#if SCHAR_MIN == ~0x7FFF && SCHAR_MAX == 0x7FFF && UCHAR_MAX == 0xFFFF +typedef unsigned char uint16_t; +typedef signed char int16_t; +#define INT16_C(c) c +#define UINT16_C(c) c##U +#elif SHRT_MIN == ~0x7FFF && SHRT_MAX == 0x7FFF && USHRT_MAX == 0xFFFF +typedef unsigned short uint16_t; +typedef signed short int16_t; +#define INT16_C(c) c +#define UINT16_C(c) c##U +#elif INT_MIN == ~0x7FFF && INT_MAX == 0x7FFF && UINT_MAX == 0xFFFF +typedef unsigned int uint16_t; +typedef signed int int16_t; +#define INT16_C(c) c +#define UINT16_C(c) c##U +#elif LONG_MIN == ~0x7FFF && LONG_MAX == 0x7FFF && ULONG_MAX == 0xFFFF +typedef unsigned long uint16_t; +typedef signed long int16_t; +#define INT16_C(c) c##L +#define UINT16_C(c) c##LU +#elif LLONG_MIN == ~0x7FFF && LLONG_MAX == 0x7FFF && ULLONG_MAX == 0xFFFF +typedef unsigned long long uint16_t; +typedef signed long long int16_t; +#define INT16_C(c) c##LL +#define UINT16_C(c) c##LLU +#endif +#define INT16_MIN (~INT16_C(0x7FFF)) +#define INT16_MAX ( INT16_C(0x7FFF)) +#define UINT16_MAX ( INT16_C(0xFFFF)) + +#if SCHAR_MIN == ~0x7FFFFFFF && SCHAR_MAX == 0x7FFFFFFF && UCHAR_MAX == 0xFFFFFFFF +typedef unsigned char uint32_t; +typedef signed char int32_t; +#define INT32_C(c) c +#define UINT32_C(c) c##U +#elif SHRT_MIN == ~0x7FFFFFFF && SHRT_MAX == 0x7FFFFFFF && USHRT_MAX == 0xFFFFFFFF +typedef unsigned short uint32_t; +typedef signed short int32_t; +#define INT32_C(c) c +#define UINT32_C(c) c##U +#elif INT_MIN == ~0x7FFFFFFF && INT_MAX == 0x7FFFFFFF && UINT_MAX == 0xFFFFFFFF +typedef unsigned int uint32_t; +typedef signed int int32_t; +#define INT32_C(c) c +#define UINT32_C(c) c##U +#elif LONG_MIN == ~0x7FFFFFFF && LONG_MAX == 0x7FFFFFFF && ULONG_MAX == 0xFFFFFFFF +typedef unsigned long uint32_t; +typedef signed long int32_t; +#define INT32_C(c) c##L +#define UINT32_C(c) c##LU +#elif LLONG_MIN == ~0x7FFFFFFF && LLONG_MAX == 0x7FFFFFFF && ULLONG_MAX == 0xFFFFFFFF +typedef unsigned long long uint32_t; +typedef signed long long int32_t; +#define INT32_C(c) c##LL +#define UINT32_C(c) c##LLU +#endif +#define INT32_MIN (~INT32_C(0x7FFFFFFF)) +#define INT32_MAX ( INT32_C(0x7FFFFFFF)) +#define UINT32_MAX ( INT32_C(0xFFFFFFFF)) + +#if SCHAR_MIN == ~0x7FFFFFFFFFFFFFFF && SCHAR_MAX == 0x7FFFFFFFFFFFFFFF && UCHAR_MAX == 0xFFFFFFFFFFFFFFFF +typedef unsigned char uint64_t; +typedef signed char int64_t; +#define INT64_C(c) c +#define UINT64_C(c) c##U +#elif SHRT_MIN == ~0x7FFFFFFFFFFFFFFF && SHRT_MAX == 0x7FFFFFFFFFFFFFFF && USHRT_MAX == 0xFFFFFFFFFFFFFFFF +typedef unsigned short uint64_t; +typedef signed short int64_t; +#define INT64_C(c) c +#define UINT64_C(c) c##U +#elif INT_MIN == ~0x7FFFFFFFFFFFFFFF && INT_MAX == 0x7FFFFFFFFFFFFFFF && UINT_MAX == 0xFFFFFFFFFFFFFFFF +typedef unsigned int uint64_t; +typedef signed int int64_t; +#define INT64_C(c) c +#define UINT64_C(c) c##U +#elif LONG_MIN == ~0x7FFFFFFFFFFFFFFF && LONG_MAX == 0x7FFFFFFFFFFFFFFF && ULONG_MAX == 0xFFFFFFFFFFFFFFFF +typedef unsigned long uint64_t; +typedef signed long int64_t; +#define INT64_C(c) c##L +#define UINT64_C(c) c##LU +#elif LLONG_MIN == ~0x7FFFFFFFFFFFFFFF && LLONG_MAX == 0x7FFFFFFFFFFFFFFF && ULLONG_MAX == 0xFFFFFFFFFFFFFFFF +typedef unsigned long long uint64_t; +typedef signed long long int64_t; +#define INT64_C(c) c##LL +#define UINT64_C(c) c##LLU +#endif +#define INT64_MIN (~INT64_C(0x7FFFFFFFFFFFFFFF)) +#define INT64_MAX ( INT64_C(0x7FFFFFFFFFFFFFFF)) +#define UINT64_MAX ( INT64_C(0xFFFFFFFFFFFFFFFF)) + +typedef size_t uintptr_t; +typedef ptrdiff_t intptr_t; + +#endif -#define zig_minInt_u8 zig_as_u8(0) -#define zig_maxInt_u8 UINT8_MAX #define zig_minInt_i8 INT8_MIN #define zig_maxInt_i8 INT8_MAX -#define zig_minInt_u16 zig_as_u16(0) -#define zig_maxInt_u16 UINT16_MAX +#define zig_minInt_u8 UINT8_C(0) +#define zig_maxInt_u8 UINT8_MAX #define zig_minInt_i16 INT16_MIN #define zig_maxInt_i16 INT16_MAX -#define zig_minInt_u32 zig_as_u32(0) -#define zig_maxInt_u32 UINT32_MAX +#define zig_minInt_u16 UINT16_C(0) +#define zig_maxInt_u16 UINT16_MAX #define zig_minInt_i32 INT32_MIN #define zig_maxInt_i32 INT32_MAX -#define zig_minInt_u64 zig_as_u64(0) -#define zig_maxInt_u64 UINT64_MAX +#define zig_minInt_u32 UINT32_C(0) +#define zig_maxInt_u32 UINT32_MAX #define zig_minInt_i64 INT64_MIN #define zig_maxInt_i64 INT64_MAX +#define zig_minInt_u64 UINT64_C(0) +#define zig_maxInt_u64 UINT64_MAX -#define zig_compiler_rt_abbrev_u32 si -#define zig_compiler_rt_abbrev_i32 si -#define zig_compiler_rt_abbrev_u64 di -#define zig_compiler_rt_abbrev_i64 di -#define zig_compiler_rt_abbrev_u128 ti -#define zig_compiler_rt_abbrev_i128 ti -#define zig_compiler_rt_abbrev_f16 hf -#define zig_compiler_rt_abbrev_f32 sf -#define zig_compiler_rt_abbrev_f64 df -#define zig_compiler_rt_abbrev_f80 xf -#define zig_compiler_rt_abbrev_f128 tf - -zig_extern void *memcpy (void *zig_restrict, void const *zig_restrict, zig_usize); -zig_extern void *memset (void *, int, zig_usize); - -/* ==================== 8/16/32/64-bit Integer Routines ===================== */ - -#define zig_maxInt(Type, bits) zig_shr_##Type(zig_maxInt_##Type, (zig_bitSizeOf(zig_##Type) - bits)) -#define zig_expand_maxInt(Type, bits) zig_maxInt(Type, bits) -#define zig_minInt(Type, bits) zig_not_##Type(zig_maxInt(Type, bits), bits) -#define zig_expand_minInt(Type, bits) zig_minInt(Type, bits) +#define zig_intLimit(s, w, limit, bits) zig_shr_##s##w(zig_##limit##Int_##s##w, w - (bits)) +#define zig_minInt_i(w, bits) zig_intLimit(i, w, min, bits) +#define zig_maxInt_i(w, bits) zig_intLimit(i, w, max, bits) +#define zig_minInt_u(w, bits) zig_intLimit(u, w, min, bits) +#define zig_maxInt_u(w, bits) zig_intLimit(u, w, max, bits) #define zig_int_operator(Type, RhsType, operation, operator) \ - static inline zig_##Type zig_##operation##_##Type(zig_##Type lhs, zig_##RhsType rhs) { \ + static inline Type zig_##operation(Type lhs, RhsType rhs) { \ return lhs operator rhs; \ } #define zig_int_basic_operator(Type, operation, operator) \ - zig_int_operator(Type, Type, operation, operator) + zig_int_operator(Type, Type, operation, operator) #define zig_int_shift_operator(Type, operation, operator) \ - zig_int_operator(Type, u8, operation, operator) + zig_int_operator(Type, uint8_t, operation, operator) #define zig_int_helpers(w) \ - zig_int_basic_operator(u##w, and, &) \ - zig_int_basic_operator(i##w, and, &) \ - zig_int_basic_operator(u##w, or, |) \ - zig_int_basic_operator(i##w, or, |) \ - zig_int_basic_operator(u##w, xor, ^) \ - zig_int_basic_operator(i##w, xor, ^) \ - zig_int_shift_operator(u##w, shl, <<) \ - zig_int_shift_operator(i##w, shl, <<) \ - zig_int_shift_operator(u##w, shr, >>) \ + zig_int_basic_operator(uint##w##_t, and_u##w, &) \ + zig_int_basic_operator( int##w##_t, and_i##w, &) \ + zig_int_basic_operator(uint##w##_t, or_u##w, |) \ + zig_int_basic_operator( int##w##_t, or_i##w, |) \ + zig_int_basic_operator(uint##w##_t, xor_u##w, ^) \ + zig_int_basic_operator( int##w##_t, xor_i##w, ^) \ + zig_int_shift_operator(uint##w##_t, shl_u##w, <<) \ + zig_int_shift_operator( int##w##_t, shl_i##w, <<) \ + zig_int_shift_operator(uint##w##_t, shr_u##w, >>) \ \ - static inline zig_i##w zig_shr_i##w(zig_i##w lhs, zig_u8 rhs) { \ - zig_i##w sign_mask = lhs < zig_as_i##w(0) ? -zig_as_i##w(1) : zig_as_i##w(0); \ + static inline int##w##_t zig_shr_i##w(int##w##_t lhs, uint8_t rhs) { \ + int##w##_t sign_mask = lhs < INT##w##_C(0) ? -INT##w##_C(1) : INT##w##_C(0); \ return ((lhs ^ sign_mask) >> rhs) ^ sign_mask; \ } \ \ - static inline zig_u##w zig_not_u##w(zig_u##w val, zig_u8 bits) { \ - return val ^ zig_maxInt(u##w, bits); \ + static inline uint##w##_t zig_not_u##w(uint##w##_t val, uint8_t bits) { \ + return val ^ zig_maxInt_u(w, bits); \ } \ \ - static inline zig_i##w zig_not_i##w(zig_i##w val, zig_u8 bits) { \ + static inline int##w##_t zig_not_i##w(int##w##_t val, uint8_t bits) { \ (void)bits; \ return ~val; \ } \ \ - static inline zig_u##w zig_wrap_u##w(zig_u##w val, zig_u8 bits) { \ - return val & zig_maxInt(u##w, bits); \ + static inline uint##w##_t zig_wrap_u##w(uint##w##_t val, uint8_t bits) { \ + return val & zig_maxInt_u(w, bits); \ } \ \ - static inline zig_i##w zig_wrap_i##w(zig_i##w val, zig_u8 bits) { \ - return (val & zig_as_u##w(1) << (bits - zig_as_u8(1))) != 0 \ - ? val | zig_minInt(i##w, bits) : val & zig_maxInt(i##w, bits); \ + static inline int##w##_t zig_wrap_i##w(int##w##_t val, uint8_t bits) { \ + return (val & UINT##w##_C(1) << (bits - UINT8_C(1))) != 0 \ + ? val | zig_minInt_i(w, bits) : val & zig_maxInt_i(w, bits); \ } \ \ - zig_int_basic_operator(u##w, div_floor, /) \ + zig_int_basic_operator(uint##w##_t, div_floor_u##w, /) \ \ - static inline zig_i##w zig_div_floor_i##w(zig_i##w lhs, zig_i##w rhs) { \ - return lhs / rhs - (((lhs ^ rhs) & (lhs % rhs)) < zig_as_i##w(0)); \ + static inline int##w##_t zig_div_floor_i##w(int##w##_t lhs, int##w##_t rhs) { \ + return lhs / rhs - (((lhs ^ rhs) & (lhs % rhs)) < INT##w##_C(0)); \ } \ \ - zig_int_basic_operator(u##w, mod, %) \ + zig_int_basic_operator(uint##w##_t, mod_u##w, %) \ \ - static inline zig_i##w zig_mod_i##w(zig_i##w lhs, zig_i##w rhs) { \ - zig_i##w rem = lhs % rhs; \ - return rem + (((lhs ^ rhs) & rem) < zig_as_i##w(0) ? rhs : zig_as_i##w(0)); \ + static inline int##w##_t zig_mod_i##w(int##w##_t lhs, int##w##_t rhs) { \ + int##w##_t rem = lhs % rhs; \ + return rem + (((lhs ^ rhs) & rem) < INT##w##_C(0) ? rhs : INT##w##_C(0)); \ } \ \ - static inline zig_u##w zig_shlw_u##w(zig_u##w lhs, zig_u8 rhs, zig_u8 bits) { \ + static inline uint##w##_t zig_shlw_u##w(uint##w##_t lhs, uint8_t rhs, uint8_t bits) { \ return zig_wrap_u##w(zig_shl_u##w(lhs, rhs), bits); \ } \ \ - static inline zig_i##w zig_shlw_i##w(zig_i##w lhs, zig_u8 rhs, zig_u8 bits) { \ - return zig_wrap_i##w((zig_i##w)zig_shl_u##w((zig_u##w)lhs, (zig_u##w)rhs), bits); \ + static inline int##w##_t zig_shlw_i##w(int##w##_t lhs, uint8_t rhs, uint8_t bits) { \ + return zig_wrap_i##w((int##w##_t)zig_shl_u##w((uint##w##_t)lhs, (uint##w##_t)rhs), bits); \ } \ \ - static inline zig_u##w zig_addw_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \ + static inline uint##w##_t zig_addw_u##w(uint##w##_t lhs, uint##w##_t rhs, uint8_t bits) { \ return zig_wrap_u##w(lhs + rhs, bits); \ } \ \ - static inline zig_i##w zig_addw_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \ - return zig_wrap_i##w((zig_i##w)((zig_u##w)lhs + (zig_u##w)rhs), bits); \ + static inline int##w##_t zig_addw_i##w(int##w##_t lhs, int##w##_t rhs, uint8_t bits) { \ + return zig_wrap_i##w((int##w##_t)((uint##w##_t)lhs + (uint##w##_t)rhs), bits); \ } \ \ - static inline zig_u##w zig_subw_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \ + static inline uint##w##_t zig_subw_u##w(uint##w##_t lhs, uint##w##_t rhs, uint8_t bits) { \ return zig_wrap_u##w(lhs - rhs, bits); \ } \ \ - static inline zig_i##w zig_subw_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \ - return zig_wrap_i##w((zig_i##w)((zig_u##w)lhs - (zig_u##w)rhs), bits); \ + static inline int##w##_t zig_subw_i##w(int##w##_t lhs, int##w##_t rhs, uint8_t bits) { \ + return zig_wrap_i##w((int##w##_t)((uint##w##_t)lhs - (uint##w##_t)rhs), bits); \ } \ \ - static inline zig_u##w zig_mulw_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \ + static inline uint##w##_t zig_mulw_u##w(uint##w##_t lhs, uint##w##_t rhs, uint8_t bits) { \ return zig_wrap_u##w(lhs * rhs, bits); \ } \ \ - static inline zig_i##w zig_mulw_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \ - return zig_wrap_i##w((zig_i##w)((zig_u##w)lhs * (zig_u##w)rhs), bits); \ + static inline int##w##_t zig_mulw_i##w(int##w##_t lhs, int##w##_t rhs, uint8_t bits) { \ + return zig_wrap_i##w((int##w##_t)((uint##w##_t)lhs * (uint##w##_t)rhs), bits); \ } zig_int_helpers(8) zig_int_helpers(16) zig_int_helpers(32) zig_int_helpers(64) -static inline bool zig_addo_u32(zig_u32 *res, zig_u32 lhs, zig_u32 rhs, zig_u8 bits) { +static inline bool zig_addo_u32(uint32_t *res, uint32_t lhs, uint32_t rhs, uint8_t bits) { #if zig_has_builtin(add_overflow) || defined(zig_gnuc) - zig_u32 full_res; + uint32_t full_res; bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); *res = zig_wrap_u32(full_res, bits); - return overflow || full_res < zig_minInt(u32, bits) || full_res > zig_maxInt(u32, bits); + return overflow || full_res < zig_minInt_u(32, bits) || full_res > zig_maxInt_u(32, bits); #else *res = zig_addw_u32(lhs, rhs, bits); return *res < lhs; #endif } -static inline void zig_vaddo_u32(zig_u8 *ov, zig_u32 *res, int n, - const zig_u32 *lhs, const zig_u32 *rhs, zig_u8 bits) -{ - for (int i = 0; i < n; ++i) ov[i] = zig_addo_u32(&res[i], lhs[i], rhs[i], bits); -} - -zig_extern zig_i32 __addosi4(zig_i32 lhs, zig_i32 rhs, zig_c_int *overflow); -static inline bool zig_addo_i32(zig_i32 *res, zig_i32 lhs, zig_i32 rhs, zig_u8 bits) { +zig_extern int32_t __addosi4(int32_t lhs, int32_t rhs, int *overflow); +static inline bool zig_addo_i32(int32_t *res, int32_t lhs, int32_t rhs, uint8_t bits) { #if zig_has_builtin(add_overflow) || defined(zig_gnuc) - zig_i32 full_res; + int32_t full_res; bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); #else - zig_c_int overflow_int; - zig_i32 full_res = __addosi4(lhs, rhs, &overflow_int); + int overflow_int; + int32_t full_res = __addosi4(lhs, rhs, &overflow_int); bool overflow = overflow_int != 0; #endif *res = zig_wrap_i32(full_res, bits); - return overflow || full_res < zig_minInt(i32, bits) || full_res > zig_maxInt(i32, bits); + return overflow || full_res < zig_minInt_i(32, bits) || full_res > zig_maxInt_i(32, bits); } -static inline void zig_vaddo_i32(zig_u8 *ov, zig_i32 *res, int n, - const zig_i32 *lhs, const zig_i32 *rhs, zig_u8 bits) -{ - for (int i = 0; i < n; ++i) ov[i] = zig_addo_i32(&res[i], lhs[i], rhs[i], bits); -} - -static inline bool zig_addo_u64(zig_u64 *res, zig_u64 lhs, zig_u64 rhs, zig_u8 bits) { +static inline bool zig_addo_u64(uint64_t *res, uint64_t lhs, uint64_t rhs, uint8_t bits) { #if zig_has_builtin(add_overflow) || defined(zig_gnuc) - zig_u64 full_res; + uint64_t full_res; bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); *res = zig_wrap_u64(full_res, bits); - return overflow || full_res < zig_minInt(u64, bits) || full_res > zig_maxInt(u64, bits); + return overflow || full_res < zig_minInt_u(64, bits) || full_res > zig_maxInt_u(64, bits); #else *res = zig_addw_u64(lhs, rhs, bits); return *res < lhs; #endif } -static inline void zig_vaddo_u64(zig_u8 *ov, zig_u64 *res, int n, - const zig_u64 *lhs, const zig_u64 *rhs, zig_u8 bits) -{ - for (int i = 0; i < n; ++i) ov[i] = zig_addo_u64(&res[i], lhs[i], rhs[i], bits); -} - -zig_extern zig_i64 __addodi4(zig_i64 lhs, zig_i64 rhs, zig_c_int *overflow); -static inline bool zig_addo_i64(zig_i64 *res, zig_i64 lhs, zig_i64 rhs, zig_u8 bits) { +zig_extern int64_t __addodi4(int64_t lhs, int64_t rhs, int *overflow); +static inline bool zig_addo_i64(int64_t *res, int64_t lhs, int64_t rhs, uint8_t bits) { #if zig_has_builtin(add_overflow) || defined(zig_gnuc) - zig_i64 full_res; + int64_t full_res; bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); #else - zig_c_int overflow_int; - zig_i64 full_res = __addodi4(lhs, rhs, &overflow_int); + int overflow_int; + int64_t full_res = __addodi4(lhs, rhs, &overflow_int); bool overflow = overflow_int != 0; #endif *res = zig_wrap_i64(full_res, bits); - return overflow || full_res < zig_minInt(i64, bits) || full_res > zig_maxInt(i64, bits); + return overflow || full_res < zig_minInt_i(64, bits) || full_res > zig_maxInt_i(64, bits); } -static inline void zig_vaddo_i64(zig_u8 *ov, zig_i64 *res, int n, - const zig_i64 *lhs, const zig_i64 *rhs, zig_u8 bits) -{ - for (int i = 0; i < n; ++i) ov[i] = zig_addo_i64(&res[i], lhs[i], rhs[i], bits); -} - -static inline bool zig_addo_u8(zig_u8 *res, zig_u8 lhs, zig_u8 rhs, zig_u8 bits) { +static inline bool zig_addo_u8(uint8_t *res, uint8_t lhs, uint8_t rhs, uint8_t bits) { #if zig_has_builtin(add_overflow) || defined(zig_gnuc) - zig_u8 full_res; + uint8_t full_res; bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); *res = zig_wrap_u8(full_res, bits); - return overflow || full_res < zig_minInt(u8, bits) || full_res > zig_maxInt(u8, bits); + return overflow || full_res < zig_minInt_u(8, bits) || full_res > zig_maxInt_u(8, bits); #else - zig_u32 full_res; + uint32_t full_res; bool overflow = zig_addo_u32(&full_res, lhs, rhs, bits); - *res = (zig_u8)full_res; + *res = (uint8_t)full_res; return overflow; #endif } -static inline void zig_vaddo_u8(zig_u8 *ov, zig_u8 *res, int n, - const zig_u8 *lhs, const zig_u8 *rhs, zig_u8 bits) -{ - for (int i = 0; i < n; ++i) ov[i] = zig_addo_u8(&res[i], lhs[i], rhs[i], bits); -} - -static inline bool zig_addo_i8(zig_i8 *res, zig_i8 lhs, zig_i8 rhs, zig_u8 bits) { +static inline bool zig_addo_i8(int8_t *res, int8_t lhs, int8_t rhs, uint8_t bits) { #if zig_has_builtin(add_overflow) || defined(zig_gnuc) - zig_i8 full_res; + int8_t full_res; bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); *res = zig_wrap_i8(full_res, bits); - return overflow || full_res < zig_minInt(i8, bits) || full_res > zig_maxInt(i8, bits); + return overflow || full_res < zig_minInt_i(8, bits) || full_res > zig_maxInt_i(8, bits); #else - zig_i32 full_res; + int32_t full_res; bool overflow = zig_addo_i32(&full_res, lhs, rhs, bits); - *res = (zig_i8)full_res; + *res = (int8_t)full_res; return overflow; #endif } -static inline void zig_vaddo_i8(zig_u8 *ov, zig_i8 *res, int n, - const zig_i8 *lhs, const zig_i8 *rhs, zig_u8 bits) -{ - for (int i = 0; i < n; ++i) ov[i] = zig_addo_i8(&res[i], lhs[i], rhs[i], bits); -} - -static inline bool zig_addo_u16(zig_u16 *res, zig_u16 lhs, zig_u16 rhs, zig_u8 bits) { +static inline bool zig_addo_u16(uint16_t *res, uint16_t lhs, uint16_t rhs, uint8_t bits) { #if zig_has_builtin(add_overflow) || defined(zig_gnuc) - zig_u16 full_res; + uint16_t full_res; bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); *res = zig_wrap_u16(full_res, bits); - return overflow || full_res < zig_minInt(u16, bits) || full_res > zig_maxInt(u16, bits); + return overflow || full_res < zig_minInt_u(16, bits) || full_res > zig_maxInt_u(16, bits); #else - zig_u32 full_res; + uint32_t full_res; bool overflow = zig_addo_u32(&full_res, lhs, rhs, bits); - *res = (zig_u16)full_res; + *res = (uint16_t)full_res; return overflow; #endif } -static inline void zig_vaddo_u16(zig_u8 *ov, zig_u16 *res, int n, - const zig_u16 *lhs, const zig_u16 *rhs, zig_u8 bits) -{ - for (int i = 0; i < n; ++i) ov[i] = zig_addo_u16(&res[i], lhs[i], rhs[i], bits); -} - -static inline bool zig_addo_i16(zig_i16 *res, zig_i16 lhs, zig_i16 rhs, zig_u8 bits) { +static inline bool zig_addo_i16(int16_t *res, int16_t lhs, int16_t rhs, uint8_t bits) { #if zig_has_builtin(add_overflow) || defined(zig_gnuc) - zig_i16 full_res; + int16_t full_res; bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); *res = zig_wrap_i16(full_res, bits); - return overflow || full_res < zig_minInt(i16, bits) || full_res > zig_maxInt(i16, bits); + return overflow || full_res < zig_minInt_i(16, bits) || full_res > zig_maxInt_i(16, bits); #else - zig_i32 full_res; + int32_t full_res; bool overflow = zig_addo_i32(&full_res, lhs, rhs, bits); - *res = (zig_i16)full_res; + *res = (int16_t)full_res; return overflow; #endif } -static inline void zig_vaddo_i16(zig_u8 *ov, zig_i16 *res, int n, - const zig_i16 *lhs, const zig_i16 *rhs, zig_u8 bits) -{ - for (int i = 0; i < n; ++i) ov[i] = zig_addo_i16(&res[i], lhs[i], rhs[i], bits); -} - -static inline bool zig_subo_u32(zig_u32 *res, zig_u32 lhs, zig_u32 rhs, zig_u8 bits) { +static inline bool zig_subo_u32(uint32_t *res, uint32_t lhs, uint32_t rhs, uint8_t bits) { #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) - zig_u32 full_res; + uint32_t full_res; bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); *res = zig_wrap_u32(full_res, bits); - return overflow || full_res < zig_minInt(u32, bits) || full_res > zig_maxInt(u32, bits); + return overflow || full_res < zig_minInt_u(32, bits) || full_res > zig_maxInt_u(32, bits); #else *res = zig_subw_u32(lhs, rhs, bits); return *res > lhs; #endif } -static inline void zig_vsubo_u32(zig_u8 *ov, zig_u32 *res, int n, - const zig_u32 *lhs, const zig_u32 *rhs, zig_u8 bits) -{ - for (int i = 0; i < n; ++i) ov[i] = zig_subo_u32(&res[i], lhs[i], rhs[i], bits); -} - -zig_extern zig_i32 __subosi4(zig_i32 lhs, zig_i32 rhs, zig_c_int *overflow); -static inline bool zig_subo_i32(zig_i32 *res, zig_i32 lhs, zig_i32 rhs, zig_u8 bits) { +zig_extern int32_t __subosi4(int32_t lhs, int32_t rhs, int *overflow); +static inline bool zig_subo_i32(int32_t *res, int32_t lhs, int32_t rhs, uint8_t bits) { #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) - zig_i32 full_res; + int32_t full_res; bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); #else - zig_c_int overflow_int; - zig_i32 full_res = __subosi4(lhs, rhs, &overflow_int); + int overflow_int; + int32_t full_res = __subosi4(lhs, rhs, &overflow_int); bool overflow = overflow_int != 0; #endif *res = zig_wrap_i32(full_res, bits); - return overflow || full_res < zig_minInt(i32, bits) || full_res > zig_maxInt(i32, bits); + return overflow || full_res < zig_minInt_i(32, bits) || full_res > zig_maxInt_i(32, bits); } -static inline void zig_vsubo_i32(zig_u8 *ov, zig_i32 *res, int n, - const zig_i32 *lhs, const zig_i32 *rhs, zig_u8 bits) -{ - for (int i = 0; i < n; ++i) ov[i] = zig_subo_i32(&res[i], lhs[i], rhs[i], bits); -} - -static inline bool zig_subo_u64(zig_u64 *res, zig_u64 lhs, zig_u64 rhs, zig_u8 bits) { +static inline bool zig_subo_u64(uint64_t *res, uint64_t lhs, uint64_t rhs, uint8_t bits) { #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) - zig_u64 full_res; + uint64_t full_res; bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); *res = zig_wrap_u64(full_res, bits); - return overflow || full_res < zig_minInt(u64, bits) || full_res > zig_maxInt(u64, bits); + return overflow || full_res < zig_minInt_u(64, bits) || full_res > zig_maxInt_u(64, bits); #else *res = zig_subw_u64(lhs, rhs, bits); return *res > lhs; #endif } -static inline void zig_vsubo_u64(zig_u8 *ov, zig_u64 *res, int n, - const zig_u64 *lhs, const zig_u64 *rhs, zig_u8 bits) -{ - for (int i = 0; i < n; ++i) ov[i] = zig_subo_u64(&res[i], lhs[i], rhs[i], bits); -} - -zig_extern zig_i64 __subodi4(zig_i64 lhs, zig_i64 rhs, zig_c_int *overflow); -static inline bool zig_subo_i64(zig_i64 *res, zig_i64 lhs, zig_i64 rhs, zig_u8 bits) { +zig_extern int64_t __subodi4(int64_t lhs, int64_t rhs, int *overflow); +static inline bool zig_subo_i64(int64_t *res, int64_t lhs, int64_t rhs, uint8_t bits) { #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) - zig_i64 full_res; + int64_t full_res; bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); #else - zig_c_int overflow_int; - zig_i64 full_res = __subodi4(lhs, rhs, &overflow_int); + int overflow_int; + int64_t full_res = __subodi4(lhs, rhs, &overflow_int); bool overflow = overflow_int != 0; #endif *res = zig_wrap_i64(full_res, bits); - return overflow || full_res < zig_minInt(i64, bits) || full_res > zig_maxInt(i64, bits); + return overflow || full_res < zig_minInt_i(64, bits) || full_res > zig_maxInt_i(64, bits); } -static inline void zig_vsubo_i64(zig_u8 *ov, zig_i64 *res, int n, - const zig_i64 *lhs, const zig_i64 *rhs, zig_u8 bits) -{ - for (int i = 0; i < n; ++i) ov[i] = zig_subo_i64(&res[i], lhs[i], rhs[i], bits); -} - -static inline bool zig_subo_u8(zig_u8 *res, zig_u8 lhs, zig_u8 rhs, zig_u8 bits) { +static inline bool zig_subo_u8(uint8_t *res, uint8_t lhs, uint8_t rhs, uint8_t bits) { #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) - zig_u8 full_res; + uint8_t full_res; bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); *res = zig_wrap_u8(full_res, bits); - return overflow || full_res < zig_minInt(u8, bits) || full_res > zig_maxInt(u8, bits); + return overflow || full_res < zig_minInt_u(8, bits) || full_res > zig_maxInt_u(8, bits); #else - zig_u32 full_res; + uint32_t full_res; bool overflow = zig_subo_u32(&full_res, lhs, rhs, bits); - *res = (zig_u8)full_res; + *res = (uint8_t)full_res; return overflow; #endif } -static inline void zig_vsubo_u8(zig_u8 *ov, zig_u8 *res, int n, - const zig_u8 *lhs, const zig_u8 *rhs, zig_u8 bits) -{ - for (int i = 0; i < n; ++i) ov[i] = zig_subo_u8(&res[i], lhs[i], rhs[i], bits); -} - -static inline bool zig_subo_i8(zig_i8 *res, zig_i8 lhs, zig_i8 rhs, zig_u8 bits) { +static inline bool zig_subo_i8(int8_t *res, int8_t lhs, int8_t rhs, uint8_t bits) { #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) - zig_i8 full_res; + int8_t full_res; bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); *res = zig_wrap_i8(full_res, bits); - return overflow || full_res < zig_minInt(i8, bits) || full_res > zig_maxInt(i8, bits); + return overflow || full_res < zig_minInt_i(8, bits) || full_res > zig_maxInt_i(8, bits); #else - zig_i32 full_res; + int32_t full_res; bool overflow = zig_subo_i32(&full_res, lhs, rhs, bits); - *res = (zig_i8)full_res; + *res = (int8_t)full_res; return overflow; #endif } -static inline void zig_vsubo_i8(zig_u8 *ov, zig_i8 *res, int n, - const zig_i8 *lhs, const zig_i8 *rhs, zig_u8 bits) -{ - for (int i = 0; i < n; ++i) ov[i] = zig_subo_i8(&res[i], lhs[i], rhs[i], bits); -} - - -static inline bool zig_subo_u16(zig_u16 *res, zig_u16 lhs, zig_u16 rhs, zig_u8 bits) { +static inline bool zig_subo_u16(uint16_t *res, uint16_t lhs, uint16_t rhs, uint8_t bits) { #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) - zig_u16 full_res; + uint16_t full_res; bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); *res = zig_wrap_u16(full_res, bits); - return overflow || full_res < zig_minInt(u16, bits) || full_res > zig_maxInt(u16, bits); + return overflow || full_res < zig_minInt_u(16, bits) || full_res > zig_maxInt_u(16, bits); #else - zig_u32 full_res; + uint32_t full_res; bool overflow = zig_subo_u32(&full_res, lhs, rhs, bits); - *res = (zig_u16)full_res; + *res = (uint16_t)full_res; return overflow; #endif } -static inline void zig_vsubo_u16(zig_u8 *ov, zig_u16 *res, int n, - const zig_u16 *lhs, const zig_u16 *rhs, zig_u8 bits) -{ - for (int i = 0; i < n; ++i) ov[i] = zig_subo_u16(&res[i], lhs[i], rhs[i], bits); -} - - -static inline bool zig_subo_i16(zig_i16 *res, zig_i16 lhs, zig_i16 rhs, zig_u8 bits) { +static inline bool zig_subo_i16(int16_t *res, int16_t lhs, int16_t rhs, uint8_t bits) { #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) - zig_i16 full_res; + int16_t full_res; bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); *res = zig_wrap_i16(full_res, bits); - return overflow || full_res < zig_minInt(i16, bits) || full_res > zig_maxInt(i16, bits); + return overflow || full_res < zig_minInt_i(16, bits) || full_res > zig_maxInt_i(16, bits); #else - zig_i32 full_res; + int32_t full_res; bool overflow = zig_subo_i32(&full_res, lhs, rhs, bits); - *res = (zig_i16)full_res; + *res = (int16_t)full_res; return overflow; #endif } -static inline void zig_vsubo_i16(zig_u8 *ov, zig_i16 *res, int n, - const zig_i16 *lhs, const zig_i16 *rhs, zig_u8 bits) -{ - for (int i = 0; i < n; ++i) ov[i] = zig_subo_i16(&res[i], lhs[i], rhs[i], bits); -} - -static inline bool zig_mulo_u32(zig_u32 *res, zig_u32 lhs, zig_u32 rhs, zig_u8 bits) { +static inline bool zig_mulo_u32(uint32_t *res, uint32_t lhs, uint32_t rhs, uint8_t bits) { #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) - zig_u32 full_res; + uint32_t full_res; bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); *res = zig_wrap_u32(full_res, bits); - return overflow || full_res < zig_minInt(u32, bits) || full_res > zig_maxInt(u32, bits); + return overflow || full_res < zig_minInt_u(32, bits) || full_res > zig_maxInt_u(32, bits); #else *res = zig_mulw_u32(lhs, rhs, bits); - return rhs != zig_as_u32(0) && lhs > zig_maxInt(u32, bits) / rhs; + return rhs != UINT32_C(0) && lhs > zig_maxInt_u(32, bits) / rhs; #endif } -static inline void zig_vmulo_u32(zig_u8 *ov, zig_u32 *res, int n, - const zig_u32 *lhs, const zig_u32 *rhs, zig_u8 bits) -{ - for (int i = 0; i < n; ++i) ov[i] = zig_mulo_u32(&res[i], lhs[i], rhs[i], bits); -} - -zig_extern zig_i32 __mulosi4(zig_i32 lhs, zig_i32 rhs, zig_c_int *overflow); -static inline bool zig_mulo_i32(zig_i32 *res, zig_i32 lhs, zig_i32 rhs, zig_u8 bits) { +zig_extern int32_t __mulosi4(int32_t lhs, int32_t rhs, int *overflow); +static inline bool zig_mulo_i32(int32_t *res, int32_t lhs, int32_t rhs, uint8_t bits) { #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) - zig_i32 full_res; + int32_t full_res; bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); #else - zig_c_int overflow_int; - zig_i32 full_res = __mulosi4(lhs, rhs, &overflow_int); + int overflow_int; + int32_t full_res = __mulosi4(lhs, rhs, &overflow_int); bool overflow = overflow_int != 0; #endif *res = zig_wrap_i32(full_res, bits); - return overflow || full_res < zig_minInt(i32, bits) || full_res > zig_maxInt(i32, bits); + return overflow || full_res < zig_minInt_i(32, bits) || full_res > zig_maxInt_i(32, bits); } -static inline void zig_vmulo_i32(zig_u8 *ov, zig_i32 *res, int n, - const zig_i32 *lhs, const zig_i32 *rhs, zig_u8 bits) -{ - for (int i = 0; i < n; ++i) ov[i] = zig_mulo_i32(&res[i], lhs[i], rhs[i], bits); -} - -static inline bool zig_mulo_u64(zig_u64 *res, zig_u64 lhs, zig_u64 rhs, zig_u8 bits) { +static inline bool zig_mulo_u64(uint64_t *res, uint64_t lhs, uint64_t rhs, uint8_t bits) { #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) - zig_u64 full_res; + uint64_t full_res; bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); *res = zig_wrap_u64(full_res, bits); - return overflow || full_res < zig_minInt(u64, bits) || full_res > zig_maxInt(u64, bits); + return overflow || full_res < zig_minInt_u(64, bits) || full_res > zig_maxInt_u(64, bits); #else *res = zig_mulw_u64(lhs, rhs, bits); - return rhs != zig_as_u64(0) && lhs > zig_maxInt(u64, bits) / rhs; + return rhs != UINT64_C(0) && lhs > zig_maxInt_u(64, bits) / rhs; #endif } -static inline void zig_vmulo_u64(zig_u8 *ov, zig_u64 *res, int n, - const zig_u64 *lhs, const zig_u64 *rhs, zig_u8 bits) -{ - for (int i = 0; i < n; ++i) ov[i] = zig_mulo_u64(&res[i], lhs[i], rhs[i], bits); -} - -zig_extern zig_i64 __mulodi4(zig_i64 lhs, zig_i64 rhs, zig_c_int *overflow); -static inline bool zig_mulo_i64(zig_i64 *res, zig_i64 lhs, zig_i64 rhs, zig_u8 bits) { +zig_extern int64_t __mulodi4(int64_t lhs, int64_t rhs, int *overflow); +static inline bool zig_mulo_i64(int64_t *res, int64_t lhs, int64_t rhs, uint8_t bits) { #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) - zig_i64 full_res; + int64_t full_res; bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); #else - zig_c_int overflow_int; - zig_i64 full_res = __mulodi4(lhs, rhs, &overflow_int); + int overflow_int; + int64_t full_res = __mulodi4(lhs, rhs, &overflow_int); bool overflow = overflow_int != 0; #endif *res = zig_wrap_i64(full_res, bits); - return overflow || full_res < zig_minInt(i64, bits) || full_res > zig_maxInt(i64, bits); + return overflow || full_res < zig_minInt_i(64, bits) || full_res > zig_maxInt_i(64, bits); } -static inline void zig_vmulo_i64(zig_u8 *ov, zig_i64 *res, int n, - const zig_i64 *lhs, const zig_i64 *rhs, zig_u8 bits) -{ - for (int i = 0; i < n; ++i) ov[i] = zig_mulo_i64(&res[i], lhs[i], rhs[i], bits); -} - -static inline bool zig_mulo_u8(zig_u8 *res, zig_u8 lhs, zig_u8 rhs, zig_u8 bits) { +static inline bool zig_mulo_u8(uint8_t *res, uint8_t lhs, uint8_t rhs, uint8_t bits) { #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) - zig_u8 full_res; + uint8_t full_res; bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); *res = zig_wrap_u8(full_res, bits); - return overflow || full_res < zig_minInt(u8, bits) || full_res > zig_maxInt(u8, bits); + return overflow || full_res < zig_minInt_u(8, bits) || full_res > zig_maxInt_u(8, bits); #else - zig_u32 full_res; + uint32_t full_res; bool overflow = zig_mulo_u32(&full_res, lhs, rhs, bits); - *res = (zig_u8)full_res; + *res = (uint8_t)full_res; return overflow; #endif } -static inline void zig_vmulo_u8(zig_u8 *ov, zig_u8 *res, int n, - const zig_u8 *lhs, const zig_u8 *rhs, zig_u8 bits) -{ - for (int i = 0; i < n; ++i) ov[i] = zig_mulo_u8(&res[i], lhs[i], rhs[i], bits); -} - -static inline bool zig_mulo_i8(zig_i8 *res, zig_i8 lhs, zig_i8 rhs, zig_u8 bits) { +static inline bool zig_mulo_i8(int8_t *res, int8_t lhs, int8_t rhs, uint8_t bits) { #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) - zig_i8 full_res; + int8_t full_res; bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); *res = zig_wrap_i8(full_res, bits); - return overflow || full_res < zig_minInt(i8, bits) || full_res > zig_maxInt(i8, bits); + return overflow || full_res < zig_minInt_i(8, bits) || full_res > zig_maxInt_i(8, bits); #else - zig_i32 full_res; + int32_t full_res; bool overflow = zig_mulo_i32(&full_res, lhs, rhs, bits); - *res = (zig_i8)full_res; + *res = (int8_t)full_res; return overflow; #endif } -static inline void zig_vmulo_i8(zig_u8 *ov, zig_i8 *res, int n, - const zig_i8 *lhs, const zig_i8 *rhs, zig_u8 bits) -{ - for (int i = 0; i < n; ++i) ov[i] = zig_mulo_i8(&res[i], lhs[i], rhs[i], bits); -} - -static inline bool zig_mulo_u16(zig_u16 *res, zig_u16 lhs, zig_u16 rhs, zig_u8 bits) { +static inline bool zig_mulo_u16(uint16_t *res, uint16_t lhs, uint16_t rhs, uint8_t bits) { #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) - zig_u16 full_res; + uint16_t full_res; bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); *res = zig_wrap_u16(full_res, bits); - return overflow || full_res < zig_minInt(u16, bits) || full_res > zig_maxInt(u16, bits); + return overflow || full_res < zig_minInt_u(16, bits) || full_res > zig_maxInt_u(16, bits); #else - zig_u32 full_res; + uint32_t full_res; bool overflow = zig_mulo_u32(&full_res, lhs, rhs, bits); - *res = (zig_u16)full_res; + *res = (uint16_t)full_res; return overflow; #endif } -static inline void zig_vmulo_u16(zig_u8 *ov, zig_u16 *res, int n, - const zig_u16 *lhs, const zig_u16 *rhs, zig_u8 bits) -{ - for (int i = 0; i < n; ++i) ov[i] = zig_mulo_u16(&res[i], lhs[i], rhs[i], bits); -} - -static inline bool zig_mulo_i16(zig_i16 *res, zig_i16 lhs, zig_i16 rhs, zig_u8 bits) { +static inline bool zig_mulo_i16(int16_t *res, int16_t lhs, int16_t rhs, uint8_t bits) { #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) - zig_i16 full_res; + int16_t full_res; bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); *res = zig_wrap_i16(full_res, bits); - return overflow || full_res < zig_minInt(i16, bits) || full_res > zig_maxInt(i16, bits); + return overflow || full_res < zig_minInt_i(16, bits) || full_res > zig_maxInt_i(16, bits); #else - zig_i32 full_res; + int32_t full_res; bool overflow = zig_mulo_i32(&full_res, lhs, rhs, bits); - *res = (zig_i16)full_res; + *res = (int16_t)full_res; return overflow; #endif } -static inline void zig_vmulo_i16(zig_u8 *ov, zig_i16 *res, int n, - const zig_i16 *lhs, const zig_i16 *rhs, zig_u8 bits) -{ - for (int i = 0; i < n; ++i) ov[i] = zig_mulo_i16(&res[i], lhs[i], rhs[i], bits); -} - #define zig_int_builtins(w) \ - static inline bool zig_shlo_u##w(zig_u##w *res, zig_u##w lhs, zig_u8 rhs, zig_u8 bits) { \ + static inline bool zig_shlo_u##w(uint##w##_t *res, uint##w##_t lhs, uint8_t rhs, uint8_t bits) { \ *res = zig_shlw_u##w(lhs, rhs, bits); \ - return lhs > zig_maxInt(u##w, bits) >> rhs; \ + return lhs > zig_maxInt_u(w, bits) >> rhs; \ } \ \ - static inline bool zig_shlo_i##w(zig_i##w *res, zig_i##w lhs, zig_u8 rhs, zig_u8 bits) { \ + static inline bool zig_shlo_i##w(int##w##_t *res, int##w##_t lhs, uint8_t rhs, uint8_t bits) { \ *res = zig_shlw_i##w(lhs, rhs, bits); \ - zig_i##w mask = (zig_i##w)(zig_maxInt_u##w << (bits - rhs - 1)); \ - return (lhs & mask) != zig_as_i##w(0) && (lhs & mask) != mask; \ + int##w##_t mask = (int##w##_t)(UINT##w##_MAX << (bits - rhs - 1)); \ + return (lhs & mask) != INT##w##_C(0) && (lhs & mask) != mask; \ } \ \ - static inline zig_u##w zig_shls_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \ - zig_u##w res; \ - if (rhs >= bits) return lhs != zig_as_u##w(0) ? zig_maxInt(u##w, bits) : lhs; \ - return zig_shlo_u##w(&res, lhs, (zig_u8)rhs, bits) ? zig_maxInt(u##w, bits) : res; \ + static inline uint##w##_t zig_shls_u##w(uint##w##_t lhs, uint##w##_t rhs, uint8_t bits) { \ + uint##w##_t res; \ + if (rhs >= bits) return lhs != UINT##w##_C(0) ? zig_maxInt_u(w, bits) : lhs; \ + return zig_shlo_u##w(&res, lhs, (uint8_t)rhs, bits) ? zig_maxInt_u(w, bits) : res; \ } \ \ - static inline zig_i##w zig_shls_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \ - zig_i##w res; \ - if ((zig_u##w)rhs < (zig_u##w)bits && !zig_shlo_i##w(&res, lhs, rhs, bits)) return res; \ - return lhs < zig_as_i##w(0) ? zig_minInt(i##w, bits) : zig_maxInt(i##w, bits); \ + static inline int##w##_t zig_shls_i##w(int##w##_t lhs, int##w##_t rhs, uint8_t bits) { \ + int##w##_t res; \ + if ((uint##w##_t)rhs < (uint##w##_t)bits && !zig_shlo_i##w(&res, lhs, (uint8_t)rhs, bits)) return res; \ + return lhs < INT##w##_C(0) ? zig_minInt_i(w, bits) : zig_maxInt_i(w, bits); \ } \ \ - static inline zig_u##w zig_adds_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \ - zig_u##w res; \ - return zig_addo_u##w(&res, lhs, rhs, bits) ? zig_maxInt(u##w, bits) : res; \ + static inline uint##w##_t zig_adds_u##w(uint##w##_t lhs, uint##w##_t rhs, uint8_t bits) { \ + uint##w##_t res; \ + return zig_addo_u##w(&res, lhs, rhs, bits) ? zig_maxInt_u(w, bits) : res; \ } \ \ - static inline zig_i##w zig_adds_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \ - zig_i##w res; \ + static inline int##w##_t zig_adds_i##w(int##w##_t lhs, int##w##_t rhs, uint8_t bits) { \ + int##w##_t res; \ if (!zig_addo_i##w(&res, lhs, rhs, bits)) return res; \ - return res >= zig_as_i##w(0) ? zig_minInt(i##w, bits) : zig_maxInt(i##w, bits); \ + return res >= INT##w##_C(0) ? zig_minInt_i(w, bits) : zig_maxInt_i(w, bits); \ } \ \ - static inline zig_u##w zig_subs_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \ - zig_u##w res; \ - return zig_subo_u##w(&res, lhs, rhs, bits) ? zig_minInt(u##w, bits) : res; \ + static inline uint##w##_t zig_subs_u##w(uint##w##_t lhs, uint##w##_t rhs, uint8_t bits) { \ + uint##w##_t res; \ + return zig_subo_u##w(&res, lhs, rhs, bits) ? zig_minInt_u(w, bits) : res; \ } \ \ - static inline zig_i##w zig_subs_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \ - zig_i##w res; \ + static inline int##w##_t zig_subs_i##w(int##w##_t lhs, int##w##_t rhs, uint8_t bits) { \ + int##w##_t res; \ if (!zig_subo_i##w(&res, lhs, rhs, bits)) return res; \ - return res >= zig_as_i##w(0) ? zig_minInt(i##w, bits) : zig_maxInt(i##w, bits); \ + return res >= INT##w##_C(0) ? zig_minInt_i(w, bits) : zig_maxInt_i(w, bits); \ } \ \ - static inline zig_u##w zig_muls_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \ - zig_u##w res; \ - return zig_mulo_u##w(&res, lhs, rhs, bits) ? zig_maxInt(u##w, bits) : res; \ + static inline uint##w##_t zig_muls_u##w(uint##w##_t lhs, uint##w##_t rhs, uint8_t bits) { \ + uint##w##_t res; \ + return zig_mulo_u##w(&res, lhs, rhs, bits) ? zig_maxInt_u(w, bits) : res; \ } \ \ - static inline zig_i##w zig_muls_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \ - zig_i##w res; \ + static inline int##w##_t zig_muls_i##w(int##w##_t lhs, int##w##_t rhs, uint8_t bits) { \ + int##w##_t res; \ if (!zig_mulo_i##w(&res, lhs, rhs, bits)) return res; \ - return (lhs ^ rhs) < zig_as_i##w(0) ? zig_minInt(i##w, bits) : zig_maxInt(i##w, bits); \ + return (lhs ^ rhs) < INT##w##_C(0) ? zig_minInt_i(w, bits) : zig_maxInt_i(w, bits); \ } zig_int_builtins(8) zig_int_builtins(16) @@ -988,89 +986,89 @@ zig_int_builtins(32) zig_int_builtins(64) #define zig_builtin8(name, val) __builtin_##name(val) -typedef zig_c_uint zig_Builtin8; +typedef unsigned int zig_Builtin8; #define zig_builtin16(name, val) __builtin_##name(val) -typedef zig_c_uint zig_Builtin16; +typedef unsigned int zig_Builtin16; #if INT_MIN <= INT32_MIN #define zig_builtin32(name, val) __builtin_##name(val) -typedef zig_c_uint zig_Builtin32; +typedef unsigned int zig_Builtin32; #elif LONG_MIN <= INT32_MIN #define zig_builtin32(name, val) __builtin_##name##l(val) -typedef zig_c_ulong zig_Builtin32; +typedef unsigned long zig_Builtin32; #endif #if INT_MIN <= INT64_MIN #define zig_builtin64(name, val) __builtin_##name(val) -typedef zig_c_uint zig_Builtin64; +typedef unsigned int zig_Builtin64; #elif LONG_MIN <= INT64_MIN #define zig_builtin64(name, val) __builtin_##name##l(val) -typedef zig_c_ulong zig_Builtin64; +typedef unsigned long zig_Builtin64; #elif LLONG_MIN <= INT64_MIN #define zig_builtin64(name, val) __builtin_##name##ll(val) -typedef zig_c_ulonglong zig_Builtin64; +typedef unsigned long long zig_Builtin64; #endif -static inline zig_u8 zig_byte_swap_u8(zig_u8 val, zig_u8 bits) { +static inline uint8_t zig_byte_swap_u8(uint8_t val, uint8_t bits) { return zig_wrap_u8(val >> (8 - bits), bits); } -static inline zig_i8 zig_byte_swap_i8(zig_i8 val, zig_u8 bits) { - return zig_wrap_i8((zig_i8)zig_byte_swap_u8((zig_u8)val, bits), bits); +static inline int8_t zig_byte_swap_i8(int8_t val, uint8_t bits) { + return zig_wrap_i8((int8_t)zig_byte_swap_u8((uint8_t)val, bits), bits); } -static inline zig_u16 zig_byte_swap_u16(zig_u16 val, zig_u8 bits) { - zig_u16 full_res; +static inline uint16_t zig_byte_swap_u16(uint16_t val, uint8_t bits) { + uint16_t full_res; #if zig_has_builtin(bswap16) || defined(zig_gnuc) full_res = __builtin_bswap16(val); #else - full_res = (zig_u16)zig_byte_swap_u8((zig_u8)(val >> 0), 8) << 8 | - (zig_u16)zig_byte_swap_u8((zig_u8)(val >> 8), 8) >> 0; + full_res = (uint16_t)zig_byte_swap_u8((uint8_t)(val >> 0), 8) << 8 | + (uint16_t)zig_byte_swap_u8((uint8_t)(val >> 8), 8) >> 0; #endif return zig_wrap_u16(full_res >> (16 - bits), bits); } -static inline zig_i16 zig_byte_swap_i16(zig_i16 val, zig_u8 bits) { - return zig_wrap_i16((zig_i16)zig_byte_swap_u16((zig_u16)val, bits), bits); +static inline int16_t zig_byte_swap_i16(int16_t val, uint8_t bits) { + return zig_wrap_i16((int16_t)zig_byte_swap_u16((uint16_t)val, bits), bits); } -static inline zig_u32 zig_byte_swap_u32(zig_u32 val, zig_u8 bits) { - zig_u32 full_res; +static inline uint32_t zig_byte_swap_u32(uint32_t val, uint8_t bits) { + uint32_t full_res; #if zig_has_builtin(bswap32) || defined(zig_gnuc) full_res = __builtin_bswap32(val); #else - full_res = (zig_u32)zig_byte_swap_u16((zig_u16)(val >> 0), 16) << 16 | - (zig_u32)zig_byte_swap_u16((zig_u16)(val >> 16), 16) >> 0; + full_res = (uint32_t)zig_byte_swap_u16((uint16_t)(val >> 0), 16) << 16 | + (uint32_t)zig_byte_swap_u16((uint16_t)(val >> 16), 16) >> 0; #endif return zig_wrap_u32(full_res >> (32 - bits), bits); } -static inline zig_i32 zig_byte_swap_i32(zig_i32 val, zig_u8 bits) { - return zig_wrap_i32((zig_i32)zig_byte_swap_u32((zig_u32)val, bits), bits); +static inline int32_t zig_byte_swap_i32(int32_t val, uint8_t bits) { + return zig_wrap_i32((int32_t)zig_byte_swap_u32((uint32_t)val, bits), bits); } -static inline zig_u64 zig_byte_swap_u64(zig_u64 val, zig_u8 bits) { - zig_u64 full_res; +static inline uint64_t zig_byte_swap_u64(uint64_t val, uint8_t bits) { + uint64_t full_res; #if zig_has_builtin(bswap64) || defined(zig_gnuc) full_res = __builtin_bswap64(val); #else - full_res = (zig_u64)zig_byte_swap_u32((zig_u32)(val >> 0), 32) << 32 | - (zig_u64)zig_byte_swap_u32((zig_u32)(val >> 32), 32) >> 0; + full_res = (uint64_t)zig_byte_swap_u32((uint32_t)(val >> 0), 32) << 32 | + (uint64_t)zig_byte_swap_u32((uint32_t)(val >> 32), 32) >> 0; #endif return zig_wrap_u64(full_res >> (64 - bits), bits); } -static inline zig_i64 zig_byte_swap_i64(zig_i64 val, zig_u8 bits) { - return zig_wrap_i64((zig_i64)zig_byte_swap_u64((zig_u64)val, bits), bits); +static inline int64_t zig_byte_swap_i64(int64_t val, uint8_t bits) { + return zig_wrap_i64((int64_t)zig_byte_swap_u64((uint64_t)val, bits), bits); } -static inline zig_u8 zig_bit_reverse_u8(zig_u8 val, zig_u8 bits) { - zig_u8 full_res; +static inline uint8_t zig_bit_reverse_u8(uint8_t val, uint8_t bits) { + uint8_t full_res; #if zig_has_builtin(bitreverse8) full_res = __builtin_bitreverse8(val); #else - static zig_u8 const lut[0x10] = { + static uint8_t const lut[0x10] = { 0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe, 0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf }; @@ -1079,62 +1077,62 @@ static inline zig_u8 zig_bit_reverse_u8(zig_u8 val, zig_u8 bits) { return zig_wrap_u8(full_res >> (8 - bits), bits); } -static inline zig_i8 zig_bit_reverse_i8(zig_i8 val, zig_u8 bits) { - return zig_wrap_i8((zig_i8)zig_bit_reverse_u8((zig_u8)val, bits), bits); +static inline int8_t zig_bit_reverse_i8(int8_t val, uint8_t bits) { + return zig_wrap_i8((int8_t)zig_bit_reverse_u8((uint8_t)val, bits), bits); } -static inline zig_u16 zig_bit_reverse_u16(zig_u16 val, zig_u8 bits) { - zig_u16 full_res; +static inline uint16_t zig_bit_reverse_u16(uint16_t val, uint8_t bits) { + uint16_t full_res; #if zig_has_builtin(bitreverse16) full_res = __builtin_bitreverse16(val); #else - full_res = (zig_u16)zig_bit_reverse_u8((zig_u8)(val >> 0), 8) << 8 | - (zig_u16)zig_bit_reverse_u8((zig_u8)(val >> 8), 8) >> 0; + full_res = (uint16_t)zig_bit_reverse_u8((uint8_t)(val >> 0), 8) << 8 | + (uint16_t)zig_bit_reverse_u8((uint8_t)(val >> 8), 8) >> 0; #endif return zig_wrap_u16(full_res >> (16 - bits), bits); } -static inline zig_i16 zig_bit_reverse_i16(zig_i16 val, zig_u8 bits) { - return zig_wrap_i16((zig_i16)zig_bit_reverse_u16((zig_u16)val, bits), bits); +static inline int16_t zig_bit_reverse_i16(int16_t val, uint8_t bits) { + return zig_wrap_i16((int16_t)zig_bit_reverse_u16((uint16_t)val, bits), bits); } -static inline zig_u32 zig_bit_reverse_u32(zig_u32 val, zig_u8 bits) { - zig_u32 full_res; +static inline uint32_t zig_bit_reverse_u32(uint32_t val, uint8_t bits) { + uint32_t full_res; #if zig_has_builtin(bitreverse32) full_res = __builtin_bitreverse32(val); #else - full_res = (zig_u32)zig_bit_reverse_u16((zig_u16)(val >> 0), 16) << 16 | - (zig_u32)zig_bit_reverse_u16((zig_u16)(val >> 16), 16) >> 0; + full_res = (uint32_t)zig_bit_reverse_u16((uint16_t)(val >> 0), 16) << 16 | + (uint32_t)zig_bit_reverse_u16((uint16_t)(val >> 16), 16) >> 0; #endif return zig_wrap_u32(full_res >> (32 - bits), bits); } -static inline zig_i32 zig_bit_reverse_i32(zig_i32 val, zig_u8 bits) { - return zig_wrap_i32((zig_i32)zig_bit_reverse_u32((zig_u32)val, bits), bits); +static inline int32_t zig_bit_reverse_i32(int32_t val, uint8_t bits) { + return zig_wrap_i32((int32_t)zig_bit_reverse_u32((uint32_t)val, bits), bits); } -static inline zig_u64 zig_bit_reverse_u64(zig_u64 val, zig_u8 bits) { - zig_u64 full_res; +static inline uint64_t zig_bit_reverse_u64(uint64_t val, uint8_t bits) { + uint64_t full_res; #if zig_has_builtin(bitreverse64) full_res = __builtin_bitreverse64(val); #else - full_res = (zig_u64)zig_bit_reverse_u32((zig_u32)(val >> 0), 32) << 32 | - (zig_u64)zig_bit_reverse_u32((zig_u32)(val >> 32), 32) >> 0; + full_res = (uint64_t)zig_bit_reverse_u32((uint32_t)(val >> 0), 32) << 32 | + (uint64_t)zig_bit_reverse_u32((uint32_t)(val >> 32), 32) >> 0; #endif return zig_wrap_u64(full_res >> (64 - bits), bits); } -static inline zig_i64 zig_bit_reverse_i64(zig_i64 val, zig_u8 bits) { - return zig_wrap_i64((zig_i64)zig_bit_reverse_u64((zig_u64)val, bits), bits); +static inline int64_t zig_bit_reverse_i64(int64_t val, uint8_t bits) { + return zig_wrap_i64((int64_t)zig_bit_reverse_u64((uint64_t)val, bits), bits); } #define zig_builtin_popcount_common(w) \ - static inline zig_u8 zig_popcount_i##w(zig_i##w val, zig_u8 bits) { \ - return zig_popcount_u##w((zig_u##w)val, bits); \ + static inline uint8_t zig_popcount_i##w(int##w##_t val, uint8_t bits) { \ + return zig_popcount_u##w((uint##w##_t)val, bits); \ } #if zig_has_builtin(popcount) || defined(zig_gnuc) #define zig_builtin_popcount(w) \ - static inline zig_u8 zig_popcount_u##w(zig_u##w val, zig_u8 bits) { \ + static inline uint8_t zig_popcount_u##w(uint##w##_t val, uint8_t bits) { \ (void)bits; \ return zig_builtin##w(popcount, val); \ } \ @@ -1142,12 +1140,12 @@ static inline zig_i64 zig_bit_reverse_i64(zig_i64 val, zig_u8 bits) { zig_builtin_popcount_common(w) #else #define zig_builtin_popcount(w) \ - static inline zig_u8 zig_popcount_u##w(zig_u##w val, zig_u8 bits) { \ + static inline uint8_t zig_popcount_u##w(uint##w##_t val, uint8_t bits) { \ (void)bits; \ - zig_u##w temp = val - ((val >> 1) & (zig_maxInt_u##w / 3)); \ - temp = (temp & (zig_maxInt_u##w / 5)) + ((temp >> 2) & (zig_maxInt_u##w / 5)); \ - temp = (temp + (temp >> 4)) & (zig_maxInt_u##w / 17); \ - return temp * (zig_maxInt_u##w / 255) >> (w - 8); \ + uint##w##_t temp = val - ((val >> 1) & (UINT##w##_MAX / 3)); \ + temp = (temp & (UINT##w##_MAX / 5)) + ((temp >> 2) & (UINT##w##_MAX / 5)); \ + temp = (temp + (temp >> 4)) & (UINT##w##_MAX / 17); \ + return temp * (UINT##w##_MAX / 255) >> (w - 8); \ } \ \ zig_builtin_popcount_common(w) @@ -1158,12 +1156,12 @@ zig_builtin_popcount(32) zig_builtin_popcount(64) #define zig_builtin_ctz_common(w) \ - static inline zig_u8 zig_ctz_i##w(zig_i##w val, zig_u8 bits) { \ - return zig_ctz_u##w((zig_u##w)val, bits); \ + static inline uint8_t zig_ctz_i##w(int##w##_t val, uint8_t bits) { \ + return zig_ctz_u##w((uint##w##_t)val, bits); \ } #if zig_has_builtin(ctz) || defined(zig_gnuc) #define zig_builtin_ctz(w) \ - static inline zig_u8 zig_ctz_u##w(zig_u##w val, zig_u8 bits) { \ + static inline uint8_t zig_ctz_u##w(uint##w##_t val, uint8_t bits) { \ if (val == 0) return bits; \ return zig_builtin##w(ctz, val); \ } \ @@ -1171,7 +1169,7 @@ zig_builtin_popcount(64) zig_builtin_ctz_common(w) #else #define zig_builtin_ctz(w) \ - static inline zig_u8 zig_ctz_u##w(zig_u##w val, zig_u8 bits) { \ + static inline uint8_t zig_ctz_u##w(uint##w##_t val, uint8_t bits) { \ return zig_popcount_u##w(zig_not_u##w(val, bits) & zig_subw_u##w(val, 1, bits), bits); \ } \ \ @@ -1183,12 +1181,12 @@ zig_builtin_ctz(32) zig_builtin_ctz(64) #define zig_builtin_clz_common(w) \ - static inline zig_u8 zig_clz_i##w(zig_i##w val, zig_u8 bits) { \ - return zig_clz_u##w((zig_u##w)val, bits); \ + static inline uint8_t zig_clz_i##w(int##w##_t val, uint8_t bits) { \ + return zig_clz_u##w((uint##w##_t)val, bits); \ } #if zig_has_builtin(clz) || defined(zig_gnuc) #define zig_builtin_clz(w) \ - static inline zig_u8 zig_clz_u##w(zig_u##w val, zig_u8 bits) { \ + static inline uint8_t zig_clz_u##w(uint##w##_t val, uint8_t bits) { \ if (val == 0) return bits; \ return zig_builtin##w(clz, val) - (zig_bitSizeOf(zig_Builtin##w) - bits); \ } \ @@ -1196,7 +1194,7 @@ zig_builtin_ctz(64) zig_builtin_clz_common(w) #else #define zig_builtin_clz(w) \ - static inline zig_u8 zig_clz_u##w(zig_u##w val, zig_u8 bits) { \ + static inline uint8_t zig_clz_u##w(uint##w##_t val, uint8_t bits) { \ return zig_ctz_u##w(zig_bit_reverse_u##w(val, bits), bits); \ } \ \ @@ -1207,7 +1205,7 @@ zig_builtin_clz(16) zig_builtin_clz(32) zig_builtin_clz(64) -/* ======================== 128-bit Integer Routines ======================== */ +/* ======================== 128-bit Integer Support ========================= */ #if !defined(zig_has_int128) # if defined(__SIZEOF_INT128__) @@ -1222,18 +1220,18 @@ zig_builtin_clz(64) typedef unsigned __int128 zig_u128; typedef signed __int128 zig_i128; -#define zig_as_u128(hi, lo) ((zig_u128)(hi)<<64|(lo)) -#define zig_as_i128(hi, lo) ((zig_i128)zig_as_u128(hi, lo)) -#define zig_as_constant_u128(hi, lo) zig_as_u128(hi, lo) -#define zig_as_constant_i128(hi, lo) zig_as_i128(hi, lo) -#define zig_hi_u128(val) ((zig_u64)((val) >> 64)) -#define zig_lo_u128(val) ((zig_u64)((val) >> 0)) -#define zig_hi_i128(val) ((zig_i64)((val) >> 64)) -#define zig_lo_i128(val) ((zig_u64)((val) >> 0)) +#define zig_make_u128(hi, lo) ((zig_u128)(hi)<<64|(lo)) +#define zig_make_i128(hi, lo) ((zig_i128)zig_make_u128(hi, lo)) +#define zig_init_u128(hi, lo) zig_make_u128(hi, lo) +#define zig_init_i128(hi, lo) zig_make_i128(hi, lo) +#define zig_hi_u128(val) ((uint64_t)((val) >> 64)) +#define zig_lo_u128(val) ((uint64_t)((val) >> 0)) +#define zig_hi_i128(val) (( int64_t)((val) >> 64)) +#define zig_lo_i128(val) ((uint64_t)((val) >> 0)) #define zig_bitcast_u128(val) ((zig_u128)(val)) #define zig_bitcast_i128(val) ((zig_i128)(val)) #define zig_cmp_int128(Type) \ - static inline zig_i32 zig_cmp_##Type(zig_##Type lhs, zig_##Type rhs) { \ + static inline int32_t zig_cmp_##Type(zig_##Type lhs, zig_##Type rhs) { \ return (lhs > rhs) - (lhs < rhs); \ } #define zig_bit_int128(Type, operation, operator) \ @@ -1243,32 +1241,32 @@ typedef signed __int128 zig_i128; #else /* zig_has_int128 */ -#if __LITTLE_ENDIAN__ || _MSC_VER -typedef struct { zig_align(16) zig_u64 lo; zig_u64 hi; } zig_u128; -typedef struct { zig_align(16) zig_u64 lo; zig_i64 hi; } zig_i128; +#if zig_little_endian +typedef struct { zig_align(16) uint64_t lo; uint64_t hi; } zig_u128; +typedef struct { zig_align(16) uint64_t lo; int64_t hi; } zig_i128; #else -typedef struct { zig_align(16) zig_u64 hi; zig_u64 lo; } zig_u128; -typedef struct { zig_align(16) zig_i64 hi; zig_u64 lo; } zig_i128; +typedef struct { zig_align(16) uint64_t hi; uint64_t lo; } zig_u128; +typedef struct { zig_align(16) int64_t hi; uint64_t lo; } zig_i128; #endif -#define zig_as_u128(hi, lo) ((zig_u128){ .h##i = (hi), .l##o = (lo) }) -#define zig_as_i128(hi, lo) ((zig_i128){ .h##i = (hi), .l##o = (lo) }) +#define zig_make_u128(hi, lo) ((zig_u128){ .h##i = (hi), .l##o = (lo) }) +#define zig_make_i128(hi, lo) ((zig_i128){ .h##i = (hi), .l##o = (lo) }) -#if _MSC_VER -#define zig_as_constant_u128(hi, lo) { .h##i = (hi), .l##o = (lo) } -#define zig_as_constant_i128(hi, lo) { .h##i = (hi), .l##o = (lo) } -#else -#define zig_as_constant_u128(hi, lo) zig_as_u128(hi, lo) -#define zig_as_constant_i128(hi, lo) zig_as_i128(hi, lo) +#if _MSC_VER /* MSVC doesn't allow struct literals in constant expressions */ +#define zig_init_u128(hi, lo) { .h##i = (hi), .l##o = (lo) } +#define zig_init_i128(hi, lo) { .h##i = (hi), .l##o = (lo) } +#else /* But non-MSVC doesn't like the unprotected commas */ +#define zig_init_u128(hi, lo) zig_make_u128(hi, lo) +#define zig_init_i128(hi, lo) zig_make_i128(hi, lo) #endif #define zig_hi_u128(val) ((val).hi) #define zig_lo_u128(val) ((val).lo) #define zig_hi_i128(val) ((val).hi) #define zig_lo_i128(val) ((val).lo) -#define zig_bitcast_u128(val) zig_as_u128((zig_u64)(val).hi, (val).lo) -#define zig_bitcast_i128(val) zig_as_i128((zig_i64)(val).hi, (val).lo) +#define zig_bitcast_u128(val) zig_make_u128((uint64_t)(val).hi, (val).lo) +#define zig_bitcast_i128(val) zig_make_i128(( int64_t)(val).hi, (val).lo) #define zig_cmp_int128(Type) \ - static inline zig_i32 zig_cmp_##Type(zig_##Type lhs, zig_##Type rhs) { \ + static inline int32_t zig_cmp_##Type(zig_##Type lhs, zig_##Type rhs) { \ return (lhs.hi == rhs.hi) \ ? (lhs.lo > rhs.lo) - (lhs.lo < rhs.lo) \ : (lhs.hi > rhs.hi) - (lhs.hi < rhs.hi); \ @@ -1280,10 +1278,10 @@ typedef struct { zig_align(16) zig_i64 hi; zig_u64 lo; } zig_i128; #endif /* zig_has_int128 */ -#define zig_minInt_u128 zig_as_u128(zig_minInt_u64, zig_minInt_u64) -#define zig_maxInt_u128 zig_as_u128(zig_maxInt_u64, zig_maxInt_u64) -#define zig_minInt_i128 zig_as_i128(zig_minInt_i64, zig_minInt_u64) -#define zig_maxInt_i128 zig_as_i128(zig_maxInt_i64, zig_maxInt_u64) +#define zig_minInt_u128 zig_make_u128(zig_minInt_u64, zig_minInt_u64) +#define zig_maxInt_u128 zig_make_u128(zig_maxInt_u64, zig_maxInt_u64) +#define zig_minInt_i128 zig_make_i128(zig_minInt_i64, zig_minInt_u64) +#define zig_maxInt_i128 zig_make_i128(zig_maxInt_i64, zig_maxInt_u64) zig_cmp_int128(u128) zig_cmp_int128(i128) @@ -1297,28 +1295,33 @@ zig_bit_int128(i128, or, |) zig_bit_int128(u128, xor, ^) zig_bit_int128(i128, xor, ^) -static inline zig_u128 zig_shr_u128(zig_u128 lhs, zig_u8 rhs); +static inline zig_u128 zig_shr_u128(zig_u128 lhs, uint8_t rhs); #if zig_has_int128 -static inline zig_u128 zig_not_u128(zig_u128 val, zig_u8 bits) { - return val ^ zig_maxInt(u128, bits); +static inline zig_u128 zig_not_u128(zig_u128 val, uint8_t bits) { + return val ^ zig_maxInt_u(128, bits); } -static inline zig_i128 zig_not_i128(zig_i128 val, zig_u8 bits) { +static inline zig_i128 zig_not_i128(zig_i128 val, uint8_t bits) { (void)bits; return ~val; } -static inline zig_u128 zig_shr_u128(zig_u128 lhs, zig_u8 rhs) { +static inline zig_u128 zig_shr_u128(zig_u128 lhs, uint8_t rhs) { return lhs >> rhs; } -static inline zig_u128 zig_shl_u128(zig_u128 lhs, zig_u8 rhs) { +static inline zig_u128 zig_shl_u128(zig_u128 lhs, uint8_t rhs) { return lhs << rhs; } -static inline zig_i128 zig_shl_i128(zig_i128 lhs, zig_u8 rhs) { +static inline zig_i128 zig_shr_i128(zig_i128 lhs, uint8_t rhs) { + zig_i128 sign_mask = lhs < zig_make_i128(0, 0) ? -zig_make_i128(0, 1) : zig_make_i128(0, 0); + return ((lhs ^ sign_mask) >> rhs) ^ sign_mask; +} + +static inline zig_i128 zig_shl_i128(zig_i128 lhs, uint8_t rhs) { return lhs << rhs; } @@ -1363,40 +1366,46 @@ static inline zig_i128 zig_rem_i128(zig_i128 lhs, zig_i128 rhs) { } static inline zig_i128 zig_div_floor_i128(zig_i128 lhs, zig_i128 rhs) { - return zig_div_trunc_i128(lhs, rhs) - (((lhs ^ rhs) & zig_rem_i128(lhs, rhs)) < zig_as_i128(0, 0)); + return zig_div_trunc_i128(lhs, rhs) - (((lhs ^ rhs) & zig_rem_i128(lhs, rhs)) < zig_make_i128(0, 0)); } static inline zig_i128 zig_mod_i128(zig_i128 lhs, zig_i128 rhs) { zig_i128 rem = zig_rem_i128(lhs, rhs); - return rem + (((lhs ^ rhs) & rem) < zig_as_i128(0, 0) ? rhs : zig_as_i128(0, 0)); + return rem + (((lhs ^ rhs) & rem) < zig_make_i128(0, 0) ? rhs : zig_make_i128(0, 0)); } #else /* zig_has_int128 */ -static inline zig_u128 zig_not_u128(zig_u128 val, zig_u8 bits) { - return (zig_u128){ .hi = zig_not_u64(val.hi, bits - zig_as_u8(64)), .lo = zig_not_u64(val.lo, zig_as_u8(64)) }; +static inline zig_u128 zig_not_u128(zig_u128 val, uint8_t bits) { + return (zig_u128){ .hi = zig_not_u64(val.hi, bits - UINT8_C(64)), .lo = zig_not_u64(val.lo, UINT8_C(64)) }; } -static inline zig_i128 zig_not_i128(zig_i128 val, zig_u8 bits) { - return (zig_i128){ .hi = zig_not_i64(val.hi, bits - zig_as_u8(64)), .lo = zig_not_u64(val.lo, zig_as_u8(64)) }; +static inline zig_i128 zig_not_i128(zig_i128 val, uint8_t bits) { + return (zig_i128){ .hi = zig_not_i64(val.hi, bits - UINT8_C(64)), .lo = zig_not_u64(val.lo, UINT8_C(64)) }; } -static inline zig_u128 zig_shr_u128(zig_u128 lhs, zig_u8 rhs) { - if (rhs == zig_as_u8(0)) return lhs; - if (rhs >= zig_as_u8(64)) return (zig_u128){ .hi = zig_minInt_u64, .lo = lhs.hi >> (rhs - zig_as_u8(64)) }; - return (zig_u128){ .hi = lhs.hi >> rhs, .lo = lhs.hi << (zig_as_u8(64) - rhs) | lhs.lo >> rhs }; +static inline zig_u128 zig_shr_u128(zig_u128 lhs, uint8_t rhs) { + if (rhs == UINT8_C(0)) return lhs; + if (rhs >= UINT8_C(64)) return (zig_u128){ .hi = zig_minInt_u64, .lo = lhs.hi >> (rhs - UINT8_C(64)) }; + return (zig_u128){ .hi = lhs.hi >> rhs, .lo = lhs.hi << (UINT8_C(64) - rhs) | lhs.lo >> rhs }; } -static inline zig_u128 zig_shl_u128(zig_u128 lhs, zig_u8 rhs) { - if (rhs == zig_as_u8(0)) return lhs; - if (rhs >= zig_as_u8(64)) return (zig_u128){ .hi = lhs.lo << (rhs - zig_as_u8(64)), .lo = zig_minInt_u64 }; - return (zig_u128){ .hi = lhs.hi << rhs | lhs.lo >> (zig_as_u8(64) - rhs), .lo = lhs.lo << rhs }; +static inline zig_u128 zig_shl_u128(zig_u128 lhs, uint8_t rhs) { + if (rhs == UINT8_C(0)) return lhs; + if (rhs >= UINT8_C(64)) return (zig_u128){ .hi = lhs.lo << (rhs - UINT8_C(64)), .lo = zig_minInt_u64 }; + return (zig_u128){ .hi = lhs.hi << rhs | lhs.lo >> (UINT8_C(64) - rhs), .lo = lhs.lo << rhs }; } -static inline zig_i128 zig_shl_i128(zig_i128 lhs, zig_u8 rhs) { - if (rhs == zig_as_u8(0)) return lhs; - if (rhs >= zig_as_u8(64)) return (zig_i128){ .hi = lhs.lo << (rhs - zig_as_u8(64)), .lo = zig_minInt_u64 }; - return (zig_i128){ .hi = lhs.hi << rhs | lhs.lo >> (zig_as_u8(64) - rhs), .lo = lhs.lo << rhs }; +static inline zig_i128 zig_shr_i128(zig_i128 lhs, uint8_t rhs) { + if (rhs == UINT8_C(0)) return lhs; + if (rhs >= UINT8_C(64)) return (zig_i128){ .hi = zig_shr_i64(lhs.hi, 63), .lo = zig_shr_i64(lhs.hi, (rhs - UINT8_C(64))) }; + return (zig_i128){ .hi = zig_shr_i64(lhs.hi, rhs), .lo = lhs.lo >> rhs | (uint64_t)lhs.hi << (UINT8_C(64) - rhs) }; +} + +static inline zig_i128 zig_shl_i128(zig_i128 lhs, uint8_t rhs) { + if (rhs == UINT8_C(0)) return lhs; + if (rhs >= UINT8_C(64)) return (zig_i128){ .hi = lhs.lo << (rhs - UINT8_C(64)), .lo = zig_minInt_u64 }; + return (zig_i128){ .hi = lhs.hi << rhs | lhs.lo >> (UINT8_C(64) - rhs), .lo = lhs.lo << rhs }; } static inline zig_u128 zig_add_u128(zig_u128 lhs, zig_u128 rhs) { @@ -1424,14 +1433,14 @@ static inline zig_i128 zig_sub_i128(zig_i128 lhs, zig_i128 rhs) { } zig_extern zig_i128 __multi3(zig_i128 lhs, zig_i128 rhs); -static zig_u128 zig_mul_u128(zig_u128 lhs, zig_u128 rhs) { - return zig_bitcast_u128(__multi3(zig_bitcast_i128(lhs), zig_bitcast_i128(rhs))); -} - static zig_i128 zig_mul_i128(zig_i128 lhs, zig_i128 rhs) { return __multi3(lhs, rhs); } +static zig_u128 zig_mul_u128(zig_u128 lhs, zig_u128 rhs) { + return zig_bitcast_u128(zig_mul_i128(zig_bitcast_i128(lhs), zig_bitcast_i128(rhs))); +} + zig_extern zig_u128 __udivti3(zig_u128 lhs, zig_u128 rhs); static zig_u128 zig_div_trunc_u128(zig_u128 lhs, zig_u128 rhs) { return __udivti3(lhs, rhs); @@ -1454,11 +1463,11 @@ static zig_i128 zig_rem_i128(zig_i128 lhs, zig_i128 rhs) { static inline zig_i128 zig_mod_i128(zig_i128 lhs, zig_i128 rhs) { zig_i128 rem = zig_rem_i128(lhs, rhs); - return zig_add_i128(rem, (((lhs.hi ^ rhs.hi) & rem.hi) < zig_as_i64(0) ? rhs : zig_as_i128(0, 0))); + return zig_add_i128(rem, ((lhs.hi ^ rhs.hi) & rem.hi) < INT64_C(0) ? rhs : zig_make_i128(0, 0)); } static inline zig_i128 zig_div_floor_i128(zig_i128 lhs, zig_i128 rhs) { - return zig_sub_i128(zig_div_trunc_i128(lhs, rhs), zig_as_i128(0, zig_cmp_i128(zig_and_i128(zig_xor_i128(lhs, rhs), zig_rem_i128(lhs, rhs)), zig_as_i128(0, 0)) < zig_as_i32(0))); + return zig_sub_i128(zig_div_trunc_i128(lhs, rhs), zig_make_i128(0, zig_cmp_i128(zig_and_i128(zig_xor_i128(lhs, rhs), zig_rem_i128(lhs, rhs)), zig_make_i128(0, 0)) < INT32_C(0))); } #endif /* zig_has_int128 */ @@ -1471,326 +1480,1265 @@ static inline zig_u128 zig_nand_u128(zig_u128 lhs, zig_u128 rhs) { } static inline zig_u128 zig_min_u128(zig_u128 lhs, zig_u128 rhs) { - return zig_cmp_u128(lhs, rhs) < zig_as_i32(0) ? lhs : rhs; + return zig_cmp_u128(lhs, rhs) < INT32_C(0) ? lhs : rhs; } static inline zig_i128 zig_min_i128(zig_i128 lhs, zig_i128 rhs) { - return zig_cmp_i128(lhs, rhs) < zig_as_i32(0) ? lhs : rhs; + return zig_cmp_i128(lhs, rhs) < INT32_C(0) ? lhs : rhs; } static inline zig_u128 zig_max_u128(zig_u128 lhs, zig_u128 rhs) { - return zig_cmp_u128(lhs, rhs) > zig_as_i32(0) ? lhs : rhs; + return zig_cmp_u128(lhs, rhs) > INT32_C(0) ? lhs : rhs; } static inline zig_i128 zig_max_i128(zig_i128 lhs, zig_i128 rhs) { - return zig_cmp_i128(lhs, rhs) > zig_as_i32(0) ? lhs : rhs; + return zig_cmp_i128(lhs, rhs) > INT32_C(0) ? lhs : rhs; } -static inline zig_i128 zig_shr_i128(zig_i128 lhs, zig_u8 rhs) { - zig_i128 sign_mask = zig_cmp_i128(lhs, zig_as_i128(0, 0)) < zig_as_i32(0) ? zig_sub_i128(zig_as_i128(0, 0), zig_as_i128(0, 1)) : zig_as_i128(0, 0); - return zig_xor_i128(zig_bitcast_i128(zig_shr_u128(zig_bitcast_u128(zig_xor_i128(lhs, sign_mask)), rhs)), sign_mask); +static inline zig_u128 zig_wrap_u128(zig_u128 val, uint8_t bits) { + return zig_and_u128(val, zig_maxInt_u(128, bits)); } -static inline zig_u128 zig_wrap_u128(zig_u128 val, zig_u8 bits) { - return zig_and_u128(val, zig_maxInt(u128, bits)); +static inline zig_i128 zig_wrap_i128(zig_i128 val, uint8_t bits) { + if (bits > UINT8_C(64)) return zig_make_i128(zig_wrap_i64(zig_hi_i128(val), bits - UINT8_C(64)), zig_lo_i128(val)); + int64_t lo = zig_wrap_i64((int64_t)zig_lo_i128(val), bits); + return zig_make_i128(zig_shr_i64(lo, 63), (uint64_t)lo); } -static inline zig_i128 zig_wrap_i128(zig_i128 val, zig_u8 bits) { - return zig_as_i128(zig_wrap_i64(zig_hi_i128(val), bits - zig_as_u8(64)), zig_lo_i128(val)); -} - -static inline zig_u128 zig_shlw_u128(zig_u128 lhs, zig_u8 rhs, zig_u8 bits) { +static inline zig_u128 zig_shlw_u128(zig_u128 lhs, uint8_t rhs, uint8_t bits) { return zig_wrap_u128(zig_shl_u128(lhs, rhs), bits); } -static inline zig_i128 zig_shlw_i128(zig_i128 lhs, zig_u8 rhs, zig_u8 bits) { +static inline zig_i128 zig_shlw_i128(zig_i128 lhs, uint8_t rhs, uint8_t bits) { return zig_wrap_i128(zig_bitcast_i128(zig_shl_u128(zig_bitcast_u128(lhs), rhs)), bits); } -static inline zig_u128 zig_addw_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { +static inline zig_u128 zig_addw_u128(zig_u128 lhs, zig_u128 rhs, uint8_t bits) { return zig_wrap_u128(zig_add_u128(lhs, rhs), bits); } -static inline zig_i128 zig_addw_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { +static inline zig_i128 zig_addw_i128(zig_i128 lhs, zig_i128 rhs, uint8_t bits) { return zig_wrap_i128(zig_bitcast_i128(zig_add_u128(zig_bitcast_u128(lhs), zig_bitcast_u128(rhs))), bits); } -static inline zig_u128 zig_subw_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { +static inline zig_u128 zig_subw_u128(zig_u128 lhs, zig_u128 rhs, uint8_t bits) { return zig_wrap_u128(zig_sub_u128(lhs, rhs), bits); } -static inline zig_i128 zig_subw_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { +static inline zig_i128 zig_subw_i128(zig_i128 lhs, zig_i128 rhs, uint8_t bits) { return zig_wrap_i128(zig_bitcast_i128(zig_sub_u128(zig_bitcast_u128(lhs), zig_bitcast_u128(rhs))), bits); } -static inline zig_u128 zig_mulw_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { +static inline zig_u128 zig_mulw_u128(zig_u128 lhs, zig_u128 rhs, uint8_t bits) { return zig_wrap_u128(zig_mul_u128(lhs, rhs), bits); } -static inline zig_i128 zig_mulw_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { +static inline zig_i128 zig_mulw_i128(zig_i128 lhs, zig_i128 rhs, uint8_t bits) { return zig_wrap_i128(zig_bitcast_i128(zig_mul_u128(zig_bitcast_u128(lhs), zig_bitcast_u128(rhs))), bits); } #if zig_has_int128 -static inline bool zig_addo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { +static inline bool zig_addo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, uint8_t bits) { #if zig_has_builtin(add_overflow) zig_u128 full_res; bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); *res = zig_wrap_u128(full_res, bits); - return overflow || full_res < zig_minInt(u128, bits) || full_res > zig_maxInt(u128, bits); + return overflow || full_res < zig_minInt_u(128, bits) || full_res > zig_maxInt_u(128, bits); #else *res = zig_addw_u128(lhs, rhs, bits); return *res < lhs; #endif } -zig_extern zig_i128 __addoti4(zig_i128 lhs, zig_i128 rhs, zig_c_int *overflow); -static inline bool zig_addo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { +zig_extern zig_i128 __addoti4(zig_i128 lhs, zig_i128 rhs, int *overflow); +static inline bool zig_addo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, uint8_t bits) { #if zig_has_builtin(add_overflow) zig_i128 full_res; bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); #else - zig_c_int overflow_int; + int overflow_int; zig_i128 full_res = __addoti4(lhs, rhs, &overflow_int); bool overflow = overflow_int != 0; #endif *res = zig_wrap_i128(full_res, bits); - return overflow || full_res < zig_minInt(i128, bits) || full_res > zig_maxInt(i128, bits); + return overflow || full_res < zig_minInt_i(128, bits) || full_res > zig_maxInt_i(128, bits); } -static inline bool zig_subo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { +static inline bool zig_subo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, uint8_t bits) { #if zig_has_builtin(sub_overflow) zig_u128 full_res; bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); *res = zig_wrap_u128(full_res, bits); - return overflow || full_res < zig_minInt(u128, bits) || full_res > zig_maxInt(u128, bits); + return overflow || full_res < zig_minInt_u(128, bits) || full_res > zig_maxInt_u(128, bits); #else *res = zig_subw_u128(lhs, rhs, bits); return *res > lhs; #endif } -zig_extern zig_i128 __suboti4(zig_i128 lhs, zig_i128 rhs, zig_c_int *overflow); -static inline bool zig_subo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { +zig_extern zig_i128 __suboti4(zig_i128 lhs, zig_i128 rhs, int *overflow); +static inline bool zig_subo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, uint8_t bits) { #if zig_has_builtin(sub_overflow) zig_i128 full_res; bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); #else - zig_c_int overflow_int; + int overflow_int; zig_i128 full_res = __suboti4(lhs, rhs, &overflow_int); bool overflow = overflow_int != 0; #endif *res = zig_wrap_i128(full_res, bits); - return overflow || full_res < zig_minInt(i128, bits) || full_res > zig_maxInt(i128, bits); + return overflow || full_res < zig_minInt_i(128, bits) || full_res > zig_maxInt_i(128, bits); } -static inline bool zig_mulo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { +static inline bool zig_mulo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, uint8_t bits) { #if zig_has_builtin(mul_overflow) zig_u128 full_res; bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); *res = zig_wrap_u128(full_res, bits); - return overflow || full_res < zig_minInt(u128, bits) || full_res > zig_maxInt(u128, bits); + return overflow || full_res < zig_minInt_u(128, bits) || full_res > zig_maxInt_u(128, bits); #else *res = zig_mulw_u128(lhs, rhs, bits); - return rhs != zig_as_u128(0, 0) && lhs > zig_maxInt(u128, bits) / rhs; + return rhs != zig_make_u128(0, 0) && lhs > zig_maxInt_u(128, bits) / rhs; #endif } -zig_extern zig_i128 __muloti4(zig_i128 lhs, zig_i128 rhs, zig_c_int *overflow); -static inline bool zig_mulo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { +zig_extern zig_i128 __muloti4(zig_i128 lhs, zig_i128 rhs, int *overflow); +static inline bool zig_mulo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, uint8_t bits) { #if zig_has_builtin(mul_overflow) zig_i128 full_res; bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); #else - zig_c_int overflow_int; + int overflow_int; zig_i128 full_res = __muloti4(lhs, rhs, &overflow_int); bool overflow = overflow_int != 0; #endif *res = zig_wrap_i128(full_res, bits); - return overflow || full_res < zig_minInt(i128, bits) || full_res > zig_maxInt(i128, bits); + return overflow || full_res < zig_minInt_i(128, bits) || full_res > zig_maxInt_i(128, bits); } #else /* zig_has_int128 */ -static inline bool zig_overflow_u128(bool overflow, zig_u128 full_res, zig_u8 bits) { - return overflow || - zig_cmp_u128(full_res, zig_minInt(u128, bits)) < zig_as_i32(0) || - zig_cmp_u128(full_res, zig_maxInt(u128, bits)) > zig_as_i32(0); +static inline bool zig_addo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, uint8_t bits) { + uint64_t hi; + bool overflow = zig_addo_u64(&hi, lhs.hi, rhs.hi, bits - 64); + return overflow ^ zig_addo_u64(&res->hi, hi, zig_addo_u64(&res->lo, lhs.lo, rhs.lo, 64), bits - 64); } -static inline bool zig_overflow_i128(bool overflow, zig_i128 full_res, zig_u8 bits) { - return overflow || - zig_cmp_i128(full_res, zig_minInt(i128, bits)) < zig_as_i32(0) || - zig_cmp_i128(full_res, zig_maxInt(i128, bits)) > zig_as_i32(0); +static inline bool zig_addo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, uint8_t bits) { + int64_t hi; + bool overflow = zig_addo_i64(&hi, lhs.hi, rhs.hi, bits - 64); + return overflow ^ zig_addo_i64(&res->hi, hi, zig_addo_u64(&res->lo, lhs.lo, rhs.lo, 64), bits - 64); } -static inline bool zig_addo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { - zig_u128 full_res; - bool overflow = - zig_addo_u64(&full_res.hi, lhs.hi, rhs.hi, 64) | - zig_addo_u64(&full_res.hi, full_res.hi, zig_addo_u64(&full_res.lo, lhs.lo, rhs.lo, 64), 64); - *res = zig_wrap_u128(full_res, bits); - return zig_overflow_u128(overflow, full_res, bits); +static inline bool zig_subo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, uint8_t bits) { + uint64_t hi; + bool overflow = zig_subo_u64(&hi, lhs.hi, rhs.hi, bits - 64); + return overflow ^ zig_subo_u64(&res->hi, hi, zig_subo_u64(&res->lo, lhs.lo, rhs.lo, 64), bits - 64); } -zig_extern zig_i128 __addoti4(zig_i128 lhs, zig_i128 rhs, zig_c_int *overflow); -static inline bool zig_addo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { - zig_c_int overflow_int; - zig_i128 full_res = __addoti4(lhs, rhs, &overflow_int); - *res = zig_wrap_i128(full_res, bits); - return zig_overflow_i128(overflow_int, full_res, bits); +static inline bool zig_subo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, uint8_t bits) { + int64_t hi; + bool overflow = zig_subo_i64(&hi, lhs.hi, rhs.hi, bits - 64); + return overflow ^ zig_subo_i64(&res->hi, hi, zig_subo_u64(&res->lo, lhs.lo, rhs.lo, 64), bits - 64); } -static inline bool zig_subo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { - zig_u128 full_res; - bool overflow = - zig_subo_u64(&full_res.hi, lhs.hi, rhs.hi, 64) | - zig_subo_u64(&full_res.hi, full_res.hi, zig_subo_u64(&full_res.lo, lhs.lo, rhs.lo, 64), 64); - *res = zig_wrap_u128(full_res, bits); - return zig_overflow_u128(overflow, full_res, bits); -} - -zig_extern zig_i128 __suboti4(zig_i128 lhs, zig_i128 rhs, zig_c_int *overflow); -static inline bool zig_subo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { - zig_c_int overflow_int; - zig_i128 full_res = __suboti4(lhs, rhs, &overflow_int); - *res = zig_wrap_i128(full_res, bits); - return zig_overflow_i128(overflow_int, full_res, bits); -} - -static inline bool zig_mulo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { +static inline bool zig_mulo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, uint8_t bits) { *res = zig_mulw_u128(lhs, rhs, bits); - return zig_cmp_u128(*res, zig_as_u128(0, 0)) != zig_as_i32(0) && - zig_cmp_u128(lhs, zig_div_trunc_u128(zig_maxInt(u128, bits), rhs)) > zig_as_i32(0); + return zig_cmp_u128(*res, zig_make_u128(0, 0)) != INT32_C(0) && + zig_cmp_u128(lhs, zig_div_trunc_u128(zig_maxInt_u(128, bits), rhs)) > INT32_C(0); } -zig_extern zig_i128 __muloti4(zig_i128 lhs, zig_i128 rhs, zig_c_int *overflow); -static inline bool zig_mulo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { - zig_c_int overflow_int; +zig_extern zig_i128 __muloti4(zig_i128 lhs, zig_i128 rhs, int *overflow); +static inline bool zig_mulo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, uint8_t bits) { + int overflow_int; zig_i128 full_res = __muloti4(lhs, rhs, &overflow_int); + bool overflow = overflow_int != 0 || + zig_cmp_i128(full_res, zig_minInt_i(128, bits)) < INT32_C(0) || + zig_cmp_i128(full_res, zig_maxInt_i(128, bits)) > INT32_C(0); *res = zig_wrap_i128(full_res, bits); - return zig_overflow_i128(overflow_int, full_res, bits); + return overflow; } #endif /* zig_has_int128 */ -static inline bool zig_shlo_u128(zig_u128 *res, zig_u128 lhs, zig_u8 rhs, zig_u8 bits) { +static inline bool zig_shlo_u128(zig_u128 *res, zig_u128 lhs, uint8_t rhs, uint8_t bits) { *res = zig_shlw_u128(lhs, rhs, bits); - return zig_cmp_u128(lhs, zig_shr_u128(zig_maxInt(u128, bits), rhs)) > zig_as_i32(0); + return zig_cmp_u128(lhs, zig_shr_u128(zig_maxInt_u(128, bits), rhs)) > INT32_C(0); } -static inline bool zig_shlo_i128(zig_i128 *res, zig_i128 lhs, zig_u8 rhs, zig_u8 bits) { +static inline bool zig_shlo_i128(zig_i128 *res, zig_i128 lhs, uint8_t rhs, uint8_t bits) { *res = zig_shlw_i128(lhs, rhs, bits); - zig_i128 mask = zig_bitcast_i128(zig_shl_u128(zig_maxInt_u128, bits - rhs - zig_as_u8(1))); - return zig_cmp_i128(zig_and_i128(lhs, mask), zig_as_i128(0, 0)) != zig_as_i32(0) && - zig_cmp_i128(zig_and_i128(lhs, mask), mask) != zig_as_i32(0); + zig_i128 mask = zig_bitcast_i128(zig_shl_u128(zig_maxInt_u128, bits - rhs - UINT8_C(1))); + return zig_cmp_i128(zig_and_i128(lhs, mask), zig_make_i128(0, 0)) != INT32_C(0) && + zig_cmp_i128(zig_and_i128(lhs, mask), mask) != INT32_C(0); } -static inline zig_u128 zig_shls_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { +static inline zig_u128 zig_shls_u128(zig_u128 lhs, zig_u128 rhs, uint8_t bits) { zig_u128 res; - if (zig_cmp_u128(rhs, zig_as_u128(0, bits)) >= zig_as_i32(0)) - return zig_cmp_u128(lhs, zig_as_u128(0, 0)) != zig_as_i32(0) ? zig_maxInt(u128, bits) : lhs; - -#if zig_has_int128 - return zig_shlo_u128(&res, lhs, (zig_u8)rhs, bits) ? zig_maxInt(u128, bits) : res; -#else - return zig_shlo_u128(&res, lhs, (zig_u8)rhs.lo, bits) ? zig_maxInt(u128, bits) : res; -#endif + if (zig_cmp_u128(rhs, zig_make_u128(0, bits)) >= INT32_C(0)) + return zig_cmp_u128(lhs, zig_make_u128(0, 0)) != INT32_C(0) ? zig_maxInt_u(128, bits) : lhs; + return zig_shlo_u128(&res, lhs, (uint8_t)zig_lo_u128(rhs), bits) ? zig_maxInt_u(128, bits) : res; } -static inline zig_i128 zig_shls_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { +static inline zig_i128 zig_shls_i128(zig_i128 lhs, zig_i128 rhs, uint8_t bits) { zig_i128 res; - if (zig_cmp_u128(zig_bitcast_u128(rhs), zig_as_u128(0, bits)) < zig_as_i32(0) && !zig_shlo_i128(&res, lhs, zig_lo_i128(rhs), bits)) return res; - return zig_cmp_i128(lhs, zig_as_i128(0, 0)) < zig_as_i32(0) ? zig_minInt(i128, bits) : zig_maxInt(i128, bits); + if (zig_cmp_u128(zig_bitcast_u128(rhs), zig_make_u128(0, bits)) < INT32_C(0) && !zig_shlo_i128(&res, lhs, (uint8_t)zig_lo_i128(rhs), bits)) return res; + return zig_cmp_i128(lhs, zig_make_i128(0, 0)) < INT32_C(0) ? zig_minInt_i(128, bits) : zig_maxInt_i(128, bits); } -static inline zig_u128 zig_adds_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { +static inline zig_u128 zig_adds_u128(zig_u128 lhs, zig_u128 rhs, uint8_t bits) { zig_u128 res; - return zig_addo_u128(&res, lhs, rhs, bits) ? zig_maxInt(u128, bits) : res; + return zig_addo_u128(&res, lhs, rhs, bits) ? zig_maxInt_u(128, bits) : res; } -static inline zig_i128 zig_adds_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { +static inline zig_i128 zig_adds_i128(zig_i128 lhs, zig_i128 rhs, uint8_t bits) { zig_i128 res; if (!zig_addo_i128(&res, lhs, rhs, bits)) return res; - return zig_cmp_i128(res, zig_as_i128(0, 0)) >= zig_as_i32(0) ? zig_minInt(i128, bits) : zig_maxInt(i128, bits); + return zig_cmp_i128(res, zig_make_i128(0, 0)) >= INT32_C(0) ? zig_minInt_i(128, bits) : zig_maxInt_i(128, bits); } -static inline zig_u128 zig_subs_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { +static inline zig_u128 zig_subs_u128(zig_u128 lhs, zig_u128 rhs, uint8_t bits) { zig_u128 res; - return zig_subo_u128(&res, lhs, rhs, bits) ? zig_minInt(u128, bits) : res; + return zig_subo_u128(&res, lhs, rhs, bits) ? zig_minInt_u(128, bits) : res; } -static inline zig_i128 zig_subs_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { +static inline zig_i128 zig_subs_i128(zig_i128 lhs, zig_i128 rhs, uint8_t bits) { zig_i128 res; if (!zig_subo_i128(&res, lhs, rhs, bits)) return res; - return zig_cmp_i128(res, zig_as_i128(0, 0)) >= zig_as_i32(0) ? zig_minInt(i128, bits) : zig_maxInt(i128, bits); + return zig_cmp_i128(res, zig_make_i128(0, 0)) >= INT32_C(0) ? zig_minInt_i(128, bits) : zig_maxInt_i(128, bits); } -static inline zig_u128 zig_muls_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { +static inline zig_u128 zig_muls_u128(zig_u128 lhs, zig_u128 rhs, uint8_t bits) { zig_u128 res; - return zig_mulo_u128(&res, lhs, rhs, bits) ? zig_maxInt(u128, bits) : res; + return zig_mulo_u128(&res, lhs, rhs, bits) ? zig_maxInt_u(128, bits) : res; } -static inline zig_i128 zig_muls_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { +static inline zig_i128 zig_muls_i128(zig_i128 lhs, zig_i128 rhs, uint8_t bits) { zig_i128 res; if (!zig_mulo_i128(&res, lhs, rhs, bits)) return res; - return zig_cmp_i128(zig_xor_i128(lhs, rhs), zig_as_i128(0, 0)) < zig_as_i32(0) ? zig_minInt(i128, bits) : zig_maxInt(i128, bits); + return zig_cmp_i128(zig_xor_i128(lhs, rhs), zig_make_i128(0, 0)) < INT32_C(0) ? zig_minInt_i(128, bits) : zig_maxInt_i(128, bits); } -static inline zig_u8 zig_clz_u128(zig_u128 val, zig_u8 bits) { - if (bits <= zig_as_u8(64)) return zig_clz_u64(zig_lo_u128(val), bits); - if (zig_hi_u128(val) != 0) return zig_clz_u64(zig_hi_u128(val), bits - zig_as_u8(64)); - return zig_clz_u64(zig_lo_u128(val), zig_as_u8(64)) + (bits - zig_as_u8(64)); +static inline uint8_t zig_clz_u128(zig_u128 val, uint8_t bits) { + if (bits <= UINT8_C(64)) return zig_clz_u64(zig_lo_u128(val), bits); + if (zig_hi_u128(val) != 0) return zig_clz_u64(zig_hi_u128(val), bits - UINT8_C(64)); + return zig_clz_u64(zig_lo_u128(val), UINT8_C(64)) + (bits - UINT8_C(64)); } -static inline zig_u8 zig_clz_i128(zig_i128 val, zig_u8 bits) { +static inline uint8_t zig_clz_i128(zig_i128 val, uint8_t bits) { return zig_clz_u128(zig_bitcast_u128(val), bits); } -static inline zig_u8 zig_ctz_u128(zig_u128 val, zig_u8 bits) { - if (zig_lo_u128(val) != 0) return zig_ctz_u64(zig_lo_u128(val), zig_as_u8(64)); - return zig_ctz_u64(zig_hi_u128(val), bits - zig_as_u8(64)) + zig_as_u8(64); +static inline uint8_t zig_ctz_u128(zig_u128 val, uint8_t bits) { + if (zig_lo_u128(val) != 0) return zig_ctz_u64(zig_lo_u128(val), UINT8_C(64)); + return zig_ctz_u64(zig_hi_u128(val), bits - UINT8_C(64)) + UINT8_C(64); } -static inline zig_u8 zig_ctz_i128(zig_i128 val, zig_u8 bits) { +static inline uint8_t zig_ctz_i128(zig_i128 val, uint8_t bits) { return zig_ctz_u128(zig_bitcast_u128(val), bits); } -static inline zig_u8 zig_popcount_u128(zig_u128 val, zig_u8 bits) { - return zig_popcount_u64(zig_hi_u128(val), bits - zig_as_u8(64)) + - zig_popcount_u64(zig_lo_u128(val), zig_as_u8(64)); +static inline uint8_t zig_popcount_u128(zig_u128 val, uint8_t bits) { + return zig_popcount_u64(zig_hi_u128(val), bits - UINT8_C(64)) + + zig_popcount_u64(zig_lo_u128(val), UINT8_C(64)); } -static inline zig_u8 zig_popcount_i128(zig_i128 val, zig_u8 bits) { +static inline uint8_t zig_popcount_i128(zig_i128 val, uint8_t bits) { return zig_popcount_u128(zig_bitcast_u128(val), bits); } -static inline zig_u128 zig_byte_swap_u128(zig_u128 val, zig_u8 bits) { +static inline zig_u128 zig_byte_swap_u128(zig_u128 val, uint8_t bits) { zig_u128 full_res; #if zig_has_builtin(bswap128) full_res = __builtin_bswap128(val); #else - full_res = zig_as_u128(zig_byte_swap_u64(zig_lo_u128(val), zig_as_u8(64)), - zig_byte_swap_u64(zig_hi_u128(val), zig_as_u8(64))); + full_res = zig_make_u128(zig_byte_swap_u64(zig_lo_u128(val), UINT8_C(64)), + zig_byte_swap_u64(zig_hi_u128(val), UINT8_C(64))); #endif - return zig_shr_u128(full_res, zig_as_u8(128) - bits); + return zig_shr_u128(full_res, UINT8_C(128) - bits); } -static inline zig_i128 zig_byte_swap_i128(zig_i128 val, zig_u8 bits) { +static inline zig_i128 zig_byte_swap_i128(zig_i128 val, uint8_t bits) { return zig_bitcast_i128(zig_byte_swap_u128(zig_bitcast_u128(val), bits)); } -static inline zig_u128 zig_bit_reverse_u128(zig_u128 val, zig_u8 bits) { - return zig_shr_u128(zig_as_u128(zig_bit_reverse_u64(zig_lo_u128(val), zig_as_u8(64)), - zig_bit_reverse_u64(zig_hi_u128(val), zig_as_u8(64))), - zig_as_u8(128) - bits); +static inline zig_u128 zig_bit_reverse_u128(zig_u128 val, uint8_t bits) { + return zig_shr_u128(zig_make_u128(zig_bit_reverse_u64(zig_lo_u128(val), UINT8_C(64)), + zig_bit_reverse_u64(zig_hi_u128(val), UINT8_C(64))), + UINT8_C(128) - bits); } -static inline zig_i128 zig_bit_reverse_i128(zig_i128 val, zig_u8 bits) { +static inline zig_i128 zig_bit_reverse_i128(zig_i128 val, uint8_t bits) { return zig_bitcast_i128(zig_bit_reverse_u128(zig_bitcast_u128(val), bits)); } +/* ========================== Big Integer Support =========================== */ + +static inline uint16_t zig_int_bytes(uint16_t bits) { + uint16_t bytes = (bits + CHAR_BIT - 1) / CHAR_BIT; + uint16_t alignment = ZIG_TARGET_MAX_INT_ALIGNMENT; + while (alignment / 2 >= bytes) alignment /= 2; + return (bytes + alignment - 1) / alignment * alignment; +} + +static inline int32_t zig_cmp_big(const void *lhs, const void *rhs, bool is_signed, uint16_t bits) { + const uint8_t *lhs_bytes = lhs; + const uint8_t *rhs_bytes = rhs; + uint16_t byte_offset = 0; + bool do_signed = is_signed; + uint16_t remaining_bytes = zig_int_bytes(bits); + +#if zig_little_endian + byte_offset = remaining_bytes; +#endif + + while (remaining_bytes >= 128 / CHAR_BIT) { + int32_t limb_cmp; + +#if zig_little_endian + byte_offset -= 128 / CHAR_BIT; +#endif + + if (do_signed) { + zig_i128 lhs_limb; + zig_i128 rhs_limb; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + limb_cmp = zig_cmp_i128(lhs_limb, rhs_limb); + do_signed = false; + } else { + zig_u128 lhs_limb; + zig_u128 rhs_limb; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + limb_cmp = zig_cmp_u128(lhs_limb, rhs_limb); + } + + if (limb_cmp != 0) return limb_cmp; + remaining_bytes -= 128 / CHAR_BIT; + +#if zig_big_endian + byte_offset += 128 / CHAR_BIT; +#endif + } + + while (remaining_bytes >= 64 / CHAR_BIT) { +#if zig_little_endian + byte_offset -= 64 / CHAR_BIT; +#endif + + if (do_signed) { + int64_t lhs_limb; + int64_t rhs_limb; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + if (lhs_limb != rhs_limb) return (lhs_limb > rhs_limb) - (lhs_limb < rhs_limb); + do_signed = false; + } else { + uint64_t lhs_limb; + uint64_t rhs_limb; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + if (lhs_limb != rhs_limb) return (lhs_limb > rhs_limb) - (lhs_limb < rhs_limb); + } + + remaining_bytes -= 64 / CHAR_BIT; + +#if zig_big_endian + byte_offset += 64 / CHAR_BIT; +#endif + } + + while (remaining_bytes >= 32 / CHAR_BIT) { +#if zig_little_endian + byte_offset -= 32 / CHAR_BIT; +#endif + + if (do_signed) { + int32_t lhs_limb; + int32_t rhs_limb; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + if (lhs_limb != rhs_limb) return (lhs_limb > rhs_limb) - (lhs_limb < rhs_limb); + do_signed = false; + } else { + uint32_t lhs_limb; + uint32_t rhs_limb; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + if (lhs_limb != rhs_limb) return (lhs_limb > rhs_limb) - (lhs_limb < rhs_limb); + } + + remaining_bytes -= 32 / CHAR_BIT; + +#if zig_big_endian + byte_offset += 32 / CHAR_BIT; +#endif + } + + while (remaining_bytes >= 16 / CHAR_BIT) { +#if zig_little_endian + byte_offset -= 16 / CHAR_BIT; +#endif + + if (do_signed) { + int16_t lhs_limb; + int16_t rhs_limb; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + if (lhs_limb != rhs_limb) return (lhs_limb > rhs_limb) - (lhs_limb < rhs_limb); + do_signed = false; + } else { + uint16_t lhs_limb; + uint16_t rhs_limb; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + if (lhs_limb != rhs_limb) return (lhs_limb > rhs_limb) - (lhs_limb < rhs_limb); + } + + remaining_bytes -= 16 / CHAR_BIT; + +#if zig_big_endian + byte_offset += 16 / CHAR_BIT; +#endif + } + + while (remaining_bytes >= 8 / CHAR_BIT) { +#if zig_little_endian + byte_offset -= 8 / CHAR_BIT; +#endif + + if (do_signed) { + int8_t lhs_limb; + int8_t rhs_limb; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + if (lhs_limb != rhs_limb) return (lhs_limb > rhs_limb) - (lhs_limb < rhs_limb); + do_signed = false; + } else { + uint8_t lhs_limb; + uint8_t rhs_limb; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + if (lhs_limb != rhs_limb) return (lhs_limb > rhs_limb) - (lhs_limb < rhs_limb); + } + + remaining_bytes -= 8 / CHAR_BIT; + +#if zig_big_endian + byte_offset += 8 / CHAR_BIT; +#endif + } + + return 0; +} + +static inline bool zig_addo_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) { + uint8_t *res_bytes = res; + const uint8_t *lhs_bytes = lhs; + const uint8_t *rhs_bytes = rhs; + uint16_t byte_offset = 0; + uint16_t remaining_bytes = zig_int_bytes(bits); + uint16_t top_bits = remaining_bytes * 8 - bits; + bool overflow = false; + +#if zig_big_endian + byte_offset = remaining_bytes; +#endif + + while (remaining_bytes >= 128 / CHAR_BIT) { + uint16_t limb_bits = 128 - (remaining_bytes == 128 / CHAR_BIT ? top_bits : 0); + +#if zig_big_endian + byte_offset -= 128 / CHAR_BIT; +#endif + + if (remaining_bytes == 128 / CHAR_BIT && is_signed) { + zig_i128 res_limb; + zig_i128 tmp_limb; + zig_i128 lhs_limb; + zig_i128 rhs_limb; + bool limb_overflow; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + limb_overflow = zig_addo_i128(&tmp_limb, lhs_limb, rhs_limb, limb_bits); + overflow = limb_overflow ^ zig_addo_i128(&res_limb, tmp_limb, zig_make_i128(INT64_C(0), overflow ? UINT64_C(1) : UINT64_C(0)), limb_bits); + memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb)); + } else { + zig_u128 res_limb; + zig_u128 tmp_limb; + zig_u128 lhs_limb; + zig_u128 rhs_limb; + bool limb_overflow; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + limb_overflow = zig_addo_u128(&tmp_limb, lhs_limb, rhs_limb, limb_bits); + overflow = limb_overflow ^ zig_addo_u128(&res_limb, tmp_limb, zig_make_u128(UINT64_C(0), overflow ? UINT64_C(1) : UINT64_C(0)), limb_bits); + memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb)); + } + + remaining_bytes -= 128 / CHAR_BIT; + +#if zig_little_endian + byte_offset += 128 / CHAR_BIT; +#endif + } + + while (remaining_bytes >= 64 / CHAR_BIT) { + uint16_t limb_bits = 64 - (remaining_bytes == 64 / CHAR_BIT ? top_bits : 0); + +#if zig_big_endian + byte_offset -= 64 / CHAR_BIT; +#endif + + if (remaining_bytes == 64 / CHAR_BIT && is_signed) { + int64_t res_limb; + int64_t tmp_limb; + int64_t lhs_limb; + int64_t rhs_limb; + bool limb_overflow; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + limb_overflow = zig_addo_i64(&tmp_limb, lhs_limb, rhs_limb, limb_bits); + overflow = limb_overflow ^ zig_addo_i64(&res_limb, tmp_limb, overflow ? INT64_C(1) : INT64_C(0), limb_bits); + memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb)); + } else { + uint64_t res_limb; + uint64_t tmp_limb; + uint64_t lhs_limb; + uint64_t rhs_limb; + bool limb_overflow; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + limb_overflow = zig_addo_u64(&tmp_limb, lhs_limb, rhs_limb, limb_bits); + overflow = limb_overflow ^ zig_addo_u64(&res_limb, tmp_limb, overflow ? UINT64_C(1) : UINT64_C(0), limb_bits); + memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb)); + } + + remaining_bytes -= 64 / CHAR_BIT; + +#if zig_little_endian + byte_offset += 64 / CHAR_BIT; +#endif + } + + while (remaining_bytes >= 32 / CHAR_BIT) { + uint16_t limb_bits = 32 - (remaining_bytes == 32 / CHAR_BIT ? top_bits : 0); + +#if zig_big_endian + byte_offset -= 32 / CHAR_BIT; +#endif + + if (remaining_bytes == 32 / CHAR_BIT && is_signed) { + int32_t res_limb; + int32_t tmp_limb; + int32_t lhs_limb; + int32_t rhs_limb; + bool limb_overflow; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + limb_overflow = zig_addo_i32(&tmp_limb, lhs_limb, rhs_limb, limb_bits); + overflow = limb_overflow ^ zig_addo_i32(&res_limb, tmp_limb, overflow ? INT32_C(1) : INT32_C(0), limb_bits); + memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb)); + } else { + uint32_t res_limb; + uint32_t tmp_limb; + uint32_t lhs_limb; + uint32_t rhs_limb; + bool limb_overflow; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + limb_overflow = zig_addo_u32(&tmp_limb, lhs_limb, rhs_limb, limb_bits); + overflow = limb_overflow ^ zig_addo_u32(&res_limb, tmp_limb, overflow ? UINT32_C(1) : UINT32_C(0), limb_bits); + memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb)); + } + + remaining_bytes -= 32 / CHAR_BIT; + +#if zig_little_endian + byte_offset += 32 / CHAR_BIT; +#endif + } + + while (remaining_bytes >= 16 / CHAR_BIT) { + uint16_t limb_bits = 16 - (remaining_bytes == 16 / CHAR_BIT ? top_bits : 0); + +#if zig_big_endian + byte_offset -= 16 / CHAR_BIT; +#endif + + if (remaining_bytes == 16 / CHAR_BIT && is_signed) { + int16_t res_limb; + int16_t tmp_limb; + int16_t lhs_limb; + int16_t rhs_limb; + bool limb_overflow; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + limb_overflow = zig_addo_i16(&tmp_limb, lhs_limb, rhs_limb, limb_bits); + overflow = limb_overflow ^ zig_addo_i16(&res_limb, tmp_limb, overflow ? INT16_C(1) : INT16_C(0), limb_bits); + memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb)); + } else { + uint16_t res_limb; + uint16_t tmp_limb; + uint16_t lhs_limb; + uint16_t rhs_limb; + bool limb_overflow; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + limb_overflow = zig_addo_u16(&tmp_limb, lhs_limb, rhs_limb, limb_bits); + overflow = limb_overflow ^ zig_addo_u16(&res_limb, tmp_limb, overflow ? UINT16_C(1) : UINT16_C(0), limb_bits); + memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb)); + } + + remaining_bytes -= 16 / CHAR_BIT; + +#if zig_little_endian + byte_offset += 16 / CHAR_BIT; +#endif + } + + while (remaining_bytes >= 8 / CHAR_BIT) { + uint16_t limb_bits = 8 - (remaining_bytes == 8 / CHAR_BIT ? top_bits : 0); + +#if zig_big_endian + byte_offset -= 8 / CHAR_BIT; +#endif + + if (remaining_bytes == 8 / CHAR_BIT && is_signed) { + int8_t res_limb; + int8_t tmp_limb; + int8_t lhs_limb; + int8_t rhs_limb; + bool limb_overflow; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + limb_overflow = zig_addo_i8(&tmp_limb, lhs_limb, rhs_limb, limb_bits); + overflow = limb_overflow ^ zig_addo_i8(&res_limb, tmp_limb, overflow ? INT8_C(1) : INT8_C(0), limb_bits); + memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb)); + } else { + uint8_t res_limb; + uint8_t tmp_limb; + uint8_t lhs_limb; + uint8_t rhs_limb; + bool limb_overflow; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + limb_overflow = zig_addo_u8(&tmp_limb, lhs_limb, rhs_limb, limb_bits); + overflow = limb_overflow ^ zig_addo_u8(&res_limb, tmp_limb, overflow ? UINT8_C(1) : UINT8_C(0), limb_bits); + memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb)); + } + + remaining_bytes -= 8 / CHAR_BIT; + +#if zig_little_endian + byte_offset += 8 / CHAR_BIT; +#endif + } + + return overflow; +} + +static inline bool zig_subo_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) { + uint8_t *res_bytes = res; + const uint8_t *lhs_bytes = lhs; + const uint8_t *rhs_bytes = rhs; + uint16_t byte_offset = 0; + uint16_t remaining_bytes = zig_int_bytes(bits); + uint16_t top_bits = remaining_bytes * 8 - bits; + bool overflow = false; + +#if zig_big_endian + byte_offset = remaining_bytes; +#endif + + while (remaining_bytes >= 128 / CHAR_BIT) { + uint16_t limb_bits = 128 - (remaining_bytes == 128 / CHAR_BIT ? top_bits : 0); + +#if zig_big_endian + byte_offset -= 128 / CHAR_BIT; +#endif + + if (remaining_bytes == 128 / CHAR_BIT && is_signed) { + zig_i128 res_limb; + zig_i128 tmp_limb; + zig_i128 lhs_limb; + zig_i128 rhs_limb; + bool limb_overflow; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + limb_overflow = zig_subo_i128(&tmp_limb, lhs_limb, rhs_limb, limb_bits); + overflow = limb_overflow ^ zig_subo_i128(&res_limb, tmp_limb, zig_make_i128(INT64_C(0), overflow ? UINT64_C(1) : UINT64_C(0)), limb_bits); + memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb)); + } else { + zig_u128 res_limb; + zig_u128 tmp_limb; + zig_u128 lhs_limb; + zig_u128 rhs_limb; + bool limb_overflow; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + limb_overflow = zig_subo_u128(&tmp_limb, lhs_limb, rhs_limb, limb_bits); + overflow = limb_overflow ^ zig_subo_u128(&res_limb, tmp_limb, zig_make_u128(UINT64_C(0), overflow ? UINT64_C(1) : UINT64_C(0)), limb_bits); + memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb)); + } + + remaining_bytes -= 128 / CHAR_BIT; + +#if zig_little_endian + byte_offset += 128 / CHAR_BIT; +#endif + } + + while (remaining_bytes >= 64 / CHAR_BIT) { + uint16_t limb_bits = 64 - (remaining_bytes == 64 / CHAR_BIT ? top_bits : 0); + +#if zig_big_endian + byte_offset -= 64 / CHAR_BIT; +#endif + + if (remaining_bytes == 64 / CHAR_BIT && is_signed) { + int64_t res_limb; + int64_t tmp_limb; + int64_t lhs_limb; + int64_t rhs_limb; + bool limb_overflow; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + limb_overflow = zig_subo_i64(&tmp_limb, lhs_limb, rhs_limb, limb_bits); + overflow = limb_overflow ^ zig_subo_i64(&res_limb, tmp_limb, overflow ? INT64_C(1) : INT64_C(0), limb_bits); + memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb)); + } else { + uint64_t res_limb; + uint64_t tmp_limb; + uint64_t lhs_limb; + uint64_t rhs_limb; + bool limb_overflow; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + limb_overflow = zig_subo_u64(&tmp_limb, lhs_limb, rhs_limb, limb_bits); + overflow = limb_overflow ^ zig_subo_u64(&res_limb, tmp_limb, overflow ? UINT64_C(1) : UINT64_C(0), limb_bits); + memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb)); + } + + remaining_bytes -= 64 / CHAR_BIT; + +#if zig_little_endian + byte_offset += 64 / CHAR_BIT; +#endif + } + + while (remaining_bytes >= 32 / CHAR_BIT) { + uint16_t limb_bits = 32 - (remaining_bytes == 32 / CHAR_BIT ? top_bits : 0); + +#if zig_big_endian + byte_offset -= 32 / CHAR_BIT; +#endif + + if (remaining_bytes == 32 / CHAR_BIT && is_signed) { + int32_t res_limb; + int32_t tmp_limb; + int32_t lhs_limb; + int32_t rhs_limb; + bool limb_overflow; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + limb_overflow = zig_subo_i32(&tmp_limb, lhs_limb, rhs_limb, limb_bits); + overflow = limb_overflow ^ zig_subo_i32(&res_limb, tmp_limb, overflow ? INT32_C(1) : INT32_C(0), limb_bits); + memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb)); + } else { + uint32_t res_limb; + uint32_t tmp_limb; + uint32_t lhs_limb; + uint32_t rhs_limb; + bool limb_overflow; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + limb_overflow = zig_subo_u32(&tmp_limb, lhs_limb, rhs_limb, limb_bits); + overflow = limb_overflow ^ zig_subo_u32(&res_limb, tmp_limb, overflow ? UINT32_C(1) : UINT32_C(0), limb_bits); + memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb)); + } + + remaining_bytes -= 32 / CHAR_BIT; + +#if zig_little_endian + byte_offset += 32 / CHAR_BIT; +#endif + } + + while (remaining_bytes >= 16 / CHAR_BIT) { + uint16_t limb_bits = 16 - (remaining_bytes == 16 / CHAR_BIT ? top_bits : 0); + +#if zig_big_endian + byte_offset -= 16 / CHAR_BIT; +#endif + + if (remaining_bytes == 16 / CHAR_BIT && is_signed) { + int16_t res_limb; + int16_t tmp_limb; + int16_t lhs_limb; + int16_t rhs_limb; + bool limb_overflow; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + limb_overflow = zig_subo_i16(&tmp_limb, lhs_limb, rhs_limb, limb_bits); + overflow = limb_overflow ^ zig_subo_i16(&res_limb, tmp_limb, overflow ? INT16_C(1) : INT16_C(0), limb_bits); + memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb)); + } else { + uint16_t res_limb; + uint16_t tmp_limb; + uint16_t lhs_limb; + uint16_t rhs_limb; + bool limb_overflow; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + limb_overflow = zig_subo_u16(&tmp_limb, lhs_limb, rhs_limb, limb_bits); + overflow = limb_overflow ^ zig_subo_u16(&res_limb, tmp_limb, overflow ? UINT16_C(1) : UINT16_C(0), limb_bits); + memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb)); + } + + remaining_bytes -= 16 / CHAR_BIT; + +#if zig_little_endian + byte_offset += 16 / CHAR_BIT; +#endif + } + + while (remaining_bytes >= 8 / CHAR_BIT) { + uint16_t limb_bits = 8 - (remaining_bytes == 8 / CHAR_BIT ? top_bits : 0); + +#if zig_big_endian + byte_offset -= 8 / CHAR_BIT; +#endif + + if (remaining_bytes == 8 / CHAR_BIT && is_signed) { + int8_t res_limb; + int8_t tmp_limb; + int8_t lhs_limb; + int8_t rhs_limb; + bool limb_overflow; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + limb_overflow = zig_subo_i8(&tmp_limb, lhs_limb, rhs_limb, limb_bits); + overflow = limb_overflow ^ zig_subo_i8(&res_limb, tmp_limb, overflow ? INT8_C(1) : INT8_C(0), limb_bits); + memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb)); + } else { + uint8_t res_limb; + uint8_t tmp_limb; + uint8_t lhs_limb; + uint8_t rhs_limb; + bool limb_overflow; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + limb_overflow = zig_subo_u8(&tmp_limb, lhs_limb, rhs_limb, limb_bits); + overflow = limb_overflow ^ zig_subo_u8(&res_limb, tmp_limb, overflow ? UINT8_C(1) : UINT8_C(0), limb_bits); + memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb)); + } + + remaining_bytes -= 8 / CHAR_BIT; + +#if zig_little_endian + byte_offset += 8 / CHAR_BIT; +#endif + } + + return overflow; +} + +static inline void zig_addw_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) { + (void)zig_addo_big(res, lhs, rhs, is_signed, bits); +} + +static inline void zig_subw_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) { + (void)zig_subo_big(res, lhs, rhs, is_signed, bits); +} + +static inline uint16_t zig_clz_big(const void *val, bool is_signed, uint16_t bits) { + const uint8_t *val_bytes = val; + uint16_t byte_offset = 0; + uint16_t remaining_bytes = zig_int_bytes(bits); + uint16_t skip_bits = remaining_bytes * 8 - bits; + uint16_t total_lz = 0; + uint16_t limb_lz; + (void)is_signed; + +#if zig_little_endian + byte_offset = remaining_bytes; +#endif + + while (remaining_bytes >= 128 / CHAR_BIT) { +#if zig_little_endian + byte_offset -= 128 / CHAR_BIT; +#endif + + { + zig_u128 val_limb; + + memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb)); + limb_lz = zig_clz_u128(val_limb, 128 - skip_bits); + } + + total_lz += limb_lz; + if (limb_lz < 128 - skip_bits) return total_lz; + skip_bits = 0; + remaining_bytes -= 128 / CHAR_BIT; + +#if zig_big_endian + byte_offset += 128 / CHAR_BIT; +#endif + } + + while (remaining_bytes >= 64 / CHAR_BIT) { +#if zig_little_endian + byte_offset -= 64 / CHAR_BIT; +#endif + + { + uint64_t val_limb; + + memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb)); + limb_lz = zig_clz_u64(val_limb, 64 - skip_bits); + } + + total_lz += limb_lz; + if (limb_lz < 64 - skip_bits) return total_lz; + skip_bits = 0; + remaining_bytes -= 64 / CHAR_BIT; + +#if zig_big_endian + byte_offset += 64 / CHAR_BIT; +#endif + } + + while (remaining_bytes >= 32 / CHAR_BIT) { +#if zig_little_endian + byte_offset -= 32 / CHAR_BIT; +#endif + + { + uint32_t val_limb; + + memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb)); + limb_lz = zig_clz_u32(val_limb, 32 - skip_bits); + } + + total_lz += limb_lz; + if (limb_lz < 32 - skip_bits) return total_lz; + skip_bits = 0; + remaining_bytes -= 32 / CHAR_BIT; + +#if zig_big_endian + byte_offset += 32 / CHAR_BIT; +#endif + } + + while (remaining_bytes >= 16 / CHAR_BIT) { +#if zig_little_endian + byte_offset -= 16 / CHAR_BIT; +#endif + + { + uint16_t val_limb; + + memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb)); + limb_lz = zig_clz_u16(val_limb, 16 - skip_bits); + } + + total_lz += limb_lz; + if (limb_lz < 16 - skip_bits) return total_lz; + skip_bits = 0; + remaining_bytes -= 16 / CHAR_BIT; + +#if zig_big_endian + byte_offset += 16 / CHAR_BIT; +#endif + } + + while (remaining_bytes >= 8 / CHAR_BIT) { +#if zig_little_endian + byte_offset -= 8 / CHAR_BIT; +#endif + + { + uint8_t val_limb; + + memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb)); + limb_lz = zig_clz_u8(val_limb, 8 - skip_bits); + } + + total_lz += limb_lz; + if (limb_lz < 8 - skip_bits) return total_lz; + skip_bits = 0; + remaining_bytes -= 8 / CHAR_BIT; + +#if zig_big_endian + byte_offset += 8 / CHAR_BIT; +#endif + } + + return total_lz; +} + +static inline uint16_t zig_ctz_big(const void *val, bool is_signed, uint16_t bits) { + const uint8_t *val_bytes = val; + uint16_t byte_offset = 0; + uint16_t remaining_bytes = zig_int_bytes(bits); + uint16_t total_tz = 0; + uint16_t limb_tz; + (void)is_signed; + +#if zig_big_endian + byte_offset = remaining_bytes; +#endif + + while (remaining_bytes >= 128 / CHAR_BIT) { +#if zig_big_endian + byte_offset -= 128 / CHAR_BIT; +#endif + + { + zig_u128 val_limb; + + memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb)); + limb_tz = zig_ctz_u128(val_limb, 128); + } + + total_tz += limb_tz; + if (limb_tz < 128) return total_tz; + remaining_bytes -= 128 / CHAR_BIT; + +#if zig_little_endian + byte_offset += 128 / CHAR_BIT; +#endif + } + + while (remaining_bytes >= 64 / CHAR_BIT) { +#if zig_big_endian + byte_offset -= 64 / CHAR_BIT; +#endif + + { + uint64_t val_limb; + + memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb)); + limb_tz = zig_ctz_u64(val_limb, 64); + } + + total_tz += limb_tz; + if (limb_tz < 64) return total_tz; + remaining_bytes -= 64 / CHAR_BIT; + +#if zig_little_endian + byte_offset += 64 / CHAR_BIT; +#endif + } + + while (remaining_bytes >= 32 / CHAR_BIT) { +#if zig_big_endian + byte_offset -= 32 / CHAR_BIT; +#endif + + { + uint32_t val_limb; + + memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb)); + limb_tz = zig_ctz_u32(val_limb, 32); + } + + total_tz += limb_tz; + if (limb_tz < 32) return total_tz; + remaining_bytes -= 32 / CHAR_BIT; + +#if zig_little_endian + byte_offset += 32 / CHAR_BIT; +#endif + } + + while (remaining_bytes >= 16 / CHAR_BIT) { +#if zig_big_endian + byte_offset -= 16 / CHAR_BIT; +#endif + + { + uint16_t val_limb; + + memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb)); + limb_tz = zig_ctz_u16(val_limb, 16); + } + + total_tz += limb_tz; + if (limb_tz < 16) return total_tz; + remaining_bytes -= 16 / CHAR_BIT; + +#if zig_little_endian + byte_offset += 16 / CHAR_BIT; +#endif + } + + while (remaining_bytes >= 8 / CHAR_BIT) { +#if zig_big_endian + byte_offset -= 8 / CHAR_BIT; +#endif + + { + uint8_t val_limb; + + memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb)); + limb_tz = zig_ctz_u8(val_limb, 8); + } + + total_tz += limb_tz; + if (limb_tz < 8) return total_tz; + remaining_bytes -= 8 / CHAR_BIT; + +#if zig_little_endian + byte_offset += 8 / CHAR_BIT; +#endif + } + + return total_tz; +} + +static inline uint16_t zig_popcount_big(const void *val, bool is_signed, uint16_t bits) { + const uint8_t *val_bytes = val; + uint16_t byte_offset = 0; + uint16_t remaining_bytes = zig_int_bytes(bits); + uint16_t total_pc = 0; + (void)is_signed; + +#if zig_big_endian + byte_offset = remaining_bytes; +#endif + + while (remaining_bytes >= 128 / CHAR_BIT) { +#if zig_big_endian + byte_offset -= 128 / CHAR_BIT; +#endif + + { + zig_u128 val_limb; + + memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb)); + total_pc += zig_popcount_u128(val_limb, 128); + } + + remaining_bytes -= 128 / CHAR_BIT; + +#if zig_little_endian + byte_offset += 128 / CHAR_BIT; +#endif + } + + while (remaining_bytes >= 64 / CHAR_BIT) { +#if zig_big_endian + byte_offset -= 64 / CHAR_BIT; +#endif + + { + uint64_t val_limb; + + memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb)); + total_pc += zig_popcount_u64(val_limb, 64); + } + + remaining_bytes -= 64 / CHAR_BIT; + +#if zig_little_endian + byte_offset += 64 / CHAR_BIT; +#endif + } + + while (remaining_bytes >= 32 / CHAR_BIT) { +#if zig_big_endian + byte_offset -= 32 / CHAR_BIT; +#endif + + { + uint32_t val_limb; + + memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb)); + total_pc += zig_popcount_u32(val_limb, 32); + } + + remaining_bytes -= 32 / CHAR_BIT; + +#if zig_little_endian + byte_offset += 32 / CHAR_BIT; +#endif + } + + while (remaining_bytes >= 16 / CHAR_BIT) { +#if zig_big_endian + byte_offset -= 16 / CHAR_BIT; +#endif + + { + uint16_t val_limb; + + memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb)); + total_pc = zig_popcount_u16(val_limb, 16); + } + + remaining_bytes -= 16 / CHAR_BIT; + +#if zig_little_endian + byte_offset += 16 / CHAR_BIT; +#endif + } + + while (remaining_bytes >= 8 / CHAR_BIT) { +#if zig_big_endian + byte_offset -= 8 / CHAR_BIT; +#endif + + { + uint8_t val_limb; + + memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb)); + total_pc = zig_popcount_u8(val_limb, 8); + } + + remaining_bytes -= 8 / CHAR_BIT; + +#if zig_little_endian + byte_offset += 8 / CHAR_BIT; +#endif + } + + return total_pc; +} + /* ========================= Floating Point Support ========================= */ #if _MSC_VER @@ -1810,252 +2758,253 @@ static inline zig_i128 zig_bit_reverse_i128(zig_i128 val, zig_u8 bits) { #if (zig_has_builtin(nan) && zig_has_builtin(nans) && zig_has_builtin(inf)) || defined(zig_gnuc) #define zig_has_float_builtins 1 -#define zig_as_special_f16(sign, name, arg, repr) sign zig_as_f16(__builtin_##name, )(arg) -#define zig_as_special_f32(sign, name, arg, repr) sign zig_as_f32(__builtin_##name, )(arg) -#define zig_as_special_f64(sign, name, arg, repr) sign zig_as_f64(__builtin_##name, )(arg) -#define zig_as_special_f80(sign, name, arg, repr) sign zig_as_f80(__builtin_##name, )(arg) -#define zig_as_special_f128(sign, name, arg, repr) sign zig_as_f128(__builtin_##name, )(arg) -#define zig_as_special_c_longdouble(sign, name, arg, repr) sign zig_as_c_longdouble(__builtin_##name, )(arg) +#define zig_make_special_f16(sign, name, arg, repr) sign zig_make_f16(__builtin_##name, )(arg) +#define zig_make_special_f32(sign, name, arg, repr) sign zig_make_f32(__builtin_##name, )(arg) +#define zig_make_special_f64(sign, name, arg, repr) sign zig_make_f64(__builtin_##name, )(arg) +#define zig_make_special_f80(sign, name, arg, repr) sign zig_make_f80(__builtin_##name, )(arg) +#define zig_make_special_f128(sign, name, arg, repr) sign zig_make_f128(__builtin_##name, )(arg) #else #define zig_has_float_builtins 0 -#define zig_as_special_f16(sign, name, arg, repr) zig_float_from_repr_f16(repr) -#define zig_as_special_f32(sign, name, arg, repr) zig_float_from_repr_f32(repr) -#define zig_as_special_f64(sign, name, arg, repr) zig_float_from_repr_f64(repr) -#define zig_as_special_f80(sign, name, arg, repr) zig_float_from_repr_f80(repr) -#define zig_as_special_f128(sign, name, arg, repr) zig_float_from_repr_f128(repr) -#define zig_as_special_c_longdouble(sign, name, arg, repr) zig_float_from_repr_c_longdouble(repr) +#define zig_make_special_f16(sign, name, arg, repr) zig_float_from_repr_f16(repr) +#define zig_make_special_f32(sign, name, arg, repr) zig_float_from_repr_f32(repr) +#define zig_make_special_f64(sign, name, arg, repr) zig_float_from_repr_f64(repr) +#define zig_make_special_f80(sign, name, arg, repr) zig_float_from_repr_f80(repr) +#define zig_make_special_f128(sign, name, arg, repr) zig_float_from_repr_f128(repr) #endif #define zig_has_f16 1 #define zig_bitSizeOf_f16 16 +typedef int16_t zig_repr_f16; #define zig_libc_name_f16(name) __##name##h -#define zig_as_special_constant_f16(sign, name, arg, repr) zig_as_special_f16(sign, name, arg, repr) +#define zig_init_special_f16(sign, name, arg, repr) zig_make_special_f16(sign, name, arg, repr) #if FLT_MANT_DIG == 11 typedef float zig_f16; -#define zig_as_f16(fp, repr) fp##f +#define zig_make_f16(fp, repr) fp##f #elif DBL_MANT_DIG == 11 typedef double zig_f16; -#define zig_as_f16(fp, repr) fp +#define zig_make_f16(fp, repr) fp #elif LDBL_MANT_DIG == 11 #define zig_bitSizeOf_c_longdouble 16 +#ifndef ZIG_TARGET_ABI_MSVC +typedef zig_repr_f16 zig_repr_c_longdouble; +#endif typedef long double zig_f16; -#define zig_as_f16(fp, repr) fp##l +#define zig_make_f16(fp, repr) fp##l #elif FLT16_MANT_DIG == 11 && (zig_has_builtin(inff16) || defined(zig_gnuc)) typedef _Float16 zig_f16; -#define zig_as_f16(fp, repr) fp##f16 +#define zig_make_f16(fp, repr) fp##f16 #elif defined(__SIZEOF_FP16__) typedef __fp16 zig_f16; -#define zig_as_f16(fp, repr) fp##f16 +#define zig_make_f16(fp, repr) fp##f16 #else #undef zig_has_f16 #define zig_has_f16 0 -#define zig_repr_f16 i16 -typedef zig_i16 zig_f16; -#define zig_as_f16(fp, repr) repr -#undef zig_as_special_f16 -#define zig_as_special_f16(sign, name, arg, repr) repr -#undef zig_as_special_constant_f16 -#define zig_as_special_constant_f16(sign, name, arg, repr) repr +#define zig_bitSizeOf_repr_f16 16 +typedef int16_t zig_f16; +#define zig_make_f16(fp, repr) repr +#undef zig_make_special_f16 +#define zig_make_special_f16(sign, name, arg, repr) repr +#undef zig_init_special_f16 +#define zig_init_special_f16(sign, name, arg, repr) repr #endif #define zig_has_f32 1 #define zig_bitSizeOf_f32 32 +typedef int32_t zig_repr_f32; #define zig_libc_name_f32(name) name##f #if _MSC_VER -#define zig_as_special_constant_f32(sign, name, arg, repr) sign zig_as_f32(zig_msvc_flt_##name, ) +#define zig_init_special_f32(sign, name, arg, repr) sign zig_make_f32(zig_msvc_flt_##name, ) #else -#define zig_as_special_constant_f32(sign, name, arg, repr) zig_as_special_f32(sign, name, arg, repr) +#define zig_init_special_f32(sign, name, arg, repr) zig_make_special_f32(sign, name, arg, repr) #endif #if FLT_MANT_DIG == 24 typedef float zig_f32; -#define zig_as_f32(fp, repr) fp##f +#define zig_make_f32(fp, repr) fp##f #elif DBL_MANT_DIG == 24 typedef double zig_f32; -#define zig_as_f32(fp, repr) fp +#define zig_make_f32(fp, repr) fp #elif LDBL_MANT_DIG == 24 #define zig_bitSizeOf_c_longdouble 32 +#ifndef ZIG_TARGET_ABI_MSVC +typedef zig_repr_f32 zig_repr_c_longdouble; +#endif typedef long double zig_f32; -#define zig_as_f32(fp, repr) fp##l +#define zig_make_f32(fp, repr) fp##l #elif FLT32_MANT_DIG == 24 typedef _Float32 zig_f32; -#define zig_as_f32(fp, repr) fp##f32 +#define zig_make_f32(fp, repr) fp##f32 #else #undef zig_has_f32 #define zig_has_f32 0 -#define zig_repr_f32 i32 -typedef zig_i32 zig_f32; -#define zig_as_f32(fp, repr) repr -#undef zig_as_special_f32 -#define zig_as_special_f32(sign, name, arg, repr) repr -#undef zig_as_special_constant_f32 -#define zig_as_special_constant_f32(sign, name, arg, repr) repr +#define zig_bitSizeOf_repr_f32 32 +typedef int32_t zig_f32; +#define zig_make_f32(fp, repr) repr +#undef zig_make_special_f32 +#define zig_make_special_f32(sign, name, arg, repr) repr +#undef zig_init_special_f32 +#define zig_init_special_f32(sign, name, arg, repr) repr #endif #define zig_has_f64 1 #define zig_bitSizeOf_f64 64 +typedef int64_t zig_repr_f64; #define zig_libc_name_f64(name) name #if _MSC_VER #ifdef ZIG_TARGET_ABI_MSVC #define zig_bitSizeOf_c_longdouble 64 +#ifndef ZIG_TARGET_ABI_MSVC +typedef zig_repr_f64 zig_repr_c_longdouble; #endif -#define zig_as_special_constant_f64(sign, name, arg, repr) sign zig_as_f64(zig_msvc_flt_##name, ) +#endif +#define zig_init_special_f64(sign, name, arg, repr) sign zig_make_f64(zig_msvc_flt_##name, ) #else /* _MSC_VER */ -#define zig_as_special_constant_f64(sign, name, arg, repr) zig_as_special_f64(sign, name, arg, repr) +#define zig_init_special_f64(sign, name, arg, repr) zig_make_special_f64(sign, name, arg, repr) #endif /* _MSC_VER */ #if FLT_MANT_DIG == 53 typedef float zig_f64; -#define zig_as_f64(fp, repr) fp##f +#define zig_make_f64(fp, repr) fp##f #elif DBL_MANT_DIG == 53 typedef double zig_f64; -#define zig_as_f64(fp, repr) fp +#define zig_make_f64(fp, repr) fp #elif LDBL_MANT_DIG == 53 #define zig_bitSizeOf_c_longdouble 64 +#ifndef ZIG_TARGET_ABI_MSVC +typedef zig_repr_f64 zig_repr_c_longdouble; +#endif typedef long double zig_f64; -#define zig_as_f64(fp, repr) fp##l +#define zig_make_f64(fp, repr) fp##l #elif FLT64_MANT_DIG == 53 typedef _Float64 zig_f64; -#define zig_as_f64(fp, repr) fp##f64 +#define zig_make_f64(fp, repr) fp##f64 #elif FLT32X_MANT_DIG == 53 typedef _Float32x zig_f64; -#define zig_as_f64(fp, repr) fp##f32x +#define zig_make_f64(fp, repr) fp##f32x #else #undef zig_has_f64 #define zig_has_f64 0 -#define zig_repr_f64 i64 -typedef zig_i64 zig_f64; -#define zig_as_f64(fp, repr) repr -#undef zig_as_special_f64 -#define zig_as_special_f64(sign, name, arg, repr) repr -#undef zig_as_special_constant_f64 -#define zig_as_special_constant_f64(sign, name, arg, repr) repr +#define zig_bitSizeOf_repr_f64 64 +typedef int64_t zig_f64; +#define zig_make_f64(fp, repr) repr +#undef zig_make_special_f64 +#define zig_make_special_f64(sign, name, arg, repr) repr +#undef zig_init_special_f64 +#define zig_init_special_f64(sign, name, arg, repr) repr #endif #define zig_has_f80 1 #define zig_bitSizeOf_f80 80 +typedef zig_i128 zig_repr_f80; #define zig_libc_name_f80(name) __##name##x -#define zig_as_special_constant_f80(sign, name, arg, repr) zig_as_special_f80(sign, name, arg, repr) +#define zig_init_special_f80(sign, name, arg, repr) zig_make_special_f80(sign, name, arg, repr) #if FLT_MANT_DIG == 64 typedef float zig_f80; -#define zig_as_f80(fp, repr) fp##f +#define zig_make_f80(fp, repr) fp##f #elif DBL_MANT_DIG == 64 typedef double zig_f80; -#define zig_as_f80(fp, repr) fp +#define zig_make_f80(fp, repr) fp #elif LDBL_MANT_DIG == 64 #define zig_bitSizeOf_c_longdouble 80 +#ifndef ZIG_TARGET_ABI_MSVC +typedef zig_repr_f80 zig_repr_c_longdouble; +#endif typedef long double zig_f80; -#define zig_as_f80(fp, repr) fp##l +#define zig_make_f80(fp, repr) fp##l #elif FLT80_MANT_DIG == 64 typedef _Float80 zig_f80; -#define zig_as_f80(fp, repr) fp##f80 +#define zig_make_f80(fp, repr) fp##f80 #elif FLT64X_MANT_DIG == 64 typedef _Float64x zig_f80; -#define zig_as_f80(fp, repr) fp##f64x +#define zig_make_f80(fp, repr) fp##f64x #elif defined(__SIZEOF_FLOAT80__) typedef __float80 zig_f80; -#define zig_as_f80(fp, repr) fp##l +#define zig_make_f80(fp, repr) fp##l #else #undef zig_has_f80 #define zig_has_f80 0 -#define zig_repr_f80 i128 +#define zig_bitSizeOf_repr_f80 128 typedef zig_i128 zig_f80; -#define zig_as_f80(fp, repr) repr -#undef zig_as_special_f80 -#define zig_as_special_f80(sign, name, arg, repr) repr -#undef zig_as_special_constant_f80 -#define zig_as_special_constant_f80(sign, name, arg, repr) repr +#define zig_make_f80(fp, repr) repr +#undef zig_make_special_f80 +#define zig_make_special_f80(sign, name, arg, repr) repr +#undef zig_init_special_f80 +#define zig_init_special_f80(sign, name, arg, repr) repr #endif #define zig_has_f128 1 #define zig_bitSizeOf_f128 128 +typedef zig_i128 zig_repr_f128; #define zig_libc_name_f128(name) name##q -#define zig_as_special_constant_f128(sign, name, arg, repr) zig_as_special_f128(sign, name, arg, repr) +#define zig_init_special_f128(sign, name, arg, repr) zig_make_special_f128(sign, name, arg, repr) #if FLT_MANT_DIG == 113 typedef float zig_f128; -#define zig_as_f128(fp, repr) fp##f +#define zig_make_f128(fp, repr) fp##f #elif DBL_MANT_DIG == 113 typedef double zig_f128; -#define zig_as_f128(fp, repr) fp +#define zig_make_f128(fp, repr) fp #elif LDBL_MANT_DIG == 113 #define zig_bitSizeOf_c_longdouble 128 +#ifndef ZIG_TARGET_ABI_MSVC +typedef zig_repr_f128 zig_repr_c_longdouble; +#endif typedef long double zig_f128; -#define zig_as_f128(fp, repr) fp##l +#define zig_make_f128(fp, repr) fp##l #elif FLT128_MANT_DIG == 113 typedef _Float128 zig_f128; -#define zig_as_f128(fp, repr) fp##f128 +#define zig_make_f128(fp, repr) fp##f128 #elif FLT64X_MANT_DIG == 113 typedef _Float64x zig_f128; -#define zig_as_f128(fp, repr) fp##f64x +#define zig_make_f128(fp, repr) fp##f64x #elif defined(__SIZEOF_FLOAT128__) typedef __float128 zig_f128; -#define zig_as_f128(fp, repr) fp##q -#undef zig_as_special_f128 -#define zig_as_special_f128(sign, name, arg, repr) sign __builtin_##name##f128(arg) +#define zig_make_f128(fp, repr) fp##q +#undef zig_make_special_f128 +#define zig_make_special_f128(sign, name, arg, repr) sign __builtin_##name##f128(arg) #else #undef zig_has_f128 #define zig_has_f128 0 -#define zig_repr_f128 i128 +#define zig_bitSizeOf_repr_f128 128 typedef zig_i128 zig_f128; -#define zig_as_f128(fp, repr) repr -#undef zig_as_special_f128 -#define zig_as_special_f128(sign, name, arg, repr) repr -#undef zig_as_special_constant_f128 -#define zig_as_special_constant_f128(sign, name, arg, repr) repr +#define zig_make_f128(fp, repr) repr +#undef zig_make_special_f128 +#define zig_make_special_f128(sign, name, arg, repr) repr +#undef zig_init_special_f128 +#define zig_init_special_f128(sign, name, arg, repr) repr #endif -#define zig_has_c_longdouble 1 - -#ifdef ZIG_TARGET_ABI_MSVC -#define zig_libc_name_c_longdouble(name) name -#else -#define zig_libc_name_c_longdouble(name) name##l -#endif - -#define zig_as_special_constant_c_longdouble(sign, name, arg, repr) zig_as_special_c_longdouble(sign, name, arg, repr) #ifdef zig_bitSizeOf_c_longdouble +#define zig_has_c_longdouble 1 #ifdef ZIG_TARGET_ABI_MSVC -typedef double zig_c_longdouble; #undef zig_bitSizeOf_c_longdouble #define zig_bitSizeOf_c_longdouble 64 -#define zig_as_c_longdouble(fp, repr) fp +typedef zig_f64 zig_c_longdouble; +typedef zig_repr_f64 zig_repr_c_longdouble; #else typedef long double zig_c_longdouble; -#define zig_as_c_longdouble(fp, repr) fp##l #endif #else /* zig_bitSizeOf_c_longdouble */ -#undef zig_has_c_longdouble #define zig_has_c_longdouble 0 -#define zig_bitSizeOf_c_longdouble 80 -#define zig_compiler_rt_abbrev_c_longdouble zig_compiler_rt_abbrev_f80 -#define zig_repr_c_longdouble i128 -typedef zig_i128 zig_c_longdouble; -#define zig_as_c_longdouble(fp, repr) repr -#undef zig_as_special_c_longdouble -#define zig_as_special_c_longdouble(sign, name, arg, repr) repr -#undef zig_as_special_constant_c_longdouble -#define zig_as_special_constant_c_longdouble(sign, name, arg, repr) repr +#define zig_bitSizeOf_repr_c_longdouble 128 +typedef zig_f128 zig_c_longdouble; +typedef zig_repr_f128 zig_repr_c_longdouble; #endif /* zig_bitSizeOf_c_longdouble */ #if !zig_has_float_builtins -#define zig_float_from_repr(Type, ReprType) \ - static inline zig_##Type zig_float_from_repr_##Type(zig_##ReprType repr) { \ - return *((zig_##Type*)&repr); \ +#define zig_float_from_repr(Type) \ + static inline zig_##Type zig_float_from_repr_##Type(zig_repr_##Type repr) { \ + zig_##Type result; \ + memcpy(&result, &repr, sizeof(result)); \ + return result; \ } -zig_float_from_repr(f16, u16) -zig_float_from_repr(f32, u32) -zig_float_from_repr(f64, u64) -zig_float_from_repr(f80, u128) -zig_float_from_repr(f128, u128) -#if zig_bitSizeOf_c_longdouble == 80 -zig_float_from_repr(c_longdouble, u128) -#else -#define zig_expand_float_from_repr(Type, ReprType) zig_float_from_repr(Type, ReprType) -zig_expand_float_from_repr(c_longdouble, zig_expand_concat(u, zig_bitSizeOf_c_longdouble)) -#endif +zig_float_from_repr(f16) +zig_float_from_repr(f32) +zig_float_from_repr(f64) +zig_float_from_repr(f80) +zig_float_from_repr(f128) #endif #define zig_cast_f16 (zig_f16) @@ -2064,41 +3013,42 @@ zig_expand_float_from_repr(c_longdouble, zig_expand_concat(u, zig_bitSizeOf_c_lo #if _MSC_VER && !zig_has_f128 #define zig_cast_f80 -#define zig_cast_c_longdouble #define zig_cast_f128 #else #define zig_cast_f80 (zig_f80) -#define zig_cast_c_longdouble (zig_c_longdouble) #define zig_cast_f128 (zig_f128) #endif #define zig_convert_builtin(ResType, operation, ArgType, version) \ - zig_extern zig_##ResType zig_expand_concat(zig_expand_concat(zig_expand_concat(__##operation, \ - zig_compiler_rt_abbrev_##ArgType), zig_compiler_rt_abbrev_##ResType), version)(zig_##ArgType); -zig_convert_builtin(f16, trunc, f32, 2) -zig_convert_builtin(f16, trunc, f64, 2) -zig_convert_builtin(f16, trunc, f80, 2) -zig_convert_builtin(f16, trunc, f128, 2) -zig_convert_builtin(f32, extend, f16, 2) -zig_convert_builtin(f32, trunc, f64, 2) -zig_convert_builtin(f32, trunc, f80, 2) -zig_convert_builtin(f32, trunc, f128, 2) -zig_convert_builtin(f64, extend, f16, 2) -zig_convert_builtin(f64, extend, f32, 2) -zig_convert_builtin(f64, trunc, f80, 2) -zig_convert_builtin(f64, trunc, f128, 2) -zig_convert_builtin(f80, extend, f16, 2) -zig_convert_builtin(f80, extend, f32, 2) -zig_convert_builtin(f80, extend, f64, 2) -zig_convert_builtin(f80, trunc, f128, 2) -zig_convert_builtin(f128, extend, f16, 2) -zig_convert_builtin(f128, extend, f32, 2) -zig_convert_builtin(f128, extend, f64, 2) -zig_convert_builtin(f128, extend, f80, 2) + zig_extern ResType zig_expand_concat(zig_expand_concat(zig_expand_concat(__##operation, \ + zig_compiler_rt_abbrev_##ArgType), zig_compiler_rt_abbrev_##ResType), version)(ArgType); +zig_convert_builtin(zig_f16, trunc, zig_f32, 2) +zig_convert_builtin(zig_f16, trunc, zig_f64, 2) +zig_convert_builtin(zig_f16, trunc, zig_f80, 2) +zig_convert_builtin(zig_f16, trunc, zig_f128, 2) +zig_convert_builtin(zig_f32, extend, zig_f16, 2) +zig_convert_builtin(zig_f32, trunc, zig_f64, 2) +zig_convert_builtin(zig_f32, trunc, zig_f80, 2) +zig_convert_builtin(zig_f32, trunc, zig_f128, 2) +zig_convert_builtin(zig_f64, extend, zig_f16, 2) +zig_convert_builtin(zig_f64, extend, zig_f32, 2) +zig_convert_builtin(zig_f64, trunc, zig_f80, 2) +zig_convert_builtin(zig_f64, trunc, zig_f128, 2) +zig_convert_builtin(zig_f80, extend, zig_f16, 2) +zig_convert_builtin(zig_f80, extend, zig_f32, 2) +zig_convert_builtin(zig_f80, extend, zig_f64, 2) +zig_convert_builtin(zig_f80, trunc, zig_f128, 2) +zig_convert_builtin(zig_f128, extend, zig_f16, 2) +zig_convert_builtin(zig_f128, extend, zig_f32, 2) +zig_convert_builtin(zig_f128, extend, zig_f64, 2) +zig_convert_builtin(zig_f128, extend, zig_f80, 2) #define zig_float_negate_builtin_0(Type) \ static inline zig_##Type zig_neg_##Type(zig_##Type arg) { \ - return zig_expand_concat(zig_xor_, zig_repr_##Type)(arg, zig_expand_minInt(zig_repr_##Type, zig_bitSizeOf_##Type)); \ + return zig_expand_concat(zig_xor_i, zig_bitSizeOf_repr_##Type)( \ + arg, \ + zig_minInt_i(zig_bitSizeOf_repr_##Type, zig_bitSizeOf_##Type) \ + ); \ } #define zig_float_negate_builtin_1(Type) \ static inline zig_##Type zig_neg_##Type(zig_##Type arg) { \ @@ -2106,28 +3056,28 @@ zig_convert_builtin(f128, extend, f80, 2) } #define zig_float_less_builtin_0(Type, operation) \ - zig_extern zig_i32 zig_expand_concat(zig_expand_concat(__##operation, \ - zig_compiler_rt_abbrev_##Type), 2)(zig_##Type, zig_##Type); \ - static inline zig_i32 zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \ - return zig_expand_concat(zig_expand_concat(__##operation, zig_compiler_rt_abbrev_##Type), 2)(lhs, rhs); \ + zig_extern int32_t zig_expand_concat(zig_expand_concat(__##operation, \ + zig_compiler_rt_abbrev_zig_##Type), 2)(zig_##Type, zig_##Type); \ + static inline int32_t zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \ + return zig_expand_concat(zig_expand_concat(__##operation, zig_compiler_rt_abbrev_zig_##Type), 2)(lhs, rhs); \ } #define zig_float_less_builtin_1(Type, operation) \ - static inline zig_i32 zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \ + static inline int32_t zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \ return (!(lhs <= rhs) - (lhs < rhs)); \ } #define zig_float_greater_builtin_0(Type, operation) \ zig_float_less_builtin_0(Type, operation) #define zig_float_greater_builtin_1(Type, operation) \ - static inline zig_i32 zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \ + static inline int32_t zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \ return ((lhs > rhs) - !(lhs >= rhs)); \ } #define zig_float_binary_builtin_0(Type, operation, operator) \ zig_extern zig_##Type zig_expand_concat(zig_expand_concat(__##operation, \ - zig_compiler_rt_abbrev_##Type), 3)(zig_##Type, zig_##Type); \ + zig_compiler_rt_abbrev_zig_##Type), 3)(zig_##Type, zig_##Type); \ static inline zig_##Type zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \ - return zig_expand_concat(zig_expand_concat(__##operation, zig_compiler_rt_abbrev_##Type), 3)(lhs, rhs); \ + return zig_expand_concat(zig_expand_concat(__##operation, zig_compiler_rt_abbrev_zig_##Type), 3)(lhs, rhs); \ } #define zig_float_binary_builtin_1(Type, operation, operator) \ static inline zig_##Type zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \ @@ -2135,18 +3085,18 @@ zig_convert_builtin(f128, extend, f80, 2) } #define zig_float_builtins(Type) \ - zig_convert_builtin(i32, fix, Type, ) \ - zig_convert_builtin(u32, fixuns, Type, ) \ - zig_convert_builtin(i64, fix, Type, ) \ - zig_convert_builtin(u64, fixuns, Type, ) \ - zig_convert_builtin(i128, fix, Type, ) \ - zig_convert_builtin(u128, fixuns, Type, ) \ - zig_convert_builtin(Type, float, i32, ) \ - zig_convert_builtin(Type, floatun, u32, ) \ - zig_convert_builtin(Type, float, i64, ) \ - zig_convert_builtin(Type, floatun, u64, ) \ - zig_convert_builtin(Type, float, i128, ) \ - zig_convert_builtin(Type, floatun, u128, ) \ + zig_convert_builtin( int32_t, fix, zig_##Type, ) \ + zig_convert_builtin(uint32_t, fixuns, zig_##Type, ) \ + zig_convert_builtin( int64_t, fix, zig_##Type, ) \ + zig_convert_builtin(uint64_t, fixuns, zig_##Type, ) \ + zig_convert_builtin(zig_i128, fix, zig_##Type, ) \ + zig_convert_builtin(zig_u128, fixuns, zig_##Type, ) \ + zig_convert_builtin(zig_##Type, float, int32_t, ) \ + zig_convert_builtin(zig_##Type, floatun, uint32_t, ) \ + zig_convert_builtin(zig_##Type, float, int64_t, ) \ + zig_convert_builtin(zig_##Type, floatun, uint64_t, ) \ + zig_convert_builtin(zig_##Type, float, zig_i128, ) \ + zig_convert_builtin(zig_##Type, floatun, zig_u128, ) \ zig_expand_concat(zig_float_negate_builtin_, zig_has_##Type)(Type) \ zig_expand_concat(zig_float_less_builtin_, zig_has_##Type)(Type, cmp) \ zig_expand_concat(zig_float_less_builtin_, zig_has_##Type)(Type, ne) \ @@ -2194,155 +3144,162 @@ zig_float_builtins(f32) zig_float_builtins(f64) zig_float_builtins(f80) zig_float_builtins(f128) -zig_float_builtins(c_longdouble) #if _MSC_VER && (_M_IX86 || _M_X64) // TODO: zig_msvc_atomic_load should load 32 bit without interlocked on x86, and load 64 bit without interlocked on x64 -#define zig_msvc_atomics(Type, suffix) \ - static inline bool zig_msvc_cmpxchg_##Type(zig_##Type volatile* obj, zig_##Type* expected, zig_##Type desired) { \ - zig_##Type comparand = *expected; \ - zig_##Type initial = _InterlockedCompareExchange##suffix(obj, desired, comparand); \ +#define zig_msvc_atomics(ZigType, Type, suffix) \ + static inline bool zig_msvc_cmpxchg_##ZigType(Type volatile* obj, Type* expected, Type desired) { \ + Type comparand = *expected; \ + Type initial = _InterlockedCompareExchange##suffix(obj, desired, comparand); \ bool exchanged = initial == comparand; \ if (!exchanged) { \ *expected = initial; \ } \ return exchanged; \ } \ - static inline zig_##Type zig_msvc_atomicrmw_xchg_##Type(zig_##Type volatile* obj, zig_##Type value) { \ + static inline Type zig_msvc_atomicrmw_xchg_##ZigType(Type volatile* obj, Type value) { \ return _InterlockedExchange##suffix(obj, value); \ } \ - static inline zig_##Type zig_msvc_atomicrmw_add_##Type(zig_##Type volatile* obj, zig_##Type value) { \ + static inline Type zig_msvc_atomicrmw_add_##ZigType(Type volatile* obj, Type value) { \ return _InterlockedExchangeAdd##suffix(obj, value); \ } \ - static inline zig_##Type zig_msvc_atomicrmw_sub_##Type(zig_##Type volatile* obj, zig_##Type value) { \ + static inline Type zig_msvc_atomicrmw_sub_##ZigType(Type volatile* obj, Type value) { \ bool success = false; \ - zig_##Type new; \ - zig_##Type prev; \ + Type new; \ + Type prev; \ while (!success) { \ prev = *obj; \ new = prev - value; \ - success = zig_msvc_cmpxchg_##Type(obj, &prev, new); \ + success = zig_msvc_cmpxchg_##ZigType(obj, &prev, new); \ } \ return prev; \ } \ - static inline zig_##Type zig_msvc_atomicrmw_or_##Type(zig_##Type volatile* obj, zig_##Type value) { \ + static inline Type zig_msvc_atomicrmw_or_##ZigType(Type volatile* obj, Type value) { \ return _InterlockedOr##suffix(obj, value); \ } \ - static inline zig_##Type zig_msvc_atomicrmw_xor_##Type(zig_##Type volatile* obj, zig_##Type value) { \ + static inline Type zig_msvc_atomicrmw_xor_##ZigType(Type volatile* obj, Type value) { \ return _InterlockedXor##suffix(obj, value); \ } \ - static inline zig_##Type zig_msvc_atomicrmw_and_##Type(zig_##Type volatile* obj, zig_##Type value) { \ + static inline Type zig_msvc_atomicrmw_and_##ZigType(Type volatile* obj, Type value) { \ return _InterlockedAnd##suffix(obj, value); \ } \ - static inline zig_##Type zig_msvc_atomicrmw_nand_##Type(zig_##Type volatile* obj, zig_##Type value) { \ + static inline Type zig_msvc_atomicrmw_nand_##ZigType(Type volatile* obj, Type value) { \ bool success = false; \ - zig_##Type new; \ - zig_##Type prev; \ + Type new; \ + Type prev; \ while (!success) { \ prev = *obj; \ new = ~(prev & value); \ - success = zig_msvc_cmpxchg_##Type(obj, &prev, new); \ + success = zig_msvc_cmpxchg_##ZigType(obj, &prev, new); \ } \ return prev; \ } \ - static inline zig_##Type zig_msvc_atomicrmw_min_##Type(zig_##Type volatile* obj, zig_##Type value) { \ + static inline Type zig_msvc_atomicrmw_min_##ZigType(Type volatile* obj, Type value) { \ bool success = false; \ - zig_##Type new; \ - zig_##Type prev; \ + Type new; \ + Type prev; \ while (!success) { \ prev = *obj; \ new = value < prev ? value : prev; \ - success = zig_msvc_cmpxchg_##Type(obj, &prev, new); \ + success = zig_msvc_cmpxchg_##ZigType(obj, &prev, new); \ } \ return prev; \ } \ - static inline zig_##Type zig_msvc_atomicrmw_max_##Type(zig_##Type volatile* obj, zig_##Type value) { \ + static inline Type zig_msvc_atomicrmw_max_##ZigType(Type volatile* obj, Type value) { \ bool success = false; \ - zig_##Type new; \ - zig_##Type prev; \ + Type new; \ + Type prev; \ while (!success) { \ prev = *obj; \ new = value > prev ? value : prev; \ - success = zig_msvc_cmpxchg_##Type(obj, &prev, new); \ + success = zig_msvc_cmpxchg_##ZigType(obj, &prev, new); \ } \ return prev; \ } \ - static inline void zig_msvc_atomic_store_##Type(zig_##Type volatile* obj, zig_##Type value) { \ + static inline void zig_msvc_atomic_store_##ZigType(Type volatile* obj, Type value) { \ _InterlockedExchange##suffix(obj, value); \ } \ - static inline zig_##Type zig_msvc_atomic_load_##Type(zig_##Type volatile* obj) { \ + static inline Type zig_msvc_atomic_load_##ZigType(Type volatile* obj) { \ return _InterlockedOr##suffix(obj, 0); \ } -zig_msvc_atomics(u8, 8) -zig_msvc_atomics(i8, 8) -zig_msvc_atomics(u16, 16) -zig_msvc_atomics(i16, 16) -zig_msvc_atomics(u32, ) -zig_msvc_atomics(i32, ) +zig_msvc_atomics( u8, uint8_t, 8) +zig_msvc_atomics( i8, int8_t, 8) +zig_msvc_atomics(u16, uint16_t, 16) +zig_msvc_atomics(i16, int16_t, 16) +zig_msvc_atomics(u32, uint32_t, ) +zig_msvc_atomics(i32, int32_t, ) #if _M_X64 -zig_msvc_atomics(u64, 64) -zig_msvc_atomics(i64, 64) +zig_msvc_atomics(u64, uint64_t, 64) +zig_msvc_atomics(i64, int64_t, 64) #endif #define zig_msvc_flt_atomics(Type, ReprType, suffix) \ static inline bool zig_msvc_cmpxchg_##Type(zig_##Type volatile* obj, zig_##Type* expected, zig_##Type desired) { \ - zig_##ReprType comparand = *((zig_##ReprType*)expected); \ - zig_##ReprType initial = _InterlockedCompareExchange##suffix((zig_##ReprType volatile*)obj, *((zig_##ReprType*)&desired), comparand); \ - bool exchanged = initial == comparand; \ - if (!exchanged) { \ - *expected = *((zig_##Type*)&initial); \ - } \ - return exchanged; \ + ReprType exchange; \ + ReprType comparand; \ + ReprType initial; \ + bool success; \ + memcpy(&comparand, expected, sizeof(comparand)); \ + memcpy(&exchange, &desired, sizeof(exchange)); \ + initial = _InterlockedCompareExchange##suffix((ReprType volatile*)obj, exchange, comparand); \ + success = initial == comparand; \ + if (!success) memcpy(expected, &initial, sizeof(*expected)); \ + return success; \ } \ static inline zig_##Type zig_msvc_atomicrmw_xchg_##Type(zig_##Type volatile* obj, zig_##Type value) { \ - zig_##ReprType initial = _InterlockedExchange##suffix((zig_##ReprType volatile*)obj, *((zig_##ReprType*)&value)); \ - return *((zig_##Type*)&initial); \ + ReprType repr; \ + ReprType initial; \ + zig_##Type result; \ + memcpy(&repr, &value, sizeof(repr)); \ + initial = _InterlockedExchange##suffix((ReprType volatile*)obj, repr); \ + memcpy(&result, &initial, sizeof(result)); \ + return result; \ } \ static inline zig_##Type zig_msvc_atomicrmw_add_##Type(zig_##Type volatile* obj, zig_##Type value) { \ - bool success = false; \ - zig_##ReprType new; \ - zig_##Type prev; \ - while (!success) { \ - prev = *obj; \ - new = prev + value; \ - success = zig_msvc_cmpxchg_##Type(obj, &prev, *((zig_##ReprType*)&new)); \ - } \ - return prev; \ + ReprType repr; \ + zig_##Type expected; \ + zig_##Type desired; \ + repr = *(ReprType volatile*)obj; \ + memcpy(&expected, &repr, sizeof(expected)); \ + do { \ + desired = expected + value; \ + } while (!zig_msvc_cmpxchg_##Type(obj, &expected, desired)); \ + return expected; \ } \ static inline zig_##Type zig_msvc_atomicrmw_sub_##Type(zig_##Type volatile* obj, zig_##Type value) { \ - bool success = false; \ - zig_##ReprType new; \ - zig_##Type prev; \ - while (!success) { \ - prev = *obj; \ - new = prev - value; \ - success = zig_msvc_cmpxchg_##Type(obj, &prev, *((zig_##ReprType*)&new)); \ - } \ - return prev; \ + ReprType repr; \ + zig_##Type expected; \ + zig_##Type desired; \ + repr = *(ReprType volatile*)obj; \ + memcpy(&expected, &repr, sizeof(expected)); \ + do { \ + desired = expected - value; \ + } while (!zig_msvc_cmpxchg_##Type(obj, &expected, desired)); \ + return expected; \ } -zig_msvc_flt_atomics(f32, u32, ) +zig_msvc_flt_atomics(f32, uint32_t, ) #if _M_X64 -zig_msvc_flt_atomics(f64, u64, 64) +zig_msvc_flt_atomics(f64, uint64_t, 64) #endif #if _M_IX86 static inline void zig_msvc_atomic_barrier() { - zig_i32 barrier; + int32_t barrier; __asm { xchg barrier, eax } } -static inline void* zig_msvc_atomicrmw_xchg_p32(void** obj, zig_u32* arg) { +static inline void* zig_msvc_atomicrmw_xchg_p32(void** obj, void* arg) { return _InterlockedExchangePointer(obj, arg); } -static inline void zig_msvc_atomic_store_p32(void** obj, zig_u32* arg) { +static inline void zig_msvc_atomic_store_p32(void** obj, void* arg) { _InterlockedExchangePointer(obj, arg); } @@ -2360,11 +3317,11 @@ static inline bool zig_msvc_cmpxchg_p32(void** obj, void** expected, void* desir return exchanged; } #else /* _M_IX86 */ -static inline void* zig_msvc_atomicrmw_xchg_p64(void** obj, zig_u64* arg) { +static inline void* zig_msvc_atomicrmw_xchg_p64(void** obj, void* arg) { return _InterlockedExchangePointer(obj, arg); } -static inline void zig_msvc_atomic_store_p64(void** obj, zig_u64* arg) { +static inline void zig_msvc_atomic_store_p64(void** obj, void* arg) { _InterlockedExchangePointer(obj, arg); } @@ -2383,11 +3340,11 @@ static inline bool zig_msvc_cmpxchg_p64(void** obj, void** expected, void* desir } static inline bool zig_msvc_cmpxchg_u128(zig_u128 volatile* obj, zig_u128* expected, zig_u128 desired) { - return _InterlockedCompareExchange128((zig_i64 volatile*)obj, desired.hi, desired.lo, (zig_i64*)expected); + return _InterlockedCompareExchange128((int64_t volatile*)obj, desired.hi, desired.lo, (int64_t*)expected); } static inline bool zig_msvc_cmpxchg_i128(zig_i128 volatile* obj, zig_i128* expected, zig_i128 desired) { - return _InterlockedCompareExchange128((zig_i64 volatile*)obj, desired.hi, desired.lo, (zig_u64*)expected); + return _InterlockedCompareExchange128((int64_t volatile*)obj, desired.hi, desired.lo, (uint64_t*)expected); } #define zig_msvc_atomics_128xchg(Type) \ @@ -2429,7 +3386,7 @@ zig_msvc_atomics_128op(u128, max) #endif /* _MSC_VER && (_M_IX86 || _M_X64) */ -/* ========================= Special Case Intrinsics ========================= */ +/* ======================== Special Case Intrinsics ========================= */ #if (_MSC_VER && _M_X64) || defined(__x86_64__) @@ -2459,8 +3416,8 @@ static inline void* zig_x86_windows_teb(void) { #if (_MSC_VER && (_M_IX86 || _M_X64)) || defined(__i386__) || defined(__x86_64__) -static inline void zig_x86_cpuid(zig_u32 leaf_id, zig_u32 subid, zig_u32* eax, zig_u32* ebx, zig_u32* ecx, zig_u32* edx) { - zig_u32 cpu_info[4]; +static inline void zig_x86_cpuid(uint32_t leaf_id, uint32_t subid, uint32_t* eax, uint32_t* ebx, uint32_t* ecx, uint32_t* edx) { + uint32_t cpu_info[4]; #if _MSC_VER __cpuidex(cpu_info, leaf_id, subid); #else @@ -2472,12 +3429,12 @@ static inline void zig_x86_cpuid(zig_u32 leaf_id, zig_u32 subid, zig_u32* eax, z *edx = cpu_info[3]; } -static inline zig_u32 zig_x86_get_xcr0(void) { +static inline uint32_t zig_x86_get_xcr0(void) { #if _MSC_VER - return (zig_u32)_xgetbv(0); + return (uint32_t)_xgetbv(0); #else - zig_u32 eax; - zig_u32 edx; + uint32_t eax; + uint32_t edx; __asm__("xgetbv" : "=a"(eax), "=d"(edx) : "c"(0)); return eax; #endif diff --git a/stage1/zig1.wasm b/stage1/zig1.wasm index d7bf519b41a74966a3bc46b133fd9b34aa200f75..fa2b2efa03b4ed138a7f8c52e72648d828f2116a 100644 GIT binary patch delta 933731 zcmZquJBji9oXL#!42i|Lj0_A6jLTWW5?Jd&fFXgYzMdh0xgJEaz(pAA6TlqCdax8@ zeF9@WV|@Y}NC8MCJA_fs025-YhZ+c0%8}y@)1bJ!2inO2)bb z*7~|SkPKrjV{HOs4Py<&syYx=$1rh^90@KV%i>=143`FOE-7Enxuj zCyOu(Kx8J{Fk3M4fYeOxVdi3F2eZ~O3qxgZGV_92vXfa@qQNZj$>l7(j9g%Kb6CU} z)xfM%EZmGTVAfj}98MZczAeNn3&mEnV6XvSy>r**w|Q@m>5}kI9S+t7#SHEnc0|_IG7oQ*qFJQ zm{|pwSlGl^n3))Pn7LVaKokozBM&1JI~yw#BNGc7J1a92BO4r}HcMtk z7G@?^Hby2!CT2zk1~x`EW=0kcW_C7aMkZz^9u^)39%e=cHU>s^PG$xsHbyoUCT12! zRyJlvW;RAnP7Zb!b|$dAjFS(%s^nVA_`7#ZssnZO;28QAR5!CI&V(HYP?!1{M}JCJqh;CT0P4c6LT~4n`IhL3Tz)Mm9DUMiw@Z z(^!}p*}2(SSh(0&xVTx^nOGT_m>3v%cvx828QFMP*jag)8R{8%*qJz)SlAd@SeTgD zLD9g@&A`db!NkJC19B@91TcdsMzB>Nf?1rIkx871kx__=kxPw@g@uiUg@J{Ik&%s& zo0*%Lm7AHBk%@dJ(g(5}gxNsGGlKMjT*J=5!otJ=5@!MvOrTW529g2s zp+c<8EP^ad;LzvbU|{E9V`OFonaRS<&CSgy$XqYTEXd5xC@8@KN*SzdTnwO?VP$7w zW@2VyW@lw(W@Bbz;{iFHg@=Wije~=Mk&%UwiG_=eor{H$jfstom5~{gcv;z**zK5@ znVI-yLCKVfm5GfN6mzU>Y@me82#QiJ9!5bXW+pZ!W_Bh}I$&n3XOUxMWM&a!1ceL} z6AK$R6Udh=tW3;|ObooNpd`c&_5w2_I|m07ix4|IyBG&2ClebtgFT}V69We)DDoK@ z8JJjD1Q}Tw8QB?`+1VLE4&`9rVBlb8Vq#-t5o7@cE)z2c11lpNGYcC#J3B}fdp$cN z6AL>N6FUPlJ0l|-Hw!Bx6FUndFAF1!023o9-7u=NFtf1mvakt&;+O>-(@c!)ER4*I z>>8R9jC_1-Z0f9BtlaD@>_QA|EFfc9xWF;U0S2rfyBV1o+1Nk{nuQINGMJcH7(wC4 zSkK7F!wzDDQVJUr4-*?38ynaXMkY`qU}F_xWEKPkrH~L48zVa-3pXg`uyKOIo)MHK zL7AO_nT?T&je&ugg@Kuyor!^ohl7EEfrF8WgN>7ki`Y9|3@j`x z46F>itn6&8tPJv6Ow5k#46GbXEDTH>3@l8{ObjfX>}(9Iyu8c;tc(Io%xnxCTpS!s z?A#n23|#CSP7EAu%;3ZWavB2+)Pot~Vq#*fybP?IoSdA@?3@am3=E*4W9DMzX{zU}t7xWZ-9FX5i%q83j_zz`()44C1jf zg8>5rgCsix11mEt2Lmg!qBFCk3j;F)D+8z~tLI>0=jG>TW#eFAVq{=oW?*LEWM*b! z;9zH9;9zFvVPRxr;9%n5=4NJPU}j}wW@F=IWMpFCXXRjGWa40E;@|+0?CczzoUH83 zte_%|0h9um*_c74I6Dg?8#^N?Z?Utmv#_x+u`#mpU43g}e3~X#HtZZyd>?}-dj3BqNf$|+2BLkDGA}F{RAmPuzz`(@K z%^=Cb&&tdS4n9yhDZtIl%)}+Y$<4vR&CJZkz>Ek8MrIZkCKet6E;c4M9%d#61~vu; z23AfM21X7hCN?H+enzm%K#|DA%E-XP3W^9eW+r9^4hB$d#O%hz%+Acp%v8_7#>UJH zDmGZTSeaRwdHI=`m?YVlm<5?Zl@7gOVTvGbbYh3mYo~12ZcV3#j~I zVPRzg_#0g5r91M&CER4(y0t^DI zY-|kNEDRhhpqgHmgVT$ViHn1sgB^tG75P~}1qvfGCohu@3j;Gh2PE$?FflVSGI4RS zGBJy@aWf0Cad2@lF)%YQurPrXFmo_)urV-jGcz!;u!Blj4h9Z(4rUG}1_l-e4kk86 z22jBYDtVb$SvWWtSXfy>$y1jZ5)+KvER3Ma8dUzVGqQrRFEbl6$PK)lyqutX%?z&D zSee+FnVFbCwG|r^3mY3FsMuv>WaD6B=VfDH1{K&WoUGi;Y)ovRgu%|t0-QnGIOvnb1-wWaxyS6GP1C+vU0K2vvPq-HWn5ZB}n37W8ne2 z5u|_x1e94A^%$k}7+F+!c!X6|`Fwo&G&EKC{PcDG_4(u^a&U4o2yino^YSx`aWHVQ%IdIkvvM;taWgQ1vKSkSJSb~1 zg9=DC1}$*W#mT@dKY6`?jqYsLxvcY88-yE$n}m6oUP^GyInuzmPvjII6IVTBoic;t z2gYnA1{DSc2FHfon07!t*ktfTM4Z`Bc za=dk5a)78P)8RFf(?wG-G~N-VpKoXqa8v+^ICgBDJXt(Y>tJiAfFrwrf#d)G%!RxR z3QUdyC5{56j?5)W%#J%wPZh}0U}8{UcHDD%GN*(R)9Kd9x)QoflSQUauwhkV+%dUc zLW*(sk)S@L^=+nyf6P%s79tyOc8H!O7WDCQLI#CNGp) zz{ADBt-#_akfp-FtvTH>T3B@YDQPzD$+^;|{BTp41&)ev>rZEt;*^;F|2i|@DcsYa*n(qW6S1w@&_3imrYJk zT*(9XkK>NhlLeIAnJ#oro@lSn^rLgyQbr@Df1Q&*`b$l|p(M!E)HC_DlBw~L56uFO zOaeNNjD@Z&4hpOaY>sbkHwiehL4V_E>B7jS1e&Yw9sSXo8NkwJk)gJ}b| z5}4k?t;CqE#H7HWz~ni3qOy%hV^0UnSY8&esqb4RKU7v>yfm3jWgk>`2MMX=rHFvVEnikZw z6+B8zAUCZ6o5rrdq`>YuxlrvQ+rf=6&7zYXOeEPJg7_c zxnh%ltD7-7GE6qq5N3t=#am;7GQ_JdppJb5HXhpA=G^3o zhSK#tySoG+);6#xF@ikY!lK0P_~lLuOam{Yo*c&w=O!;OZe{zwsY}4o1e9LDQO>k&^W=}Y zQj<$f#2J@Q?lm!DoId%0i3!uwr<1>#q%-X}JvrXgo$Y@|r+}j}NRu|xQb>MT4azSv zGE9(+@oq_rfFlpsm$1BY_U2?)vpH-_rlKaXBOt!YWJ5KX$>!$nAPXAI-PsztIt3hc zAQr6e0%yprU6Vih%jiKZSn;|U7V6A8D)`dIb(d4gIMobrOPu8?nfEw1p z2eG(^55wXXKCs2Jtzj1bfLiN+8iX) zCi~dh2yeUDBH+ji4tQQQa0%8qdA6;&&<$w%b7XPn1(p9-&P{$|Tf)}3rAxrk4ivYL z6nAI~IK{27;}`z@u2H~I5@M1O*rcY(r|fD#slvnFo#}fkxP+eH21*c&OQ#p|GD=St za1hnJd=MV>yhaKz>yQI0~UWbA8u}PN@fJ5oafF8utEuSqTk?9Z=1CkTg%e=V>Rp_*}DqBL_GfV6J|3VY8uE2Q1Uyfa<)1q!ViG z6@FMo*7$s=Nx+c{#h5vuEXIu@bos(&OP>x#kRuQJ!W?jG1u(mSBidyu7dgKR{OA8Un`wY)0e#P;;ipXs&d;zffA0Sas$*0O#$ZO=t zsA#WHtq75N@S;V)kq_CS%a2WV2sMWW_6=zG+(8PT$rD3eJWoAo5^xlR>6^l+s0X&G z_h>7^I7dds0I=w?w}>_a$ogP~MwnFFWVtY@r0-`N5ZV-Y6qOY^7!}>XdS8R`E3{Dq z3IRy?e*}p_G88xj3>0z|Au_k7x4|1hAoT`dQyxs76(%cj@L>}|&XGaU87z1F;pFpS zGWC1sG{Gt>aL}oOCC`FHVTGmwkD{nTz9K|s?$c&iNd#6e3Rb@wBnpc#r1_8yV zj0%N{5a}Q5kb=#r0Vd6=z{c#@wSIDLxRgZir52PZ6a}#vNU_PO zCLabN ziip?9kwGyU?7I1r1>$8Tw*GEJDup-|*}-zheouCdS76$AbaHyUirHsSPm&#)?Hw5u z{lIG4@4)LNaBh@UuvIixaD~dgUNZS$yomI_B~3_0VveG$0*|5;So`J4AL2!2p6+Ue z#~rAU@K?x&%3pXlSusIG<_W0PC5EuuM4?{MA8f#uqmu&@M1;>CZAMD}py>EIxgxVmQtq{X|laq>rhSw0>` zONAswi1%O3n{1FQ;kou7Qc`hbR18t*W>ky>2gkgpNEM1BqoSQc4@e#qqW#EHV8y2= zPfS)}+&g(&vaWF3{02}CHFJb#nzfU^C95z#nJk@R2d%C<1R+g>9zjIY09swQ2!g8X z$*WU%>i2*WITs{cj1CP*vt_`pmnT-OD@Z`p* z7zkF|{ErlyI!K9Xh+8;auI+M*(g_)jgo$Q?|DYI`IQhD#lsHmvW%BUCwHfiSM zhEyrxi4&Uy9Hn7y1-Y?p^4e4t#_N+WrP@I&;Tce0&q4C_WUVwiSjP?&Wbj7X!^yR2 zilFBCf;3oLcn4Jf9%TKu(lVjOuYgLgL6(k9w-bf7)sc-q^>FfrbVX28dqJA0>IFfx zzSj+?QFoAy;?3{@HSVJ`VEw%lP*rD;R6%3$2q+f8y;fL59ohDilb>g}LtXs?YRDfX zLneDbE71m8CMRc= zulQZ)wK)r*9d9dEW z@4T~&T$4}cKZWZ00oC;fS=Whz7N)jMll=;f7C`=s=+aWT0?Q@eHVgW{5z%F-HW+ z8+j#}jJ=a@l)PrTbae93QY)q#b0;&FO=5euvP-~GXYzwnqLbH^`7`ZqoczDci0L_~ zG?SX_Ti(nz3p8Gc2xG>5lN$@ACo5FwF|C<9IiSLwZOt@LB!ErWRPl%D+U&_2D)pIO z%%1$P(unEj?CB4<7^NobS0yvQncg{hW2GM3zv-OA`B7=@h}9TgN9v)8sQS7LPJP+)ZA@ZKC;SHi@!;PT{? z4W`hL`Tz~7FUTP!(U{2iXL40zE>zJ3QAlu35k(44#wHu43700@H>E>W9e}Djf~@L5 zQ#Mrk0#y16vb1aS8c@>u*bGZr+%5N@S#kkX{SqYg&@4Gebh5(>{mIs?!Hh39_qT>I zL9I9e^~@P$&-`y+!F1x{<;PCv$$$+tRXG#MPVA%lxw zt|F2pXmC**k}RQviz}{8USKRU*}Cf@S!5PSWIM9R$$eb{`V0)#j0e0FSR5Dbhw^rScuQft7oG|% zj$2{886e(){apg}nhXr)4BuT8SR8lm?-Bs_-*>hs}9pk%zF52ZznhL&#yX7sdmJ%~==^95y!&bqU0Q zQUNG%SR5}Nf&>*(*gQtD2BsMiHm?q0gw6XSNUnc7dD?`1^}msYz+Sw36iE>{`mZC4 zfW7z}S>)#tsHebQym%DK14sW=7!T~lr!XGaiyw}53B)73`0^-*7k{Hz1JjJ~V$(4U zFSegRa((NG$wrf02HKBES(_$)gogJIcSk)%4jyKB)`mqbQr4zx+`^)^p2d+4{zepO zFn@!~uU)4Y>a!dN#NRM)`-4U_;6*tV(zL@={rbts=^UJIUn7fv^X+?N5pcf!k1R6r z1T^J<^X*F*51en`!g%0(`xnLo=i82xDEYSWBu2iSd=e=WPCN;<29a-Poy5qu^G_pb znR|M&@3bmtgP=nU(s1b!Lu$A@oaSJPG{wo{0Gbdw-6f#O%wW#EBbbMgTfs$v!*Ri7 zco!Y4WWu@0VbeqS8*W1u2`F-d8l5*c@0@PISic?F1>g|vKZE2ZaEMMu76FIoVq}r6 zr=ea2hiLB^C=VQ>6Jb1Xh%SWjz#+Qk3`&SDJ%bUV+fl56X-0(TzB3phdiWfY>kpor ze12xF$i3!H0Y^rGbnt{4FKCg(pIws^W~qxj0!c6-Bp4k3?3z4#mLAjd=E7CCzk8YAHN zS$ZDI1IN!w7!MpjJ7GL<{2V!tQW@_(j}brTQLKS!M#Rsx^BD1S`y!I-Z%z)M=Ou|A zlwW#V1RPmFA-ZdxIw(W~=1WcfJ5NXfUE%HHpxFw?Kf9)YMotgdPc~d64^gj!p>__) zP-d9g3kN12oTmU$D>eDXd`z?NfQJ29Vd{4rL{{&%KnTOpw|0V-B!C=UxB%|xEm#!( zKh+HN-F<{YrG=O#Hk@n~aAX9T=(`YZ;sh)TZ>$H6j)4^JMkxHZ5Ytb`KyHMG!3>lz z$XSG`{`(@(m^Um8Ho(;%ScDnApVvVB4~~lq@VL-joXFI0-TBS;7B}%4 zHnui^rf72^SyIWdXL-ATj*KNBzfIg2#`OKn@l88uhzn)}ctz8nU6UI(3A23!MfUH>vo;wr{n<77>?SpiB_PGD0=bjt zY_yzgvNW zGC7`~Jael&%i1F^^``aX#x^7JVxy@3#;c^pb z0I~qmibINvtG%0jx9?=udoX_jXyKR=uOmm9qO9X1D3=e!Rd##=KNxIoWNGgY=&!#EJq2DFr@7ZUtqq{Ld+q7w!?4 zTye4)lvu&DmJCt~N{*oAb6k!`?@zwCr;?FtviIJ@&|3R|BBa(nqKH&$YwTOV(Q~#{ zKvB~1_v8!v)ERpx|JWxdwDCGfh?~iQ$w5I%K?@Wzjr&C=+w2!+de=NTaK8vB?KbWg zVFS4xl(6?pfJA2R*M?el18UhFWXm4xUjZG3n4tu5%N!+yTcD#5Q{zDeEaYb zM#im^7aY}RzT40_`O;A@rp4zb%N^5Tygu3eSf&Wt0s(dfUdNl0j~p}R-?MD8fTONJ zwE{@!&#uW2&&V^qSvFbyc#RlT3Yw%DU@CSVS7&;&Z1bbz+Kfyunp<>xdLsWrvC;gb3S5E$S(u3{t@-6|#AJZ4IF^Wyjtk7e-IjvK`@gju3=9C`O zxoMN{oRVYgoBaD!IMcf?lS5AHfxXsx+K*|)>&aJ7YcQUf{O|M?{&yfBs0manFe&hY z(#gX!@*LBbPX;w=swZ!_AUWCPxboyT7p%mf3ZedCf~lW!R-I}3^2K}4GJ+y;|2ake zZ;%XIt-!0m2{Q5hIWhiUFfl#_ZjhMB`5dtF^RkoYpXU;TYJr-q4AZ&)yt)`vCtM0@ zz@PI5Oik}5>s<(7nz?*(!v!^_Rm&&aU#|o4G%lKgc<~oY*?ye?O{GsRydg6A-9=0O zH6V}i3RElbC@_JX0gA3YAbpCHD=x)~L8YN~slrsfzNF5yXZd82%k%j6fehe8HQ*G; zfI2a#6gJJLmQS94rHbwAThKx|uw#s`S~8tkJ~{8I8q;O4cD6GhlUXL~Tr>oU#9vI9 zy!M(XTgzEch5&21bIp?J>hj6r*VULFfVF^l`>&gVc<~p-CiC2gXZm<%a_)^l{zo9Q zSW#X13am#ADuvCJua-}azbLl&;7vw$&{7*SCWgtgZplu5dP~|7dHw8EfhF01gb?2D1&NAq!nl< zpc(fJa>o68I|f<;-GEBpL6U}+Kv$Hf>vJ*cO&-iaaR=5gam_3hba%yXFC`nBD<(>KaHT$fU>xDw`iGOS}a6mq`H0ztFBx{bO|r%)k&= z&;)sG-(zq!bmOr+sBrlCSO@CR6HteqL3XJ96FbI@lk1+qDuNqOMR$-D9e6U4@y6t! zr=9$pCV^VA0@Vr%3c?_}UOkm%+%cKsS&JA{2AU-pU<$50S7+QY`P4ICowuOY88cif zxE1>0Btw=!jRK1Tj~P>gw*reJxPkZi;biCM)nZT$SWIO4{BZK$=dd}&32KlCn4*Rh z0p>3vpx#~pm0p4@z4C<<+rtA50*+fYe|!-RTl#iF9b(cMb%aUK>~KVV`d>Rn{mHXl zZ3ito3x4g+w!E!Vz!5xyq|I~*vcU2NWPzph8%NYCC;3e-)76&gvh0ldZ0|w&R~kG& z)7uKJvI5_Ru|1g8CE&=8=#RddHTmw_EKmgM{*+=~|Dk!Z;KvdW_rOneru82tv%ZI= z-VP0jqkA+Ej-KrGz8U1q^Y1l5)UWsMY*TtV1sttG7HBhF>Y28b(TM3r&*XrhV8gS& zy0bTc3_l2-^VDWK(hc^+scx_*tiHiaXIwD3@mm?xV8#WL6~DvgX*;x`UeHGJLeqD! zE$lxu1rZB2@kbL zEt6mV^oJ(C4jrhKI!IOq{ptk8%H3b?>}x@WAA$PlOE1{)U%lW|vFx`Z)3YU$PySW~ zDgN-=o$X;iEWtqrJ9;21*xUbzfmF=@u^jm!88R@Ly=oozd4 zsW^B!u{P5W$WrltU6UXFke}@PZwAy;j6IW?{?~vw4gb{`dnPacUjy~h2VIDlzUU&n zG}V@I5z~&-Qy((Av!Cee6mUEMbwfAA4HNq(Grp0ZzMhfs4$Kris402~Q>HIvVmu73 znI`B%1og+Q>p25*cn?v z_9$||?Xl)y^kHi0ncm64sLA+y`eqKsH0X4)m;#4m$DQe_oQ%(*4*3Cf$RA{fsBtm2 zFwLJ0?v)<|Px5B6tp-i(s)4c=B>JyR2hHv-gBml#5aORXhDiRg=V1h;?&(K(7~Pp} zwN3tC2A-j3YJ;p;>zUrj&Zxk8z%a|PW%@K;#zl;;r(5zdO3Qsm9s~MdpXCUhfBbBp z<=A{_dJ!L^vfRsykYOr@oAwGUj$dKCryySArRkgb80|&+FCmR3bsu2Ja-4l>IvYQu zwm|PCsP4ZY^)oL`cjRYGX8bjMDL>;4#*NeK1Q-jL{%)TBL4Yw;?KQG%{vTk-azt@o z_dy1*tEvSVtr4#J2pW2YyXr59*LZ3A6+uRQR97tq>7IRQy0{QyB*Ilw9Tiv{XI`4# zAjBBQ^mFs{2SVuXcn;Es;*QTCk>*R&vxSk}aT8<`+#OFryv9q@j|ekbqq?K}5F~Vj zMHmwi?)VE*Kl9S`77@lcrjMJaKM_H9$9a%G6nESPi8NoDo-c~*j)P9fv2+&1YrHi5 zgebZ@K7(|F!b1$%9Zx~(XI`4#A;uWTG;z!H7h>q{*nSv2UJipqnlDW+7H5n>#LG%& zWOr-@@ft5pe=E)?iy9ucLAqyOnl2~77>5Xtvmo^|FHP@}V2op$w`KYp33PYNKZ5R# z)gY1POVi6Gk=-#7H1h$Em$@Kb=y@boCe{(tK(9Zdt}ig!@l|tb)7$Du@Sn|3eV3{TKth{CE#C z0>%BmK_as+O;46XcK=(D#+jFe{{me_#vsAG-bl#TfylQ9;^-)82=ycHP6cmRp z28lFZn%=2~?9fioQWdyEr-FEmm!|(xLwD$IknY)+rfaJ+Mk7k2wIKB~FHN7O&e(^M zOglk4HLyf~sRp`ZpMwlXaqMT1Nb{xXn=}}M5n1gf$U3-VpMrSsto9Man+{s~11&24 zgN#7QYTY2&*_Wn=Xd?UJFG%CeOVjsjGPW=s-ZkA-i%}mnD{TkqLvh|=kVx~T>9e#L zgPq|e*-FrwA-MClf_QM}9R%^tgS4SI={87Y_ND0t+Kj=7U_A>`JoD1@$=Zx9Ol`ZS zOX{FIsU5UL2*pX0K_bnUrswG(NAFLNNpL5%f)+eAUYdSi2R(XMgLKcnG+jU!IcVmB z)X%&$y;_&?B;(!bu6m3{jMJxIRAiKzK1q*JOa-M~a@-cJS#sGH+$uSvhwPx8w#dcJ zQ4p{3(sWyWMrmP`fO%~TZYpK#BfIA=Ncqf5+pp;}axycW-9G)H5u>4C&yFt8pa{Hp zzc+oIDWfXWgdK>|J=qvN$xR1Mq@V=gVvtDlrRfWdk=@$~>XgF+a4Lw`cxk$z33>qT z2I-!CX}XgMvV+%x)X%&$eXR-O9;V4Vre~Wn8VO#*?%rES?tN#9?%w&J)nq8{T@4ax zzBFCi4B5RCeUZa=E{NB7Y5E*9boU+x>7IRQ`dKq%_ihELpLuDzg*oFi!C9bc(;QK4 zuAP40oKcnW3M2u@TcA5}`vvq0|1e0T`O@?l3uGs*3_y0`RuHf8()6K-K+0QU~o* zeK7)$_Dz?vW%OcN*)cuMma&uR+KcHQZ5dxeI~XU7p|%(!+49AXaSK$w!2}}TVuB=p z$Q~{~11diUS$={8<29)M8Bp`*Aeq0t!IAMIH)>BT!k5vV>1^xtg}#hNOkcqxDBIur zGL~?1v`lLeP~>oInVucasKN{4IkE{P^6+yzwoIQB&ZxoIGW}>cV>r{FUDM?v7&RrJ zgW~B*T;TakUIqnK1u4fryOzgCFfuXCoi#l%it#+tk=xUaq8Y82j^3W07tNT=v|{7* z%h8OJ7#p{z#V{5!GrgP&QYSV2b3EfQm{)8eK{3Y`5fs}mCor-zLKSpC74#q}nEoY^ z@f1}4hYiGlKQ;&hrr$_ntY(}#JtUcNKjY--A}NdsOm|whm!&ZBFiCVbLPtg!VP4@; z0L>#$pOwa_$=E;rNE%}b5OV1?+2zc-hx_n0&3M6WUE{=7|R(SP2ZaVD)A;a z779;i&SaECjR)mSMt8Oip!FI0)4^vaK-Ox22ezI!PrsPSsBXuo0-AdQ?}=7o&}0%Y zX9h18&Qf3i9fiT5!NlPxkt1+`5yaSnW(n*E%?M7n%3}0nY?DWC5 zw04%o!Leoc6wp~AiYyAuprbVemU2u_$Y%@$hk*iXwi2rXivp{sv?42LvX9BZv1j*m ze{+7N>2LEHrMP<`#wf4~ESf&uoL`m?F5t*mCa`GwLvw!N=>Y|ds)9%oSr9oM3w~J? z1J)HV)-mpyu3N}x&e$|PwUDua@z?Zcg^cD*cXn)-FJiQ0tiJ}@JjW!^&IHConrf(FOK)+oL8VJHbMCuY%Tbz*K^T=71)$ z*ieKX>;=sY!G)L{8QIO4Tojlbe@(wr!Wh8R{%E>hDPsX+$MpH7jN(!qObSf;j3K;C z+zPCq`c)qyB%~FYL2JeyOs_0sbYW};`u#tQ?9=~LFg7rro?ctY z_@1$KdTtft1jd`w|5Y(6Gqz4ws%Fe*yg7YRHDfB%gge`RRx{3HVLY^bMg!v-R_+aF zCJQ+3IWt*6;LvpcHpVE%wbNI%F`6;1o&KZ=I!iBO9`9OV1t!M}Oj!zS3Jk{6ANDXRF|M7ysFyL1aqV=$ zK1OrKwbMQOz`PxOU|r84ys&<-Twgy}?k|LAIswdEGJ(;7aqaY{6BzS2z~)b2$(sIl z3Zv5W3B8PhjBBU&PXw#`HxaDPeiE3sZW5UH9>R;8%;?FucKV9RU^($AV7Z7XVBYa5 zU^&65U|z#iFz*zEXE}{Ah;i-oo@tDr5Ir-EQJwMB^e@vGL->EpoeWuX2s#_-!`$iN z(-}W7?wsy1gVB(2?ewY{jFA{h9?f9XVLUROe=Rk+HK-GsVWt_msHT~&Q#)XV6)7zIZ?q<9=-FZ1)#O`QUc>eIW=3F|TK0OfDt>F3S~v&lO$3M^)1;Fg4)G-dC& zt}+N(J=8?af50D$HPn71r(WhEV&)I6qz00{A&U6EFio? z7r=7n+>V_GT0!Dw+>ZBGHGx>B+>We@%#IsxgKXe2f$-)-Og82Q)mDzDS2TcB8bNqn zH^3?lx$D8!zB>Zu8gPROTgOM|z!vF4cpD&2(t~K+^c<{G7sA`Nsu5(U4mYTVbL_YY zmeYptrY;7XqQ&jVp~&p`{4Q8d6C$_gGgwZ8+p(Tqk=b!7#6RlXj+~0jj=LU$eXR!J zOkkV>`%%dLB^-7qr??k%>nHV#Mr)VD}0`{CV#@m?s3`HA9RM zgy@?EiAe#7zII4t@k8WRL*)1%a{UlFUWh4gHiJ`6JrBffXCbl04N2=6<@1{Q8dK1F87RgmamR)KJ5 zLE?vr8&roo-h>#+2r;zn==24f7^M^2wsr|PDmXp{o$Vv-cytS>a1i0<24_e|7J+FZ z+%p)NnChA8nCh4u6qp>jvmMJJTR4S4C&QIE?zlApvO1YLTM1O!33-DLt71`L1|3%Q z$F4EGc?+W^pCkAboNOgF&=z3N>ASWt`Y^5H zm@e4Qt_GqFwlcba=np#?l|l3#by3;rJGL^GF>P2p-C!G|bp4gZlLZ`AKzqA*xVROV z9XYZbk%Ty*LO&3yIG{pvmcTW#Lxm0?gxH`$PY^<^P@#^caE&b7ju0!?AjFuV$}S*; zn4m&m5JHSlp&84jb8lxR~)qP)C4K%8^k4v?`Lx zaR=y-7AApqbnzb0*%|DZ;=4iWSuw@?Z$R3c=;myC)&N>SXoz7!+b;0=0T|*R?t%~c zYoG4BmrPvm9b{ zWBj(=`w*i76XV9|IY$^X881!0c7(A8(&k|Ww|Q6{ITTnyZJyMljMJDHUrhgVobd#x zk#p<>;~GZBOVf)_G3qhC*uLl#qZT9M<>_ZnGpaFOp8n-DV=Ci|=}~7G|A}1zt?A;( za{RJ?f&l!i$~V*V&N6y3UYWk*ETgqJOv#palMzbRyqnH-j?shh>h$1qjP{IIr%yh| zxQ+4Rbg%P_8BDNjeCIr)tRQMOmR4k)zIO|w#&n|#j1o+1c%}<(Wt3oJ1@%SOOwYQ& zXbcitpe`!G%qp;E`mPI%s~EYar(R?{sdN~0pb4|%UJ#|@*w@}EAaF^9yM>LBsos%C zk;!p&>-2z2j5F&0Eo%}`Waa_agG`QRKqVwIk0z*cV{+^QRm#jf8laMu$?;f2D@dg} zgg509n5V|=$gIfZIBRtyNKO@0n=&~bg{V{k6_8AhZ`U+{P;fQK1U{)yk(oypVrUn{6d8!2Yj=Y!1XqPj zj^EaSdEly$$??G-utOz5wG5ME$G!SykZU9$#`LcTD;0-G-h%j048r>iY9TO#szWwK zCdcjzVBd>C^zEGm_LDG#H|K5}$V?$fSZszkMi633AH<>H>X6BC-s;ABkWzj~aL<1T zww@0X7T5QKt>A@(#lII|hk~m^CdY~Q!8~q=%4Uc@E{GN9Afd(y;Z1?);{dhMm>e&i z0PAB1wN{uMx8H6S2vTI`VS^~01c`faeaPha1L9v6i1ph)fqlpfv2Zu2DZ|Xe1PN}? zrUgZ2(9(5QMJC55=cb!oWt3ywKfQ1{pXp=)MXBj)E-`XWUwf5Nr+z6YFqn9hLH=cS zY=Bh2N}zyZcAO5W|Di3S-5|#>@hCu4-s%Nc1z5(fnrv*QzxCMF&+ zh@K4)Yel&o?}4HQ-Wa-Z3T%Qf#G?J*z%4Q%h@L}`W{e=j8}*mhgIhHM5T#ckmO)!X zO_1g(IQE$xH-eHP6Av%MS@Y+C4dvl>Y`qG$3>@>!j@yobdC-Q?!ujA(f;NQaF9hr1 z;CAFyWOh8Ys98WUf(g_T;sG^j=YcgsTS6;A35W@FE-t89zX8Hy;dWfz18zSvLmaX8 zDcE=>ZpSyT!MYhCMn78FJbmFNM&aoPuQS>+PMFSkgVCFD!gSvojB*CxokENPHQ?@_ z3In$QsQ(IG;>jrRiI1Bf)IMU+U}8~V%n~@oI9-0VhzZ0Dsp)yEMYKTdSXMcCB!dh< zhvT9ew0e5bO~zx4-P0v+F)A_kOkePl(VvAOOQ3gp|1HKuEwE;0#|KPVO6&qPj^KeL z&?t@qle8n4W|LN818;zuE_ItRUka+egE>ne-x1t96HtK4FTTwf2^w*I|B+FW`OWj@ z>D-?f^FRW%4;fXM-aMba;SS?+#^2K&UNY)1_D*lT%V@;d2Xc=e>kP&$f&S^-_ZX8I ze^1Z92bQ06k5N$@YFz_!mJ+8xjRLCzXe-tf0R<3;S%J%pse@U8*|CE;%W={47xx$y z8COo{y3Z)fxOTe6ea1YPYfmu2T?;x#3uMOh4fh#U7#B^ydY{plapiQ*2aLvyYp2^j zV6;KFaQOp9MI)#SZZKsjaUi<@#9>xoH)DFhqyP>-$CuDmpVJj?Gm1?Yc*qzGv%dim zT5$Wpf%5X<^u-SugBd4GU+|LAk97rOmcYd6UXK_TBOD?97!gb-z`?ZeF+7;+LCb3u zK`l#Ar}_hv0w^>-Fl8xmnlW8qQeanLgDebY1`P!%aUr`O#9>z8G-GODc4Slp`JsV1 z%W(o|)R0jCwBDUdpas+j<3tMMDKJ?M1&HkQ4Nn+(8D~u2_k>ZFanAHRPZ-l-em;N* zbI>71;4qJU%BaFPVS3Y3Mq|b))3<@B8Pi{Zs5#RMpEHV1H-5$_itsv#;ol5$9h*Qk z-tdRJ#pgMr3S%?K+7MXq-v9@{6b2D@GNjf2XFuf5iwotpuC| z8kj&ypy4%RAV>u$D=}R=G5y_Z#(WSz{tcrFh~N5#F%-lH%Rk;Zo$C`sKI1K;3e)4A z)BD~c$zNMP?HxqA@Eu6|`svf(A*7i*k0Eku)q6%2#?I;U-!sl;oHX6~1EU-)2o^Ab zf?(|jMr(xc#XiD(@5j1<36z89d_;2hpEYoIRexesVfwRX`od4h7T;)|&iD>eM6`Tn zRAK78F@4o%#%|Cwhxr#qZMKFxT>=7!r)PX&lw||$!&GFNuBgT%KHcYq0LS!IUl@hz zL7OfWnRqxstqW#H(6&lNCLU=27PM(n5nQT+23SB_A{CiH^*LzIBjluWMnz^vP@hDR ziH8MZ0%-T5A`=fYga_K9sK^AV%b6X)bvYBHE@yTGclxI<-oz-l-RCP~5o7&o(DGJh z$E6_Z1cT$P9!RTF6jZ7!Wa%<6uz*&_%VY`MVC3dr!wEa4)o8top$jBOKR+UNMCL_qe3?*i;fsCNBO6C=yv9%S9Sqkh5Oadkf zYzoY9d;Wk1`62eOm@!o-fUPS5ImL{r0Ay#1A`6I0Py`*D)xZd`>O(z4mLtrr4n`Eq zCV(uPzzCWEbF=^nUtk2Ca1RM4HcjRejP>Ts1`06K zUV!w!V9ZkBR^USN@duFb2aJID0TTWJiww}&2a@IsOkZ^NrOp6 zkr(6=1w}puHb;pp1rCUBB$VnoSR53X6*v{ZE`^>*%*f5{$e_r<3_2EzIZF{V(kcLQ zJv^j(w zsL1KapvcS&@&kuIC}x-yIJ1=a9e?bf06x%%fkA=aktxgZ*Zv6t&@+OV9htMhjcaZN zUe|i0)O`cw5d)A%Z!l&la4LZ2hZ(a4ZZm=-TTuYyCkv1rYzhMA%vilcq;J5H1v2D8 zJ;+W_V&PQaL5gyU3SPkjz1Vd<+CGWwxfNaBbz_yt_B%_`;3l^g)9z^tcs$J&v#5ez{%v#wD#z9 zK`tg`_8p+P=r@<9TXQiPFus|d!^Pw-iRN;s+bVbjrZ;jjiPYa^$xdG<#Du>4lL2ZOM8N?*CN8u+pbSuprf=nC^3#KQ735qcDX>>r%$N?q z9oN*>0NVdut-!4y>iB2ZbVEL-0uGp->5jZi64T!ZF>#7P)xjMJRsMjFDV6E_nd$EQ zObPDbIUylOc12M{G&!;;ih`mFJaYn(W9D{zu&Y5pQ3#aSK+O&CEC+2g9$pgmI4CkipC zi$V3E+c8myNe&$Rj5|Sgh)w4aX1d6DZ@QxhlgRW>!c4Ll8C_07AOiV{VOmC84GGywQH+`!F)78k+|64$} z4*XJTyMGHhmZWG=K~SP-DVsle>G-~vOI61yX#z)z_38AeBYm^4(JQ-Mi? zX$hke3urMAiv|;mz|ZOLC7Hw|VS2ux>zOVm#pKHKn^A#Nq0C94W4XZZ>G@Jj#=L(( zBFv6cK)YoBOkXd>6sZ6eS_2jWNwW+5VdPfeRAAC&xXUK+ce;i&lUy{!CdZ$U!j2!q zftCV)L3)KClmvuQf>0U|N)${vG6;a3Wd-Ib2nhV0zEGM;*c%i?AS-XPK_r-T8SX%t zx)2dr2xShTL_icXNXHta@B|0g^#9UK;yMrm9p^A*Dd`FP138Y#@d0y|l0KB9!Ssg_ z6s`?S0zaoa$uKE%{{(s4@d87ZqVV*Er}%`YH_I@Y2>t{eE{zEJ|BTa5$S~=P-g!P* zK%jw1i4{DK0h;0$7Pv8;N0!N;{x3*}4#<71jtpQ*!2&`t=`!@O!HJCg>}9gx@qWHDWaNiZ$C43nWuYmj-&juSYu6j%isnG~3(@0MlqV%q+6 zI=dW`6XT8PUUE!9j5nswm1F8=yfIx`Vs}E2+CJE?WC(~Z(JD+HQa2bCm>o|rWC`diFeva@GqQlsL{VUL+&q1+3X`WaPWc%P z(-l>jJQ#0G&r)TQWxO%HLzPLE>Giqk%T<|N7;j8}qspYuIB~j+8j~mEjp=D>Otwrd z*QYO4WAbLaG5w<&lRe|a>4xe|eh{Ac^y%tMY9JN+)tLfdV(J=98yIg)zoEh8Ce(8s zQpGb_C~!MEWIIl|KHWf*X$Iqs=_fUr7K*gofl8lXQs4rOzBu;WnO>*Gq$~L43V8BE zi3QYq;BtI(W%?d1rVQg5F!O$ZG=Rop7F>t7g}59)oSF<-X41l}z~$J(oaOlA)bwy| zCQrtj(-&$psW3G$P0!S1if6pBU0sK%kx>wo6__1YSb&z7d@#=vXq>)Nm&uoD`_t_l zdQ6gx5-0)53JPczZgAqU)Mt`ryfHmgpXoGX^K?Z6CRz3-CWw@)0h2Lf^Ylt&vAG6J zUIxb*l~};J2-FF2TyX-_0TpOrQji3-vbhCVrMMNiSR53%1YjnK8#3iEp4i@E$aIm@ z@G&?M@9_gCVrC+=Z*Q|^5@!`!0t+$?1vW>6EXOt1r>}QlieP*)ox_o-i1F$4YDXp? z#@6WvotV_7zjI`2g$UX6u_%D{CNhnrA>)(j$<9owP!-cpI5Y7=b#DLT%(Re^ z@#*w=u1q82>aS;pml-IrYCqaN$~P;D{wg800}V*fQ1Yh+on(QX0n86QDA&N z{jE2Xtu06elfVm5cfj!i$j_h?)=v0?G75}$0K_{00G`{28^$#Tl+G}G2WOS<;$dp z*M!BsOlB|>zWFkVKpZY*#^KnoZW?HM^#!8>hXS+XkMj&!0QUn#3Gj2?e_G6M~x^R1XjUST@HGbeT-jzo^nrqXx>5jBEof?U zVF1$?My4Zkw@(jZ3SbmJzMhFmv7Qxt0pAX$Y$ecwUIu~H)BgoCNok6LR$nqXfaY!m zl$dlGF0g=BL<+oNR$z2|!I-7Q0+PAIsKhkgF@#Bi>BOn&(IHII^-N|=8$b(+8k!n7 zz$3DZpmEw0pj*#a%$PuDp&mFj0la#RX%AT4?k)i{rZqd56u=5EtY=bSRA2xP*_>F< z1hPXwiP>=p6R5wU$ff`~&HBW8rYtk26+4)i92A%}m^Ltha}lHCiC(6xdIeSuCK)9r zM-EU*2bDLB3QS|1=-xgn+0~RqezzHCD5G$>;fB@1iHayUg?Kf&S=HZ(#r%^sK^2` z4;=hVW=svepc7^o9YNutz~U$ZGO`|Gazigu7O0|U0vAzWKY-#45}vvYUsyo(FO2sS zg?9yo_Zx-R#0u4?%P8tia%SgBcXy0`~tIL94tqnC^fCA3y|QVgkAhe^Jc) z2j#J9Fg;;%WN;VwJAGpqlf3VGCMBrL86c@mK~#f@L5WF&=>wAjlj9dAuwBTiAo8GE zLW79`l&dtDI+($uE3DIH!|4;U5MLG%+wMOJ19@X}y-OoGf|2RRPW+Jabh1{{GftLj-CC9^=;hym

b)a17_=GtN)LdjyU~p_;fimkE9OuB;430Yx%qwuF1`~L(!VT~^pTI^Y z1#k`*0Ik&A1m_rJ2_QIatXTqEU{bmax4_{E(WT398--VY2Z;-c$OFs@OpZq&=>u*T zNa6%Y;tZOEF2jE${kjYd;M51R3X-T`yt^nW??HI=;F3g_;XZ^5*Jw)L>Lg76| z;wdp{GCMedt^hs304kQG1vbM10VEXwmb!tE0xhJiXA;n5Xl4a>qCgSN0*z!PCU7)^ z$~+B_k{2wFjD=PV5G7DKn8z+6yMe{Al@(`#YGH+&0xql-7KVP+)PCfvEtCC@8Qv zsz61c=@Fi9w}A5P4oJTJ&j=Q-2d!!cJ5PxTRF3}u&B*#2^;1CdU=bSpr)@Sr0TC3i6@j1?DV)?Vwm<)L^>72+GWN>KP%494y}fOUfXg zF2hS`P(yfMk$9jax(DQ$18|o@EY)Rr&LXf5WCcXiDuSFnJ?AGu_N1PmlJ!IJ@-K(&Ub z22(wQ5{u&`+=*x+Mj}E^Lny|sVaXEM0X3FEmtitEcS5YxWtf7*Q(|%KhS^vTD(_JX z!Y)=vc4+Dcn-9$-;4A`7QA#jlpMV1m#(RpwdxpewyupOzIMB4ezz!xy21O=CkcptW zQ$SwBM>_NGl6LDljtD zgIcMOTAk6cqrRUhOP~+pKv3TT956~ujyG0L7I3rxHEI<=ZUNUA0s@^NHmG6asG`8) zs34%K!05Q{#lywyW(+UD^$R4~@`5Ia7(oYr>;`$wv5Cc+aR-P6${8D&G?)U+m^MK8 z^|}lfL3@>~774H>9ajE+0{n6d;mg4<0~KsBEun>(nz=qL%Mc-cS) ziCQsS0y&S-iUH&l4W#+f1uOLq{L^&v;ZXF*tLTxOHm4>?f{b_C^2_{1SAv~ z9Y6FlWhrt`H%MYqss~M6GlJS=bF=YuH0QVgBfKmcTy<-DN7Bq`y0vb|c)L`1t&!oiW$l!Q^;SVuy7*fEm*X5LAZ;nNS(zJ^gz2vpo4tiJ`$J4-;* zM~oUw3m6pz6c`=BJx^8zW`Tp?_}j3fo(W_+XlfnYa|Caz1@%i6SQMB94uZP9jQWfR zb|6&k*uiAZ%%GqE(k9`Ev^|W`@dIqHSi=s`S`bi0qadxzz~HEz&Bx5Z#>fV$1;PDW z(Eb7i1xLng0h4#2WuRcT5|b-0n*vBmi4~;lK7$)CXsA~q%aJJ?F5twVs5G51l}Q2I z{bX_E64(j$N&~MFGcT6{qai~Ruc9)OgMv~3i-Qw`q6#GSf>uZxn;F$k;` zR$$a*o&lP(W}d*Pq~c`X9>mMUt)T405Tu}@prW8$tE3!S%*&{ttf&OalZ@Gl;GR&H zA}gp184b7s(31*{0na)i{w8lVvlPDp-b1tq5gOyKg9S%DK&DT0MI zFoB1D--A}MF=i_=E3kq}c!+-#7(t8GK$+U{0B@EexDeh0F@h74_jspINMjP^>||2l z1odJ$r>{$6QZz$&AEmN_mm`jhRty)Im>ocA0fIqN)BmS2N!D{XYJj>ljyyS8p!GN4 zRqYQzwV2}raH;P2090*pXfP=#a6lUSj(2uU7JyblPqu?fckm#E2GfJ>OiIjVOfNvX zLF;%vfEeJyo6+$DsJPk$t`Sb~DuIe^4p7`LsOMEuHfI)40P{L{&6znM%m!Xi`sQ$y z$Wj7@@(SK8O=boqC6FMvJ*1$lpd_##)Jy?YAfOQ^0R;{a#h}0hra(1GYG7^!X3&%iv^mPlIK3g0 zNn8Sv;y@J*k0WE2A`cG(H^X%2OeRI7?C5w4-gsgMSqv$66chyBF>`~$x?=~EB4{PK z0;6LKZx(3D2B^kmFk@=qbyNp+7r`}W1Fsn)gMySJ3uwTrg*Qu|k-?e~lo^?=89P8t z9|cfLeFqcdgjwc#@E91VXatw24Lg`X2?w->S0YPE8eIH<(h<8OW1#}Oqk?x4FC%DS z4tRZWiDO_xE#jm1GhKtU3;3QIwWNr4SgjwwN}D+LE0B0(!KI%Y6f zF~FKNtf0kX%vKDb`4>hj1|3kt4ceLnRVo4sk^+gKl>7rU&tfq8Iytn+jQMrCe`{2;BxW+$T&vF3!tpYq`;0`${v9>@R%TdEUdnN z3~Gb1a)S~ZaRmWLEy!&L)cTn~-P9XkS3rlWZy<{62@{w=Z34#WjhRd$^(>&m0NDZ1 z7COlB;D#r9!d2kbWM)uQ0u}Zz`k53}L5vgqOo}RygbdUM6z}1_g!bw)spF(x4^RVqz{lwa@|iU1K^#WMAF!baX3&VG3NL7(E~I+`n&J@n#0(0s23|#F$m|KI z_XZkzfVAG3%pg-0;E4{$HK60BSp=#TlpO^?Tgf5K@z3C;@Sp()P38t(B_*f-|NsBz zk6@{H01ZikCr&_38~AWHIETWjMo>&Kxq{*VR7_)ct0prjdX+%w0MgS0#S++^;Nk?- ziNxmGXsC0c%VC%yu7)g~f_6l{FoO$rP`M21Ybwfsl5YpEqO1a=;|}I51qR0+(1;7T z2?MGfWX+h)fOc5QOm{3~vW*6vM+0@Xf{cQ!z+03c2F+@Ks(1~i3(Sh3DX$02Sqdy* zg%iQemM6^M6Kxn2m=)LrOh7vlL6HSk@OJvmLMA1CHZ!I_pq@6f8Pg9Y1<~nTMNC?Z zqT5Z2n3x$YmVufv4V+nEjW3w91RjG;ZQ+EP3XXb+X~^yponBDPByI^B+W)`+8tPL5 z9jWsJ!eq<#SSihUt#wOrrG#AfJKiC?(K# z;)VmD<%A6f7(AtwI6%oPM}b4&AZXB7#30ZBFH=cYy#gE9d`AW|rV?&P@bGpHNL7Ur!c36U z+?1H49ak|rf;`2X?Z{N3z$E>Ln*luekq$0}6c`*|fIY9s>IAyAlnJ!86LheO22+Y6 z8)Wvf0Mge4r4~>pUV&NOotGK3DA$px1Y{v-kEI3^gCHw|61yuevjY3{h6*N$dM400 zA{yWzU;(LQafCQui5a4SMUaaD(pg{v&%i)Tgvh}R1MNov?NDJ+U{+x9l>P&{DU-=@ z6{7+R$Qx`5ObRTHOb{&`f(#7Y3Jjo)6FCYD3d|CYOl6?VQ2=Tu{a^qkXOS!=1%Z{* z7nU-m)Pu%=LCr^ylNl7)L4%wM?2ZfqyBHPN71%YHt}r<=W|=Wv0VT5zPSC1%b`2&O z&_p9-or}N*CM6DmW8n7a1l)-ZG>pRJ03KA=WoTjrH4_*eXK-fKLu`?Ony`Qq6dRz; zTbSt5GHyy0Hs|- zHkfWx4r-F}g4*QFj>u`B6O;s7K#SfWiNA#roccK-JC-@Ysht^|a6yai5y`)U5t9Bn z!Ezu&dKeWzy3Ck*7~wepY8oUBv1Wm?!1RaZOp^8RB;UbERGJ6Pt2TgrikaqXU}+v? z7O2IGNc8T!tdJCnmgd=&xLkQz6}Ui&UaFoAA?tFY;$WmZ;{KAkWFk!l3C6gv2)AZ;{CKFH47#*lx$L@#} z`3t~70-pa|0bxSY0TaA91WnDr(*Y>yESc_5&LrlvfDvbL2u>1n5J_SV_9QXgpo&Sn zo<#wX0+zs1z!F3XfNF-M0B`~TZ69YaV_L!pEd=L)OV$;Ppi&7^v@RenO@Jza;2B_V zVlw_4)y^~P~UF{XO=+M^n2Az@%5nj)C-(h0Jw;|osE5Y&kqT>_3L7{PT1qvHy$Ebu*Tpzc1S;}S0DYLn^KwM_Qf4ZK+bUztIT zIeo?sUQkSf+r~dYPGE$z@64G$fOtyNm)0`rGRjZCT+8HD4;cbxFk`v_9_-!I-vl2E zmWPf2gW6ULxIh&WxU~kVx_5vSI|-UIL59JfbTDNm`YIgx}EyxCnAU0iwmEfK%8)#x{C9{IGg0uz`gCl6Q5Cf>U#{pV^F$+9!%>)`3 znFZ?ffG0H-zd!6M!35r)#so5Y7Ap^9J!k-u!HQuPE9gik zO(yVY6nOGU$($K94Wz*|V+VM+2r|A4s)#`oOiRJ-ybU1Zc)=|hD~7$G0aZrmDj`sh z610Yg8MNSoS%V3*lLvedjKDW$P)8qxK@O_F!K=g$iDGCc4HWzPm_Qu@P`g}7fz_1B zAP~It=KyH=5;O9WC}wcW9=r@nff>9c3NnKO9*|*l1P?YdIzHe5j`(rGOSS)q~a^>^TiNJ4Xq$qj`#e0-Hb$C>4Ur z8Azc5=`@Cc=GKsw?&0h+g4BWI88i+6DoH>Skf0QR6e?Cl$fOz zq^JLAX42OIcYb;pmDoVdOVBb721QVN6;vxTXfSm!DlvijJ)r!=<~=>Qg~?b0T*5Jc z+G#&nz(v~+7Esd(lpH_=E3b=I%F;@tbDM2Tsf+n62<}!Ov z|JcH0%p(Nu$2+cJ%u^Ka}TYCU@3-?*ii;ha1%%~S=ela$2B83O2LDm4dAE+kBTTT2>b)*_%om-oZvPC zivqKN2{a;4>|jy?jSzu0!-A$TK+ytP#6CT76RU8&gaSAkHZX$bqW?2PC(FURG8qI` zf!bu?`T=YfC@m^TDDVq3uz<$odLXqkD0M0Dn=ye#oC6Qh^JQ1y0;Nn)SsN-dO^TETCcnr`6-ITkeC!UEnX3tmkE8nn>K z$x`G6udtTL$%3SBX>brQ=`x6bszoW#v5=r!_&}8jcnvTMWR?!HU1+^i6cwme5CmWHI0ZC<_+vX0=$m_jL=X9H7mfyAZXN?CksAB1*(>rc=3JPbteh-R4{Gr+aVYRQ3S>E6SP9-dW5xt-0YR3ZgXWmv1}I2_ zb%LCKVm*^N6N>_`BTtrs6xa|3ZUtUY6(pm;3z{R8P~Zh6M-c^HN0ls)ID>*1$h86{ z;1xBBpo5#Z73%pM*+46Cz-!pCr5<>yZ2=YFpxzB=VbcSiEXOM|TLl~?1ggQV0nM~Q zOL}Nx<^)yDkRk(5Vy;(W;swtD6H3iyOrRVFcTF>7M-Hzfxb_DnA~Pm<&V#xP8b^|# zas?C>;2HrG6`GH9{^Iatk@4uIAQg6AF__s?pAwC51z0djfF;j5?F$W6-&s#QY$NJYE=N8ddsAuzzRxm5(=!K z`cnjQ1}><^108H+#w4S_qadarq`)fB!UC;7d0DtYGrgeMNbuw#=s-tEYIM8-npOf? z1MUZc`r{Y+!CO8YIS?g1E4V^B0SZv`aE28r%%JKB)W-+M5k?x`3rfQxhz1L^R0hWa zC|#lkKT|#S;3pvg!=2I!J>XvyTLOkU3mQA33XG0RctLqMfe9269pD`$pzr{tRM6TK z#|dB|P@uuW3zXyM@PZ5nx1}J<{Xo%tVF!~L(+sc-cz=r-(-hEd6>vXLY`S5Ch-iH) zxSBiwZ6|>zZ@|?fXvQDhBisWrLjlx@(*Si;K!Gg-YBqxM6sUE|o8_nj>Ux2y572on z;3f&91{3I56-Ll_J@_mPS6)s~7mfkcRR=dWm=weW+E}>jK?M|KfeZ`08U|I+vp~z5 z6vRNgj9DFZz{BZc8cd-565tVaXi>(Z0M6TB7juJJA0X^0t;nnZs_?-T77LyVi_fv1 z6;lT@9v!e^6>dCe>K|8g0z)yV>xrtk9?|#^Q(#fx7iecu0MEVgFoMQHK?7QdmLI5u z1$VAM=>e1r7@+MFP_6(q32uNG;M48F%|l4z@CQ?tqO<}dIGaJ%C`N$Rr)e<7DDpej zGbpk%IWRjYuq*I`8ivr`oqz&6tbND}T4$%h#30ZC&&o^+yrBJ~ip(H-4P^HWGpN}J z*$2f6Qhf@kfd!6P$Pq5W3gDV?1ET`3;}XU!jd~^qMG25G3m6r7m>m>^6(m5;kXGPP z5Qe!xQs5L5s4Xa=$fv*vT1N%iD$O7utH7ur2I&a$Ir3yFii1*o2&m<&!4#m#!Q`O8 z=O_W5P>=()961y?1>7MCRzX~#lLe%@g}2@jv_iHD#F@aW#3HQ-UXInk3l(OOR%8XQ z8e!C6>fr^=SV=3gEmwrJTG`xqL5uc4-c?`$Z=(Ra2ej1NbCUHi&>4LkMM8K=;#1%lg7$MylIR!aKmMkS9M@9vv5+xRa z9&j(Cg%>oR#R%$UfcBYWDKaZSP8S9(nNj3WU{T~^c7O>faw-TZFgw;O$Z0S!lsMKG z7b-D<0#=DlpOFEwW896G33QMQQ@s+KD=(7*rvfWz-3{o}9>#2IM$jf+#%x6{P(}r9 zrGhk+!5tsGZtaD;6&j?-Ze=V1E!}{*l?me7dd8A0tj=XD$x>p|WdJ*u(T$e@wA_Oc z9aNl-{~1AQI28CC!NW3;BWsxyI6S2l1r<0H zL1WLH3WDyD;3Nzh5dfzTPDjQpa0ecgIN&LQQG;m$uQVuGDI$leB9|j*e;NyTKd6ub zvjSJ4Bgom@x(o~oY||~LGRf9+D{z9`!KTOwTGpr_uD}6G3ryS!oS^YEP?!^RA|)=A z#o|P!k}M@|eMV#_g4}`TMDVl?I9#wKPC-cGqo{#FSV-E5?`=1Ql2unZa9> zL94Eq9AE;9914OcNmHqwyHE+N7L?r?l-L~WL9xxG!NdZYhy<^MV^H8wU~**4293O6 z#x{pzJ*eFbD%MdV8+1_&rvj@Y8|Vlv$VyE}We1LHHc(tMIo30kK%5TE{z#F{0V)PS zdwoG}R^Z4|;4TDt3VaR*sKf%TT!*%^Ss?v=Ch%%*ZUu1#R)KC7ZUr#~Nl+gWwC|%I zUX+41a7zkIW>OFag;l*UIAwr~$R&&l>>x4NHb6wR4{r%zHV(k;bI{-`Xv__~-Vy9c z0mz0&ACP(AO5FpzVimO85WL_Uv}%lpiCe*$8MMt3G?m2yDpkZ3m;_eA20%cI@j-Pw zcvxCWU>az%6lmg|mk}{O!Q{%zFx_tklWP5~7SLn^XfvHk7HAp(#8Su-XkzA8U~oLc z05YAy(IgAB5FR}9175q&A}|4*hFjH{Ws zK?Bc>jvqh+Wz1kR%yk)l^n&V9@InlbgaDE$4BQAOqZq?DJ$Dw94CAEfU9*^E#V4^K zsROx)32fbTf!RzV^`Py=0;@qwNEU$H2xfuS3<<0T&D4Ng2};8on3P07%lRBY8$%%z z?F<5wSwKUj3L>D{1!Z&Q2Jn(B&@iZih=K@oIv+k=0i8ha*ulihs~`eejG_cu=Ti?V z{&z5GFe!irU>xh+qyd^Lc( zM+>+VnG{4EO+c+pCI$r&N1ZItybp+_k>v=BK@mq-42po7{0g89GNG@YNs(Vc#8Dwj ziAA7^SwTdBU!a9qkx79C;(sPkiB<^mrsIr0CeSFofC4|L8iy|L1WjtJ1qUA3kpk;L z>;}l5aggIdjT^|W1!Z$)@YED^r9NoG%ZffGg?jLE@)dpHMW2u@O-km>;Jr?cplARs z76KV9;`j!#p>H!YcpQmQpRoh9Fprl(K?-zn2{<5HdYKeCK>ljzg)9JuZXsF#GJ(-? z1t`eofX5S$^nq4FGbk|DgOnfWV=@Ev_Z1iwIM$kjhIv349H9xs1MNHC2-bTBrdNaM zL?3vP1+okfVu1pi0*AnHu)-@ag&;dFfb6&f3KP%}KVJp9)*-C<-#t+DG9FB}70*~PJb}?ic2fA3? z5tLCt%jH0BVANo;09j$9#12_mxdId`V4Fa*IRg5SEDbU23yNW&1?Lj=pvdC{nXmv7 zd5%B8#)6ligM>j_!kO0!oB)UDA5f^^FuVoSW(LI+`1k?vPCyN&AJ7Qu0Ci2F3n4ia zm<3LPjqB-Wss}IVg7^>WSdgt9pxtCsz$UVofi}XycEO1#NGNcE*XMyY+6h1+3%vIY z%~{}xn*my^DR2rjK>-epC$PwZE3SuFDyYCEa0*<*f+!IM7J)Y~rMw{B&@FMGU3~(3 z!EOidfdk2DFfHh30*%NzLU*o#lDPtlz-h2D@MblLvK9T1#JT|z7GP!U^=kz{tM2p} zAsf^{3*ta)>_Oh&1M~h45Cc5=0on}%i5}3-CV{hHE5Perc|q%_7&Sm6C7^1X5w;%z zYzr6EF_8TSplazTgBcTO+J&(myb%G^1b__5z-(Z4WQOJ<@N#@m%N7(bKejW0jOPY5 z4!ISCL2Ykg&}q+*9ar26pq*Oa5*;4WybKB=3OuO62~O=GPbnzZn}ZfWf^rnN(0T&i zAPDaMfJ!Lv=oDz^hS3pPhJfaXAw|d+(5Mk;;Uc3Z^8+4p&{iN&VFjAYh3pv;cnZ$R zdq7=sPzeg@b~8I(0$(}=F0elE)I+=ut~Mb+pvsgdOF_hx z=?xP!>Okw2nLsK*A@re_$qY0!uK=D(giIUHfH(?iz8Dg7b9R8zKS(EJn4C#LN6 zA(Mg>Xud2+K?+n+aDa*@kSjq;WI>Gv0eIMg*Osyh=!52dK{W?F&4MN5LDed#P(ojo z!Og?N4XFYZnd=oq9M>>qfqP;ipdKvZlgDY9}ah$yf*G6=vMWGoH}IPK@<;o}C)7ZNr4DF^ya7B$531l{hmwGL>;Yd4Aeda_2faPrzi-4+Rfl5se}UP+$g2DQYcLCfYP6q&*0tp*e5%oAv{T}nX^)KLV5prQs7i=w!KBB*u9 z%jCdR&oo_0O-^{a&Jrf>`W{e11+BsdZB<|@ah!0c3*7QnVDaXK^c*0`2t4YoY=*-vDp711&%nRsfX*a2G(e zfN!D{26Y%&963FCLG!_ap5O!m>clXD=UN0n`54rNh6F7r3ryfuVv<(mcVqx99E0}v znWPnY92pf@N)$OD#RjOe$fm&K#>=L_uK=3ftq1o7S;2K9xEIKzzyUf~7+iO9DzG}% zD{z7P-Jrl!5&-qOLCg1;LE$c-zzynd34k`Ff_5x0g32Q%Rwf4p0ewa$C3ffrcF>?b zbTulf4p4n80P316a&s$yu3}MSQDC3WxRpt)9^CE+l~14s2&H~yh5D6fuQJD#WGR7e zMp0sRtcUs(Q~(QrN(L4MPoLLxUYHyd!0DRVk<%YEpU3Pd=ntAhcU*zT zTaYcvkfaaFZ_uRQ0vqH4_i{mFUp$~(03YdKkyhjetv4xA4nd@j2J zI9Qh}v4MK_purdgjzVxcXF;TMUO@#GeMZn`7f^T6lUGoIO#w9e4>`3A77m~VQOqS- z;Qq7%_*_YFV6bq*Msy&ZVNm%C8z-&@Rr&+qZ$+rL>(TrT8t(z^A5&s>{LkpdD*!qJ z2-J!H&sgZmD?o~`neq4<;puu%t%H$>iH>dX*+<2BkK*oTQ<%RXgd2l_GbQVuNXxA?^i}Nx;913d4L)xjJ4z&O%BZ8*pG?*ZH z5_C*VKa&zWXoi3hIs39pD{?M}bOJB*gFAs5Oo(jHF0BL^*<)sLU~*7kRp11bgq)yF zZVVuMr~9sElHdllC?TWC?9&TYGbz@C`zN3p732verYr>xPXrbUJ58MfreR;OK<^Q21ZC-#aM`3RVgYU>%dctD+rr`3Tn{CPS7DK;1Z4rRKkId z))Rzm(S#IVNP#hdmzNO|W{Nyenh!#QMzYYVKcprVL=BRjdT^XU+g2UCph*VMq8&wO zX~?a}4<6xEWP@Z60p#ofKI(66`JoW-A6pWPgDA`Jk;Oip&ZE z;38IuRe>Ak34V|#I6$5Nm1`^x3j7KzprpVLHin0XyB^d|VJuVt4FGXCa+E2td4i5u zfhuDIZOc|@&rGq9nJyv1c(5o0e%G*km3AbxAW9;Ubk1fQ`2 z3KZ0q2EPK+^u`&?!qWp7Sy<|oKu1z5u_-WvItbk0WVeG!31%o0XtDupA0xtOa6ST0 zQ-Ixo9EI$lSs<`Hs{&URXqPrS%s1Rn-*7^F!wqT)Gbo62gKqK!sWf9^0TufY_i#*4 zXlE4xEk+0V6=aqomjZ_(n*ygI6C@*nJ9m(EJ$wpE%Rzy_ttgN7)LBl(s5{((uPh(Nw2kmA8&p!#Yg2u>sLETu$WEiI+ivlzFUJ+0_ zLFuXS=`+fMrl9^q$_qXP4p1iI15J*CMxsFF*K`plCWU%rafs0hY>FI+{usD*qW~^l zLBj>0`BTusQ1A>Y<=$fsC&PcBhBhClKmpCLu{bF3L0Tq!ptWS6Wtd>kaXT|9)q`6s z#QO@KbdXYxD=!ld_#jJA6;7gub683g(IXVplmRuUKus`WBNWt{;R7WyHfR;br@#TK z!uS+eLA5U=Gfh`?VUn*$7AM~0A_}}_Of$fT41mTF6%-Xj1THdzMod@~!4v~%-8rbI zxnlKj*6b0}S z`W2voCh$%a$7#I{0*=xG)u0`EunpCq0X)#EevrM0ZQ!sasGtHHw4EMhCwM(LkHeOn zfwr80hX}z7i@}nBk6cnfic?e_&j!=XQ z^+CE`(6K9U)RG#GW=!>vgaMkvf>{m?Gg(mO)WHiGoNC|&58Z==e4k&{-973UZ)s5l9ng z6(49LvrbNyqYh}9H+bRVbgrFDQuT;kFA7YM*a2;gfi6d5f(JDdXtNI+D8@v{D19>yXx|Lc`{Iy>_!-nO1FyW1WN}bX1lOC;{v`{fEuz3Nea}uN zIdkwdD(FB9aARNsXr`K}1a!P9Oqn!j{u#7|6ntSCC~lb?6vPpWa6pa&ISj4V;g?nv zfb<186vRQ53@=Dg2d^TRf+DCS0*&>FLzme=+BM)_1-_CV!^nDqW->c~7X2!47Ao-| z`WT>c4b;;35AI{Y0su6UYsNGIv|kN04w8AWK!T%WCOK-Sf>Z`K1+kA;}fotLgwX%rTQI=;R<~rg~;jV}MzKHA{&{fu&H14LoGSp;-^w2)SH| z3(|Il1qNt^6gYWtC~$&uIkZjA3+c-$GC4AUy5fooypY`{pal`&6NeyeT+nslEDn%0 z4-=S_&=P$K|p~? zffb|>G^~v5JT`6xh+|klD!|D#ONrT$G20u|fraX3Rp6=5Qshx!0e57%KA3iZ5T`@oh9f*Q1f;7|Y!3WA&GJv*2b zg+UAE88tzt@IzK0gUdb0Iu&p^xr51(r9|KWqk=FCXrU)`ciw(RSW*Wqki;?hX05`@ z1!_cs7SDi3(!txXtQi_vaZlHQG=L7dn67<*Nu^!^y0k$9)a(T<={p8_xHX3%7M z!VC#f&^F8l7IUU0ASD;TnwLO~1FsCf0Tx+$q68G#Kon>i4BWT^uiA%>%tFo}hYIgK9!HzIO*^LTn{MJL3+Cp1(@b=bvCM8zT1hppU2uE1E^#W)* z2h?yS=pfjhQ9*>OpvMzI+o^(}lmcpy)N_GYpx!Mg`Ex?{w=%-kx`4aHpuv2k(-c7s ze9-n~@FXT^%99m*>jgA?K*Rf>q3=!LP}XMz9n8fJ@(HLN3z=62?RlI4y(ogQUXvN* z3}ujZ$U&B%F;dWKB2ae$)UTKdUg7^{J!sQ3Xj&b#fd;DDQ3bSeAM63p_9f7HpP(sZ z=vpgKk*J{L#2_#YY{C)H@n)dOCR5NcvPvoepw^s{n}SM>qLPBLf=UoCXtgaQB9xc} zrn7+3guo0I1y;~$VxZ04%!RJJ;Kk1nMFKNexWSSIKArqHpmdHV4qOolkz-Q0}oZf*rG93~>%xDuoX z$&KvjZp7w9Ii@V!Zd}(qeZo;DG0?Qq^p!`MG#MMGUp~rYX9sHeDY8J%(gYoF$(;pC zW}wvxjG7D^xXqa_aD#e1KbSyS2(-ZA2WX`a=qLnk1(xal$CxBRmwRO$W3sLXEsz8y zuMgZ=0y9}~7t4I0UI+LPFGWcO2FDi2!O5T!6x2fom93E733GT9!A;>AJc@jfLroPV z!DTn-qz(lp@HL{BVT)6hnCl%`!OL)6c^N^&Xp9iWY-UW54QY^RLeP>NJ_TM!R)JaI zBn?>#$Oc;e3fjKg0$DN&s#p^!)wMg2SoFkG0gz& z)?qLMpSWEQYNm96j@)JdmDP@n;Qkq-8B@a~(4p+ipq2H^jw?8F6d1D{Z?I%3DS#$- zg}{qnK}l9YL0~r6O&5BZl-NLv6*NFv72!KsAX{|7TV(`pfX*ve!2y~GWn@)g16>Dg z$PF4;f|Qw{Y@^F?2E2-vQ3G_=vm&^ZI?&4m8kE&#IMK_b#6Las1e3gC!z4yW0mvLO zxUdpZ0A2Mv2kaEk5+t_iQ%^7{#6yJVvM52@)~tMzJj^W2%*;G2%q%P{U?vMQE3*PK zGYbP~0^1~;kBNzinHy4`fHna$z|Io~_ly}KC$oa`1HZtm=^`hYl zDsU_EC~!f}T?O^$uYj_L0<#jE854NDAt)`LSP!~<2~w6o3p8jL-q^vU!~r@%PM6`z z4p72m1mEienF$1K50nN)_k|ryj!bT#mC&GIQ(#trocje?9|GEo3raN73akQ<&}i8) z{n|+;RT1!NDvsg-y8-m z@j(ZbGMj;phXlE~pD9a;$FV^{0c4=6BZH!#0-GXeojNhK+2+{&_1gP}~ra>ce&{#nX zB7x%`$q>gwpzRji3NX`nxj~&YxI1_`6~JbJwts3cHPlZ6C3@)2DE!Hl51d*-!=NOk zavrcY1*Bk65CUZeMg;{8rUjtE$_KqnAodInQ1$~=Y@qZF+AGj73AC;Vyk3r3LBVkj zcqR=LV9*2&^P&PX$kRojdJf?|}BTWk4n#Xa?luW~c{kAr}I* zE5R39fwhA=6QB_#u1lx zAMDt{WX8k+o|{}3ArKc0C>h=1&<=z^rEv&O5mymoapdckIi=QF0Ttb zpaZLUAa`kjhoe})gH{?$S9m~YqJj>%lNY!K8gu}~2>5DuP*#I1Vt3`O2OVMtIrfy8 z3QmYe7h4JH*{K^`G)P`Yc zLy;RKDWIsL!0adiI=LHk?kl)C4jx0&%yLwRG%p<=fF_7kAxAc-m@|X7DS{RUd;m40 z6a>wf-hdfAW=t=@3?*|=qgH_tJUZRa1m5riGfMz;2vt3(8NvxWg$i*Q3;6mK@CYiX zaNPkqA__FJ3m(e_9oY|R4?{0q;S~pMm<1n~3Mq|2=PQ89c7gfO{pO%kMIo(zSj`1G z3B&PoL#IH!Bcnh%q}|D^0BU>V)QBje9ltkp3LxooVo*@2RZvk-bz%qxEj(0mVo*?3 zP;p`iT3b`ApsK(mt*GMIzzC|J6jTC~m=u(po-l-fOLE3~(8#Kif{N1<27yh&3XI?Z zaZP3kC1nK`&=Kk&3Fy)L3d~uK|3RlpfEscfwaXnDOB^RGZ4=1SXJD{q0G$!a1Zq?$ zD=L8oGngC{l$r`>b&k4LpyMz>okx&~j*Xx*7GWJha1ds|0+kVzUVA|ih^U?%f5Sqs9yBxts^B5L z0|i26fKONg4|_ZQhnWF2g4>Y`MfUvIBt1Hkt4A$?KrIbYN>+Xz z0i+=t@a8K91=QjWbch@1Vl-X`#7-;F<}lE#mE#u{@Y#>|E;A``ECda_F$gT0u8=Du zQje|IfG7*W>%Vaf5W&WW@YPT7;uUntk_s;;4;QGMX3${z02wF$FkN9b6N?_GH4a*W z4(Y^0SfJ?`5DU}x=?zzyBXZmc}4?V^Qbf(W57)J)$qlceKv}Ol*Moj~B)g|nR z6i^`rjxW&dQJ^cguyy&AKv#jm`lU)tuvB|u2UC3(mRUaVpaG;pMVa9P&l$l7O8UW$ zAuzoM*<}r~9(;d1q{$5(BVlpi;Z}pRPCz5epuq`VMRo;2P^%rZ!IK>{f+z@H$_qLM zO{5;w++cE0Ks#m$obN#=HEA&IU{qoTwO1jlbQL6!kAaW?w-q4y5jxQZ8t;Iv5DKLZ9aD%URE#E_6M+V^K|3wM3u!^UPjK}Mx;Tyv zvU-*oG*JR>et_J_3|c+Q1r8ujUN&%L2wBmh!0*P(0$P`n4eBg`hGn@FKuH<4*a~ty z9kH$lZ7D=|KIm3>mMldM1+cq8^IItH2F=ECfEF}E7KD~$DS@QH3wC%}L2U&V1r|?U zR?s$h&;k|EVGjIm;Pv>7SqMjh`<)UB^^jAvG??~)#-czSv?Dvf_b-4qz<|%w;sH;v z(?C({3_J@^UM%3z&cp+h$M@oUV40$)XDQuuyM>2yh`9ylkpwD-K_jq>!DAqx33RZ-VI#>x^%_hXN-Ut`zJ(lhKtpk$X(J}kAQ7m5WN?I> zNh-*|0Cu$kxY;WNy01V$K}bOjbbEmiXbf6IK?u~m1-p<#iCvcga)K#4XijGqE6Bkd zir{6yELIF>z-|_T6vOrGjwayc4s@9?%J@03dO^8g^(Z=)Bxs46vN0MGv^fv!&%(qK{no%Eo=qyU*QU;%YX zK*0fN?14)J=x#X3vY0)HqM#l;hy_aRh*}eLDh>FQCeWe}@a{X%sa3rb8U!2#1gaId z5XVV^&IW=WnaKnl9Q?qO<+uQ(lu@7>bie_mVuc*5$pl?&1D+ppTmn*6&n$rJTunU3 zYC@7Z>NEyu&nzfJ!F?uZhXsE6+lBS8nt45V^c5WHpspb3=1QDC2jwF0iF8oEL)VTl zpa%i8@&+XlPh-iG!URmfWQOQplv)% z3<}(!#)pgoc#Wlm0yk((TSS2ywk`!U*~thhJQU2C!45|{B^7c;D!7rc02DQ#GzqEd zp#{Z>9g$2~0+2*`W%nOf+8hA&5{w%u*CkkaTPS_t(M8IHw0XuuFjU+i}!OI(9(BK?9pOmT-d3 zP6Imwv5_x(vH^!1vlAdrufNpx(ix#8l7X0KSywQ$Lfd$rV0@VNlplS}Z*d2AF5tFo{1bCnmyp_?B0o2juas=B()3GXYDhPrba~ulnj`a#Wpt>8(!Y-OD4eUrI|o?!DT55LN=fAgSOd$oDJ#>g7z-3fDi2eEvZD(0p7a6qre0@ zz>KpVw1*gcI8GL5w;ia}1v;VwG^Pd`&jhCd%6-m(#pg^6iad@C^@=PCID8IT)(KuG z4qY+L51Nx;04>)Cx4}V9h5DOCfgiN#jt#Ww4(??ZkSeGi6fdLrxE`_*i-!?B7Rvxy zxBwl?ho?2rxTFF+nL@9G+yKfB0?2K7&{_@H{%2_J0-ZkzO*LoGHvE7#sDT!KfQEiF zn69jcZm|P3L75a-+;~|bTj_*AO=8IX>994opr`@u#lmddgHO=L;wH#&0VoYa7bzeu zLatwR6?$BhG-%m}0#k`1C;@?6$wJ^n>R1oj-vmBv34D4K$cdnBQ|yqmiQKqnRRA~c zAwv#mEoHDzp!+2tt$Of*F!hS;;0XcH_y~?5#piq^*K;VafEKZ_DuA8MhU{!+Pz#G0 zw9|=Afjvus8FAK)Qavbf@v?xt$fCgR$;*P={uKhX?x6_;9PIE^Ak51Os^LIW)S%n} zG9T3Xg~TJMw+5QAhaSI$hz?biV7OjEDm%!V16j-Gd*s~Ow zc(_KOgrc%M$jcYiY$;jW^tvB zdLl!Y6Vme|K5c;3C?oYUm>|6j@a7>;@UdK=9s~HqG|={L(Dm(%+2EuH>Pvx>1|nsE z=lDV44Bj>b3TV(|7sRWez}~?m4GJt)&}lr7Q;9(%_243X7WDFFQ0fPpfL4OnPXKR| zCbkG?#95HxF2_J2!4L0mK?-otIzjM;qIyQu5*)Po3>3)!A)C+Or5Q6^2P1gbsv@ia zM=8yqX%$|WNh%1NF?BF;Ll!TCny#=dx3CRq(`9!tae$9%!xlOdc$FmJt3;V0)fxD9 z!wz0WK}bdeRaKzk6;xY+%LmY|08o2@H%oz7Bhv_)$?TvYrN9bW7zbH<$q5=#0B_;~ zP2qx9iNf*(XfK}`Qv>MaH_$pu9`N!|Aq4>iw&^#XGs)HCPzSaFY&dvJAo4aC(0B&8 z$O4@V0SaOzb_Fib5>0T-f?UE1T4D<7O@m_>wB{Z(vj|S?3ZO$PSV5~lnZeu0c=#b} z^_f8lW_sfbCb1BZk&3Wd7witGPeE%dIYFxgAQNODuR@l9V)KJFBLigZC&aI>Uohz+ zDmO%tfgC*6j0|AwAp3)u&^B=4^ft&@;EI_Q+!g~7pe3LD;5{Xv;a5lnpuhyK+(BzS zAzp_lPyqW}kpouj$8+V2d#ouQ1U=lP{INZwCWF>NLWGhH4F+;kR^nm{uYaZ0Hl=$8fyb}DM8^04k0Db z@ET}Y27HkvXjL%|BS8UMk7O!xf&v}F2)ct+N`YO01GJD6v`dT=w1!cMAJl18S`I!e z4xE_4+qGcMV^LrSDF?M|kR1tM_sOil0xmk0*g==FFh)TZe)21T4FTDu$N>(1HbwBw zr=W3fMo`HKTI!^#2X>W2#O%kPzb8z^w2j;auVbOhW_5m*Y^r|tM*2UC^`g8~yjw`2Q;CILrr0X+up7lup<^-Oij42~1H zvXwZPKnffic28%0%T&rZcY5_(rcI1rryuysBr@IU9g`FP)Sd7I>6Ravf<-s7fVM;b2d!gc z03`*8!i6816d0FHKlG7Flc{0%^tT_GiW%2WPyEE>!nkqzvQJFTj2oxF`o!d}Idco> zZk!XG3ScV~m>f@VW(gph36T)^I(^|sCXwlRpP8f>S4{8x%#GpO60zc8gTexAPa3zGuliRq`lFauJ(&5o$=K4zF$nb!tY=Ki{yuMznBzQSAjY^)8GGM%4PgEJ@z+~ z7US3H9lx238NW^61K~aY&E(AZZMya!uw2+5ru8BlS-^M6fT9UBk`IX`$-hj>jNhi) z{spVc_{-#itd0R5^auVjImoPF0rg8Ym>O7>m_bWfTUeD?vXwxG3bA@lm-)w}&v2CR>EBh5s`-FgAdeWcXA^W;@2OQyZAI85^dqVU}S0I`t^Ci}bl;tpbjx z7z7eQ3Hb#pNLXc&kn`H#% zA?XJRIyV3B0Nv^{Z~7y4W;Mn+(>Xbq5#0 zV76skFkOX{S%-1X^gvE#4aSAjt2mhz80So%&B<)VxM2DdJ)aA9$Fh85d7q$IC3qbYsGFbzWvursnC><9L}REO2Y=n%*Vg zs4HLqOD1fPNicTD2~1f6P}6ozpMH{;S&8ZL^yzPTnJpL>OjqLrJKCL(*@bb=^d1mz z(e$-^%r;ET|EIs^V^(FHGhL9MxtMX*^j3bb(q;V2T8s;)U*u<2WSleoGe5Hv(~bGt z%>|g57{v}v0Ift2Fi-?02&@v}76C0P zV+7rp=?E2EEyAtC$XKt;;3$wi-B5zrp#B0k6L&o$sPS`wAzKO5_F>Rq5&%u1ZeUVm zRA3eW-6jlL-yzTho)84hQl^_H58Z}H9&W!f)6ogaD?1GEFcHEzsa$O z5oQSJa?%z?H2pOo?JVF^{S_EMm!^SEI)~_7f}(Q{W0n#lcu1WAbYHP1(-cOKR?y{+ zpc!EX#|7XkE(PQq859{oU2bLvrg~6Qb_X-~j$cRceN20pc^RcZeI*7Braj>0)GUrH z*&0j?+@Rf|jwLyuDI`bc5=Ca{l4CYE(3rOZOO_4;gCm0s{*ql2WbCi z6zGB{MMlu7Cj}Nqo@^yhX9cpK6*MjeavC#aLR*2^5xhl5z(j#HTY<@uBTHZflLE4K zP_st?94p`uojzM!L5j^$BqvMY!1PEdW_6e*(0I!hCeYdEpd{tEf+-6sGkt*+vq(ME zXppKY%y3m5pbQAH6eP0*Au|J2<_p5)7jToAKzdpbdO+=dBzv|XWL5|;XMtC?f>Ia6 zAI{RuDxf>d9Yw%L!-Dq@Nr0>7z3vQB}PXLkfR+Lv-y}A7#YEdmIoBo zphO1h6@b>lD1fF+1xyqe9aTWKfySL=vXodrGd2vMBPt-L5p#prFM;oUs0Ur52indC zx}z@(9Dj`2(4)2yK@PsMT10`tQ3hnbBNKR|83S}nH0XXSMo36Qwws**-@Lp9obJHW zp`Z-0hS3~!z_+6YC{i4ARwtaW5CQQ{~Dx95m^^ zb21&CI(;^h*d29dPWcB@I|UqBQB8h>ZnC@%GbhvYsnZ3O;U;o-fCk|^Sh5`3rcL)j zh)>s5W#(jCJgrl}k#oAE60^wk`O3_kOlzl2U!~0K%h)#khcdGPQ zruQo|OLF~O-vw$Srz^2cKd8$rJiS4M*_Y|*jOpi9m=)Px&u9>EWC5GT54ZLMNKk%y zpboPvy(-0rY9IPvrM0<%52Q|XZl%H<`TBq ztGWandBFyPqm*qWNI(c80E$nUJs<&;$P-?!zziz*1!jO_kNX7ZqLdTNS&mm$O+To{ zEW-#9pAL#u#<$adt1(N$gg{Zq)V6xMy*9HV+oaW90*=BUt7N5@f!HXvfR5N&0d6RP z(;GP2_N|`2L7iCwW*R8kq_2Z?iX%rGsD4D!368Q4U^^wa;DPpMx`PI@5>v~X=> zicD>5rnhS_Pi5*{GhI%TS&?bln(2<3%!+KQ*K`Ruih;sbbb75OGl$F}kbsl`Qsw{$ z#|D^7Wf<>G->1ne3v=UpO=eE%-)p)+Cz~K?0w-;l9%1ekpdehqoaH!U?Q}yeW<|CY zYr6y-#X(^s0*)8y10Vqud%<}KZp3s@WHH{EzE%q!+A8pf`3cf2Er4V{y8^Q#+|9z^ zsA8J74rI5)bZ{gsTL+1xP3yV@97RD6k(E9HVq-)Sk}=>&daw=@Ng~rPXfw0GLIE68 zKSA0tVhTw+IHo482U#qE$W`;!Pj}Q{R%BYX9uie6)=!_T!|cJddj0gfps3rmewr?` z8PnPI(=BwFEtwudmA`~4f4zRXoiVc#X#Ju?w&ThrT>_5G(`${HrKU6LF*`FI|1jN6 zk6BUf!P^#4sma9c$gRlX*m|`b33vrvN|5Q3Fa|Dcn?m2d5jR1?_W%}&}UxBwr_ryfTJ8JNFi~{ zeitNgngJpLibtlE3#Mxsz)KhnctqY`&?Vrgf*g^|ju)7+6qvz%3TQD5%6Cky3#TtI zV3uHnmaw2Wm7c$_3pDnKlng)zOM!YLP*vb^bN9mOe1-^}dFsrZ(oaD;<&o{>h1v<# z36A-;Mbm4Lbb=#&*`h81M-611+^9N1kuH4!q)HE26)1xsT!u&u4U4BM8X;TE_2*@izFGXVnEy897T~NRvFmw7_ zW9C@Z-5iSGlOsT#v>B|hJ~pT~#R%QT&g8gqdcFy>s0=n~(Dg7JS&nO`FEC*iwVT1J z$N=i#p&AD2i9ig4sh-pT8qrCI*#_DP#3(Rh`ezg7T*kH26HS=|>;G(Q6HsJwWZ@Cy zcD($&8N_Dg5#V;*b-fA12A!wlxOHzkh|R{s$L+ZMd@G2}%EQa;IQJk_FAulltp`vw z+}w@}zJl4TJY3w4uMUIRoIIS|j&JWm*&N)C?+<|4{+vAQ+>S5SfjOL@X}XQGplnud z#|d}A7I1=c*6RCUaW2sG*6)*GM{w{kaXT(J3U)LH4NV$0BX)`G-Fn_p4HzW;HWN;4jp~w1+`oS92p(oET1f(z$mbX10eyPJYX!<74FpKl=1x?U1STjZ_Feoqy?3*59!#oo-LfQao)HNu0ZvSD!%*DdEdpoluvn(SI z7X#>E4bTt>_xF0)TE|R|MVN#h}5& zq6EG>9XMRiaBrA1I{6Wr_M0O>q1UB+8ZB2z(uS%C@cKhQa8G78L~o&tk_8))6I zg(4G(GJ&)$H9)--4JHGSkPc`D$*@2IneA*N6qp@vfch+;UMYhkN0t&O@q*n3x~h{I($4|)WEenuvLwuz z7(i!pu!2g92h-(Um^G$*x-iR&!p~-B^vqIXaAZ|raAftKUhTqsN8$je+W`(je(2bR zBa`luFRHYzWu5^VC-rw@)?++Az0Qr`Y_b!UDBp`xZA z@?h>`JT^VhlX*SkvFVIn%&v?lrn`GFPvbt#%*Y8E0C~WWJ^hU$n}IAmf><5-;1LA6 z9S0Oa)A_%%iHl;y2VaiBf$0w4*(4$q5b?oc#srU#V~n6cghvRt&94DEa};ZYKvbi| z$n>33a?*@!(-Tctgt-nv51Cb9nV##+EHmBOhnZ{oi|=fF_0auOpi7h#L5oh9z$-`` zL6;JM`#i7|g(L1j{=gS^Ea13f01fN0a0`Q@#PPtX2?CCL&P)~%I3zOtzYnvH7(7Cm z{6VvZpvBls0!ODi`Z8xR9b=pxSjT8ReU5Xh{_cx-xoD6`b`%4TNn?e;;;_RNgOw%3O;$1^c*n|?o> z*^Ke~cCiTNBaDnYr{9ibUMqo^*h5M~%mUlDFNk8UW@OyCT_%Rvmr>#}XhS8l0t?#c z%H`>evCNYgFHRSUWA11Cy?sd>^KwT13#TW5&T0dvZO0R*r$;3)PiFi%{apgHBjc~> z28qlPj6b$}CNeWKGX9+Io5ZZk_-lG~5_2}+ul*B12N;8vfv`IMnEoY+c^9%6qvMb1 zdy<(?GG3kDmcnep+R4PoQa}B`aaL&)&=m@x4Ja(2$u`j1640V<&A6UEe$Fb6_^}h{bHzrj$q?Jqah%h7!+9?>lGP6S)Bt^jY@D>Gjb>} zfY!T$ZjWaK1sc1fK$gn(9VyH+85P0na9JH6Fk~rl3e-UEYRzzD1TCy^e8G_AIDL9i z8go7;T-o#wnam>7|D`c=FixM&p3dB_36;{wQsMy{z-h*0pup;Akmb1YQj35ipFlNe z4>_p%*q_cUp}Gqs2w7GQny;NApa9pFp}-2ZbKj-uf6|$gnf~mWo|wU`Cy4Cj$lD>aQen9<}{GulG)5Ej0dONWiuzpAuGMu`jL1Qt+*TMs!uj0Kdy zVV(h7X94mH#JeCq$W8|Z&>$xB1SSYAp}^wU0P(+)usL%FqXLV9usL&p0*fOopqMoj zKs(VLH$d!#*t!Cu2QDX=)sm_DV1SsfJPFi$Z%o?!#YYygjqf`W(H@d#LC z1tVy`Iq0-cX2(5Xkp)O1TRbbysqI=ju7T;h1jaou1ZUKpCG6z7+oB-iDK#ZIL;(?MF+{if~5lvu(%!ndNxk>h!tg%!VRIU@~(+GD|qK9M4>x{-~UJ8)MV- znH9_m#us1;dO(Iw;mC5l0kIzvzR;4z9DMwR;{u4CZz`CT_?{puyE9#;lG(%I1G3l) zn8{$Pzrc7WKn}UUndSHgCISlD8=P5=E!U! zuZ1hiaRIX8B{1nJpsw~Dt}Mqj$kHpgvK)6H3v7U?U%;gRKHOsmmjd(jv?^u+-y_KC z_Q2Gw0hxOO#ybMyo#D!Iynw9u3QYO}Ncsj>mgAl6hpU(;FbY3_$vj{MRbZ@HjxVOC z)iApWe1YhPL^-SDo9TOMn3V#4z?8SJE3i8DuxB|oT!)7Yi-Hie*kS_Zi3_zZZjWYi0A27hgGq@4+-TBZnlN3jmRVd9T(opBW`P<&Z&*R(7jQfO!1Ux= zW>wywzuG}PZg)`DXPUlXl7y%|xM%}iBF+MyNI%91n%qc%_K;XKmfC1zXP5Nk@SNRM10=00kx_By&J($U)b&g99N$krQNc3h1~?P$i>)2sdS>1K{}B115Jc zLSujh9EeQPAZOFe>tc)a+qYVtQT1S za5GGI>Slhx_;b2)53>pX0??vO?(vVh~A%ivx*w<05BJ$Ml7IU{WCC}`>*v|(1@#dP*Q<`g5) z+J6Nw;VBJTy`jOxp};8ck`Xir#Q|D>sKA&FI#EXfG(DT8z$EZ;`rJNdNyeAcxA!q? zGQOPtu#fo$BiHm@{md5`Urz6tz?>xXoDn)h2`Z8q6c`0wPn*abrT&%?I_%2gpaAM2 zf|k0xV+3uE0{3-UK)Wg#1l~_?oX9M~_+k3AiOiwchZsIiXPm_B&-iJ2;3Q@-#*fo8 zCNT#wy=R;r=PtBpdfa4YFVO1K$;>W{U#7pC%-q8G6+|gbpE`wEmixw<$*@t3>3gRz zYcjsy{(K6v1S8}7Y15d)!KBajCDWK6Gcms3-Z7Ioiiz_l6LjPVBLVWj$Bo!XN`O2$ z0tZ0D5GIPZ{5D->`sL zh>`LA^bHG`L#B%_V&22}e*2Y0%=;NlJDETW#TA$YIzh`8z@vZautiErj4Gh(ESW$( z5zyKJjx0wGfuD>DjMEn^VOG)n2_G+10A&Zzc%;B@P>3>uX8&O<4uRj(-!5TRX8bu_ za4EBx-(N=1!449TETF*z+NUZ4YTgR`WaQ?S0vqJW;4T1C22rEH1eQ8u%%oAz0$MBe zfgxK-P=!H35VTIbbSd*v#;)no%a~&rmrk!(#_Y|wbo!BH%x;WJr*ki7_GDZ-J#;y9 zuqR)hqNoC&BV)FLWTB!2cy*hkCul{GQ$th7i~|cAo7v5o8w?aU6-2WXxD_O_l-Lyb z{6Y6tb7q4^a~whE9v=0MObBNo1rw6HApy<9!mYrj z!0Omw0N$&C2rndikwP5FxkzqA@+H*TdNu_nM}%`6kwOv42`CN&MLXyiK7?y<*#M6{ zh!?=g2FU`DwXpCfDlCgO8gZ8UPg75!f2HjvM>iC}-wD(ukk*S1L zLDE}6bUN#5X4QJpECn`D>gNEbe%`f822KiXE#}M}1`4bS1`52O?7;!f3>*r4p3>O} zwIXYk^qdshlyqCnnI{-1h~QSqk5fufgj)gGbVW8M2L+Modsj0{=qzQ0jGwY9h$!%8 zD{(mT1}29+KP zA_`2|N}LMZ3jEnh99bX(6?hc*6-1zJ;slF8ty16+SdPzZSwT2i9(h7Ae z$j=~qKtd?y^D8iAg9SOjP7qi=J#;OzqLfHBXgd>!8PgU6kneIpXW#Q=E3gVIojzeL zvsnFdRs|u@r3qlI>>yWxZedgayB2(X2rNLru3~ju0SY(A4F*{XLINvTp_)OV&ZEGj z0MgG5p70QLWGqn>RgiFGEK!nB-~(-16?J4R0tqSbWh+X6W*`|9IUT2~u45MShZVGt zq{hBhNf(mTc7T!^JI*ws#JScoejI%X+&%&>uu9Lff*UC$O+ z2@Q01katnzo0kD}iwH#7a!?Xtb=(2=>j4lcu!+@|46tXTJz22q*}EJT0prsvzsB#3Zf2;n>LHsiY{a$fLmF*udhgq%=KXJ+nx? zv?GI}ICy=kqyiiG&Ra(YMFCLm2K!h-K^j!&fNCCjPzZqbxbuO|I)-L7CQ!Bn9j*?} zf=u8ns3`5osK^OA1qy_{L0hPKKr0Wq71$N|!I_;&ffKabkV%oV9@NHWb!;%qf~S98 z#L1kj3ZPvIW=tK13akpe;O6iTP-MuPG4&WKfU?mKP_Q_57-lJO3#?&P;#H6b-7^in zRULGJ1L(3QMSf6f0IdoIU%)2j4N66hCk(O_#OpyzVwJc-w?%+bEcj?JZt%%Y3fu~y zB%r_sirp4-CLRS&1zC4qM$i^k1%WIjfwcVTj z;*Jc83JSuGjG)W8L4oYZsHmX8slcHilGb$X#pfyCm4cCrv>0(T?-CYkk9!c z$3P*6A2|3pL5)=o1y0b3)GP{|3Nq6+F^hYFJf*;@psT>9Adsyju(rh<6h>?cve}A4 z3T%oZ;GkmTR^U+pt7KDP%2pJD`b-#fVVQz}A`j>)38m?Yo0!#DL7^i&y=N1%ow^7j zh=mmtTzNqQF5-@iioyz@lQ}`*%dEhvAT#~rCT1%(P!dw)pKiFBSt3$Ifn8AmG&v;> zN{37i3gV7T;1GeN2C#2rn6r3jVlvEuXSUi<<6a*AR+=~=tLBmrQ43y+S^c4do#Vkc71ug|4P?@2~ub>KA z$_lz-U3_}O7G`-yh3N~nFiY2qD=~pmw}gUrmJ&NSPqKsaB)D1x^Fc+Ebhe_l0+SLO zC`Yq{a6;~8iP*P%3;CAC>0VhdCHU*HFrh=9) zFN1=(f_%1;umZOmCokxHZ3QL;h3S@CnH9~271SUSyv*DR!j3#SpgRb8vK6=#*g*bJ zl7duoETFC7!U}BJ3fu~8*#ax4Pu$8ZA_1<{K&O=RD}oNvXUkUNP!KCnl9|4DE3*=# z^z;W?nMLcRK}!#KprhQ<3c8?iXdckjXP`T*c|ey{b1H~sDRP2J00maiJirY@CI z#|9QqbAweuNxLy zyrmVmvlOMI6@(SUvK3`Or$;z4DDo;WDe@?QhD(GLrKk6AW0q!@26GX};m}M2f z93^E1R>vELSpw@>73CC|l-R+S;&LmnD~L^hw~bj^4D4}fsGGSIK-=KO1lCQL-_9J* zC^x-+JF_aNB7nFUoO%`Hrf=EKtjW0nT(Ph!$W4E?omshlBP+K8uYz1QxYZ&Hz9?7+ zRP-sRD2PG*3^H9nK@99!F3=5S3SwDGyr7t16Icf}jY~leTpe0+WIqs1D;+ zkOrkeX$4l$mhEgMHjo1LwF|j=5TseL34rcv&kWC;BYoX@B z)Ea`qQ9&vT6dm5uic$(3pjZPH$zoZG+zL#f8?L#bH%&T%M#4lug$fhsL<>hoZ)rsy z$9hH3$?XClw<-yN4hrm zfj65~frDFtTVUmM-QCQUf}krv!CGL>o4$QFvjOAM=`VIOJ2P&YuCs?(Qh6n-f`B7a zwxWOnsIgyPoTbR7z^=#*DH}vU0V^^+V-K?oqww_BJ(v+LJbl+5W=-}Dte}Rb@bp)E zn3e4|vO=^wf=*x)c7&Hy;JbSj`5o&NA%zt?Xd4PEsFJ$?ssm1d>j3`g-g}v?GlW5V z(?K;UKWHBulLBY90=odbFau3XK%3(4J?;EZ89*;viRxLwo>kav?Vs6eScu ztuav3!M{jJbo$%9%%0NSObUYfj5bQ3%SQxt87x4}UC=r>4uPN31NSjY3;qOk-$DB~ z6*x7RSOoqvPOsg^oXFTS{r)~?NydihKld@)GWJe4-_LBu13g8V#lf+C!}QAi%-;1s z#F%&>bI=V;*^t2l(5?ywC0=vTEe8s`=FA=njE)Z&!Pn`)XVw@&ib~9xZ-ADDy3_QeXs4L@TihOk`4Ea$EzN*Z?sZ75L1VU%*_?q`@==G%_*=G-nya zATWsuv|UC)iPfF0ch))22&2`*dB%~&<>Ie z$QCKc_6`ju1`(9tf;wip=ON~VdWUQ!CV>l})q-o7Ktq<(Ko0l;S-~dbD3axP>1eZn zBfG$KuvIPKZ7&*3;N^j!y*Z2;Ocfv-Yrq?Y9I^#$LHmS2b4d&uOkn*p8d?P$nFXeU zR`D_FGE89vZ4Lz8i_QpgR6S@i7GyvN_|AnMMgdUhg7kvw9FSXJW`ZVFK;j@bZN3QF z9{_dJ60n*~6DZNhfT9#(8ZYSTEk!m3-sPay3WMVfCQxd2$QGCZ z+M_1|+U}&l;CO)n$~XdHFghB5l!1h2f>Wsh=j{$(cwaPVKb8WEqePYxtH8|ZGmbIK>fka1l*+(y zHk%1N<)gqPFl~D85oX1BXtZX5WlK7r&xiu1~Doyl?coMsE}fVob(5B_yfl2yhoYEeZg7~nICf8)(6zg&jiZv zj2cYf%+Dl^5;|b-vM4~pXnNIAW+^MUyBRc?-hdL~7jSNXZ@Bsa=3-`!2aMA%9%UBy zpwvlF$1qK=Im#@Ng6x_H(#@DF~aUe>}!43`zqK!zDn5&tb|EmpD?lZm1ZW*3e1q8vaEb1aEifOFLcqrY zfm*fP3XGt{g6!NmAm=WDI2SVf%>ddd13K~#Bc9Op-T?*b6L6fu1N8-%i=h>Cnk0kc z6^1M&7J&ue947z?RuOoxLUI>qh#r)yvL}MF9Zwc?(;+mSEd*tJ4p0z20k5Zn7{vj( zYKKP=)b9b^8^WZ(?FOo8xfGbdttAf7fwauvDNk^{1+KP0z5yq=8`IC8U{(@`hYAm9 z(<*p?+=N?!eLCAoW@TB>RSpi>psI%n)CPcrD<>osES&Cfk~xg=#PsDi~4l`Ic{>OW=$HpnjE z6QH;SoqYp3c#bhkKp)hUQ-GHnOF*WyfcB#bOq_oF6tfaL)DP1?oMM)YTZ*TcV9;Rt z0WP=xFbT*hFgQ+N29<{hx6c8a3U=lMu#3SyngeFe07XK>^o-NYt^BJ&E7_F=sufrj zSU@Wf|D0wn)PV{@R_cM`0U|j?K!MrOAPeaKz=U}6DPZ0wF4*@|3XJw8{M`5774PLH|D?810+`ogQsT8wL^pSjB1 zCkfI9%7;t_3XBS@0_#ARWH2kR3H+R1e2rPD{uB!nXB`u09#6zZ9rcsmu?Ta{Qq>RFrwJHbrQ zayecQ(@C%sbc%wLXr}@*$Rsu=g-)k;jIA|FY&A+OtxD`*?O=-(*gy)Hz^1d+C^3N~ z1v?ek6<9&K!ICUsNsv__Nsz_$5Unh&pmX%WvaHBjSwLD@LFR%i23rbptJ7V^RwZU9 z-ZljmC*zJ51!jmTY^_Rc3J?cCOkoB&pIHIqVt>z3I3f$8#U1RnXS=iSi z0NU?u#lQ|;?aWf|DFrrNh7i!KpChQv2o?e#d_I9$krk39*g^M7I8Fgy#Rd6T(?@&0tVTg!lc9i zcBw^{0;9lbkPQ-90-*cfnLzD36$Wk{Q1)UJSO}VBXB1e(0?I-AK}|~VNnZOwwbchu zt4(0)^u*iDI*g~M_uXc8;)WJJOagmFrrY0PmSlXfJ@O9o4MwK9&;ay%#mvSeaA|tp zJ!UDGYG#n?3-_4KdG?%`Ea14~D|qR_bWp%E{+Pb;0kg^US@)T>8NW?GcAwde@!NFX z2h0wP-=_OLU`}G30#=|0wU|XHj}_KI9ozs=k$Pw%tkgdn3$L$2UsXT z&#w@813D^V4l5{GfWk+EX$C9!>N5rnrWLH<-R%sH3s|!h7^m-h$ShgUD9{3`H$aDj zK$#ang}=aZ@U2i;mU74OTPM@#MVmy7uV`h)~ z1&oX=(DD{EnGTNnX{_L`*B(Yt^J5vf(FN{yf|~`PD^|ff_ZbBKfDRLvLA0g7)h)QQ z%wWz8Y65_o9vV!0Kq=`6xS``H3-!tgFjt8Qbmj$<0t2|$2U_&Y1X?J~0b04s1Zu>A zr~5xKGO@tpkVS<-fdzC@xd*7kU;tG|EsRR6j-cxX8NIh}dcvH*$jCL_U^2Vhbi-%N z6--N6rr({+E;W7G5@tbrNd3U%$e-oN2wErrx*A@Ef!m4^R_8E->l{X;34YK~9MBaA zJ<~;>Gix)xnC|$T`K9D<(8&s*z7NCCU0br^R~H+apgEYJyQtuZ-vaDYmV-tEb+nb$Ec4}H(fXt`DxobEs#GEibx zV0N-^cWRjO`VKSXCMVFP#UMEc47urjADG?5yADm+%mVUWh!UeCQ%RX4=%!H)&+V^2 zFmo{Sp1a*7;K=EC=F|iMfk)FmF&|)DJN@7%=0N@*bHOY6!S#jXhq==gKQs3*?wr2v zGqVcg+Ub`+Gv7e00Alk7Z88FF#N-e-JpJGo=1j)^=^K7CTTEZ^Se9pc{#WLeFoBs* zWO=5?eq&z1czF81Z_KWs8sMNCv&!_$@63}K`=|f?&in#GMNN13$=t)(KmF`a=JkyI z(`$b*`!V)UKl6+EF=PMs4ZoQM7#aJgAN<3t#n`|7%^&7wX2$-l1uOwfoC}y4xk1_M z0miBz7WlF>c9K>FLGGspZ=k_gmd2sL0veISx(BeINr5>4X;ly#XgQDuXgQDulL4sV zrop5Gx-Jb=mw+xy0}XF6gN{mKhOH2SE=mJ0S7U)JSDSv1nFX{B@HaDyNj>2;Y7nN$0>}UV^tU&XF$pg(1E9l zETEEd3M06DWpQMz&sJgqO%5XXki2c$V_lK zHv@F|4|ud7OW-IIbO{`IuM=p+kP_$+9k64;!)A<*5;<9*84pky333KpOMM||>nX@h zGZ?`K;+rwe056c60$S)Ok(1>pkqvPx_)KVtECpt8OBl3NQ3JHYTL9dP0k4Nw0P#5x ze2`8B7SMPKq+bf!>BS9NcLz5?AWIaSHQ@r_<^)I9^eJpCM!b#;{M-u6-0zu~IP00F zH$G+dVU(CYe<`E*^k+|*rGzAM96{{^aIpXyIuisJpsw)}d5X*+53wkMJ3kEI@nTSs z$D+WUrNpTTI__P86Vl061l_~Unx(`A>br6}!ac(UTCm3pnuXy~-~mz4jw_3!#PkD` z*`=8#F+mFGi)<|JQop)81Qb}L&6q%)A5bVLFgprlIWh^{oNmOF+sMY#5JB*WqL_V>~`RnUkf0v48pqkETs@i zdHYi?78_8G_u*oZnQq6!@{V!qbX{JS0LD|>>v&lV85x&O-@wPRk@3{_Bz_i0M#ecH zuEO*q0xUg@r>6%BvIz69xB*|i&gQt_#`I!Amg9W;;0+iafqf#=H|n$KAWAmm?UPR+ z+b5qgPIoY30dJq=Wol)bK5;$&3Q+ZKu#MG&v30tFD2v+k`P*1)Kw=I?EKsp6j8CRt z6=m@OiN5pV+s<^3X}YX4pCjY>=?leKl%_j~vm`KH0I{E9E%2%*cs4K=%6#zKnp=Zi3)Vy*xBg? z3M>+gPp5Y(uoy93oW4haWhvvS=}C$#K8%;9uUBM|XS_1~ydsOb*em$*QeH+W(9T0v z1vY_;(|MFwv>C5Xw@_kHk+=q`=|DvQD7i6$?ki+;6vz^|KD|VVC6)2U^jk_Sy4pQ# z3XJ-U;C%y(x(uKS3)CnDsC+s*E?L2dc6tnyq7oDP#i;H8FrTDuN~hlt7IMgDe5C{8jLHrV!}% z0Fa!(jp-{?Srpl}fGwGRUX?|Pamw^psw`fNH>R7avFI}%nVzb~q9AgVS%V3bm(7?! z1I(bq&jfBvpQ*-T&3JP9RW%k%!COqA71w4=4ho?DgJa+H>GJ9<@=PZl)7JZPy_v$Pf!k3w#8|=X+j4>*(3rwD_rNN>E+KKI_!6M85 zjRn+l;Q$dlph1r+4Hjh#!8IBz28>Uq-_>AoVthVbL6gOY@x}C9O%`p=m+YX}V-$Ek zeW50c8sjU7=xey>ACTx9i0E6mXt);3P4xqyFaV`1@U%Lpg)RUN7DtUNaJw6F9@dfR z;o2;2jGfcxX|vceZkv8bn`OIh8#8!Nj?ob^0@V&~(hGp*8yr`Fhjq>|gJy$4rIWz< z=?8RJ&M>~1-mA-E%Xne>X%j03Bdq%lLC{#)9E_;EX|D1r*F__(G|zKiTVZ8^ov_qGa2Vjw>M(3 znLf*a1zrj;&YiBmjnxym5P&Fy3T=^i!KBCvS{ZHt+P=-o{R`*vWoCFog|y|%&|||u zO%jZT3cmI8bD0#F)sY%1;N{CYpd$)E4HeKP$|S{9eC;koY4O4n*Pv~MHrOu4Vc-) zrt_GwL^8c*nm%!cw952uGZtR<10vi91etiJ&o^U{V`Q1W&y2;mo>hTS;1rW0Xl#WA z+@0iyoJVH>+RAi*6*MZK0cwc9Wda?X>j1fU5OltqA`7T}WCI#8WO4kkVzPka0q{;s z&_NRtSxVgCX*+>;pxF`zQ2Js4)uiA}EKn;!M*=X^n=uuDruEF2A{3YuSk0J16j&U= z3!Xt2yK6G>C<-VrfoID=!OZaT6#df!EKSsAmAhJ;;rpKu!dAy4lT`en6e{0aB0q290MifwUX|9iqZ+#st2}^8gdnV<2q` zY>teDO3c!Z_1uo^3e3_7zk<5^999e$Kw25im`;G&j1a$rZh2t>1t;jlaHcHBmXnhO zK+BW_1irw$#|zR8vIspOm_ci_%$Od4Oyw|Rx&a#AVg%hGT(7|7_<$)3>=#FNfv<3r zShzuE^MFqPWCEqd7a*5B0L>qO<_-kDA**C^T z=@WFqB`5?sK*7rhz8--IJY&q{_yM#$z>x)%$Qqck9RFNKi?e!WP>+TQWThk2eQ;wz z*#RZ>@`ByL!wbrq9A->2K-O`XF->4n-~|;>OpY_K#UyA*Qmk8V|DU;# z7qskGg9$0F7l7h=1~{%4Fl8adHO!|QKvpxFF|7b8ghasvuyW9RFB53C4#}&qxCZ%@ zRfCBK9F8EDoL=5h4>~LY;uNS$AilteYiM97FoCv&DnJqy8%i{TRDevi7WfH|dPW5f zfnNw7sJK4C2s#KC)S&|1aZ%3zDR3_^g08;+b*YrtK;1iL1_f}}i(i2gawI8e6&n+S z0vm(}E@lrfg07a+WIh1iU=Err0Uf%-0P25%ud3sK-Z;Rd$rPi=4{DmFfRh)48B>Y^ zlM|?E&Q%X8-C02SOGJT1fgiNu6})r=rVzA5C3-&jG$fFc&MYzADb zfQ?jQRbUc$25Y{8j+Fx4ASnUbYXRyJ)H5k?fp3olts&=v-5tpRo=6ZVaTF+ZWG+Fv zj86el!5JtpLE4Oa?}AIfdCqSU=aAnG~LjFMHIC0gb8%Y8EANPdXNK) zC!)#74j&R@75K?EeUk%A2IIZy3N9=bd~6B=3Y^e;Zv|SXCpxmMf=RJrNU2TFUMj;m zz0rxK$owZ8G?c-Oel}1aAJjej#Rjf`TzT2RC*mryD6oNhZUXn16<8eEN~UW$v$!zc zo1X8?lFs6grN}g0p_x^liLGS%Z)X-2h}Eu)V27wp?{Q&S$#`$NnJbGPW9#%(R~7|0 zZ~InP7Aq#kd(%I-v#2w+ZkO<2iDU#BQ|!rN0-{%XvY3MC2c9fZ_1Bo0*z1`=JF}pZ z0FYsJ=okevsK{c_Wa5B~;)_6!hyWiz&7i~u8p&n?pS-2S3CfS0P6{0gT+2acI4dwg zj`CGv1<&+=u7hH9A@v#=AH{JBOL!NBDwf^*ORMm>9A^r$j@?n0G<(MgYM_EU zKpddn9uxEc2@VC&T|c051aviu0xM*w3B(5tHLEa8&-7-IuZMP#-9f!GP)Caav|$o- zaInAxcF+oSP_G_zZ437@HbxFm*Xsadwi2rfg90lkaY7DJQ{n>OCd(vnib;vXktIus zU4dEPH)wYct3IQM5~%CLs=*|n$gRK%8Yo~FxXuJ>8>lF4I ztKek>m1rCqOdLvVj;9$sdBHKj;CK;SF0yGbvFI}B)`L?YXeNZgjLAcR6;vKN-Ub=O ziENMpyWQ(y@@DEK&( zI6+PT&64qe7HViPF(`4EF=Z&QDsY%FZ2(Q`fNo=$($Fd3i0mfNVtGgCX++ZebkMv4t0U-U zmZfm#gQJ5*mjOH+1WLC|3M`A(cPj?SNV0$eD`+4`feRA+p#2(*SpxOTV3zYT zfVwp>k1;?z#$d*@0%RH_niqf^v;f@lTfvwmfb2C#21Pbd@;<-_8WLdR-VZ$suYnP| zRu;U}3zQa_9PhwS?`Kq~XK)nBQepvBnT+5P0km^mlPScU8FT|5X!Q%I|LQ1_1)7}% zUmu{r>{#z6Eiex>S42n$P zOanSb9JHcHAgkVS-Fj%J3_4~7TItGQ#oz!s2pp98K;vl~aO*+s!g@Dp&?(}M7FnRK z8bTY${h%el4AzV(3XG26^Boz%%`H%4h!IqTf$lf}oi+mYYlZ@&WBm(;EXQ9 zc^SAt%N83Lr|%13DP{l61S$cUrb|s>F;#xg1{yJtQGgVljtuVHGZ-OLpP((vY$^;2 zY||fTii(5wn}T;xM1TkXK*u_=2~1*BVo!IhFLsnKv|>mEZF2>!W|4*qB`C0ghY3Ip z943L^Yzl1pj5$h7pw>Sqr?6=-Wq^*2Q(yzl^a%WB1-Xk26bRs=IT=1E#;n3Hy|;{! zYx@1ojA~39*r)F=V^p2KA%sPS<2dMc89{-s)31cE=<3`C@t6g^g4=?SnOp^C?nY+F zg!Tl+Y$dRd967R-*ipTe0CqjtAqwmuk8vunN~ePsKtp_&j^-867%A8@kn%S}ksVYT zawu|uwvT~AUcOL))lt3>ymHqO$^mVYVS^R}1)xr~LOq)q264(Ma4Kh^8EU*<>1hfSnZYHF}-v+K$*+3^$fg^8D1q)j}a~)$XV~sL{ z;{@61_7NwMN8+W5$ve(6Q@Zr{_hoC@^lH-XF=LCv^8ltAHaXs87!Zxy49f z`nnhv$?0`#MYx5&-)t3d9WC2G8P^TQ` z7I;0-0WzS2Im=N~;3tzJ8;=4vXs-%5YfJ!1O<>LvXq`TBy@+!C3=n??bC#n9sE-FV zNfB%flfXWB39Se>djUwp0;t)r>I>AqfX31akUZ$f@Wr6g4b}1B3oaPUm~Md7JYddJ zVyXviAz}t?(%ZnCC9oEBVG*MOvc(Eei$Uiqy#Q(ZzzkX#`Uc$V2PH&DW`Wzxpo+YO zMTw190aUrLfXr(EFKYmezp*+_0E_%!2A>KBuC7=$nLjX_Gk4UpfXp{z>W~H1QL06_S|^pu?~lShEE7gGS*XI-s`jfg2-mJ<|=giOAQ3ly|VACOWu3 z;Xy19iW^9(1!>J0gLxRVPl8?G$Mjk8 zEHd@oSk)qXlpQH|$-y170%X|=)-1v!@j!ekS z)g2%?(C9dWLI@Ki3&Yay6;>rSUOC92VhXH|7r@J*AnEr3D=0;uU`0v42UyLSZ-9)u z0hZkX3VrtJ2R4f+#eV<^e?SXgNJt>(ErgeTfHeF-3tzC8AmQ7<1}c%*vK)Ui2>fK4 z?zlxngK@(2dkHMkE*)&3o)oL&1UAs}Cs5`BZBFa}H^~;Tf!sZT4aMCZZ05`}K<06P zBxkT?32dKko5&&~vV#q@d_W)zycY)I@eR{+6ImoISFkC7hLAw(JRpgKY7Ze}?EGW`HsmcX^?`x05id2WFCH`uZqbp(D+f0)Q3#h^#0pv;cbmQvHA}%keLRz|ZOPlUS7NUx0)_M|mO_lCt2Gg&dx;@Fem9q~QY_ zYI>CgB@sw?{s77UfQ2V4LCe5P=?2hgaUJZS0BT^*su!5f4(gZmu!DLXpbkg}yBX62 zcDV8xAmt0d%0UMvvVy8sfrIP{teVU_*aJXK8JOp8fK=XK&vIN0O8gvHG79MYMM#!~ zr+(0gDk7~s02%Rs9UPkQQW7bxya36+V9#>A47%V0IWK|(0Cdxl8Pf-lq95#_aYI(e z5A0b2y&wSr(1OvaY#<+kw+@0?0*ce^Q&=Rexy_j;fG&IIHfQbtopT7jYKqly0!Nm> zH6~C&#mfNdgM&H>pe10)ewePfLqw#028RMG==ehg2FE$mrwE|tzAYR|Y`mbd`~)~z zZU7HhKoa2ru*eDylvKQc!<=~s$iN+7*%=(jbpw(|FM!nC0I6|zyugtKjmbNp1$MAF zc>q%KfCG}?;2{b&1Cm-^fE2udDVY9#hlrBn2awnY4%8x429YG0!F_qqIs{%&vVs`& z1ElE(2RK-9}0P3SCz{7aDb{dOh z{R~dHo&_L1D>y-CF0eW-;LH-Z3rg`DII|Q$!&YE@@QV8grxF8bs~|6^M!dlZO7sUf zLE}Y`;J*MC*};ht{2MsUnNNVM1I-P9qOCzTtKRVis3zqGC2FvNkR}?))DvJcW^jTd z2~|#k(ToXF7fWY3eghc*EzzYB+4TX)H!qMKufPIrb^L)jUP{51)o}tB$n`&vT;Bl} z`GD&B7o6tI4WMHm8o;s-K<=6zo6e%_HUlI&g9{q1NRcRoHEN~cCC&nn#sys9sAZb& zyIVv&cm+sk1K5TYTv-B#*cDhEcW`Ba6F;co%^@%yaWZ61c_Y#8?AfUB&IhSgXVhDQX-~aAi5JViY(6 ztH?n;1Q~8nA02E6s68SD3S4G0rWYWiUT{Gwmg(~|Sj6i;fP_APO;_Y$b^z@{4q|ao zfEo@hq;smR772@;UVQeYD}3MzM5HJK&MnP*IYzemK>yFnI|tXUm@NP}X3 z26vXgG4Rk*i>wkeOT7X!s{;6J@djBlCJo1@4BU!rJQ5&Ra+onK;D#RO!RokxJ4@i| z^!&XdaVaZ6mTlk$nF?A*4{{_dy&d6(W)?|MdygAr;Q?->%mNnK!Ht?(xXqbQfQ&o= zmR-S}C2*H{y8J#74WaGqilATsZF2-?N`d3kBln3|aBzd_jsQiT=`;6<*fO4&9+=JI zAaIghkxzlukwkTIY!!V?Tx z#*7RCXW1c*1&|Dn0=K}q=@a*h_(@}Qt0ce?#3ZnTefrP+BC!b)@di(pz)tq*KMshfikxJ(V0r$;7B$gR z?3T=LQ{2q2C@U%6!6ar-daEZ=i&b)wEfmMNf z`t5@vGE95fr~f=Cq9BRh(*s>(F3s(DzGu4OArW!wqaY8nfC@Hf4^0xD@lWuAhE932 z96vv35O8dK(ITM00%>+iJA!mfpKwS-T4+7U0OSypgoK#xVG%Bq8z3WY@Inh6grj*S zLEDys!Ho_`s67Cwe88LKs5|{n5sQ@33lRSWFP5Gv!hs(^YCiC0DR4M4gD&9CXA#%_ z0TTOxq7T_xCI`hNRETU)I07`hELCq8FilD9v6R79K1WH+;zSr7csMFY>77v>v zXxOoVFH7M3biV=?C63J?MWAk0MFETK^w~#5IL#ml8g%MQgDhUd`1XKJ>)^{0xWGO= z?1+dWC(Jme>GelMWb7uu9C(UdksnkI5_BN{Ua*-H__72pPLC*LQHqBfiIQ-bc)&#( zC;~voy3YVvqyWnIpx^-KGYwGQQse>UH3e`_oC(yS1$(GNHp>wfI@50!vPfreFL&fG zRAO^9C{kc^yv~rNz~;#1DQ(5Tpup|KAEv%`Kb$fLmP#1N#w8z2p8ZZU%nz&a@d8lz;+hK+2`;L8%Y#6CTyh(#h1-aW?dH%1iq0CMhh|6?MOf*7uy{;!BdLTCjj$XD`$&DP+7nU9p5kndbvY;sYNz_f8KeVG(88$3FeRaS^flA0U|@5G_nfpyL>z zHxY><%E1kw#0uJg16mK}c#pwNT9b*zoVkHtfz@#XKcrrUw@F1n(F5*u?EuLi;D@yD zcJPBd+rf_*zyb9Z`B_1SD*u?ySjr-9dIF^W1V2&`BBgO&5m0gO2p!pI;LmboDN+D+ z(|t=>#Cb1(v|iwcRK?Q^OIfs79T^0!uuq>?%AyHno+@RLs=onJc!NJnpcOg@!Rq*e zKg-b`+>8XZW1fH%++#3fdI0h?tK$RyEHkDLpskmng-EQ9ANaFi;}k-O5c&bq^@Bgl z5iv4@)GiYQIUPJ=HT~X65i!9AaH%7Z<=C>OL%@+u;Oq3RG8So}4v=t%0Foio4Ni%O z)bB&CBzOhju{{B#eue;!p{_Lo&_)1A)d2xe=&yjEWzXuk11z#Y041r-5HM%n08&2z zEW1GC2$(T_5KsUG zDXZf#2FK%|jRmI}{HMpB77?=j0n++G0LNIA06e~sY-q$&_>0DLG)0Fsj?RI-Sx zt^nyrl2 zfGRPVF>Mfp=j=ToCa5qlW7+{?G9Y!2{(-tj$RpgK2G9;cQ2N^-m?dzB8O13+Ts%C8 zL~sD)?h|0O2LvJgLC~TYQ1SuE+yKj55Cr!w!9$TZ1kIQpfTSJ>f<|%_&WWgMzX0)H z2xd7NKnAvXVQCj)Fen8>2Pb)^e>g27502aqSR$7P96_MLo9P?RiHPz102%xP8o3G8 zEDq)kLZD2b9zQf${j-7KEzh5f>^)92N)u*cs3-5tYJ0;!Q!2tl+$^2G`v zct!%p7_%AE4$wK4pf-mY(*}_38^E^i5Xuq&8w~2rf!a+6Kyn9!vILG!@2_PM^F*=a z1d1gm5SD;-Gn+BpKv;4CWY7h$B{x8pOn+C)qQ!W5x?~-Tn($?iUpQfDNr}yj>Bsb- zIu`Z%A42d^R(D>|P(GU(Q-d&QC|?*9NDUB@F-xEgH2iRt!Hj8wFlerzL)eiqdo8$G zBhbeN?gFh4&QfA=Ja7`eXO9(hgZ&g?(3uBjOdCLk?tn=y0Ex0XPGA+zGGm%C{cjzM zMEwqsiUVK^cL;;VvHHOML=C0`!e&e-gcX=UCrU6YaDZCt!qD<8OA&NoAh!Z|ULKMg z&6qBL47wl;Es#LvB6wL@AulI1%0Lr8;537j+jzymgCXF$`3A^<8^Vx+YE?apw9W&N z$OEw38W^(#Zn8TaUNVuxkMD|A6L+}3(!y;xY~fEo(_KQ?T=L1OP2MKWSOrO=nqH3(b=6I4J+mXqW zmjSd?95n2pz~*?1A)AkhiIIU@fz8q04YY6=GMX(2$WL_wHk_-#2J51SL|RBVdS5#-O3`(0r6Xp^z?x1 zBBIliTUiY1PqQso1g-K0ClpXIH3Kw?3M!Kz9U4$yb>4l{zA7(hXCHJ;&H>;p5V4WPl(4bsrI_l7nW z5pb0bv4e+u`UQxYFWXp@MY*}b>-bnTnP*5Ta7Taxj(fUdJ3Qb(CbLZsZf6mLri(Mu zDCvTCdVM>KjK&XGb_Vt0p`|8d)DFB+9~On&AkWD$a!>cWEdukG5D&y(ph2@6NPdz4 z1q{?nFF;;;0rQe#2a7~K)H|R}Sl}ko97Y9rkLWgoo3sWK3wZA=T<{h|kXsQHMUXb` z4$yeq0ZCBW+##7IaEc8yVRJ;1r5-Y`dO*^Q=>$m039u4SDRG(&)K_<8w5n&g2pTQn zwqdvcQgJ~ti`B8|^kf0h%6V9&a08?QV$%Z6w!$I0MhsY7LJIX2`DB(EitflkQJ<~pyi{`gj&z^ z1Elc>78^mk=^!zM-1P^|=s}#?AP$ORacImTY~%$mtp!c%?SMrVbbJ7wFd?JQptM^L zYT|(mnjj8Kyhw=!R6jyuV+Kgg0&!3hm>~|Dm2+eOtwRQ7+9l$kb$#G2$O3UQrWNAw zuF(dNrX65S8^9ze8$+g9FNj0tKI%b^U@&9)08;uvJj-zjsG$Mb1%#N9g9HVu;|j1T zpw!0>T4{jfQbe%)0Gaed9C_pfSsfx(H-NHpeS<`nz8XJB(fkgvnwP(XH9~pel?i3NPzN$2Ga%! zQ0=e5v_=9nB&)%+LPC)%2wX%U(kAH0Z_v(zY|yz(3LKu`qjo_%c)_^_JcY{vI!l=& zTak^~0aVdtD>1q9GAV%8=7R!E;M(;2y(|*-CqN@%phW=Y%oji|JRt#!y9*Lo0uND! zP#{6e?8uP~>6U>57HP&0)Zm7!wLJndk<*On0EmVp!5tuzcYsYkfNF9Mb0IG~w3Ne4 zhDheKb1Sfb%SMQaH$W!dkbsqHs0yKZhUo!F(F3?5w&@L>EFy9ppos4f&vN_@xz6n? za)HV_-LIcTjb{U>DY^mDZl2!I4;lOW_eeyHcLzvj2c(h8G+ptrh&b;7kkA29NWPeU zub)LF?1U&habEz*-T=2@FNh*H^KXcnF+BiDJ`jbLIN+V_;Jt3}9>fcff)}EYMFG?I zKNb}IQgBl5xr zNQwc?H7T*xgGW(#5xH*%NZSrb&j?=JKy`qYL4%#x0NQ*Z@Ca7TB56MW(tbb$IrBm% za3E%&_cxqi-4TQfPJoO!Ap$KHVY=0!_o;|f+y#)X3uwAPX;Xn6wBG<0 z5jQ}}ZeVjCxI}>TrdS<2Al?Lb9cw^c8**Jyo>K2S8`QaAbkkkAsHMSwP$IB+Qu@z`h1e_rhH4qQKAcff z4C`x|80*0srwm|+6)1p4(!qzjfDYmVtw8~8odO+!CZPb?G6mj71&&80R>+2S zjVuM0h8xlvBfW88gBj{8DZgo-kMPHy)3C;`x|Cy#I zPG?bLY?$6Lou!bmar&?6EC!7Cr)$q(sbcJ$zHA0dHd7bN^nLC^64SM3vXnFSOkX^c z#htNt`sUB6X;|H?HLAzr$Uwj z-*lBZEasX`%uFCFK!*(|u|jq;DzWG?>|u0d0ku0>Kx^GVivoqGx6EO2@c|7ZGFmYN zD!>k?$WdSd4QV7OFhOox1yi6i1DHVDgFu^hm=u^anKP6`%$W-mn7|8MnLz9N1)fe9 znag6sC_FuIE{nD>&|1*(Scpm3p%l#$??af$&h=-pMbXEwt%h&XJRmC25p*m zoN#%1>pYeqrX`oB-<-!HEjtG$djfQ7H|SQvF4&qLf$r&o^I1e0d#0<;XHk>rV+I|^ zumT+6piIoe1RRY7rc7^K$RZ(#Vd3;u3t40}r-Iziq`|a;U5Rt80yFsf1|=bL<`XQCbaH_O zbhWGF36|-63t1#|FiZhEn^l(~8dSV9IbLANa@+!P1cSiz=_ZR?=AZGf(26FCnoy9DQjLp;i7PH7m&xADT zU3r$gqABXJL-V6pn)n51@NJoO3a`mk3l;J5%Drz zVhM|kg2~sI2Pz~zLkQ8L|m$E3! zLhZ?gwE93hfDqQroPK{Pi?kxt0*Dfd19jap7CDW1;4qxRN-zw+EMu{j0w+C2oWGn{t-@t`FsHe-K%b?@P=%&a8>PmvoX=QSp0S@&G;53Pd_BjNd3_hX`v^!V{ zyg7dctJ!pi6)cck42AvGX!WIfDEP z+78wU=3Toq{r(CTMUe$ygEz1%vG6j3H8|e7G+ksRiy`By>H1q(M6r|uIV)LA7++0a zxRON-i|mP&EHdm1nYk7C1r|+zy^_V+{1g*tVY&+B8fOX6p`u)%iem>W__`)0O{OV~ zpb`XIb~j*Zo;kgL6^m%S2ufZyXI=qH$dD3k1$&l)$Z`Q`Q2D$A+N78R4RbOmkG4p0#q0_uqp9?=oZky1`Gnrm_b=%3TU>4NrPzuBV=>9 z22%qIsBy-k!Gx52r{^wW;l@=#8K}PLZW3@*C8lI`Atr%BjYA|*e$2Y5$cA9G1ok}( zxY9-UAKnVrKy~5l1_4KDs`&?M9De^86Xzc$#~Ey(HVx?5YM$x!n^>fxup~)Pqg;b& z2Ad*y#)`*`3DluH4c;0LTCTyWz|8G2ueyXx6KMdbCPBpuEiF2-*$91lkp;07=bnKnat_5wwt$vBc4?u*{JObfD65a0*?) z20E{lQ3004v9ukSd04nX*9bX+PS0R3ECZbwAmv!;2)bI>zR;1)OeFRd#l{U< zx9HfxWX%w$z^cGAy>c^)j>ZbGO&bX2h3RKEvxqaU1h*Hll;PZ4Sd?*>|Dju0Of+7B z&O8Op9fLvwkC&!z+`^JfLK(=!4G$o~WnleQ7Cvdr80SW==%uC~+R9?Z2&wdAKvg2F z(gz*2zzcFdCv@hW&ygid05UW$RfDd%A%SvzyiUV}N8|d%?&{5EguHczE z0mu!3ECQ#dmuzEEsb2-_tAR`gb!-JzGlSMo^Mbaz^C~ccx^0Z$u@i8&S70?W_z)Zx zZUsI^R#5*7c2o@m=%^YXUbvui7eS07DcsmwH*g-Z*ERdsYK%+gNTOszGo(f(Fr^EtTjmqSBf;CH^ z2DBF+G{&RJJb@9dJq@W)L8Z6?xKdRRhSkB$O3a`pwV?0dF@#fovtbj6)4g^VwzPu|JW zFZ~j{TRz2=7kubY0;nheolvL1Dllcb?JgE)##hrDcd^(ozMj5s7YpbVPO#t`kf8K* zt351c)AM$-1T(&cN#EVg;>h@JyZjy&WzbdU&U;x*85c~i+{^NV@%i+keJnwY2d5w1 z#}Wo{hQxG}{VbY{FQ&)sXYrJSxMdF`Xha{}L)*a!y5~WGUEmcP57YGj`&lIAl-LDc zgPjag#;Aa*?9KE)`&ncq-@+9zAS)1fH{Iv}ixud|%>yjbjCZGZ9$;C>IB~l6L6!o> z57XxyWKn1QIQ_^$mYIy7rpFy(>0$cBHr?jl=3d zR(F644e%r+kHClNcBfgSnLb>a9(|fcl5yGe($g%(q955nO`#o(ASeA`%TnM}U>BG^ z{qJcOJ;smIbY5W`28%B)4!bokK#SNz@os|Iz9L-%Qwck)4k5I zm@>|t-f)gZj;j7^2t5KXfpj{o_^vQOFiSY=^ocvqS>Z` zuCLiQedTqQX^hvWJKSJVWxOyw?FLH$+gU~>c7dkpcW$sWFg8z*y~&cvcwzeSn=G}A zQ>VM$VhLwFI(_afmMq5O(>ZRl$a5_REka~gU=mm{-S9SxsTQQ-VbA8{VParoV`F1x zVPj)tWdkqF5V*mn$P8M702-mXIeo%y7BQ7uYyrHW!5GMVnScW5kSit7h?f9(zZOX4 zwCSgAvnVNS0r}Dbv?+>F0Mg&HP-5l<4X=Xhr)kr9?y$(IK{h7}Ok-9A6{4UcLcxpT z0zhLd;FBpq*TG$%9&m@nj&a)b{yQuZjMKKSxWiJ-$ar$I?E#B|#8GCr zDJR%KXHhXbo?rv5ljM8I;we1^VMha-0<&WWc<*xqTb96-=|vA&G8v~&zw?k~JL8?{ zQy#I{FkYR0@ezx%+FgXM9jpq>jt5w?lt46S@Aq9c&>cStOajxVOFd>$WV}7y<}r&j z7bG~q!ikSrBpI(xuYSy;$+%_u(#I@{T%hBaL9<8#)2Cm6$i4#UoiLr}2}=*-gy}P% zummwqpZ?|v%N@o$(@#ESQDwY3{ryuGDaNbQxt_5|i*I661a&1Lk+%bMnb!`cEP+ka zZJx16Gp?H+@r*^CZymD&v*QJ(EYOIW!1U?8&sgLcPfcI@j75_1)b!)eSd1C(O#l51 zY_#HY7DdME)19BQC^B7Vn*N}kS7dtCa~6HZ>DyO5XW?LEoIZW?3zjU#d(-7!vM4j& zo9^_IrH1k9^!+bc>{V~9nJfTW=n0Fw8K7tbof*UoI)C30B0XLC6^oJhenjL{K;j0p znP&g=oL4OJlKT*n8K7=HXjTU_2(oYbl2t1;n!Tt0f6MZi@$U4;?^x;>?@mv8&r%1f86Lf7@t0c6qyW0J3UXr)_+V-o zP%Y0TaCf@*2bMLAcenrez#_rOcxt-fCzcY%#nan9vAD4vSTI>YVDa=zpI9^)7f=8F ziN%s}$#m1tELx09rzd=7QD9s)z40@PC*#iPr$4iVF#TtmuK0yTgmKn%vo9=~d^Biq!Oc*y#&-lh-!8m{V!fz}( zjB}=+`^MtHxM@27cb0C(&C_RpX9;7RGyT(dmRQF5(*u66WH4@-zVipmJ;u$`XZ&P2 z&Nyd!!Y`H>#`)9t{$i$;w@rWghecCpJ18anUq^O#Z#b+@qh)Sb_Hl0OJL6Q$p0*p7SucTauIs>HZwIv*peI^)ynwT!Gf z(~}rkr66oKroGJ5>lj(3r$1n1odglnnLeM1RgiJt^o>lcprhj(nOSwF{{#u`M-d8U zW));SfFxAJcyRg`W>$aML(Gbx#u4PczZ+oJvw~cIgEdRw>GT8^R%6CF)2FboI!xzc zW|d)lI{hOHtL^j@2peo&IV-Cm<548Db+%sw2{9hr{+Nwbh;jNgPF9iW^?a<_(@i*7 zrI5J29ALk_=U~;Deg)2UV|+RtBqS=v4O*CF#u6W-9h!wMJ`r-#`)73xxu2k+^mrx z(GG5~+MV32pj7jWn>CAZ@AN<()>cp`Lk#6%wPM^mU5}U5m~sB}BwkiM#`)7H^0N9s z-8Y?!58?bkJ|v&HG96={ZphE7G5sAMt0Uus=?46)Rg8P5ujXf+%=mP=y#T7y6{a5& zV3lIrJN>Qzt1IK?>B@qvzKrvy7Yedwg9HUxjTq-o7Z3td&O%`OCkV01>Nzqvg0A;r zbv(n6?Wp1@t-u00^-dZzUI;$8n%VIHsQ3ddwr3KUGyS#@YXalm={CZwzKjp2cL}pP zFg}`oT9{Ruaqsl!!eCEIi?Hf3&YA8d!s^WUczT}*I8Ch*0n6PJVg0Fp;}T?=iIIVa zmm73?Wuzk`sGq>#cm+iUG&98}uzfn47^^bl_UT$;tl^9crZ^7*zIsv+4OMyvX!E^y}R%6Bm(_O?_%~TeEs$*si zrVKMi1|HDDEo9R`3oIRPT$(;loK+E0D>Q&wC}2a*inFTdf@%uTMDYYha8C2eaR8jK63yGyX@@NEG(=>godMhR9G#wpX6Nq~L(Pl8num^y$ z7^hCZCdq2WxL~@V6sv(Inh}l+Rt#YZDA5A8>B^<)SyHSrHsF)}!N-4ZfL5QNqZJ%C zFlB)nIp9;=cQBcOR%nA$kqQI%e{RU-JQtX!ACh9#H3G#1hrm5%&^VwEbV{8`gDFCZ zO+~UXr-|7H4p&c}j#gqlQEew2MI}bCf0t;w3fJK49(IHzAR8KNOuJUByX5gMa zTbfmY@yGNy8CG$Dr{E6UAMnyHCJm+^(;rK-RzW2NpRqC3gT^iYfbM7k%eKj|Hfe!Q z#)kL{+-&A_WGexA@dXpYj~|$%L2jOID9b9Pwu3oK0j!?EkqNAF4>LmL31(@~ke5K~ z^g>zIpd7I0K#7S7WH!{>;JyIJlZ;u83?MOX21f?aDqQd-)u720Zcvxrnh`WpL_60h zOz)9nHHrp15tO8{Iq?Ce6CW^w7b$~PD}c^Y1D)x^lBL8b@Ejw2#pPM6(8HGvPx$VV zXO+@|g)eBJM*)8r|C47ms|R~v0r(8-A55TV0-gU04`U|GFlNf)2CXdsoqPvc6puC0 zDKLY3X<%O}f#P8S+-T6s5rJ2rVN%Bn%vmba7b~z$ zqvHw2EXU{5U6feMn9d!TzDJ2w(f~T)3SMu?s=)+N4%+1dQVKGc5k4)$#0|O&l~tK_ zCgZ8;^OadkmFKd-R(yeOLt|Fp5SWH4A~0*ZxeBW~a6OFE2o>Ov+6KTo}R4EYRq_O`gC1kv@HtP-)E-Aw|D96U_ij{J)3j&D1` zJVtIu4n=my2@4tp6gfZ}P&l#_xCAbM&tT?J^v8FKn*)_dDn^js4%^dg$55zc-8f6`*0dP5p zIa34_z-~gj6?eZjYZv3J>0Ualo{Zb3Ptjqu2etLD>abceUY*Xb%c{@24m1I;0qOJJ zpYE;8D#N&PdWJ5mIpdA#3v^jU8ShWurps!_xPAIpUDjB}mD7Xuz?EW+9;+PVl#-^^9+|GB&#DCK@9VQ#Gp?N8s1G)D zl|HK~2k3Sm1xA6D)31YgkPf84%IPcytO^{E&ZfZ1={g3ia*WrfdmFGiF|M56XTU1K zxN`b(16E_kQ`2u7u*%rJVuV@`p2`7bItEbRNr6#dHTZfE0nk{n1``ixFa~@=z67Y= znZpDc&{#9w#E>4rwET700zX`o|XnLy=;+w@E$R%6EI>2r-()fv}JKWfA} z*YM@^HUUQ_Xl~{O?d4;1)W{NOhRlodGAn=%aCOKA9o-7L^T_et^vTAou8a$(-!x{G z2F2_*W7cq{|E$yfO<2vCdj3yuH(@PeY?%Jbgw-6xH8y3nWo(>YXbR!pH)EBVe$13r z*JA=`7b&b50S_rMg3e!uSq$2_2D+dfauJh3mg5vO^=t|d_0t{9StXce{GA?U&Z<7W z#*9^yv2pq`Gq`o05}jx|xfCE?gy`%A>$Cy6b%r^s1;{Ix%vsI&dO(Npun4GvGI;xj z>0%bF>Wt0P?JQVjnf|eEkF{X^%fz^TdbBlbG2@}>=dD>K1rCdF^Ffa7WO6*lAaHp4 z4{O$4;`2eh9?(5i2+5NS0!OBwv|-)MG(%*1oh_@4-!UYurx^s0xwjYuj*D>1V=li_ zU=sKaY9XSUeIH~thaGFO&Iu82UQiMGoWb!hg8+>85XOUE;mRyDWmE_EsEkO4ZY+wS9XFFs&{y05Bz;Od3 zsBr)qq2>VPK}N@=Oj)`N^$eiv;~f`(*bIuG1D2sPnVW z1x*TrmRm7pDS^(M2Th$uLa0BK-P-zZ2v{nFYH#3;~1ayBTc$^h}S0$4M=*A*O zN9Gbx&x9FtxIXx}YOwz_L09;$tubeI0L_9unO^9|Dq8;lG{y%~&g>`%QqJtSdpqb_ zGH{Uy@{^b&V-{%TGLwRwInxr5OPL(+fN$e8V*=gDqQIodbcNBJ8GQ2#lP0r+lOV_n zrg~6XoWQKa>I&MzqR8yX017o$N1kjY&|oJxU2 z2*^D}D_F8XCvroRDmYB)LHE;`F~KAK8%Q(gmK}&9(0Vmcv@Kv#1hpd=AYRsFp24Ij zs{p(F1mbmOGbYfDQwp-?Ojj5|i)Wd#1QbCDmO&9bk2r@3dT$Yv2GbKpXpB9n2g}W1 z0yW!-i#w2W<}fLO7ENm~p}I?h31Sc|`+}nc<}OC)^;j%sVE2KHdBUhvFAk1g@CsSb z%}9_kMiz7uC=+Or{sSfjCZ~pmhK2%2385fs&J4b2M;2N-aA`1YV1kqyj#t24l{wJF z0S;f#Wvif)l<5p8XTAX|SiuNBSA_`_G4)@-LK_f5knW?B0QjPsCm>xDzy^XsQIolY zQBhuj$#D%c>^?9@&`HVQF(M|%4(2S7`#|QlfbQysBwHrOKTKIlf@Vyh#YGD8=1hMe ziFgTfmJ)kCcuWp_Gu8&sI%y`y9n4vdd!}_l?|XtI1JEosD8N>LcGH?MfmT;&FdYEh zMS6w_R3U&SHbDo2D6)cf!GXGW%nk}H3LM~_BB1s*sEh@z>H}pcHZ!IZpyHg-jOhq7 z=&q}0pi&nk!sK{>Im?Xc0w}3LQY>g`RKqFI>OD}8473d&WGQI51uw`Gpo_|2w_v3RXj>R)HCtiG}(;OKDzQbY;o2ek%5%*8w zwjJac@ERo08auE>6Iejk=>EWAcs(;{BLQgF52)5=&VpU|1y4qcYOpZH1A$0)E2IURMGfY{gpyl?U5*nOFum)Z|$ia}%1C?mt z)B^)07xNjss;&UWIxl#|~D| z!UXUVba<))t;Jyh@5X0Q;B^C4MULRKq{ssrU1b2@HwBtWf(Dun=;(Jyq=0)$U~fQt z1s+W~3p%6+6f}HhOgBI@=w?9>%~QYD9K2*6RC9q0W6FY@;>F~6g9UW##%=IIu0Jf0 zTYON>jeZc~{6$gAv4io55a&WQ9puhpT;fBfa2iVn+a*)T2sfAU6Nr4+u-GMFx zTmZUo8&u%3W;xCS9Zdv24gum3P%Z_jSpjMffe!0l08+pQ+WU%7;Mf3O#|b(B{SLg4 z0Tul0W=tDE8aJ?JIc@-H1f2(O$%i@15ndL7Y79h(fmA~; zR)HDRLBjDq^X4&)XMq;(*)K7uEki) zm?l8OoSV z1tCn()-^c4a%3rRI$mMO5&$)DHJCUQB|x=5kD?^Ag95LDq#KI^w86@x!Q=zYnxFsz z?YV{r4P>u1TF^WNjc_o6)|f&@*roO$4aT2f0uQl6<`_bz7X`EG);|Ibim)g!3p@s| z++|Z>0S}U}fX-TCQ~({EAn*jH~*)GY~9VL9+~?CvO4eH`afO8W(K<$v~wSHq%znl@Lg)4FbCVn2$En zfC@)5CWz4wKusM`7mfvV6gOz!6{Pc752_y=6rg5;91m()34&&t1i|Z^!1o-^V1?8N zpqvZVAaDg&Isq5n#1xV5a z?VkswSb>)?YoYNB%ebIa46}rVTR}pB*O4bD3)G2ZQIrC;0vQyg9T^n)AX$>hL4i*} z3e@nHu2h%Z}xWBB1uE0!x+- z1A`L3s{*TQ5ih8xA)>(V_@ANBU4h?`!LNvy0kry0K^Syf7$kl0gP8mpOe_MR^@NZT zi_wY!RE~m%tlV3h0#sD`;^xW0oQx zIDA1Z1<;0h(D~n>jqnN}TNPQratcfea`lidGANcnDe3`eRNw`;FDsy>z~l&8TnMs2 ziA4a^QFlB6x-AZp*gL??SD>mKe5E!-0cdvvi@-!sMGCr^iv@J|DHC{+Ba6T^kf=nK zz-my^f)tlZJj|{N%#L3e+=^Tkn5R2Mux8dnF02F(^@E$QERH(h{wt`v2EIfPKBnOS zx)dB#Au#zUFgc!K$Wnq<+n~$b96=*b0_xD^@}Ln)1!e_Cf!E;1s)7F@QR^pnWAGpm1{BaidE>;1MHe$J-jDK7znX z=pEspt~!en1LR(2&`_6x97^%dAixS7|q(l_biX9hV~I)qYbRC3M}A)1#)XHivU~&c&OQt zBTL{DHzc?~i;5K(xtC6VXu)sBIAJ>D4Mwr)dY1fpj1#8ETJlRcKxQl%1ulVBqk|6` zSc7y69_U_BXkugl-TwzV4Fz(b59q{6fh;Aq=>f5<;;9Ud;PdK1XF7n^N-{W3=2BpB zWGZ%KE_7thDN|x`WG@754`c;jiU&&WECOFZ7ap>LZju8vYZ(+6tr<9*`LvXn2D&D1#$-Uk4;>2~3}m$0(`>>V|QETE_1|JJ3J@ z$Sn+>WpSJV>UZz>I$1#AkjQj{$IQk|_n4>Wd9fHzk4=J^f`O z>kY9#(?q7POJx;j0X2lDzq8|$WNe+zn8JFC z@#*wCDXcP#tvepeuisGRDHxKBt6VPOcqKX2e zqY1PNz@nf68n$Ev9iYIXz$S2pRfz-A+6N!EqmTujiG!9WETBsol|Tz_II@&vKzWlH zv|<8Ot};6cWYsIl!NfqOf^M}|F=sLXHNL_3RtSJuOg5kcErIViAzE<#jb0G(9AqQR5`DI7uhx^zt-gR~+WsEwyk&jP9_ zArZj^J^_czjh9VIibBO>g?KnVyOZuOw-%mNCy72u=_3Ah)afO`S)4d@I(N6>Y) z(125bI22Tdf*WLPpk5~%q(R04>fmEbBshbo{_3i$RiHq@5kABR4`WG|5)bH_a0bW! zjBdOf3Ou?Dpj$ZqGZuRCa^MP_dL~da1(fBng$}8K!-6kxKyjxCzNs83nCd~L8fdx{ zl9_OZ6KXJVfkF&98L>lB5gWvQ9#AS`29NDPQV_&}Lm$#vir_6{;POcj)cs*`1eJ??0-d16q^zLuxdJ)~l?6Nr!XT~43lf4C zl?>8KoQj|#sh;T!A~z~=fzS72Rp14c`266@yg}KB4N?XXOeLV=8CLeZV0Hu*3CNiU zRAfUk5vvq8wAiUvU;|};(81rJ+yovnfE30`tl(Kg83oX_UQ8uQ3|XKe9Mp2)$^sRh zJWSwQnG^&eX&$wdf`tW&r{Q4%E`=ceuD@EPz#y&2p#T~OQ4~=C4}l1Q&Ju8BP-F+S zpAZ2{&@jAaF*_)TDDWuoXDRVI)+-2sQ>2n~y(1{!azTqV#zG}lQ0@gaCP22yzypCx zffdyA4QV8OWwt7T-DX=K8DR5*dfnwW?X#)QEg9a36 zH8w|?5~vaZl{1*`hDM$QH1Z(c00)IM$ma_6ApbblgN*|PiJ|}~--3!%b_Ege*n$$T zf;`B|ICG; zdcB?pJa>85zLgBMk}1y)dUEL0K#cg(ponHZE*mV>e`_UHz6dALARSuB{*EdoiD;3bfp3cT=UG$?!4 zJ1F4IpC}Q|pdfR1MGgf>9P@%QDyYI>%mNq5+zKko6|%(Z2@DJ zKnuGfs9_G;A;1Bq;yzK&H!q8`+ugdElfZa+R{Qee?%Ji@5N!OI3R{s5yA zH|PvU@WK)>iv{LJ@Vb4eEXOH~ps`<&Yt5KgKs$B74mbgFzzN1IfmVnEK-&*c9RTX8 zuuR{?DkD-4?w-5=jrD=MC&!pUj(7ud1h|p#f>8-PEG1yZBmg=95b6hZM}aIwZjgWm z69+z5aqzH%N(^o@rVk)jeE{9!0dZ9acrz6rQuoBMfob}BRvGbnNGlI459%!J1-oeq zlM)9nIFU|ZLJAlb@L&=606|b-fp)QgyM+Qj*g-y70MW$_$|p0J%$dMvr-MUj1*o^O zf+F1!NBs$es_#_OO8Z zOQ0L# z-UZ-(4#K@k(>Jinh}DA{bD)J!pso^gmcT!7B&`8i%LsDp3TBi@+5k2Lv{Q@$v{jb@ z(szeO(hi6&uwyqcn=`Q}fNCNhP@RV}ZPjy|G4X&Vk-=_00CMvI<}868h?_yne!xjy zi4&S)SwM@I6u_N4C3ct?C{@5a!VkbsJOTI16OgOGW1SC>{Q`+#rFvFKaRF|BBV|Qa zZcuRs^`9cB%L(>}B7=gM0?aMK3K%W`_b3}!KqD8RQky|wGdMguSd=(;8Nl1WSU?9r zLC!5}VNqhQha89tnjix8Vigz^z?~5#9*DDeK-~f)XYp`@id^Uv4RmZ6)R0kN0d1>R z;1ieuYV9+j6=|&8kYvLS(##E7X~1d51iEHKfx8~efP^Y&)I*6KJlcWiszC-9z=;`D z_wouXX9tZxfu?RjgTWt|6d06P9aTM*I6>VL&@l?2o(-#`cD53mBd<5;JR3(w1-25$ zo_`F5ASIxkZ1rwMyv$%@>X{rs#;gEEJZSmdqCXv=6QR?gqcfl*f*l_)gN6tA6vP!+ zK&RaDIx;%`xjb1wU=cWDJpjcos8bIang$mnjtT;6U;{j0BYuEp1QcXoMkvFLs0W+z z1C%+TW^4dC1+-Z1!16B84g;7|TzSFgObYRWF0|0ic4YJgHG>^LfQuf`)^t!U(!hex zsSTiH0J4Tf1L|!?(4vtQj9HFtpjG><0x-*X85I;2*aS{7L0v5H6WKeUaSU*Qe}OT} zkqgJ#TOV{Q<5gKnaws-VvOxmAF9h%Am*&>QaMevlZE(Rx5zU zq(M43S?U$o!ACbUa61}iE3rGWdpj~Ju$MRa zGAMF@dW@2wCbcA_D+@Y;7qrZoMT5z}jLAiT5fnuZ3XC8J*(flA3Mc_{W(fsZ1wLq@ zB@4=ie2!A^o6NxrfpkD^+zBk8nGg=FgWF2eS<_e-*3VX8l7Z|6XkdkmX|pIWfQGTn znGL{$!=OrZ19)Se0u$)CIVnenY|z=kpylO?9H5e213WOmq@&0N8UeX#R0Vc0d(&)XhxkC)RZseW#(33uLpJJ*%jD82Ty?pC_t{h09peJIw=Ko z4hFmsVbEkczzQ9re8HTh#HYZDF82VG;UHs*pt74u;1?(gKr@sm#p?-XbLI??!zVB+ zhJbc;D}YN`hAe^m;ObjJ5j2Yn4g@6yb7l_(M$ky0q7ajVf{cQ+BU?$9l8B=TxH4h_ zPk}2if?9K+o(j7!FN1>6^!h|r`Fi&3thG*080wrZFf=SrhunVvZi0grPceX(YcYdd zte^n%6?o+E1alVn014EI3~41cNB%6wnnEQ|V1dS^I0RHc?U@4bT6U0cGL)D=tHF3c zDTP6S$yb3%fjtXU_kb4Va)6i1gBC~fWGR6b`Y|i8J2EKnXfQGS;RfX>wzWB1TT~4P!O8_utGw(o*lH7fJ=ejkptAsR1|e&P-FtFa8O`U5O4!c zP%H3*Runigc(8!FMxgW3v%t#|IY8mWE%2UQQ2}fxXqX~fNlZaud7&bkf&zF+0%(C1 zn*uw?03}XW@R9`36)1uVoRFD&PRDx4ngnjpP7?+NHc&@P-~(uM8e}>P=uGMy(3pf7 zQwC_s5wvat4Yw+Y6)HkUT#+0BT2jCYaRMmODzIlMae|lmgIo{R#tPEL2pVw%yRm*b zY(gK@r-wK|fg6+xnHWGr57~;m3LKyrSPoFPqKDB9G93s%!iNRa&jFR23LIukPeA+4 zK}n&9Q9%f_NKZjQ2$Z`YFoPEMffI@nXsS;UbcC7^C}u(HlEH0%@XBydq$!v)KLD)? zWd&s$P?Bc>&AfxA>sdio^C^fp8f1Z!1;~-0IRMa-JqD02P;U^N-X1V#3Ai!UgHlih zxaiYhDp3@H^kNi6AvCCvV$oo#FHsZ$sVh(v1yMQNin0ofpm`%jevom@pdJ9Y8LB7; zDR@EiMxZ4HpmM@-4P%xfXdHbCBWU!5#qkeA7I^qx0F;85K&OF=fJPAo9C>mSWEDUM z)n_Sjfs+hVy@HSi6N6GcXg(U0<{ax41r-!f7Kku`ViP1-$gRkxAOI>+1r$WH6hYlq z@B$dnN)knJa6B#tEuB*Yb>CSP*s_!)L5oHBTou^CbGl3p3K9x@kR-Z99TG5cDAPLH>o=WV}3KFm+#t2%T1<7a-Ej^6&-~rAa(DW^v0y}uEhyrM!0hC~P z6!<}L%*3F;14=7=piG*jBn}r7$Wjti;CB?rQUZ0pK{{AKiEjd<0t;*_MTr8m=m(X0 z2?{Krf++(e2HH)b#A?P=UjPvVPtq}JFvXZLfv58rHJL)pnQ9amHJLpW6+vk~KoQh) zVYFhn1M1{4fOmPYD2Rb~d4SE`0cyN3nK5kv(Te6wYd|!I8Pf|W{eTfP%FUw41fm!e zK~vRSy7de!O2XhY3KHc2ISJG=RA7a6CT+}^8bJ2GV9atvEbNBWHlQR5N%7!D3uwjO zK6c26Tp_3p=?K!M09rJStb;L2fn5Q-PMFoP9yGcHSE_-o6v^-pj9HF9_M@y4M>W*( z2xvEl2x#Fll0*j+=z<;=&?YW{kL(I8pdJ~Mz;@8NXrK{*dL{u#RV$(hn)+i?-~weB z&~zMVyqyVL<0`U3ioiLbfOraR$$}d7pzU%CK!Q7%vXnR#SU~&lKohu(SxTS*B~Zur z6R1JXv=$Wo2S6%8n-WeiDX=KWn=yept@WS_FCKsdSk0L3fK-4sC4ierpvB}rKr#yE z%pX9soEcLCs9_G;#sH#O9B+VJ@B*X@QbmI<+Y(mbbYvE|4er`3VOC=01)V(sI+1|I zaRD=^h6VLASir|PvN+DDX9n-l0hePen#>cJ&6z>XeNbuo0yL8i+GzCw)a(b}S^^DM zkclh6JvmSp_AWCl#zEo4VFp@_#R6K?;`kG!kr^pMpbCC~O#Z==<@oncJ@nRBBqPA< zDOf=J;}lpxr(rp^9KaUqAV+|b*ZzO7YXXs62MP!zBS7htDa(-&baxc8D#omO#}^wr z1RQx$9f{%u)Km@i0=P2_>T|Jx)I!$svp6;|L)Y@N=ri7dggv;!eF9tofjf8L-W;fz z$;6<*?I=(WYA4-5iWe48HKxGgc>X46pD4I7!(qns02E>mSh5^9fc9ZR^A0%cfPw*1 zO@X@RAWI=FL8v?7k+>C8BdCU}2kF8cgF8XGz{~Lw!3Ihj@EF_;Qh@3ncF1ax#UNM>W0vEt zhE7md@GBE64S^&e-TViPSpuKIoyZ;*C02Mb(*tr1#L>`ujKIA|7RL#Y!2t%)z$K_x z3Tl%mfqHt5;LSB(z(!3083oFZEE-G`Sirk~SR7}tfW}1>z|D~bpryIIpgoI_{+psC zcr}NDu)tUF0Dz1lc^vY&=kF*q5>a;CkT>L zQV<05K%)Z+;1&p|1>pqhCUb$B5a1O>N=yn$paEBI1w{}AZZJGxR*(R%9cKWwUBnf* zp~VtEQkz+mX$_;I0;p-ag%R8UVSqF+V8*Xu1f7a##uT8y2x{ASfO-jxjtjue0RvEp z3)=n%2{mQ~DS`Q*t_65#27L4cWM442LV~V6vQPl6R8o)v?Fab=okj<3>R{1e0(Xpg z1ipcc;AP>4cN`f&2Q-5&-T|+BQ(|BNZCzi&2wHg$I__HuvO1pu)P`CB-vkXB0$@>K zR;UNxF~taKXE7)+fL7%*fEGQng2rkfMHP745CihIAu$0((B?x>D;=~nmsdbh0iqPN zpdFMn8o;Y1S@}VS*uXX$se?{UFaWK=+XuQSO9I6D%cNih8ZA~}1TFjp9V;?}3A!*( zfyohc>d629i~^HIxaC2bx3GfFCI@x<9XGIM3EY_Oo5`xgaf1;w3?MLbdU+bNa_jRtv_p)3vf#WBJjPPoI{>`kZm+_PT7=QYOaD z)4$}hs_JeAT^tHJbse;TUqBVqZ($YK%nDjQA>ha$umw~B9bn8-Vc_R>+%erPk2Q#K z!gPcEvZB-1<*^zwPM9uun@?o=w>;Jo(D5)i`K*yh@?6uO=Cf+T6e$<5uH^4nf#ijT z71M7Quuf0g3yNO`YsLuhsVt5g)=n01d;mFHixo7EePKO9fCVHlVI4w%86@xlNdSDZ z39HbBmCX6$tw-mA3iR@;9mO9`R3|>&V zXZp1w))>RR;7Jn;P>HU~-~jHMfut2c>n8-TYD+F=tzJ~$hENIECzxLYO#(D}9dSrph67zMN;`G!$o z4~qhW0-H6X4LG74_ne&|AOJ2zH-Pix35G0zgVXPousSdvnl4|;YHoNKW;|%CodW3O z=82$!9BdC(3#PZ1vYIg-nZB=-wTJP@bnh}&DTAXdpn;PWj7p$`$%rvyY8h)R|B*$I zqxqN^6xbd2ESk<(&Z@!qdb(CQs~Y3W>4D{}{_X_a0y<8VC^tVZXVqeA*gajif;CVT zdh-S>( z>E|j~)1>FDm<-yHy`KYm?+1&60%&ci0;9lz>Hd|hiXsQG$+T3mD(akJ#nb~jQchoi zL4n1ZQ2`W*%#NV0&xXs>FIKYVFwJ-|-M)&|S8o0a@NG6$4B&$upw2XBegP`sCcK!w zs)}{C_8|_?LCi~-At&=OgDM_K!htytYL(-H71Il zy{d*)fBNTYR(;09)0JyjEr`*O$vAiVlN#1lj7O&T*Rs0vBLy{wRGcGzfYgg$ZEk2>O^sa3RQs<(;8S;!h}JM6@;+b^!@Whgr@&~$PU%##0XNY zHvMNKYaiqB=^ah1hK%2*?`mQ_#`t}DV>7EB@~rQJqG(EUNv1!r@LOh3}a`d{%V2ei!L9+l>@{Fga$M&VEW++tp1F9rwdPHUC6j^`qqi8(;4SZ_npLQ%{X_u|72FsDd~BW zS>>i5o5X4?1wO!>8M>YYw04En@dQVf0=EDbEue$d4@_oNX52sh$z;|^EOVI@IHw0p zVwIgfeG02Iz0CMyy6$w=PmIT>E6iXuVLrj3G~ItKt0LpN>4j@qE%-rGGLE2Qcv(R= zKfIcLUoqJ=CFd!mo8k(YCiq+9M)F;Gb`Xl9joJ!71QJAvWl@?STR{Z;LP;uxvV{mXQy+_ zV=WLl#{n8dRA6;nzzK@=6C7Cr=co6~V>M*FFn!-V)Z^!kOYb|7mPvO4hpIECc?52vQ{E@BlIc4PphZv`dL)|GlUX(dL1 zN7Kz0u^KWynx46cHCE;n6KHFojuQBuQ&z_fU>Cn&&jL;3O@FkAHHPugbo<4uVxHa1 zAT^-$`vR=y21tztlaIhD4h2?ShButNj0!votd2X_vlKW59>H$9b>(FPEy6&OnLckZ zs}JLm>8}^FDlwj#&bNeBQnHO%3Dm2Aq&!wfsM$OMr=~kBVU-lV$D||%nrmhSjZuj? z@?;6zpPsXX)k+4*8qgLxP{!uSQV?^L$r4yO{m>FtYsN>@|1Dva3Pn-K#Esph4BQ|C zKCppebq9Nvz(ghx9~2m1Zz+M#&4;)S%$)A9kW~_voc1qe)Cm$9x4y9{2K0y=dBdVcCWtJkiK)N}xxQ-ymjbKf2Cgi}i(jTUEN7KryfA(4a#nf9%hUHRXB9W? z``H1}11j|3dO#*_;LLKovZ({$X=rn9VIRbhJlX}Z=5)-sTz z7p-7jpfsD^5j3vi%F6+|6cC&9rl+iARc5;QWqRjIR%ONu)7P$KRpW+gAd5HtG*sOt3fy42{%IT8pS*saeO`p1+)r`M)LW6*#fIv0q(jm~*9kLR><_gt&wTv?~~X2@Mm- zWS0%B#bQwFpy9;;v-Ru-RuwU*t#CPr<n(;YXmihxcQ4ByDw z%>#FkTR=;k7_1m(!TWUzAYXxwz6TwP2s(5_ zVDVXYM=fj!eNY+=n}oIBm#h{b05 zECUu^rdFou6W8;vV4OQ$e;cbOW9xJUQ5Lo7^S80qK$Jm+wlMCQF1>?QpKw% zG(})1s{)IFK4_CPpTOPivWHoP85ws@H$1}XuSdK>8>YOEf~4B*PUQxXVeF$Jtk1v10Pz=qr~ob z>1Z?PU@Op$a?l(ys6qh+3#$UVz_#gIPO>Tnwy}V$1s@0mI^zktm0W=da%YPmXpKK3 z=%#8%7DpX{i_p^*8Ndb!Jcf-@IPN(yS-^468PN8@=_03C6&O!VH$BB_!q_=I>lEu% z#?I*ur&-rBc1?eGnl%r?^P9f<3~MYz$Zop(Sylta&gm6rSywZ5O;ceDh z%-qupFR}^?aSC*TPUDxzQeX#7-LeU+obGj=RbKoQ=)eq^sbCv81z?&iKvyg)FgqH6 z4pTh?J_B_%ivqJQg9WH(q`?I0{%K@^&qxKG_pZR;2s?50^m*1ah51Z8;Duk1!&X^9 zdO*EO$S^RY!0PFN7g&XZK}89RqXwv%e1}O2Gz0-XZ57moX9V3m4LvWFfyqIEU6&yU zG$#+5ni9AITL1#NwoZXj0CWK-yTHBa+b^(oFy5c;aFJD!zaOd{WE_XUKf%d-*_XxllPqfV9rlfVPeRICE{_EUCU zh7cA9(4q$`h7bkNuHZYkc@boTd08OdrF>pKfPItV-x{&*a9TQtU6KI=f!}Pb8 zSdI7<7XOqK$Rz#S&g=4K91gT?^VB$|JjRn`|0y%0$T&{`M<1xC=y z7)J1-AW#brvgn2hG|>k>6NE((w6>bTl~))vqyid&JjDb`0W1P{rgL6lRjEJ43f-XV z$e7J5zyn&#ufZe$naTw(6$Kr5s06-2k_~jDI2Xhp-n@d)tJ9S@%$XG+8+utmdk&S@ zLD3J&a|$e=Ym=Dt89_Jo2r96H$2>vX)EPii%AiFi3}*FA6`-kYGbYfb+#Kf20gxTS z2?|W0^(iq5piAMvXNA6D$O0`f;s6b-f^XC{JrigT{|fM2xh4~fIr9!s2?1JjzzSNW z1UlA=5q_T@X#SoFwD}e){0Wp~K?#@%)LnJF0vWDPhXyF<3O1->(1JS#(BAz7B~Z4l zcawHxa=ZcRpVWU~LMUg1trC-mi*bW=r9h5f$p9Tp0~@4H0ZHU2f)+P2m@(ym2FT2q zI20H_=WBp62MZ{m7{Fy8XsfWmE=C2=Ra1YN6c_~*!H3oe=<^70*DHXAWSI*=Bf=~W zpy5!^*}|x*gt!&J^(iP$l|buBm^7Fw1pY9AE)rH?01X!-87T-}=L$|hpwVdtffm>S z)Sv{$2fCV;2~^@TKvLQCQ`cFAw7_W+v|5Y_bigb);flZ#E*}%fS>RO2EO2T%+YMHg zB>4S=d`t|2{Gd{X5p)9qG%tXQDJIY{?w}L{b_(bo15n~W#soTU-~gi%Gia0n93?v# z1rD()GJ{U*05y_9uH@n2n{IfYNqGAF8?3yHzovg|WD=Ra{v|7GJt%cRA|G@VD(E(L zP2eq0*!SE)PRx`=$ubY<{gYm9LP5TgVMM; zGbjy$l92)vXjQ)g2lOJs1>hqGHJN8HDsq5Md;^`g2uiISkfci3G0;Y%5EeHqFevX4XzbH2giWAXrNj^i5avVL|_~E z^dkw-K^ve0pg}DdR*+g2P#p)V&li9K0a8}Zn11d)tBmN|^X&rAGZ9s?`IuN3n7AF6 zPyc_PRnlqUIgk_&I32J!{#y*G_XX z#ikcLU}dY{0Sc-MpjD=z8*`v(0dy!s2K2-iP5PwAML=!cdwW+C|i zd~(kX@DWbtpjmoQg#{{@_e|gam{n4L1taJx?HyqG6`(lgj9IJVK;5AJ z0P+TCP7j(FK%4vU2KBAu&7k8=VF?2@E8x-%59%)<-R$6?0Kqvq!ETwsm?faE!05`$uE3x>{YoQ~ z1S_)wg8+^OF$=U3WdW5apz}=`vmDoUH3&HB2!IA!;pG4*NwI?~Q5H}LgARLU%yPT~ zQpqGx4Z5PC#8IHsk-0>P5izpLqQK|~A6aFD6oc#nHTBeJRpY8+K_`@hib!@$4?*im zzIxEX(h%2xTL;i9et7vHbu6gffEO50AHuYPk641{0Hper4^)~R05`%xWgF-WQc!uV z!0gD1UcWLbGENV<%_@Xwj_-KRYC660C27s^75xnYj?w~yqtgO)3^b5<(b^%Pd>b)AWBA1$3R1g8+Ryy$5*h15(iPCB>RrEP}&AcOLBuGEkPXv4J9tzp~QtVvvCp= zN{q~eyI{+7MVk49E^aHn9Sx5=@ zhoBl&V@QNM)Tz*L2W{-Z7Vhk*Ng6rac?pNRi5F=3F?c%yn*xi#WKdfhbfBFIg90M{a?W(^q_EHG##&$Iqd5E?8Xo`+ zdb2BV2&|euN&z+aZ>)<0O4dBI!pm>^`x^z0w3FBlh25BbUJ$9QA<;-9SQjF+dM{>iG#cw_oc z5O3~u#b2!Aj5nrR{$jP}fXxp7)1!lkl(Z4$wto&`DX)o`E0JjsLP9)j5u=l`YHh zG&27vvQZ~jPgnWJ8pgI@(PROE!_yo7v6=_JV~3o^Sm=0&5!5#UEyPC5i)(>TR0XYm zWO8H?K&TbQR0=w~6@0kwy6Ig1S!Xj&n0}B$UUd5I|E#8r6Q+Os&+5*&ZmT7m5Tn|k zdGK{3431ysLD$4GfL0TL=EK2jkp*T>FJxq!=Xw;h36Y>P^opB0&I>fKUfsF zr~3-Cfll$AD8LrPco5_;J0b8fVxZNO&?UkUO`6kV1=-BeH7QKLB*@3gT=a zEB1(k!|AR#IGjWz*la)@A_+EKru{6F4@wJ9pDe-Fz_?+%h$Pz zF3Z*qTJQTnmMxBH!ma5Ja%{GY+ospau^BLKpT1s>Z7S1&Thra-*(NgXn0`Z^t(qyS2FDgr;IFH~f6mPp}-892S+sxv|feNj0v=x1GMc7lu235m^eTe zuYu+tLFalauz(LFS73Bx6*w=#y?FXXWwtUlM~M&$pPeL7RVtH%;1|JK{Mx&bs(S{4;VCz{j z4aj_fEXN&ZVTqnchFgJApm#dI8Ji_jGsko4LoK2DOra>u$>#Al?&i48Q^6fAQ${IXOm$3 zI9=R=Z93zG>FX`nbQ%9lziYwf%=luuq9vOKQv=6zZ%Z~QrVftj>6UD6AlI(9WRqo_ zIQ^_8n<3-J>HjU+Oc{Hpn_97{fI=z15D*jq&63Gj?q0jQ^&a*t5mU z{9#uBt!PkTfNpbh1nqc&wdU5_vl;V0XLV#O1aINv$a36pdHP3tHc6%xm#6bMu&FX` zo^Is8rp$P7dzb^82cyuA+wkikK!<@j9=JVygCmvkRLRwF>?h= zB|>=9UD-4kPftJK$|lNqX8JV{@6`1F5IIvfwi?D0(>J++^}TRo(-A!jnmh%aFwUgF z47x}Uv z511Jrpemj~E(1F;z0ZR!h4I|<&mL^CjOV5YdV;C-5Q^6e%*%#QC%xG870 zIK9q~O_K4%^cj9&gU&(?AQQWN2b6iaANwq0FW`;RRY=c85vJZ4+#Qsr*{Rhr8AzG{wavf zn(@SRy^hgPunBD}D-VwqUY;YAMy+JYy z)Lv$GJRq5+z^1?`a1A7SK{AV55tINPfJH%z(*#aTw+;oHT^Pz1DtaBH?1lu`Y{x6$ z`>F*_Ourqt(+$GdR2ffA4+{eu&=ba{BYG2LzyhcN zJum~#LR2t^!&ICQhgh*e9CT=ez=`QD;b1eW;3__dK~!82gR0mKQSlM3Vg^*j7g4AR zqX@7W84)mB4v0d`SOQbA5~AWhT*V6!h>9EFn@|K!Ojn2mn-LZXPJt^RynB&sIzqQV zKKmgIRqt;EipdScj+(jE9-= zfFGjb0zcG@h01-n3ZY(h0eM+!x_cs<1mmvh35jfr81GDHPGWQBgOs)wpmv9i>(GCtaVtcHydM4zYy)6-AYfs0G#dbUN3C#El}XIsqpc)C{uTL|Nm z?eiPhsu>xdZkKLi^90eU&1^}GjL){8XkiNm(K2mp=8TMQmWQ;nF*3f{9@@bc&&c?8 z`_@i22@rj*i%pS{@$K{v-E3MM7eu)4GBUE(Pk-3MrohiCou$MAT35oRz^K8bGyOh? zxTG#<87fl=NI(YK^kjuxWC$v-I6(KV*nqYwIx>Q;D4TBB%O(=0#Hh=_;K=00QV+V@ z5wzFR(ZrqAk&)YqAp*2s!i*_Iff=;*BtU@~w2IXS(pdHYEzSfT!pH0g+Ohl^ls~}j zPwu0jD~3RBROVA+g`Dij3UVjw^u!)EIX(qON0=K}6qvzoaL4Zk(E3`C8`waH7=WAT zINUJ3qL*ziH5>yJ~F=8t~i~|jSRR;f_Wg7+1!qO z5FSV_i`#J)LS-hm;|7FW2Djt%O>q0txgD?8uY+^bxE(J*l!6RR<#t?#a9Rqt<64Lu z$ckic$Hz0_4o%{AybN(DNM9ni)C zdXS;9+>SrG;Jg@a$M0YsPc*mVv|DfkqqrU05e|vucAO3gSCD}b+>X;AJdkWym zD#N%P=R&*#k_+W_TmhEjsSn|Hybp0bNFtcqaVt3Rc!Ibcn|i>`0T~_0?KlIh!4ae? zfZK5v!Z3es$0cAn9zSl!I|#FUxg9q`%<|!OTmf+y$SiMe$9Z7&AhYUuyto~gKqNs% zdvZH2Kp5@8?YIW4l*gUh@e#skH*UwB5TjkW9an-`Aj5cExE(Kn)q?}rncMN_uXb2e zIB`2pg~);IapZPv0eP<;5*H5Kjx!+Ef?R0N?YI!3)Q;P+8)5}W&X(J83q%gYv*C7} z1mS@UwdQu*jNn;uJ5B`icr3XcZ-5=oW5MmX24WVAA~TOUx1;td2p43S8Mk9MSOeHE zrreIxA#xz2O}HJqruQyp6R)57rv>CCPDN%OLvF{zCz?S#Rz+qW18&D4^c3G^_}2A0tK-sx8qBQe?S^VxE+^* zjR(h`Ft=k5#6KW8A#TU*+rj!kJV9>9eWyS?9szF0n+U7v^e!5gqrE^bE_MP|p|BVd)B5M!o30LyW3J06<{ z7H8)MMYQAHBVchhh^mdxL4M_7<#yy#WOiH(PH8+W+>ZCIf0Pig zCWsLS?oKya#+JsoZ2Qb*Y}=U_XH5@W$u^B~*7O%E*?uvenf`bcTRP*J>E5f^!WhqN zU$mNS4kP24>Go^cv>DHA&tA(W%*c3pd;K~#Std|B1+=|Oi9>-|fx~n9sts((jAy3v zZDdnsJU!iLBU>HgnduuhvZ*qj*?xT^n;H}2nd$#Fvso~n*>1jt%@|~C_Et7s#xv7r zZe{xc+Wt3r8=E%chUxpau_-Zbo&IPWn;PS>>73iy3>e=}x7yD3h;i$5*&S?JjK`*X z>|j$9eGfXnX$9!4I1MK7Y48GB0z0QS?_euq{4o934z`_;ErE>SE9Mv-LHlDlyr&=A z$##VC?`D8a!}v_4Q8-` z&SK?N;1c*aJ!U^!8sn$whxfBhVL!mg4Hk<#z}Cw6Y5J1`Z2K5@PG5YG&6XcJBT=ou ztiS-8rg(plOdMA*np&-}>( zjxR2Ow*HC0#BQvC&&`QUFYJ^x0S)H!NwA19woc#IDSHf+t2SLrl4Ty_kLd*`*u)uI zr$3ZrX`Ie^lFf|q({zWEY`IKl&Q4!{lFb%GzdOn1$@ply#VNLECdSp%r<`H4Vr-s% z{tTNo%s;%E-8I z`j7K$O8gf^xOEwr>OpOI1xCjo(={)!`7kb?UUq>^neo{4Sr^#qAe7DY(2HzNjK`+W zxX3m^bnE060Y%W+n|yA(%-oLuPBjZS{+@1oiA`2;`paek$Z9|)Q0HA>()8?0Y}T^i zStw92D=`VwfW}-wCqIFY^aiDs!-30;@#0ML+}Q zpcQ${j!ceV!PO$%&5VqU^^R+%@4Le0YyxdJ~BHziYb5RW?8V8&9W#JD`f7``RBoo<8v^+kWOJ=bEMmUSgD-o^y>& zvi|6O&?0gka8b$Rc=2SjfFd&w2e%`KB9r5rtI|uo%K4K5#!$J z7T4Jac!caKezaq0Akdu;NIOSf0tV`FAy zTspnxKAS7!^67`}vw0{j2c3!q>Y55{2JLfW0^OtyN<`TZKBK_q={gVCY8aPJU-^J- zBIEMu)(_d51r^v7Sh5wE^FT+YDKI-SdQU(9kWGql>GW3**_=W4K4LS*qNwN*n=<3l z>C+yu*)y)+e(e#c-@0`AjmK>Dj7zsiJOTAF*G;c_%9g;lo>hqvWCg1NOSZtu=`WtL znKEvgF87SBoe``-Y5S{ZY@&>6pqXd|&^nd#BHSlAnK&Q|9X>E-E3txCI)E11&uEw) z_<~JBY(_&X==7xxOajN4zyoRuETFCT^)J{Y>R}W3$a0%6Hh@;ngJ$7DXQ@FB0+xVk zW&)j}<0z5^>Y55{U{V4d*8!S4XM@UvEZzcI5yK+T4&KlRGQyD~%kc*CjQ9oUjQDi1 zmu$+6Kc-u~WOHHsHNE~Nn=Iqa>2qJQnK2%ke&r>bujmOT1y+5=6!6XOkc(tF1ddNP zd&TC%bCVGy)xijI!}Rs9*~HjE8*)L?t6#D8=pP3iLC&neB4DDxpulO($iV}?+lXC( z4csGgWQ1QQ4pP)QJ?%AHD&z6#XI`_}$Rq0oIsOT<cQ=iNw0_anp{NLHcML~yq zIb?%Ma|RH>;mBMfaA3N_cQ%PMq?tYzGbZ><-(2uCo`oWqG69W^fNmsU1C5j!AWiB) zlzAv}fFxX?11#XVJr-$@kdGsS0*kZ)+w?>e7GcJN(=UH#^R~q?p$Bpkz6m`R@Pyv< zgdc2Hpd}5+RrFk@=?E^QiUuhuJi!L4#ldy-hhJ=BjB}^6|7Po8{4ssPZ??IhmZAP1 zHh0Fu)64&`$uS<8KJ5>i2IIWx`~R>tFrJ+5@Rv=3@znIlzih#bN2jm-%cjjZbNbD{ zY#TrtyZ^B*H-(FNL3badc{pMke3 z{kQ~9sOC&pKpU4@E<LehHFMpbgP;m>?UUkAQAhpL2Pt0lSPy zANZ2<8O-2Q9+(^(7_$U=rzWrm`t?9+2RdzCI!J*15Y%1t13;HCqUV*^_j+7g&Mtn3~_CsslZSp*%b=_rupcwyyq9%gob z#@*9nnAv4SKqnJGJcjVa^d4q*J;qtncQdm~b0G;oU}m>uyg6Nph25X=*z_zGb}eMt zc`WP(Fn_%Ut$GG6u_Vr4Os{*VZ)9Z`mY}{@wz0vzqJ!`XJf0k%GAJ^GP5{LS108mD z8OA5mJ=xg}1hzsC#Q-Ijc&5xmi5-2Rf)2EdVv~jxZODZgL=#FW#tB`a-~n2d z0NFne8XM*>1l=CV;>b9CH5dCLTm>4)mG}xY$O?t+{oL&4%uEfhwx8f*zs4-|iy3;J zAukiS>;L-P^bJDnviuWnfcMCPcRVsX?wfu`h+SLr;?`CHM`7emtZa@uxUvLl6xbA) z%$QDaDX=-7;L37*KHWf=y;2OS9Fprm*D`}s4swnH1L!OXB|%Vq$e|<$ zx*&!hbeaVGR1DBvRjkuHMA?<=HDCuzD1t7qVFDfQtDwNI!6X7Y7+79`$?*nTmLp^z zCuo@>gtZ6MEdcMFH)Dcq(g*FcH)py7x^F?=oOuICUeTQC42Z7?*?J2y8M3L=jOheO z9CW~eSv?b|2jcjLEem8RXep->`1UW*svahgg0nmzp1B_V;{EjSH0;gF)M^~^zZ-Qs4cbvfvS|xx~ zQ!HUet2#EYXQ5Obpv{4M*dgV83&;{C&>megrV}8G;8n>45D#9J%mMM>RmlpFE=JJ( zob@b@FF3MnSXDNJY)as#aE1Nf$I$SEYC^C!VoI_SJbxf1_0i%+ERt$hAez~XsRKkaIi6t4GGl51sa?R7<#+(CeQ*UN!C=Pl6-4g<1t&9uIr9vrdQh(r z>@1Ks91k#MDanGiUxPwx2V5K^vH_&Cff=;p_6b`SC~NZwoMlyD0_7VPGo}^bx(9q= zFen5xn4YjHg4!0KEB%^KE zbc)IaX0SL*JxJdMW<_4mRq^0sdcc><@`301M6AFN@YH69Y&;==ve1k}Pf|W`WCKZ9kZmSV6Uy;~vKB zdOj8&9v&W6@cvL;hA+&ZW278GcMpJ56lgBf@dI-f)WR0f#R0f1YycfX1M0srId*_n z_<&NR0<*vsu$|!B3qYv_)IVhcb?7rm!f0lEwsQn!vtEB1j-Sh)51ZU76h(=UB3k6Z%{U8(qLM^ z0y=dWRA_;A-#h^2iW4y9AcLlWGQte-eHa4QVQEo`oy7rEWHEsbo7%&e&Bx5Z$i%|! zc*1){Gw5^_JqB*ahSp{QM|lAdb6ImU=wul^22c(gBC7!H7D#I4Mjc7TEvR91rap|fNO+??(u%dS|@CU6V1nhlg3HJDDYfo}EY&|nhK zWw^ou>KHRQu3*hl0A1_|svW^4$PU&l1<>VlFaZV7dH?3j3s@DH6lB4I8^CG&E0ZFN z0+T+&cP2$HP@4bAq$r@kq{)1NNl^(zKVVW+R;XvvWPZVl?H zV>$z(70j9IZ$Kp;fCQAynJ<87C3EHjAX>nYvCxX)C5Xvo&F~zo)QaI5h!0wVVa4zi zD)a*aU8aO!&hBx=V{m zpYaQ;5-Yfu_JcJ`0hA;@ux1I|0jJbIFsTM`ngm_Rp#s`-&6*`}7p$O#4Wd_rseug? z@{S!~g`je>?@x<>BbUHEu(}?Qx_VGMib;d1gUyU-0;t%W0aFaBd)cxC?t|6N0jULb zA{MYg+%tm>=AI=msTE+IF!wBAgSlr7Ou+`Q0!UT@Df@7FvH-YrUcsg$;CQXSNx)Im z5hUoyC;*BMea0;?t*DwniDLsBSg8URm~#BhAn*{D6~O_w1B+z}0*;T?HwZYgLiB)a zsRxy^dvIH`17eNiaR!JU40{fM?E!gbKcj+xz$0*Q9f2CC!SshkQ9wabK}Lbeu>ri0 zQHe|7F_J^;!Kb85U{YdJ;K~9OI}^YHJxogC3d|rOc5t~Vt^g`OmBc`+@R+j&K*i~lL01+$>- z?Nh*e!zO@Qj7%k25G|kq4K!{k>pUG#!0&hh2b2Pn;{ha*AK=<%2SNl?er#Yi zXZ`@nX@6L%*#axnQ#&iM{054dwlte&|W61(vTP^@9p}>{12qFrUxIn`$+#rvD2G$s} z6+t`r>bVuT;7;a3b22E(JJ=zC(!#FDr62_whyw4L>tR=!*%tEdvbo{}Q1xsfF zM_E9r64YV>73BJi4V+4>yr61y4to};PS9Zb!vQJkf7Ek;+K`~0+Xgmp3k+OHXfT0$ zGfUV(OZ`BuSimj-TXYSopxG6{$#D)l=w39CBajB-SRCP9BJgG}aEEgTsAvOCw=y|` z3kD6Q1?-@!m6_`G8GnGPerAv@A6S&Yss9TLsLKIy1X$%k@DX-%*d14a84Ey}5_Eqi z*z5x!v%!fDreg`afE>8<#=L-?TLIb>7MKVM0Z`TikLH|U%M$2iR;UMcgu$tq(VU3^ zbUrt~BeNAlAbwq_nwUYm6hL_&k^@2K>w)KnAhF2+I=qtG@dWl}h!eaW;@J8e);hsv zIk=qyaWjhT3ar!TsCKw#>h(X^VEu7WU`v3S@u1OGP%|DfRG`Uh zV9tDj4H9i1KvCKNy7C!(#T%&Xb(GI?WGxfe%nVAU`ivb+ki9ibjt!uGiUv~)6DV_n znzb0+{T}wLdQg8D+PrLF0yTjDK&)Z{b?+TvEeGZa?4ZFmM+Sixpyogedlsk#1nL7T z@`Bofpo7MhK%HlaECp_GR###H)eoRsGMNQl!s-?9B`i#i5?M+N0*jdxcoi7xL8*^j zfdOZdu1aKqN)0ARZgk~kQh@jp?g4Z= z8$j-FV9w$OnF$I@4Wxhn&Kv>i6>@-DRiKlg1(cXInHR8vE>8Ra za?c4+$T=PWS7@M~B$vQj@cEQCSn8FSdBH`@85Yp#r{LB#i-Q8E;|I{aEC;~11aT{H zf}%`XfeG9nd&i`}>1YB?wVaLyS&9-0oQ^i25>bf-6hbVz3=B%lj+~ynplx%4p5P0A zSh5@$6_`r|-hgk1VgcO=2jbTYyk&*tOGN1KfKOM3X?Vv9vY!F8hJw>k0BS9h;{|Z@ z2AY*Xsk;X;N;H8z3v{0rNc09+nL{>gw8ZfNSl|Q$BoBfHK|pC2G}s3d0<~GeiXI?| zfHRZh3kHxcA!&yRoH0Rp6jb+satkN}2beRz0Oipw>{*~&vOrF_f^gU#_AF2b36$Ry z%$e4(D}Z|QW=vZ^X$o{Hf}jEyXv7lKeOBOf1h3gwWK`hPWa0sLG|qti1s)qb!JY*g zivyW|fE|>sm0FT~*nJeJl z2gS()4oE>Z12haMZ_ZS|1myPv99fP#5Xb2=Ic@+=v4FDX4v_R2jx5I`$kKbD(o9D{ z(hSy&A3*dCxc#6(qYEIR1sqwRi}66Np2Go8J2N=4lsG`8JOikiqhQWFfdzDf8YmYs zIrbpbP2hm1qZW=VC1EG%wQ!*LlQ(Bx0czr`fer(%V965r1nyXZR$|Wp4aBmUF?B%N z4l~%YAcyug{_PM@0QJz67^NM#9oZG?8KnhabGe|ax)qqfy&}+h2Jo@Hh*Nt(egK(s zV0jnlLIz|_0v|!c8lVH#co`Hp1b!j$Kz4zeS4^PYf(t+?a{(LjWwiB>`8d$A`Cuh0 z*uae?P?w3z@yUwG0-)Ohp=vE~CBpogGZjVL$M&le8j_f+T2JNVbxQBTGq^A}h0l0warq0*@{O10;f&G?;d9 zD6s1@GAOZtnyBDGJv-3w2$O=mlR<|q;~r3g1C4`%DiF|hn@o;7ShDH`zJU976PRIT zc?&bhGr9~t;64O1sC2oGq-K{j|<7O3jl z!3-J}Wd+^j3>v9{_#SfK{f2r_4b#8`DukFELH_>+_CI7)3)KDq4IF{)PlMNE5REGs zp#j7!ttbOJkeo%488ll2I+a)eVg_m$Dltkcg5$jf7PO4gilX3gyapr@&;%~1;$ifb zR+I(pcy_E;N-IbTY5p+R~OqPaCq9EwV18T~Zfi^IM$_NH0g-+0gk30&ZSxTVMCndppNckrS8E#@?P~cOL2Pu(K zU<6g0kU=O=-eCmYb`839(j1gx1R!(m);60NP#LY@D*>2 zptxanWXV=!23@J0tq6)Dfow&_dhnU-yr5IsdBL0V!51!YfI2jwyEYU#6vRQJkm6uv zk_zDD2fAbfl=>J2zO#aF!u!Dr8oU!WV*+&=6okQjOD51B5>|nqtd0zjaOVJxUxO}; zWC6`WDsX^K!C-Q%2i+mYroaJ8)=D6sfHDQB=!bX^ym&x?3DjN{_ytZGEFkBAiv$Kx z(1S-sL8FukjQWfWpp@&#WW~_H32NzBGydQJ7f8^~(GCs;W>DTxVg~IMX99KZ9CiO( zo-Cli?8ufS@Ee|-L5nb%vOt;F@c>(v<84Txldi-({evmHL_K&ouY&{BG=Y^+pxt&F zOkda)#Igkt$D%6;LtPLOjz)j#M`Qt(Bk(x67lhk6!}1e4bWaiXhGEm@jv;e7g zl&H@UH~?CQ1#VYMfV~On4l-#ne_%Fe{sC$`fNs78r!WOJM+wjh0VaLM7vM|dI6+ms z323;QIZKHL!~tb*CJm+sjAl%r$^ay^1zapLXDLVsxPfYz3CxP%d6FJxMGo-bVgoZI zJ9RKaIv$Wx8#EjXDlnO|Alg7vY>+}!g9%>BfvXfHL1~b=Jxq=a3WCx~pv7yT_DPij zpR@vpBZC5;v=W=ZL0BOxD6PP*zzi*M>-nS=*aTi8$+19Xz!J;B1H+J@fSqm%y5Zgc zL|A|V;{YQ#+ChB{NEm_w99(9B!x8KWsJ}ayK)Dd)1w{r0Hc(FvH1&q&6&C3%=nw|j zJD`N0FmR#QGodaF2|Dtl7mDOsQUWA3`)S98cYJ9 z>j^j&>KQ;jVF7W#eN2Hr;7$lABSP9b&|3=NlHfZ|L06f8yFd!yW9qK3L#`%(DS}>b z0P5<4#uq?i9#A1rqXkq}GC~Hl6d39q&w#TOs4Fb+40ek#sP6>3^#R;P{tK$-LAR(N zbs0hBwBrQ^&=e$2H7wi;{Elp(!5SRK)xQS&m&uXcieUn%)z4tY)BzfB=ht9TF=J|h zGC^w+%$OQLJZ9uupQb{)4>LeAOq$H#Q44TZgN$T>E`5Im%8{T60~Y9bga*?G7Enq6 z4gZ0renESvK?}nmM_~)R1b0Zkuqm;EMk3{xD=<3hID(Qkcn(v8i9?V9JS9cI>&&2w zpusWXr~}$gslcEhr@$!ik4b?a*bG$uoquL3CKl^6t&Ll}~BahMNEMW8M_=$>d0 zdjThCB?{!0;^`%p>{A)fZRfILKf=WHoOOCbG^;G*=IQgIS(O?Aaw5*|vbT-0z$I(}7)uar<;RNA^jK7pHG@ zWcOvfHvPXNyBOn*>0(aodW_emyE?JQGv1iK)QP=?@!E7PXZ9M#Dbtrbv%521oc`XK z-IMbOvj!835~IMy>5eY!hO$#Z9VgIrOyJtC0Mr);r*+U0z>CxSUD$0H4^Kbi!mi4= zY5FG@b`_p=pklTIe96$o=}NBbX^dB^h9ErfYey>oYEy9_7IvBQlL0 z)H!E|9J>a-4@Y3v^iv+}&WuZ^^LesQ5!rDXmNR~UZeRJqoF%Yq`ZiB?YsSmdKY6mJ zGv1#b<;8Bqcx`&G7rUjxcK9+C2FE4aAxkZp7 zN#Oo;0dJTA64Rr-*+X%eB93f|Cgc6-zr5Lt_)x8!Ugg7XA$66>QNGZT%MH}T1y%J5 zj0((-2N<&iR!l$R!>)p^=bsO|nk2G0JqV`@b59raXBQV20^O9tk>$7mS>257)3bfq zH6?o>YokH!AO^=4$jWHY5HtVug}+&aryuZT7iXL@{e~~QH{<2$Dt_#FjQ6Kc_G7n# zCKXnJ%hRvFojRZH$+v^98cIFrJwn9LOFidze|Dkp74ivu&n6p6J0t7Bkw+dokhCTMb1hH2$Zk=8b%r5Oeiy5@ioQYe31ymFXC~$$d z>Tl(Src=;rcm*b?5>PjQLja-!T=`&z=Js2`?5WJcPZ+EiKueQsvcMixU>5i~Jt&;r zpYi7OMd9q#9I&a?>8cUz28{QohefdK^Fn4D6+owX3Cx^6DT3WZ@(L^HtSS@mYI+6* zMr%g!$$-p`2dCeUU{_{;!2y!?nZ8j-NMgETB)c$ZWu93iyE5ao=~0pFQXp2e0C0Fk|9SU<1vS z+&Dd1K!HVpRp1UAR3RIvGy(+$lfbg+7oynB88=Pmh-Oze*~qK_oB-UTx{NQTPmE@d1}#u#0o@k`T6e|bc!dEpgFh{X zy&7~{PHhZ(1k-`b)33*{8!_#;Je@n1U5@d|bgfu+4bZ48y8@fQ1khdvjx2$R)01M^ zqZtoQ-xtd+3EE9`GnQS2>B9Nx|6|$JnL1WX*N9_RaD>k`g2%tvFbo0BcY#)mU0_sX zRNzp+R4~0Wj(r{D_URV!?7H>$F0_F5VB7=UH^vO{Fb_AVkN_VdFnwL4fFmzxCY?cn zS(BLql(V=%vY@M<9sjOHkre>Ra)M+*w-`Duz0@S&$PL#k0g~ka$tr--*86*4SzDOJ z3Lt5AkTmEPHpf>T%>s^saLpPZSvHU?=w3O;uYbXJRNR}cn7}T^vE*BefFq~Cz3KKK zUivZj8c6r7})4h|~^_hOHnqHI4F3EUk`m|(rb;h;R_a(DSG2P;sek++> zpK-x-t`zn{#v9XXQrHa`pH5$!!tTv@d-~55b}iXUp!CJ5$=tyNiUBTgyGnuC@y_MR z0s{Z0JEXG9D$QV2;B*8nQDhN#4|UQ6uxd_j@NxC`PE4;)Wsg&ahBODrg)<<9CV0_; z0*mACU0ni>3<8IzrLn6qKANtc#_q`YXnJNEy8`3W>0N2;#*DM4?@MDBlLm*mBS$tL z4=W=WFtIQ*vNCdm4yJsc#;(bDYI<%4dq3+B(E5+*jG63`jI*X6PiHq_ygU6*I=d?4 z;ps{l>|PLMlNs;9lt^c?OH0pX2F+Z6!V)y04{FtdZZ~IDU=z4MJus8qiShpQDVgkn z5=XfeICUB3u!0IPPSDP_9jprA82_8e9wRab9NnN>>A^E73s|!R9!}5AVwVE#)9B1% zmlZk24T|F@tk6h?sMwXou2v6;{WmBwpde)e#XmPF;6RIW7(r?J1uHxOd|(Cj<2fC{ z266Ckaf6R$1kKNYj>2LGMS?&pC&&m+&?-Al@It@2%t{*F>@IX8}JwBTqbeu|B zHoFVdmpyE*yetZwph>3{Y!F`_%4Ux-fcSC`8+aiXr{e^+EP;n0m$E>tf#ka#Y~Yb3 zaM<|ez}uH%pWi`|0htH(IXF>+rfk4IzrhCg`2#kH&%p+9@Nl4b z54?hd)A0pptte;|2~??o<5D1(-HoZ0b9zKByB6c4>CGVO!Sr>x?6yn~d8P*@vP;N8 z(<=DnL{QjpIwA+dF+~ubEfm>vrB@aT`Qkm zp7G*zuY7h{6+|#GgCZ2Pju0Wl1QKf4J-s`h-B9EbCpew3z(R`!l#(vwvn%OCLhB8R z3_P^JW`Me2;Lv)(BJg0kVgb9b(nAhVpMeuJ9>)V(c?%vFR|F;935;0+w|ErUr{76t z*JWHVy`_LXm+{8*PX*xOK)I0JkMZ{OqC%wN;0HK^Golp-0@wIOr=Kfi=ir$RDicmH zfo_(3KmAc5yP^J6W(5w=wZ0rmyx_8!$w2`$E-CPs2UNg<&w~Un+6TERK;YDLry}-r z##hr<6|w6vPMm(Th&_*S;&hK<_89@Qm=!reKH&h}=ma)n9n@?IGbRNE4o8hFB`yU9 z1y+HnpxJOPfqrn*@G>ZHfTsN+MF_h)n6Hr~aA3MY3A>uo6CMQ)eMS={PSE7829tpz zy8;L321y3U3OE6QBhxEN*p-=jxTnu8Vb^2C)NKNdwKgzk34GvO8wB>t2?o$IUIqnj0k91h7(j+HgUsM? zWUyjLQs7Wvcl^&dy}FEDhw;et6=g7ATrFdFV>~ilrkq`!@!oWoa&`^Ir_*!G**zKm zPTyY6E-$%^OOX>aJquc|2Wn<Kk*r3!Xk#^uv0*=+>=bATdk4-+KT91l(Rsbtq+d@;SWlHEw` zDL6*wFe`yBoM!-~y&22`v!?H@WS2s?^?oJ0wA3+9kTGwV6gfd_b`LOvI`y33vjC=x zRIwXCT8qNdBdXZ>8P815sAA7%JUIPI6}u+iifIC%)d;g84bka_N2SH5D^;_XGjdIz zRn6|f_;32-YIZNdZ=l%-@R&U)_?QHqPq(XKcVT=!y}5?nj_EQR_+Z>?HSAHQ>Y)2m zL1Xd^j+;O|4@L$vM$pz~mMlfksoadv$rx@Vbvw3Cm-J?l=0y_Puzk9}H;Z)rO;$z@ z(B{kqjM+*oDxe;)K$ZdnC_RCq;sxLi27LJR(?1=c6`WrogIS!+g}mT1 z@Y$!E`>+_+-v-HY3P5GSk_v2&N5IR{LF*e>71+2Pm6470P+)O9!I0&+2z1veFRD%! zQ1c$-7tpS76VSvHrvjq_mm{MiXevNpGrIzdK4SpHCtz3Bn=wUzE~ySuU;&-rDzJqe zv0H-$G|Ryuu!J4tNe(3r&}s$H90xb(5HryF6?QYG1O*n*F$FJHZW#^yKmqD8&E=Hv|{K0xq}TXQUQu5EzqPl z=n8OXJSl)TItgrJS7Zelv49bj?^wBSF)?vYf9TF9Hht?P_9{lM=?0V8S2JFperGbf zp8EyRB2e%-xC%_5J~}Ag99K3sfF{Gg!sel2jt6xouTASjk>h1lU~s%LJ#Y%Un96S+ z*c3HrZiYdF=?bGGW40M++bh`IzYGFDr*}_bmzr+)nAwQwGwXEF!R7VBpc0J{d~gS( zA$&-LyGwwHAF{>%0t0x9JtziQlsG|?Tx?7ZOt71yI2}1Wc^N>GJmASLcF_JkrV_^q zce=m_LMU*0gJ$7DZ4Nep*F4Z;VtE-ASRECz6xamzGAh)w>N6^UPori9RZvWz3!aq( z_JQ}h+d#xMm@E{*Hy`qVw!VX=Qw8>eD{F3nAFK+DphXWnpaLD;8i>Obxk1${NFB4l z8y=AHpw;@IeItr23apOxpdKb@YL>;3r35r%D*zpoXVqYmP-2n>&9{N3u0c{fP$`fo zncMJ3yCuLAG#H8*L7roBP~-!tvr!ZPQ5M{apdy7O zOOZ;O9jVGGo~VCV8MJ1DS$as-nCmx7=p zBgnNpg-X2Bb9S-H)GHu6j)#X^fep0zNr6d`O@R?)CA%jt1GfT)0w>5L>>yD^PH>cf zrj9|=;!I$p!4uqk3ZTT91&dTx4Z`871qoQNLve>I6DVA#KX+qM_Tg7x0Ug^9ay57< zx-}zcSs+r7g7-8*LkN@t@dWAg#{bOX(^q-0aC?K+t-!<6ni13OI@B|s5#G%17 zJzy5Qcs;Ui(D(owc=atbTm{OM;Nc4L4GH1O#;t%maYHk{qJZOcj=#)2{tl4zY@^77 znymRC$(jqP)2(570Llw2YK}WQ3D+nkEFIPe& za#n80DJ(2Xpmi0XWs;zr3knDh&=5N)KQNbowsC>9!1p3@N@sy~67VR1%4?7;NE1s* z7O3C?F(Fz&nn8k$+zRy=qTGs%3T)stG3HDRjtroaml@Y8u&i}bXlqdv29?C1;RqIM zMgau|1z~e0&}H##ASZ#=1c0_L@hGr@8geQO+&h^WSwOAT4-DCm>v%wur=U|%6c|Cx z2aw0=1wQaVTREVP0(b!nxC#N)grMtB$OCG*pzNn-kyc~` z2|~^V0TmyhwN;=cbl@VD1#~|< zZ5Ex`K3TwV&zZ>r0*6GV^Uq_CX8OiD{lHx2*HGGg`h@xHYvG(z^O-kI*I3AI1QN;f zVlf6?5@XEB18Hh?FhP=pLADZ`3Z&zr2xIOh%nUxpn3_WT0#Ld;{aOkQJ*F77N!GK^|65(VGJ5f z&>jc70taa7$B|KiMPL!T0vo7Z2AWEMnW~|{rputDz~-otC9oLQvIh5u85NiXzQ9ce zdrUw9ZXBcovy5G_o)uIJg74{IPa$aGNmZ7fY$snDzKO{gC>AL zk)y%X!KA?8IDrXNlRGl!2>jqtU~rrQ=7I;4KnI&c8iXjiK7iWBKfpb40WAdv$A)^a z7DXmd_WQ%A2-=p!#Nwa;DytMh$(LE+4-e?Jw=6}_0lp5H!4Y&u6QjUikRrh!3|Rt-3Ji`jn7~)V zItom`zlhym_91Ax7ks*21QTOD6X*yCSeOWWojzqTyNxvB`XB)mfYYYQ9ylLf&A%(Mv{8dW0$aV>)&nY6cD&9!p#qE<0~*ZGCMK}Y!~5P$H>G| z&sYcQD+%mim2#|C1RX(scY2|pOv7~XrR))mSEm;&WshcRWt+Z0heLe&lcntDjBV3p zma*$IUAZ*fcNx1pnC&;kt>ftKmc%h@#{qM$K%m}t#%c16a{>GPJeO9@@M1esE1 zWB?UV430N0O+U4qT~oM=&5^m#kue)|><$xXZuZ8d>1-?5ofx~Pd#+%&NU4l1 zP=VE{fibWMlx7`~q?sYQ;npyLrkM{gxG69?HZZzjC!1Nz?qJ3Q8j=NBxo7@#y|wJm7>{ljUB|A? z$hdC0<9c>k#`V+V*RxA9uAg4Hp54ZDJxC*@S);%NTKBI3D(*pr%X((e$sYY|pn99Z zkrAW@6y^#{0{zopt!FO~h75YLx$`oC+R~t|%Y^N@8`zDR^iRx(9Am@`8f7{F=kTb3 zoCP|S4K4)Azw@V`-^6auIB`1vW_EMNY17>|v#T<8PtV=V9?sZ3{pe=)t=yB?)+(`s z(z3wh>8)GX?Lp#O*jwZ$u_=POluQDX*}y$2P?~0OTmw4VLV;PJdwRxJ_SKBN)8}kw zmzb`zja>r9jzwbkA+h%(u|FWO6}Kbw1|hMV;cSKa7s#G^0O#<4lLJSV;}5uy0)yiR zIEM$EJV3h?79a)M1~`WYoLo4v96R7bpi&gh;Sq!cs)6GSxEQFMhI4oXAPO8;z=aeT z92YE@p1p%zit+yRwjJzBj9t@L?qFACoH+gb4t6KTDbs~^va2w5PdDGmo&+gNZh$)I zpcxN=?&&LcvRg9VpZ<6!SR>mmutuF-?2cUENmEd=nL53E7rPQ;_x4%4*d-YGcOYB6 zVZro+yV<1}4^O|dn_Zo8;&hfh?0QVUrc5{4!+yJd!76ahRb&DkUc3a(k>qw<0q00? zJFbCq#JL?ez&T>vj$7ayQEtZ_aE=JK;~qFinA`CHoFjzHsTV}%2_SR$kvV+ajz{1o z@NzqzfOB}b9nZiy+}w^A;2bV)$189SC%5AbIERDV@eYI&rpN?30QUier^p04f%6HR z!^-XW0?uLKc6@18- zCthrKKFF@c$n)aGRDmpgM$jdNozp81v1>4OyqLcD5W61JiWk!_A7am7ygJ?HFuOM6 zy6qW<*?mE`tj#;Y?lk?!5q5sY_0zSFvCB@sdxV{Ry3SE{6UOz^6OXc=gow{%TtEHj zG4>b;Wi&nHIJ-9E`soeF*#jX8WT(GB&K?3|!wgZK-g|;wfN}lwc_-Kt__ly{hP(hz zMlcFY*v@m3U7iti2gND&g^UxYpF71K#khOA=4tjY#);D#PP4}`?w)@CG z?Hb1E`ls2Yr?;G8S7hv-zU&OUCF8p356-YBGwz=5a+ZAw~Nun5Fy6%2%*69V2yj-f)p!L+AmT*)JAO z-*u5)opJZ{M;F;`823+?zr-%iIB~l9C3YvqiPNhtv70eXVw=9{61xTC?&+^DvHNf| zoSH14z$7qvy8UH#Nycr{BQLWDG47te<}$msCR7ewp0hY^-~ydvqrfCEi48P0;CO*6 zOMx9+_)MI3g?$#|?&-6yutzg?P5*v{-HmbDbi1qUZj8I9w_atpVw^nvz*Tk`#%a^< zUS&@MMej9scm8%zLlGQGj9?ecy2h>#S@h+|D6nh#rEBcg*nB8|ojubVGwxt<$-@RJ zSwY4+f;_gDO%c@O1TUImP~b3UW&j;5$lwT8prF7cuy^|9>+Fh*d#8VeP~tb(l^G9D zx4yw{%(!=Y{tb3zmIG``%+noju**;1dV^hF`XGjpph4{q+*v9N(+|&OS7kf0bFzTI zy6NxcvP*ElTBp? znQ6|R>1%GYn=`gfe{!3>1LX9)JM4=XPfZuR%kB%I45s(rWtU)_F@42d_5hfW<~?>% zt^=Uf4CsO`uyTXx75CUhIKgcr1tx)+)2H5J_kcvZ0+Yb3>Cf)5`!JrGo_C+!f|)@8 z%&>y+b*5jv&u%UbA3$dZ51@m3M{ELf*ro^9vt}|*nLeSB)nfWA0~TJUR;KBU4Xi6* zVts~iG1ck&=ZOeS|NW3X3nFVZeZeDkZm15`=|3A;`-JDPDT3D_fjebP+&h_>*rqR# zlrxy#;3vuH3183)8fJkVehTUmGJ|$jg2tj5KrKI3@R$W?CY}Myy@xEI(@;31JE08xhRG*mXoX{ghypRh|Y9-Kby z341)QQv*ScA$)4!bn~a|zKmC=w?1XpVq7qN!&7z(#@6XCpRyYh8+Hx0w{6V=j`|P-z)pMHHa*}i!T^vhOh22a z+r4AY0nuyTA><5Rvo}w#d(SS#b_0BV0mPIo@7aTxZZuEl_<&Fu{(*e~WBc?UAJ}bm z9zF!!JS|YI!0I@IF-w8f@dsm;zzk*u@EpSrM({antd1Q_S&p9`PEY*EUSL>0(L zwxuUp1RS-%>L4aAJu!X5cd+xoDdqon_BnhVY@n_;s9teo5tuuD%dqmKXn zW|xNZwtM_xH)Ud+GQI8}JLst6W&hXmtf>@WIQ-MijgB;eD1<#0f8$b+ydZ{ z0!Mwvw+lc5(=RY`h%@cxo}Ry5!j!Rfx;zude}N_7rL3SiWl-HHuync}Glw$QGB(g= z90gW^(CG`di)l`8VdhvX%f$eh4F!#ED6k^NA!M9n1g=i^W8t{O^kC<7C033=#y!(Z zSUI$K7wniU;Mn}U33OoBs_Cm)Iiwg@Pd~=W;lg-mIyW1KBIDudx@;VdjBBRnvvCA6 zo|=A;jU$=y)pS*M4o$|_)BV{wQiNZ>Zx?VB6M&>7S6)WP_6^hTMv5p*f5y&{tu%{S z5mZSifR;NmW`h^+g7&aEf))@kYA}J8>lAZv#Bsi20=3{|lvtYN;TApUet4g--H51RxW#o?AQfmiR{=H%#NoV7iTi-U!cbuF7?!|Cby+#KCd z=6`OES&ZwZ&*I@IVw^agmzTplb;aok0-$xT;EpS3T{JR(2Al&L^<)5@7;peC1Zi>Y zK<00Nb3k2a7Dv!5HK<85eI+l43ghJI=Xg27WY@!D5j3>xI0Lk1otXhN_{QKk;nZ|T zK8|Gm2IQvKj|JN|^Km#bGH#gum7l|aapUv?K@RchwgMdTFm?$Ndo>dK5fWQc5TQN* z&NhP?u@5dJ&IcYp0biNU;J9GnbY>wA4VW@#Ar1-2UTE8j13U%fc!0qjG>Y!nv2c2c z5Qid6=~^KUQ1APg5QizKUHDfB;dWPHuvDBdhZxg@h11J~5xUk3a|kkSnto82LkhI! zh{f>$H)yFUlLDJS_w?7o9QKUsrfZ9EXfSS`9wNeF$hQUDrU5ls6gUK~PM<8o;li|H z;q;p#9H0ijuqcNfKWHsR1CJ7DKQ064DE$CY4q3*n)AK|*tQogWUnR<+!8Bvx^edtq z>Wo{b{{e}vn{FY-VZjM+qE4?AlIF-#+5t{1yr88J6Zk+UxSrt6 zQeYBjV^d&t6aZoG>D#0^RK;K|K?Z316x1wTv2glFX%00=^T&}Y z0N%F9;J5;sNSPQwv#g+2_U7qtWjPcXH&5r615<`_912RC*&sTt7(jD2;6Wu0m?OcB z=*`oM!z=hHEf|>EWsz zs*DS!*Q#=a^4>swD%FE*DkPT{6fo`q8Kb=vHLx*wubUihWIgHz;pH}0L z2MyM~QR4vJFRrD|5eMRSsdMNu-k-ihoddMM@3lII72~$)Y8o7xAjv=t4k^Y>({nU9 z^sLu`b_uLtgoV#GP))|7!L)+WjFEwd4-}$E4&voTarRXW4kb3w<|cuw)BkF4D1$aS zu|T%x2wb18qsd{yxL|ssCWkR-ym*=>hXyE-uz*JXK_hSq%mUk{pVQ<};RO%&D1kO~ zf`tBSawvdYsi+0^g1;7r8ROLHZCYS29Mpn%;f)rD9OI_x+}a#6kSM;v1oy&pb8QYs z#>3MqwK?<|r%Yd|%@N7CZ920Kha$*WH60Fh5v0*#Hc&@M1GJSXQHMiT5lIHLz7}m< znNi@z^h6yF332cUHIiD;fTrV>OViKkaJVqtgZ7Txbvcw6uTIYbQC-t}bve2i?@yP| zC6Tkdl`36KWM;Vz&LICCj$U0NV4k^Y7+oO#+rZ6&YpZ>vwqlfX@^jcF6Y4Hc#N}yxRnY^Wy7{E<` z(8>pa$#Rnu0a7|@9Eb~IqbQ?u?tE!0{5pYnsGQXPMMx;#t|rc4RpT-Xx@s^ zm6rvyriWL78Qiu6S8W%~IJ6ljPycJip~(r_^asM;)Ah|cEM;FTm@FW01hg2CO`nkm zycvkm@hqb|q+!r%&SB1YboyR%j%=}GY@h|-p!onuf&(u$KR(^gf6O#z^xbErI792*B4%uKmpuIkOL7~O2$?Rax#K6Ne-7$(;xSk!f@(;8P4RpvO zyFQ~2__SLF1!j(01JYeBaX~%eq{4sM4-QYN>+L2$D9!-SmvKp_Y6<_{(_ zrUp>9=zwg$0i`z3y>beyjw@Efay+{}V|@>^5(_B5gL*8WWWw$^K^Ro{gNB-!m6!$Y zf$n=_*Jqp|ti-{~z*MimuFEh*7@U^aHJBy{n=v&AE3iB65C)wC$L`o5oF#CQ6O^3y zfHZ)7vp_IQ2{eNY3)&sR;KT-6+oHgzgObP~ja6Pym^~24QUc9*wtzUGbjB?3etLr~ zhk;=SlOv-$FDO1Hp!f}EzMB5fmP4f;be0b|_a25a!w80_~v_cpqe}K-q2Aw(w(XkHXZ*~o)2h89@L)jfK z)PoMu2KybnAA{ZT1`Fs+E_TNYELosN2fO0~sN+DT1lS~9hF_o*3|eoDC?y+M(UZF_ z!w(i-Q1pS$Zv`!Lg}4#qGgt=dU^TC2V&IVmg#jqTLp>ynR0LvpNJN3%aRMvEjUYXs z?HkbS&hE|&O7k}X+glGHD%L^i5wxCxnTdf>92AWc zpnJU#k$nVOT227%6yuQvDFPiK4NVdC44{kB5oPKOuqsftas-_u4ONAvXaQIeC{{oY z`@o8D|)WM>PTs%S`TS2J>TrVhbIC6t(1`bC-_vtlG96YuUxWK6beKRCD z!$09d&+v{fKr<|mc8CVkgXu?|I25ywf}=*4;SIP#cWmGWr54co5Oxiw7hD3zz|q;l ztpwhK$^cpp#sUe9KOnn7`y0S(^1#igAE1@QEM`n!Ks2bf0^QmI8tP$!4D|#$bC@tq z5}K~)&mmPmK?quwfzm#xyoZ$b;B!Af<&7}-e0Wem?GRREQebytFlU}13<@Gpm7>II z#HuAtRt-uxn#>b~ zm1Ld%|NsA=KMb7gq|KQZ2!fh%0-%svAef~fyIepTROG%80M&7zM$iKRMQH_g&{1_W zKt7N*XP!_GVt{K$P$mF-1#~u^5Xch84G7Ob2G&4lD<~lQNgy9Q5Ce7r*c24cf&B*8 z3Cds#gt7!+1H0h-EvNzYD5!M;_T+SZR}Kr#3BroZpg^B7y}*@2m4AYeHG`l6lcO-m zbCZOo2O7v}1w)!O0@K)_n;<|7yTFGB2+RWYJiyItSy1)?jSzs&AYcKl*I{?;0FUcH zvKTuksll6E9n%%vIMgC1fYSQ}a1sR7D4@nPZ1)o0JFw#h+iq`RO>{{~nes8U(M4Q_<6>oeZrR$>Mn!~^YZ$brn?09J?U zqzzyfgNv0NU=M>FcN@F`0o0y`>HtY`K(F7JzR{gS)a()~C}|zx25+Bb04*ek=EoCY zEig~8YcL()R$vu?c;w9VKkgjT^(VMN>wCa41Kw>5F0LM+>$?HccLEx)55Ud@6d!SN{rXk8LJ=!niYp!@`# z7zFj~UVw`gL69M!FnJ*iDX$N)IWd6pqCVpuK_zBh2~fWbs)!Yo>~{z%@e4c!)vx-D z51=Y^8SV(W@^XNR9u1}&f}rD&I|L!T05hf+pv*r(2$Vlw2!hBCka?i0=`OgsoFe4P z3vIpxm@$121lck{2-KkitvqIT{2-Vm0Pco^w$iZ(Oa%??vuo5dStxLUR<47WC4-6w zP@w>BV1TmneQ+w+A_VaQ$jA*sN}Og)KS1u{G-J8}D(G$qWkFjQ%42C*OqHcb##V4a@j#i3BYKsZZ*4ZLZ>aiMUQ0;@g)r!}Z~;J6sX zbL0l?s{~7f8m-`>jmeB@3Mk|sz)M4L=_GIxoS~iwKpd>W^gsZ#M@E4~U@|+BKS6DO zkY_*vC8WUa4DJVjN&zKKXjkQfP?iAX932*xcH<#g19kx;sNw{97Eu9%>PTqSG51KbfTM~)wId_w(jZU;rpY{E zx`Qu=s4^%*9U(4j5YAGNhSrQLKyCqd@)rnBuSn;R(fI&Mu%OBoY8bTgQUbYmhA<>9 zCkTVmjN^{!2YfjsIa*mjzB(X0{h=?1u;eTz&{`T6kaAEO0Bz?6b9PMU_2V#Ld@$Y4 zkHa+nkq|7!f;JdJVj8&?V^QD|n8m~+240UWFpF7{nMV{9^`LwJPKMx3tmfZW>vZ{C2D4!a|$ugYx3)BtMUFhiO-pk|EYhJ76Zj_d-UHVddz&FQB zZ?`lEAvJ||g49Za)y@zGwQraOesO}!ta>Jp!Uv!R4yfh=wT7;M6f+7`gIdRs=8BRW zauY|!oEem2p%DpctI1)BNKml~YT|%1g0wj^4&T&6nm7xDvS1#9#xN&VRPy?qyC`;fseBU^@AqVj%C?g1EIYRxI>p1;<0EZ^$0|7`g=f!l!Kn_*@ z6M{(1oKu3+eWExF11>ORVQ=Gr5(T7bqsRZJ&Qjz6Ee(Yf44|=(AA(r|m%+8i z0ZnxEh1=i_|t&-yPGei{FLA4R6#B%%qu9hDN zWeGqs-4{@WDF8D5gODN%MxTP&jOhS0(_H`s-v!|;$D{8$1RO=c83_~&?2ZReGTi}C zX91-4gK(DP?ROpZptFZSIrN19B-hP=*8L!b6TnRc!7Rs@AVWd7U9vlV5C9zkipa$v zbtgdTP6%c>{sXDw6sQIr>ICV~FoU}?pw^ubsDuC&xuCH)p)AL)_n=-%Jt7xR5XQ*G zXvHzUTnsMMKphoO0t445pwVtb@PLYWN2CCPW=8OkGCZKrb1}$sACN)|6j)G=3<{uJ zd;qe`lb&7(+I8^y(h=5897D%UPmdJGfY8mxJcF>`%Pe92Bl!`$#s7e4u5~P|0x4+=+ z7*KHqX~(c(YsY*MQUY&MXVYMUHe@~sP3I5ikdXytI8X@#s!owh7*Hb@T*BDOgEKIs zWLW{q%TODjxp~3#CE*-eeu%RM!Fe2%wNc6xkj>CC1+E`d`-6%&MGhWbP!)+7(4Q_7 z!J#S-ZMH#t%528;MM!}exrG6$5@G#+OK|^R#+(_PCP8%ytp5+H9rr-XCJiRc&j0ic zF&yIc&rdcBI7)-_B_x-Fn#Hip2I|6t3jk2<)^e&@z)=dVd4icYz`+1XxuDV;mN#C2 znylca3Q8wF0Xz;13J}n^o^Tc@bAVduphKNDKx#NpCm!lbc+z8s7D*t-qIAb0y>TQr zPro0DlJF#=I8^yid*jo+qd1J}UkKx>r3Aro1Zlmdm0 zdaycjW-0Q5`|^qcpmBJiEJZ$0d1;^sZmXCmiaRnWayf#xvVximOpNu@|3`61sDnxd zP}zv+aWE@@FJ=N)#Q|naJ3v{2XFB^?4h2{fDJ7c2q`pZAbTtGoa?_R<)aqpfkkCnn1u#+93?;OQAMSL9qc&U&zg;O_N&%9A&^MW&^lhm;frt>*4LE2|`F?C>KDA zc?7BzK--LA$qJ+b-cY>whA8>W}Wa+te=@;)e{PC$FVO2Xir&+hmEls>^HpK?P>Iss^B?Tnxj_<{j$P$36y zt)CDC6><`4oFJzOXMvkipsqCwQd0_aGdEcAlIezV9H#Q%c9etyGprp2wFT0WaC|U* zS2Bl2J)*P&r7BR_4sSsn099}xCzvtqfifX2C~%W^hcI|cGZ%{kxW~r`IxGP+?TTm^ z@`@@jg1QZ`&H^a61eh^R0Ohg?B3T0W*&P|BL90o?l_==0D3E>yeu0ggp!lBvYAA_Y ziGkWzZ-lboWhZ!$PJ;>53<75yNZJGyFHg`))dTSyCiSNTvlOu8Ay6HNKMx6k+>2Sr zLP|7HV1cXzr9sCDLRpT_7B|82&;xKDf_6dbp{*QHlN(eFg8M5iOTZ^Ff?GpEsI4K8 zI(QnJ1X3rBQ9u(v%F4_OYIbNc6Us^m?|@q`NFIWwZ%`Km?k{-0fJOz#&FFrE>9YX0 zUO<&PTB#1wgziy@CRbi2ZXU(y8(TRf`GvVbv49wQy_krlPvQ_IA~9Vyi9^sDRN)B< zJYWaS`fD)#5K;n<*!~ex;>uQH0o}9ZDGe&M9Xo`x6oo-1e-TpT2hksdre`K`=z$u^ z%nE{8pj!}F6v3O@85DUzbN(V(0^p?|kl7a&Go}tumf$gCY5~!_kl9&C8s#=)>H!Ht zS~;KrF;H_*fg76bI|N|^HJoVK{)#X(-*dwA{e|g#;T)3F9g;a%lwb{<34)+A;1FFX zq(4R|_Ufp`(3#~3v7s=>5E2r_`Q zKnOI73@ZH1TJP;m{5bV%g^j&ykC1FrWMpvO6=tqba|34?Bs26gf#2xd7p?`aWm6oo`e z!*t7Z4w-CF+Ca1v6=1Od%IUm3pjzVtJQlziR2tHZ#$_}lFhJ`dG?+SsL1Cc5)F7+~ zs%0NcKakEL$qw=Ri|G&2ImGy$Ks^jub2ObLgQKV(oVp=>0G<+)S6uLviUfe8HI&~8JL+sMX%!w$Qo3v z^Qd$9lt3pJFeotTgH~ioLL1UE_{^9VfSOP%_&`nR1$>YNE0A%J2F5IbgIv=KGda{n z5u+m;_+XPnd#1n2tX-@Eg<|=N0T=@X(${Q%I(@IhAAeE`v*t{@+3 zSMWjsX;ZtLD1MHWQY_rzY8BBytcPPz>y7OqPX zz>0tHf!ZmYu(49;6e-eNa0A*rp1QIicR+1dZl`+bN)qB-BIDoD6EG!2N~Z zPC@k?#BXqYpg|T;=M#CTREQnY`-Hbs8um?pmjmg2f+taCOuyNR+56;arWfRKh}18@BDDal6Wlak0hWekyAAxv)30LCB@}D;L3u-gU4v-_ zzZugGe$YHFKd5ln!4D}MKsgDVvB51U1xA6RT%gQ%gkOnC6tY?dd}uM)sh}l>SEozl zb7-1>YiJg5v=sm^iUrM0fP2&j_|2G3fULLxb{WK2cE=O^urlV#^oo2A8IB9!@D}Bs zu9(juS`RwL0vdKGrXp6xIHDGlOpF2o0*p)ypz#C88(1dDZh$AqL{ZDI2Urw7;4x>a zX8;YIvpc>3%fmwR0}pa&f+k!*h4>2|(2BPoJm66V(EV!CP7MtW4Ikv21HcOl6{MGg z?vri+wObo_vjomU7FL25v(4azmQJ9VI!HM*g;#-HflbkjsRzVhHe>1l(JW?66L=L^ zz)e=@v<-0u6DZoiD-yviZ>-%XYztK89_tWr)CQL{9lW5F0V$S#L1t#ar478he(D%# zFc~t1GldsYEWrjMKv^7RiON%u!O(^sa*+gD4+UzXf!Z2~u`Q%g2o6xI09Vfmw8(IR za29A{8Ppo`DxF?2d3hq0hvC+>7in0Y^#bCN5scxG$(kLhCs#5Hizef)`0p z4?-++ zrCWHXFD&PfuHV25Di%dhv(XNOCU|kRgBMhqiGZ3$2f)&>Ty%mLxhVirpv!QC7gi!2 z-~~;dTmU)c23Q-r;{|XEyugs<$RHs94!p?r1}}O})MdEE>&oH)4p}E~N%#Sz9W;3i z$y|u1 z{=!@DcY)ligqg|k*8A)BO}|@4RK2f*nMHnp(gnQUzW`6+e;_Gb;0G*)Lo5F2XUaJo z>rrwjDA77LfZJ;~7(kh?$7vzJ|b#h~@Xpdz`0 zPYJw+K;Qy&y~_r$)4+b+0agYslMuxVSXvl#nz@)eFAvD@17KxL z3iV)>3J~KiLXA5BRxb$cX@N>d*rIeL4$y5IER4Lophb2Zjv(Ve!@5w@uo*T1Y?u&o z7|(zzXH{UY2aTHw+=2%2447KTdOWD_LGE1u)q-pT%=Zhxx&%SWR)Cd(6~TSK0xT`S zt-uAUVidR(1VGC{n79?Bmn(2hKUKw{ZVj4DzQhe(_z2$P0cvqvKwc`q%K#lAVu6eh zF@uf*=N6bXU8kBu+8nf{0o4G|QbIHX6c`oQAetGu71$I&n(L}Lbn82qz->)%BkeLI zRpDJm4@$=?*icrTgIkXen8Eijvpa(BMTHDGVqjI^;&zk)ovaLrKybo#1RahEUgQSK zQQ#dIpvhDCwwZcRYXx*K2Pm6^0|C5mh66h31lu>mmL)*cz8P@d`GC{?AHd7CLGFjO zLBZQq{xE}Aj)RttLz6wIx&pff;t7^)K29bMW)2PxMh*s0SULUx+W}5%s9TMGfUV#K zrAN>W@u1Bc;7ff#r)5Jo)_}^j7IyHi8aFl2+BVQiM8^(x~-jfsJBC8()01L1%(>`IKT^}L|9z>J{Q5NN9^WTh(Drwbq!gKw4i0BU}K z#;ri-R>1eJ9bi{t0S!Sz*SGR;qpTb~0rnU-Xm%4Eq@W}Mt}c-FSAovVcf0`eHe`Jtv+%>m#v@&c+9 z8l)Ob4>%wx3fy*i0lo+f0na4hvOqc!2x>^%$rsXy62;D3Bn?QJ@jy6OA0I z_1FtEQ1pT_50O5C`v$T@3Dlzlc^K6jkoLqA$aSEQNk>KnK_<`)6=>Zp%nQ?Nn>ZZ7 zZ3?L4L90qSI8o{f4%iJippCIOV+EuUG(Qh=3TD`YyUCy?GGw9%)T09rx94H-HwMffjIrN*K_h*|p~}*M1Q{eG2Ym;Ti(~HQ6A8tO!Sf zlA$ALX${PoknV{9$dir;=gyoyH-|%-Y3=#x3tBOH71MokIjrhYx)Ge9Y7i9i&;$u; zSG#_>%rqE&}ka%Nd&wU0yw%Pv8Ie`b4! zfTJQfiQE7?9cvN+WwQ@L^;wSZLCV2BSV##*!Y(H8)=dHl1k|7eO$35n2u~nTH|C0h z$8W*u1L0Ok`e1xNeQ^heC1_j*JeW3P`qK^$RlWt_jYn*b-~wXNbcs$53&vB^13Ni% znLY_kZ|LL@tp^n)sNn>y7aD}K9KX$L6mT>LFDif>`wbci7FLo$p7LY{Z>|8(_kxO2 zNHHn{UD}MYdrJms_m&KFxV|1-wqjZd$qb-*It`{7!k}fQppDI-8Axyq#jL=l!6XAB z6qGm=I2;*aGmk3-A$ul4=XS9On1I^-phMmTE-@=W8wFe-F?J0m&{BTT(c}u?E$9oT zZXS?{Fs9VApMq9KgLia3cX+kAQbnKq?bNtp{p>3xn$tMHvN6=>A1;dIv2g1epk}K0%d$0;|9g zZcr{QZiVdJ2Ul5;CN*aB2Q-!V0G!o9r6j1vbX)-5()ouI5!5rlB43b1 zCV)lWAVfex{e;t;xq}O|vzV(M)|G%XQ9D4&u{KdpaDkdpu+#|Z+2}GH-~tz?u&uoy zCr;pkv{6Cq1{P2oRe{~H19I9YW;>M;Jg@-TObQ-f1vQ~SmvuwhsaH@ov4U)Lya81T z56=r+NbS@cT%h4mc2G?MnK|CUj@bCz0G>J7v+3w*fX26%pm6*9M?3%<04O_zZayuleXa|P{|GJ{M2H5@SJ86H9R!h#Yy)@53t zf)p|k3ZC--B_7Z|1N2f4R1$*L3pn0i(+Jy&13qX$6M1POC^>>`0Z-XO3OrEalwB@_ zxnC8$D~B7&-03g+Im|(YG`Qgeoe_KhE~KHmLmy4&Y2(nG-ZFthsQwg_5@=Hb=v-IG z>L1KCnn(rb?0qc)juIGUHOlf7tYtN9VFXH9Ev>*ly(XPQ0@54<)df)Vp#?JsD8FMZ zn3+HYGn1nNsNqSVYzCc%&XNV$?+QMtg$=q|1X4DGR*SHJiVRTMtjToZ)b!gEK|4s1 zt}X`^Ndl0~v8HdPf!L^f-E-&jV}qD zVw&DNnL`KET2S0WsI~C_Ea)Ujj1)-A)&kT@9ElP%Rt(CV%nAaaL@9tjQSvB&8w~KU zfu%_BRu@Q$^LnQrUH!9RV~ zOb+!pcq^M*0hB)#c@zX3WkA~}*d1pGg7-~0UJ%Rzt-N)7Aef~Hnu9+noCVrQ;rKxy zOA&PB&A?=fOcGiwiSSu7YS!6z&dO{gq2u$WkBt>55k~P zJkVZL@Zx^x_Em^4&6pktE1-G{v;?*OKXW0kGN^k4>YbZ0HNZUxatC^ z&Jt*1gakB_9`rzfCPrlixcAu|L0X|lcOdDUAQBH6XA#MAT!&^IXq*G22a9!}osP&M z2Dc8hI}o~MdIxy5r{e|T>5X$aM4@}8ZwODHH`)yEF%n)&81off8KAm$PI?e@(W#q&mO;qBL25G~WI6%uK zK{1cb8_?iY0!>81y#t?vR{|v+&^Z;Deu8TRP0colAeJv9>4C>3#D}ot2HyUD033(l zrax%t=YsI`h4VQ?bsz=V4Po$#4Q2%)1r7ld&@n5#pyurbVNhH$DDZ$dSA?hQpXbnK zdLTTV@e+pu(<9;Oh6}(2Lg4}qUB;&Aj$1jzd_evN^(&AH1XjpqG0?~)JimUJ(f~PQ z2XvrKhe(zZmq0IQFpSlV3GRvJ>5L0G#OixFAyEg;$M6m1QlNvZvE^gfjyrHZmV!o= z9Aw}HoR8%o+t4BT800@th6A;e(H#c~@mO9(1*jth`k*_*!CRalIUM2IsSN^-s<0qe zRFDPrRzm0Jv=P%+=ci18bTQ}plK`;xnwhA;z&TPiC29yIipc@L9p!>0z!P^KR z=QTicWY$%@ITBP`fc8RzstH(*JhB^lKoYFs4$6`6lL44ea^y9TT4hiASz{tP)^f+Cv(TV$gY+OWumjP?&MK&mXf+8Cf>5ib{1?m}4WY;qn z@`5(Pfm%c0Vg|kd4IJTU`4Z|gkVddRP#Fu#m$0=lNP6Ij1nNP!9!MDrZeQ#WR%8Rs zXk8GVZg?F%V}cIRXBIF4ofQYmm`tE`(y)x#_>x0-dfEXF2~h4#T*M(V-G3^(+&{LVa9YoPyr<^ELaN9$n~IrLd(dY^^Tylz>HKn?Z4Fw z+sd^Aa&C=C7JS`vhX_)3z6(;x1#Zr&~5)w<&#{8R$aHXU%9$5pkS1F2OPs8--tz*Dt;2dQGL2jzEgc@6LSN)T1G zN)U`-Y%K(+H=yw(52{+>-ho%G@{kxtDV(5wf@=gVga&0{*cwtKJ@70H@gc07mj~61 zq*Sev+zMDz0hX#&VS3am4td5?)5)k`*MMpxL4j&m)rGBoJ%F!%6(_2G6^F(bY4t1A zagbn-<&}du5?)uzf#M3Semwy45xjnt!>C{7pfLd}Ord2dsD4FlNqxAG`%Q=h~Pm$Ax{Rzqz;LTZ%H^5~NsK!LiM)20h1klzqP#abdUq;&nQiUy} zi4m32#Gny}yRO8R(V*UdL>#XyD5Jr>1J7u(pvXh3E1`aZYXo&-kuw^S9(YEB_z;%S zWTDXu9(LU!tVpm8D~^a>l;Rd^M)O_I!BPJPRIoBZ_8x+F_d&Kwg6eh9>1tabx9WmM zOC39eK)Xa(ia;wB1);-AJ+N)p;MBwpD!*GmQ<+TQ@p?#WiwU$>7BVunLs)?oHn_?H zy7_ndmGvCL^(R0_qFI5*PEUePFV=^h+;tknLmNp2H*r|NJ5NEb1kEFXj#~h2AifIP zCJMSpnpczuw4ny%oEaeJfO={m8Zyvn#xw`S0rk|tv^a`m;fD$B01e|QPM^1dLtf(q zXuGboIWyRD4v6K79N~A=)v$v@cY4J=4(WQ-<^)pH;K|bl0Y`o8 zO#_f^kWn6d9d=Di2Fh7PFx3K|=LS_e&yQqYt|bipBlNLk>hhE?R?Mi}o74hhBy z(;w{QP-L70X{RsQ#G%c2YWjsu;KtRzO`tgi5sYCIXqOk%XM`68y`XRb9o2=@_3seL z0uP_CAcfy_kUCJ^%#Kp&f)9~k0Ua&H4!Izm85)E>pz%%QqA1h;7$hcI#B@M0G3Z~&Kv zC~b0VBV~f5WIU*K&~T8z9S(>grU^T-_lSk3W65c$JHSKBL?nRqAdg#MPXILVIMh0t zd0c)Mu`&K_X0w1J4v&KlN+X=|1xSf;sC7_}i{p-Q>}@nb5>x)A-QX$*++fB%%E$^G zWrPk)frkM;FK85S1%x_y%Vi5l z9XBL#)q_H72hurFph;M)fs8V6#ShZfwNA`KMy$iow$ID@PlfL5UJ`m-Lo%nLl)2#YkljcQP2p;Vwyr@_M#)RF_a@H#hS zY5-gKLmivzC=G7nfzM+-03K5W5AcE(bYcwF>mR^Uk}^K$5T5?_A%_HG!}R(C;1P(0 z2RIB+133zm&`=w1SepV9jt~mur63i^fjqt7Actr@dZ;1?Djz5^@Kv<8P z$2mCc!H2tWLQi&Kf}FMl8R29yWBLMWh;V2!3D_}#&vRumV|pW?fZUIUjKM(WpzE>B zL9>A8XW2pJEU4@P)q##1w{-|OazREW9|$Oc!U8lI%>tQt0xj?thD=m}tI~ZS)l34_ z3KB@8Fb{;0()S^d3K4lN=IEXN0H!I?18HbWV7Bz;h01F9l{r4jvZJ zlsgCL3d|eeW72p)2ZMkv#Rg3~vg?o>U+{ zg(WZWVsUsghXXvJ4Vyznt&*9bRWc90v{H8mGGEqkfJ0>Z`7@CDGQsy8GK~DwZ8mVI z^B^w+gp@eA`xMw>{>bqT0Y_d?i32*w5VY_d_sWIKAhn>=gCvm(6VPM@O3Z%+slpcX z;Hz>7cGkhW&Pa}Vs5hX|CkPt#T>v??5or|ysM8Euy^g#Z1L`NZM$pnEP|pHZq9Eyk z$2i1?uowqlb3w}V3O6Fc36J{DJI}!}o$)MqbdHRLDcA2pGd4ST(*rm$H_y#mnj0JO3nv@iwHxY%$J(zqZPpWaN|4B)Ml5};E40(e)845XBo0H0C^ z+A9Js=pk|%CmjNg`~uYq1Pl7FAeEpaL*OML_JV%aQydGGK%0XJ7xds8 zH%U$pP;Wq^l@C`z59&Lh7xYkn!L`D@2hJO?f*wgHJY7J13QHH@v!Y2V=s6Ji9&=0{ zXF)%m`6`FH!xCZm5*pBAc23a33h?yJ98i;o!;A^MU>$lT4QRY&f-vf`2{M+@`~|Ia z0gXq1SD-?x2Gpf7@al8oo(|B;BG3W2bA&;+R-&pysUYfifD|K|n=-JCYM>N@+|K}S zhy(TXP?`pWQw-=v5m2`Qy3hjLG=O>r6z!lTG@xO8(4uWfq{CKNfMXqPS`g|pkVac7K|4nnL%5y^7wxZMoyx*#otgGVgrq9@dd#kzEc4cdVOb$eia zc#OqSpvE3JfrDnsCWvG?LY)W=Y|yb!2$#Yl3DkoH-SYuo>jPRk19dS%BSlMR*b%uK zr53<9H- z(aV0Qzu;Qo-h&ha&`Cigo$wqD^(|Z{Bu7Jr-H2)AvmsIk31z<|Xdf$R!0H0%o(0fF z<)EDV#R9vB0=@e6d^^#i0o zvI8=KsSR0JdjPhu7BWN#bLtM{g|&O8|Gvke!*oG-y5@Zj1*S{F)BoP&kkJ6SPnY46 z5ctkGU53j-j*Nx89FRUKO3ws`J9i`82|nna6nC;hgOmt&s)4psf)*Fv0hN$UprfK8 z$3!bJJF*lBtm9M!?TuJBz4tzcgf8f4dToC}2& z*$9q)!;%d8>Sg%oH)uAe;Q(aeIH;G+#KAp%(R~hm##1CkG-O$|V+W`w47$?@b{7h0 z&JM?ls>vX=pm{rX#~eZo&PDxV`tMr z=MIBM*`ZBjet|~NVWglFS7B|GH6S%=;M=VbvM6zS1*8HcO6wsP@6s|(p-zVfGpub3 z-hz`4jxBc3Qe^NU?r462YXapuP<(b*Vv1TA@>cs6%AT3jE+T zHF%;Eyw^kTIV8&lJm;`uJT<-lImwZ@bu)CF1hmr-lucPNBk?py4QPCXSpgx75{X|x zDzHT&6Rv@Dl%5R}Ss4@RbZFReDnNYz*|r7Bs-S@x^p+6RFK|tuO{~a~h@=M|iBR9c z^*|yK)~i9DP-kLL-~lZ)XHl3Q@REZSHBw<~TbXg>(Z-jM4Lpg9Adw25Re$r6+(i@L zHgyO%g0d%QgA6EU5sM+~x|;>1SxHLk3tB19-r51)wh1b% z!2=ASG=W;#!b9)~NUa38<<$Wm)j(B;lK1QHffS=;enFm|Bf zY{O08`JO|lz5+Cj1}f!N2xcj^d~JAw}F!H@yp z>H!I0(4ivC0%w^(J@yvxrJ{}pKv(5zFfo7}3m&9eAP8B2;5b1Td_waM;Vgltj696o z(;vR)5K}(Iq{sw0a|nE*<}Fsx*}l9C(6R<}-uJEPQXe=}Vi32aA#DRczM)CLQ66*$ zCa6T2AdGs&4P+o5w80U2dIf0NGtwC%;8XQL=UjnLKhXsr=LR}I1ZpaB93$OH42t3f zg5V9dGLR@Wwgd?Anv2)rpnK5~`=FQMjMO1PVW?4;;^k18$&(L*p4d zXfOkOj2zf?pk4`RXCw>KemATqkFh9lDX?Llh5##)76^h?Y%xGK6)zD~n*R3-hdZ1?Xj{_ohdE`#|0u;Dh&MGj_n(!3xDI#VZ1%v;TuN{`>9P$0s>P+rXT*sp~!NINr`Q` zudI;x^#9*DTp3?Ycm2+x&2)3l^rG(^$-+11v(nKRBWp8>Tz{$JWl><&XH-yP2c0Me+Fro~3Q{%&21n3=LDMJxS&Rr#5w)oV;Pa@`F}Y$8Bb5I{LAr}@yztte;n$JYo@pU3H{Ndm@i@61SrwTapIrdUaX?gdEocSFu|wom zKLE?IL3kfugL$kFl}jw{w}X_jK)7EHgL%vl-sKHo9utH&c@CJz2;m)`zJP_(lxZ6K z^y@5~4l>UfmDnMxMi~^C9C@;oq!id3B|taeHL!0tX61}xWcvGJdOsVd0#pCX=^NQN z3nl-)m<*b=gl2jM1$hNF#}kZM0?pH{**T>_$|Be~&6y^?oIa7AQ-NtNSew}NlkA+4 zk`rG}7I5T*CTU(?1qFm&4o+#1+6WHLP^S4Wr?2PWRA5^Ea{5IM&V0%FAZ@HLZ9EE! zaBY#CoYEj=Wt^POj9;ej;^Z{pUkOqT8tLHzFF2Y0my=VLX)D4R%F{!+IL*YM%Ao$> zR)DHr$i=AwQhl6@Q;qS<^mkmGdxV`B9TgPVv)8sQS7LVr)w>+t+xK#F@-Q;qoqmdk zvySoZbWdK+8;lR8EAeq!GJcsJ!^dgDza8WbR)K1m$@BR*C7Jd^-Eo1BGg1ty0_p@d zm_mDgPBoCibbiiwravsxPw;cfS~jplV~WK=fz5FOW0r!Hf`|gY0-NIlhAe?bW(77~ zhAE7o6PnpHm?khPusO~EsS)U$E+@e0z}PuGUVt-;v1|G%0ZvE8&gndYoRJX9clu^Q z&KAb5?G{3usf>(W)8`3uzGUp2-XX$ylCf)hkSM1$qwYy21vY)g93^&M76nbvA(9zN z+ybYVl(-Z`KrJjT1z{_OXayA(2L*0{yW4k(an>_3c1*XD;PjF@&8Wa;$Z&A~1Gy-||Wkn#2O&61qX;_aYz zmAHT&12;E=0)qmhBd5T55pMnIj8dEu)Bj&*=9wNV#VIO2kx7BUv4t^9fl&a=1ceTx zz{KgDQk>Gfi@ymoJz}iqtW#!iJTQHu6sH)wBB;a=xG?>k6sMHR5l|l(R1GT$m@#bt zaTqn3M9i7?K$$G&Ok1Y&NOM{->P>f-<}_r~pI#}=X;m+(z~uOaAxlw5fzh#tF-x(Z zQGwA>0d$@Y6X+mCMOpB@K@A(0H!>-`VpL#tJRk--EJsOuZHwYNFkckRw_1x(*rNEJ zQ9;0w19TFc;(U;zGh$hanhLCrXGF6UO%+%jAAnEXQCz@SufXc~LKtL%qLTuv<4=Js z#R>&h&_R-lAquRHO9ZnN4}c7~D3+xdtHA2GQzT2#T!GcGRU}JsAxPq(SeD`<5aWbc zmSTVctK$a&uxSTDVkbqi6yp?F9XE<*DOxD7I_?0wRq+r=?uH0RUA^LA5a*;|mZGBq ztK&!EEX8;QR!7iDLpC6%31%rS1}SJ1%u-weV!RZ{Qmh2IOgu|*DM;XhSeBx*0;}T< zu`I{X$ua%RwrC2xKYNDzG}f6wFdw0TQ?=oTXT&!0PxzI7@LQNZ^84mf|W9 zqeCQ15fqFw#IqC|KmrZoS&9h?td1u{vlL4}AtIcmn5e+&*dUgrI02;KgJ_oGY7pa( zuzQwbBZzfHG)r+Jh_Qn+OK}Z|v4%5Cu?fUD!3_#1#iJnh3LY@~7>GTC7tB5mVlSBf zO_o!RgI7Tow1SXj`^O(*8I02nS$|eYN#(_oWVGq zaUtUZ#`%o%80Rw1W}L$~i*Y97O2!q8%NeIJx|!T(oXR+baWdm1MhAPl>4l1%wz9@m z6>_pZzMmNxSlJkvm|67L%gf|HGV=0BOWDRv4^-jQuRkFQx|G9=>3}FGaYIkbW_3Iu znx()da1C@0B&$B-5kVzT_l8x0RfFjOxFL6dpQ&C!N0)(t#Q~C@K|Mgw8Jvy>_(699 zUFT3>0X4o^K?fr7<61WLE zwh+8e0wnSR)EZY{a#T@Z1s&fYaFaTGs2!H|yhe6$7gAOv7 zG2H+e^Z?1A8%$XO^$?SufK6h@Yf=~3B#@I{fK2)THVNdQ7fe|Kx4{nj0yc&fuQ8oq zV?Yl2!KA>d@7TZ$@*2pTA8>P8m_Z(6#$wKNR~1fca#D^N6S3(Abm%Q46|zE8)E$)g zWH75|LP=Jjl*w<#bO4dGXq>VnLF-XTOT`}ZL_Xcz%8R&*HRt=^X!e&e}K+yxrmk`A>1fltiRg?L^^au7l64Ue5 zI637}tV5*bHgJr02v3hsYPeEPZ_cunf+ND6!;u}PoJaCsa6jc;c`@QS73KM z!;q!G;a^mO+snRyruKXfT0qG-K6g`~kYcpo0mt#r}(cE9jsZ zh)z}wrVk*uFgY?Puw|Pu2`I3FmIJ&1o$IDz#&m%nR8#Ek5-?+`p92ze0xtw((qLlI zWMWZ#4vKIFD~3P9kP__+NXc_XbEY@Kpa28y;&%kysmSU$LpV!d4l|PjD4dUg!WkNq z2SA$%K$fzbF)aXR7)(07PvN$r6|kj;<8*sXP9xO@WP-P3L&Ory42s{BRe*$th zs8f>0k);TZ9`N89JGc$-0Zq$Ou$CVnEucf`!THXNX#yyJP2kTGxW*11n+4^k7oa0F zz?OjmjKz^b;28(VPcNnm>T-(L9}vq@Vpm`ic+LT83xH-cK(}!!aKKZ@9AQ{+@EUX$#7p9l$ahldYVz6RZ07?v?RjeSI z&5UUdsP+IA9H4YCLoiF=1vq}zh{DWWA*$HSs9+B=7F4lnFkKK6c*?4vsh|kzjx{qv zvp$3m8aZNh>;OjwlLDIphk~HMOK_xh2rEtZxWL6b-AtcTiWj9anjWjqX)XDH7m}53 z@G5|_)(0s6#q^E(oRV6Qn*dofm|%s-A6{IANRt7la=j8LRlMMZWvv!mMGm+oWCPt~ z@E=rYfv*IF3^o?r>IGTLs>`s4&z1KVBd840VA{Z^AP5@gW_5(z zAkM16w1p2^$e#h1v7nP=(c3x*BOAe$41+b}6+VS}aAO^Gd-(-EP-C1g3s$|H;8SGb z`3a64(7ou8kOf^h$^k7B^cmOiflsW&5o%!PgQHi2X$8ozAB^1KP73J!dkGCD29|mS z$>|DKoI;RN{sky+fHn<+@`jQX!w*os%wop$K>$+9&j6=yHb`0og@isMysUvtCa{8T z=}>}{H=soHKtPGzjOhhvZ6T}U3($Eu;QM8vMK@?1oz?M&07}XILBO0D)U;Dz2k)_E zg_hh$gh8eJ0eFXO2Ds=xAeFeY405psDkVYT0WH=TIi@#0=MtIz z!vv#1lQ-qGmU_SgEzm&Kg_0G+2Pppq&-8`eTq57+b(dc7K$5>c;~L1N$T)%uTwsCYS%YZ>uOiFz zbLN~1u?^4!^aC7oJXs2Q3Ty&zz-xd_6j?yU%mf}#TSbGZheweO#Gk?g?gYW_J9YfQ z4X)EVc$BytAAV>Oa5NTx)JO}ads~na&mhO)a6I_1b8v3}R0qxAF=t-E18&8GZed|6 z0Zk3FDX?dO8uSXspwX|yBCW)(z*euot-vC!1R5)61-Vt=Ev$1-pl$+L$E3j&0tyC4 z_)WD6Y_KE*$yH3CEvz9}Itj4M6$I|afL98DQWFo0187i3gJ}bgJ1?l+1-YCTl#+O0 zDTzmk4Kyo{)9C5@Ejd+nuYjVCRfA~4+%E+n`<^DAu8^FHp-}REb+) zGPJMRBdo*$s{cWktxn;F48cL_F(${BSCheU2`oR;10J{d-{C@!&p95{vg*LGTuGNG3 z0ZPoE!~kAYEv>|?07~~D=OL#GR?r=wQ^0e;-~m8zN@G)C7Ptm#n}d2lV&=>fARY!a z_gNrg51@V=Xb=L@uNDLK51>7X4)H9|e0x2p2l_`W%aIYZY=G7AhZv}f^a9bl0x4<` zhZJTH1kIU0fW~fEHJGM|J2GaOF-;K%jq8hLDS~b}1l1TU0`HI#Gk6?DI1AK-hWP;8 zzyVd~U^y@0EPU%~4Z1|h@i_x1IZJ9VF>q&rMn)jL@*^UU*mDH66xbA41mr-MT7fzu z5La?oGs41ofdHs4&In3`2Sk*(K?NS@x?Cwp4^c@_fkWUfsJsK!nBZ7(Y^!Hf5LDn6 zcn|AcfD;xGRxw&J9N`CrGH9p*G)@Q#2L3EX4$xu?4qXONs} z2z&tB^+E{jt$J4cmNA1nd9Q?c89`UTf*lPq2^49NIaE-q9H$xV;ILuj2KAFbAqKwe z_JSCwL6-&Yim}!sCin=O0&WU}nm1QK=@dHN0QTSo;VglV9A@BgB?TcfCeZkl<9!Az zh7X`t7^qnYs&PQWHjsYY5kYWMfYmV(N30^b92DnISwUXV0FR-jof`t{-wD|&xELIJs8{B40 zA3zcI0Thv-f=Gku#dHlvPBq3E)BPPeW!XV%XqW_^PcL@l3^rK61-kdujOhm_C}V=I zi!)<7!3oNk;A_-a9YLx&;J&;t{hK4Fg5(U&EP)AFrw#O-IMvk8aDkG62Ga>HNO^XI z3zAF@a4B*rfT!NT228JU;?zz1!~yap1CxUSJ2+n|aF{VY0J)0WjOh-D<}zct!3C}h zSV5P&34G>&=0A{f97zJ)Fb0JUiz5%Hu%9uV-nND;RDDW zJZ4NUKy}&=aL9mC_X{pi3y1}D5tYCfa0&H?3tyYx4xEBO&H^>^L4gA5NP)uQDtNf+ z2%{n!q#6L9P;!D1v_go@kt17?OF>BB?)2`ZyejoPg^DZ+&l#O8JC-Z*GCP10ca{zV zgA#|U0=sJwFB6l40 zEGG-}&Y~x|zX5a?J4k*>uD{#RY;|<^x3rnpn#HLnI1b2W^D1$X) z4~XtSq|^p(MHZf~jNA(Rkm`UrrSPz;w-*alRfWTK+_07wmP~te}@?-(f0!k(jha<~z z$>qtgrBV|a9an&4nHfNB4JHPKiHxAdR2v{FAj{oY6$Bx3t00eoI;#qdW=!CaWr9rq zfaXR(S7O$KnixzPOrTB1pcV#bErS`;6EOu%M~*Cziy0I&6+kl$pm_u)4JI8krZ-S^ zF=kA2z`FGr%B>l;gPFPvCE!uVEl^%5n70+ms|53Qf_UIbHZ!Id)0ew(I@KS5xE&OL zfsQ*Mo-=0xS;=C?v;@@DVX$J@2ByuJW`OjATH}yb5XdJ9W=sp9vKz#~QzXnQKzvYh z2hy4Z4K_e(T{cISECqH2K?N&;Z{VSV8G;I+sR{5r{0Gn#ouKwei2@6V(u7dc6T7*@ zrq6TdcPFKUyR%e?h1mS*&)!nPf+XR#q>+=oNCB^vjiQ%{G3t264j5Oh9YQ^ z1b$07*omVCn$v{sdc!WTzCUk%a1Sp3k5l zQGiaoZ1Ch17Fhrtk^_&3F={fgm@_|^e%+H(z8-WHW8E^kuq!}dWLPpkzv850~@6E1)4@s;#LpvG$?@z`UTTl zy*Q22X9$9`2dKR40cA%}$3V=HF-wWZjA@FP0v9Nuy#Oyz0Il;eV|pP9iuEVpIR*`; zH=>F{ApQg~MP3j+M@*3qGzYOoOpzZ%ZxvHi0i73qWV*aJr(FF4F-4GBn#?Q26kjtc zu$T0pBiKp9=(HKRE=rz^Z*G-n1C0SbJOQVFC2Tu^`l1XN(~nlViP(L#^{ z3?%;o>d|09`$$zy=CqHi3_9u;c(5Is`AQ z0)^uRP!tF}hG}#KHTFU4uO0|yDS=m)H8LuI=|l)+3ZkaZ^x-sMyfFQg52r#P@)QVo zQ5Cd}0V>GAMW%uixK`I>{vZGM|VRaOM31Z58g|fK{;$+Q zzH@-a8$hdqTzNk-PWSNR)ZqHg0dDqdFm0H=!H-i+46(!p+zk81$nD6Ws5JeKAE&JP z2Bs_}HeCi0&~QDQz$9j9Qw|h+;E_T$N1iN!f6UVr{5d`9H!y+cgF!2NK#M6A1R&F+ z2=l?kA84Qj+*ng_WKf*X$nyy_@B&@71ge@RfJSE-tQn_(=m{KIU{Cimf~2s`GK0Dl z;Dvr4)Wyx27fo5(7|1Ef-UD)D z2gmeF)wL7u18C#;Cvst)7mEDsh0~2~w+r zvMVSli7K!N{NhmJ0C$$a*$zib8r&`W%*d?(ZKu5hcgL9&1OnqO#rEhY5TS)izd z>VC@z8YR$~t{==9TmOSSOTpg}X(ft3*ef5vv%P=V71vj%)*^!H zs!30p1stKre}d+YG?@>GDZOHZOaX_1A|2G61D(weDNn!)K^0yxf{ylL)n_~bUBRKr zd_Yw3Iin+kq8n(@oEZ~%?GUJ?f@&>jNJcQranh6NT_K!CS~yxqp!5ogCo#wv(g`s| z?&;?jvq((;7s6?-e2Phl9dhF`xcXpq{J@^2;Gw`K4XVY!OJ9CWj|k;d(6|6fT#)gy zi{SAxkU}O$hinBlX(c9o22Rkf4pzs@(-(wtiqwN$>;`W3g1R)Iw8AX#heL@8oJzn$ zUW%Y=d2u8a@a_$e!$H^4Ged4Y2IYngObP;Syq`g}1tin+d}d??Uv{YoDu=-R5>WbJ zQB>l2Gd=MPx54!MG*0g6zv{WUr`LvYDoj6eoJ*ko1ZavLv=&Q5fz?sSieUqELF5Y1 z%+d_-0u4}_2iHuX6abAEM`JUl4A=+d)$djtq({p!B1`^kaH>IHyMaZIHu2D@Q~@T^`WzC1~CYRD$a>E`hoZ zGBU%6! zW^$Y}y?zQfp1=z^4v2uqkXiK^mp~P2FfHH*_3s@wfHlA>rY%ru@Zu;&1t!Ngmmx#Z z;N=c0L}A8UfvN*7ngI{38*^ISf3&bQ#~kvbQzBDgL?ho zLA3+?W=tI*&v%Gs3H$}EzyM2u)_y|G1C3d7KnJuz+bTh&s4l}5VNjvQ3R+^N2wlJm z8f;@!U>CRtE{#uUf|i_u*OeU+gs43rk_Ax?6mSpxsTLB0g!36RioMk@yJ zbP1~l(*glArYC|5tU;iW7|_Zm@UBcyA_vVJKn6vh2*9qx!B-W5+X){KRncomS@E=h zTWF`R(%LCc9iCB_bD2?CyD0A(>qEeVPtea04O1_PBBGr*1g z21rH&Ri~_uGlZb!1?U<_E94cHD+IF?tQ-ZhG_9BzK$~g7y8;xfkSjJTaK#3SMes?Z zAZw>GBg%^fLenF{Io0bogJzaGM6(p&1qNvS52V28fjSRVV04I@F+Bh!&I#}W19b2V zs0$5R&3FjBq-6?Jg9g(Ch(91jBrK!Pfl51?m@!>|6p!`b=_}C23D9y6Sozo>g0`ac zhcHM0X+;9FKq}LqO94j;9$wLs=Y-)91!= z3f6-bWjUghnxMgRNFbn>nh!u#0ix7=AOaev0HsI z#^B4Wj_`xBG^9`l4c`3_$O5lF(qNjtYce-i{U7iG8*r-qAOJ0gL5s-1JuIYh0krCb z!;EPQXhMkHj0s#xK$cvxf+`khxd5&#K$B=I7_Atgw(;P}m$z_TfA(AAru1+pL) zUl7icfi8NR0^S#lt+bwAQN}G;4_=oGTFS!e2wE!%DWRW$N+ECw{XjsG4bt)hl{=7z zml@L!P}u(v$#Q&i8B#)nO5F#7N-tqeKIpwnprufdmLDjqfhrepJ%A{)PXvH!7)Frx z1A?HwCAiQAmwTW>8=sw!LYwy!c*gDlbfWMDQlTw6iy2lV@-o8O-fyOVoWQFjhoi{| zYMHY-9+)1N$f;Bhnq>y1Jbn0<5YX@lXvPY>F@#OvD7cV-HD1B#Qq=J<11NnR5tTM$ zIsl4N=(vs`Xv9zjH2M#!mXX&~fNC-pg}ICZbHJXbo(SuJ=e30iY$(hb>k<*vOpsc z43PK)HL0Nm6=>F*#Sye1nZsLJiA$gfHqrpC5)Vw@l*FmScw+kfBu*b8P@5KXe3=59 zKr?tn#*yi^$(#5$(f}T=5V*jo02$H3QAbUGkjyDk z4=Svh*cR)8%c)L>$mo_~g033Z`3$VGyX#b5^nr=PpS zDPJ!LZLz>s8G=$a)RBT&3T%$&o;L|NstBA1ucn6XS^%YVrZ3<^2AaE=96x~C%j^Qp zoUne#6^Mse9QQCnMvxDPfqD#}k`9z~K}r=^^_dv2OxH~3)L=tukxpMJ#Uto*090Io z>IPQF10q?_Rw#Je0Jx>M0$jO+W`05SEo413ba%oAku1l}-Hif{1_ITfLh6O^^nX>H zQuUy$4c)5+swd!WP|&&t&^kg;YZYFcf#+dB#Th>1A;p<1FCz~q@j`+PwAK&OVFsP+ zpuxl;u$UR#`UVf6z7U3VY(Gpd$l!DlpT(@m!VS${i1O|!xV)3VQQkcVr7i_WX{Z9Lqabv7HE8-9)V5l|1eyN+0uH)0pn)mSjteHpVGZCIgRHa! zIUeE-P&!~2xCUPr|jZv^Ulg64d{K?)i+sz>Mo?}rkY3pyTz&5=Q% z1)RJ;K#c$mGK0Ebx(siG!K*_-=>ZbUAR9Rp*aTWR6xNqXvhwpbzBBBI3C~P!fWAN)VP4 zp9m^(@Pca+NHq@1iJ&c-pm8SAEF})`K~Aio&J(y-GDA4a@$KOb$br$|91ZDXf{wog z_lG-#L92@$86ll&;Vj1~M<6F#GSxePGVuXXL_-BUHU%2T2M>aQk{`HzhG?OD5Qc=2 zCi4qO=N43jf>-8(bm4L)sNDeC76o%CBzN=Lfk!aFW0s)B#jqompfx$f-5_5h+zip^ z%FE2n^Nw+PViK>kEE{-g4QvCb#n#U0$Xw{in9aw;z{tcsea#wf8CVJUKATexPmr^N znk+;I`F>C!nuCf#(0~D9+ZtCezJ2xdnKTQ~Y!S!A@FA-+8V> zz!7w~9Jnkb6zmT{ss}3Ap-u&do*pFFL6swT00@3CE4H-$@Z9vbd7QfZ>^K5h#}3qy z01tXVdMO={Fcbh)fZ%EvG#mgKvswTuOjto}@Hv98!RzT03po{#Gle%9s@B;Bo@g1c1c;3}}^) zQp8OLsk8y91nufQS{blm{r;e~;0ZAWM(~PCP+Jxp ze*&GHh{+@c76s5e0S_yVETJs}TG_`UFpC+ghnE>Xzw`uLX30(8UdkztWxGFkI_fFB zU&1KRMP#=IqzBrjcmUVVfM0t((h&ooP6br|eYk!`fo>wLe~joA-GggqAfR0g(Ik3= z(0>=)?uAU&;OJ5z&VvwDVgdJqAr%0qi31vi1Z65vdmhvZg*1qiSR4i2Ktn)oMeyb* zByvGRkf0?l;Fchw>86aMRsIm+&^sW9Dot;=#mmiW3t12g>E};>xPx0v4t%}?w5bSc zyMQ;{zz(vwGF_{JQ%2wdxRnhWzXdsYgUED6H90X$=)?%PC8xyV$PR5jK!rE4DZOA+ zU|$=^%MPA*L2A!UXFSL)R}UJP0Uf^r84!Z@sK7%PT%ch30PfI%+i{@f4&VtRP{4sY zVjx$5+Wep@*|C3G1L%0AY6Z|*4^Zb!34FdPD@YNzLjXEqV>U>UIOrTr1ulUW&|pzL zXnqlWu!#$_@Dr4^K+yy4OoE5|5luPpNDU{b5CKm!;+$ecbt$O%g6vjMSpYgMlL78# z0oakK;JOVoreX#j`vFbpI6@r{(!~s_6G0=W2Sl<2T4DPlS-5#X;{}k3s(w!RPGQh! z8K|fNZG*cH*%u2MD+4V*+8`&AB>;<~C|>ZANd^+)h?Ar^x(5n=K>_@6^a-R$6giHV zm=8?yCvbumiy}`ogLN^_V&<8yV8A7# z0xHBo1I6$-3j`Gq8cd|b8MwPmbiUdKiVo0;V)*jaA&?^Q`LwWn1&Xu0T0(KgL~0g- z#u?O6(6E1sCC;8gR*xM(jWeh_5&F>M45SS`&cu*Le-TyVJ4b4tpngVy7NMjSxX5+}gN3GjlpoPawQo9j7k>L+q4ae*rgUXT*- zBoHjcunC-E0xc*Ng^n5D0a?xh9@YijEDCO~-4KBe;4oM*^niNZ;G>5+1Q8qa8w3?t zK{K^H=FFf;AW+Q(S+)q8xp1t10q&-P=7T`ZM#!-v(36PXK27XBfUB`^ux%54!<;^JjdU{L@ECCIo2Q8Ok6P)6-zR1g&S z#>T*{0A(q#IwI!z4or7%#LlcJ4~H#*+N{B} zKvYpg0Tc$HQ58rD1DYUXb-V#O=2hU%bnPZi8M(V0ihQ5|L8VYe zM)iCOo*GOHN@5D&<+Dl(pzTpgDvt0IW>g%1Gr03ID{v`@Whrtf2!SXTX3!aZjBdOv z3Vfh3A_gTPur@A)HZDg4cV1Rz2L&ESMmJtI(14HvA9x9h5(`MJ<70+wK2}DKdIkmt z4sHbw(9tR!AjMgVJUoot+zR{(3ZRwBN}xUF;Gr4NSRMFa5!g948$hAK2I{-55p`AI zP!IwggX3Pr%c{VyAOzFPqQC+c2K8`2`=>yCKu{DlfToxlc6Zeam@(}E#liwc(7__0 zWCU8O1e$+AIo9S2C;}PGncjdR546}EG{f!)n%rb{Y!CwNmDnK)I@Sg>P&S2Mi3`-3 zbOg<}Kjh#6byPtqZUSiKg%D&Ed;*gFOF%O}Ao~~agIZMlAk!v*&0N3_JK<&v7Bj(1 zM!-`gppHIR188jtw+52{^st*NSag9Gkbnvau0#NIcT?$0A$}9$Ql92i7AetzB#yO2Z{^O$RB883o;A;MhKjsOnspvsuP(& zt4YBH_X{D=i8wPrsS0$y4JcJXri~AXW`U1%n<111I?|0*AGDALlq^B(gh3;L6Q{3g z<5Z}>AgTynZr>rSC0Ws9Iue=$c!H;^r$4SAp%kejI<6hJOYT&^q*kUBzN?ONNjEX~CUV1qPhjHo6CIo~A@KMGClc_AG|ZkO7w7~8P`4V| z!@dRD@#ctgsq1vcR8EC3P(upTjs?#PfDWMnm+GwgjIi_AK&P>R765>|Q=o-mlffm$ z5zvAP@YLM_(1I7xS!Yt9SOiTbIv$w5u9GuV?f__2y8?^AR8CO#WpQLsU~_!S;3m!L z$iOWyZMtF?r?3vDH^2*&K;3(&H*SJgyE1ApB}@-A=9Qmb-^HmC4Vj<;&6uDq&}GH# zVNmpe91B`cq5#SOppC>Jhl4rUusUq|zb?)I#sky+yE)|rAx?)@#-M~VJ+PZoL=NE# zPEgW9l2>3Cm_B`DH>a=uUSPMu8FKBHmi@*%nXcuV7JFBA|^t=-` z$QCk3d+3Zehz}Yn15NIMk}AkAp!jD|V6_ExwH3Go98d0Z!ky;m%;F z2{NoK44v)+^-dKK`Fjg=5mr2CNCq~(1a6Li=k7EhNB4nt5i$tO1UI2!=k;e@l zpgIc_KHz1L3LK8dzJgaM%mSRSSG!vw9LK@rvPbf-&s$b9%g^;B!x(uLcI!9K}k%}zfmF}P_AGF#AF>Uu~x`P9+ zSp5S~g$)`dS^=ulAoCNTHMx+5Pe(v&ZNb&>0e(=qhdP7>J{}Rg>eO+~-DUwtE9ls@ z7-DJ*y6_3q;|DK%0*$+a3s1;U-OGB|YE|%H6bs>=G^U-9ey}Sq6VC%iZpU?uplN?d zNP~uqKw%Fm6F@WBpghka@DNe~LORGipmOkpm?9VGK%(gn6F5buD@^3%;(H)q%^;}2 z<_J2)&+*Z8i;0{%Sl3To2X&y8aP^sQf)cX<+G(lqwHlyo%jS6D^kf0Y3ICe}95n=H zgHyu^F@oz=Zh{AV88w(%7(wR?g8ZVy2HNrKEp5is0$<&A1-xu!dfFLo>FN2#yll{Y z9MGBqQZjnDfU*v_ph0Yj0ndDcnh6)cNA`mA6b0(mu2G3kW$6~<+JQFCgXMmT+p(kWeV+2&DJ6_q@D&PpZrkNEq3G;9= zr*tNGSQ`{s;2=R6Kji?ec?3=7!WXxImnDIm0a{N6Ds;fZZQxQ`FbifnB+2sL0(2nIWxFFMNSds_} zXuJhn{X-XdVvWT0^I8QQLAN-Ak_cv52bzZjmvuPPO#rC52pYGC?C-%j@D6DsgOerH zPH4ip2}?NOQP2}&@Rj*$(_^M{a=|>DGo4dH4cQCetO2T~-hS#3a5RG68wB#k^i|V2 zWzmPGpQ9Ac;H?Dsho+%JY)Cdh9ef+?;1kf4en1RV6@g}oK&N?XFv)-~6wqL@Wl#c* zW-w+6K(0aot!x6Fbil5_4Z66HTY(KS3CIj$v1%}ZTIFU;3Xl%Wi|NfXI73BeF+-O` zf^I5+HkvPk3u#=7T(5$g)H>5A&g2yH1LYJZ$2Fj_FvvcFBVwSf*tqrsLKYWYg_m#& zZjOxEif%j?kjw-fVyeL8xPUY>LD>)De2|%0if%mT!DT*^f}rDT27!67;+Me6&&!P5 z^{_>Z=immsBHn;YaKG`Kh3OZVPo(V^L7HV8!KX2t0d>G&+w(Oc=O!>O*nV&prvjr9 zlLP4F3Wbj4;1OO>^G6?aW<4t#I|nBhHxDnL0KcG+u!yLbxP+vXw6>0}<8*;JoHq4* zjtrnK2dm=+M$j@mAqDWcE}%mjzkrT0X37FBh|^%Y!U)=1t-*AG5p*sMmm^CdsKX6f zd91()>bFA&pP{`c@BxAy;1qj77}T2JaOJfF73JVA1C!&29g_t>N6>PB56}XgzQfzj zr~sK11!Y!6Hl8++XF;Q5(8eODCBowPWXEIy=q`dMpd%dHL4!C41hYWpIjC$@WZ`LH z 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 G(mVL>gzz4bso1d3ife$or&7i=f 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�+`o;XyJePUgB%J{2U^GGcn72syu@IU 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 delta 919109 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�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

b~7eWKNeKofm`SBGDJ*)qOt?rTEkWu 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%0Difaqvf}y9k zDnoBHVFB%NhNSWrLS{@Kgg|V^4?tsn&2*d+{FhQTIq2(*-m8zjJ!rNE^C7I?u}@5EROnrCEJV9QoySKtP1 zSm6e5e*vv^n*k~!Kq0KetH2IAp#f|ps|M2yVbD4ZR>uXxSqdPjer^RW&=MF{4WEj6j&4#6nLc-73vkZL2EX+71%**Hb6`H zK=Us=W=v;5`z*Pji}Zxen2vz>LeLG;pq2ut7zZ5*u>)L;_aH4(1|7q-LRf*%@joN@ zW|ay@25SZeg$hTeEYOBJP~oNoy>MHh9@>b5EQN-o+aE%pAvMtMHN<%!;8+y_9m5Ta zDA0u=h=|$(ji?PGN?f2#N}$Ul6u3d_!+1a;3z`O5AIeUN9;g zU{nYJmBS3svi=QJEhto9fDZ*;AOyKP26S~3s6>RU7-s_|0aizlz$9+a9JC{&6~jkR z8O!F#Uuebf0W@Ltfj>(TH0A3EI{gNc20=TASv8q|)WaP22Wp`P(+_^oWlhkNVL{#i zspSxWoFUx-I`EPMw927H0JH)TTq%Pti#KChAp{cc0I$sio%1XNyPL2cl!o97^FXGt zI<5fcfggPJSptx=rDuQ)<^*|nf&i-m3%4251|bD5M@Dzh`hG{qnQa?D#W-lEoQNZX zz!i3Is|}>&JD|VW&y_|;MH6p(=?ecfY09r z?;;KZ)wT*O;DJ=|v4Wtg1kzS}KKXhEAQU<+tDKnrNuAWJPcK}%0rLHjB|9a{wjaKCScC};t#)KoV< zYsm3T;JwC>b`~h)K$jguDm(C5AFPnd?gI26ebBXb;IVvA2a(N@!95FFOPv5kE2|>| zD9|*RjtD?%DbStg%%EzCTS3U1#X*5XK?uC$7PL2#h1n3)qvZl$h2;o3Qi;_OkzN#7 zpy_1`$Y4&0+c!)%WaN{s?*JY5$^l(a$g08AA_$&vVRhUAUY8{TS{W$->AUU$>1GGj z(JTr=S&Bli5_1K(z(G%2{E&$|P>x(92rXa0MFMCEIsC-e`a)h71zueSMsP{QfVRyX zwpy)$$&BG6_KoG>75==s3=B%pEDvg(gSPpwI=%p%ybj7RkRz{7fY+pb0S#9`k2m}v zXjad(12pLTLoiDLv~!8oafeVAbldkIuu8}P0%(K)2hTJ^9CiY{f8vLrVl-&w5V!zn5Xv$D7ki+llPGK}d=KcHTc`^= zKy|W^0@qr_m!PVQMS*+zPbNN@`U#-20Nm~d)p)NNYXm@duIe*R0qF(LnM@D@mxmXS z6+#t(Z(#!~x*!BD>_DgNGATrZ(uLyzp)7$Z;B0;bbXqo8*#YpWil8Ibq46jKD#kbz zKuebz__LrVG3hg&0qdB)ftgRdzJVXK@RSX5DiUbU8C0`5{$db#4qF`T%FDpb!_LhO zDK|m!4=Vpa>F)$6N-ltQazVWK0(Q>P6_5p>mO1D~G?0ryeF1P&1@7uQ5GD1j8ca7t z!1I5gV~|1h3#;P^&>#!Ae+0ev3{o3_oC-Q31)Qb91I?geGGwRnvT-X!E3!b=M}VeT zL=?E8dG!G(#92Tw1a2RK2Se`&DT;wx1UG~f*_c5a9=JVOK9<+Q#t>r9N;eiGd`= zpgjsPaF0R^+@k>R*XDOrR#aEuR%GX%?zoX#c=}%sKE66vsFc+T0+Xk!@bU%WHGd*6pAO@~>HB#3#2F_~zsAegE!oTo za?BHEC3Y4E1r*b&`1q6=Cr_Wp$7jfRWBLU?J|(6FFQ$Lt<1=TxFh zGsctCXY%vubHIk2reEOaGehwB1o&(uTev{B9AHu6V{uU6Rp0~76R~qUE_g9LSpaOp zECD`kgr2hkeC~`lri%*l88M!m?jgvhgOIBcE8wUlr&|tlte*i z(egMhUDNX0)Lf7{9y|&>juKe{leocs zjSZ{{JdPV!L04r$x+?6DvIeTb9h5sEB9o>⁢JfLnT067#_z1tXWEG;M$eP(IHy_ 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

7bZ_|YAkTRsrbRkU}L@|1&by$n1(hPx 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

=J1@-9pLF;8eONYQWnJKVM7bunyF$KF_ zAj=Ux3JM$219i(An4sAJG>|Qj<=FNMGO;mTvr9&u5$p^_i8daX7VyZG0IazN8H||M zB_qT*9kfJG%ny88Mh|EcENI5FhY1ps*eYh&@+a8hr$R?2&?-f6IPP8C1>OTP{Zp5W zhcRSh8+2_OqvI*io~mPg9iX*s+y@w$Sn3(;Kx@+k_OVJi)`M1w3p||uVH119^kqFV z+Ln8nug|Wv9+=pURK-y#l+GYS+`UTxz#gPRP5NL<)uL2)J2iZc! z1KMf@%8HJl_Q!@^85zbu)6e$GNU<#gIgELFyqJtAXsn#2PsWq+{B*xQ83{f|(8?#! zDUZ@hYy#7!m-NYWGj5yKFSC#vBZzb+$Y?VfOy8)%sUpH5FjWX#VyiH4OM!|nMuExG zt0u_EL$xt0FbX`}zGQ;T3P#5L(|sq&^zy9|;r`3U#01%t@L>9jNiyz2(}kdA1iL=t z1V$wm$7|CaC(C#+ZkXORStguu!}Ld!WxN>|PuH6w6TtX*di@j`UB>;>*G`eKX52sh z6)vC?K6fx>fo}FI0@Z<_n&<$N0K~#O2n%m8 zO?RCp<5~ZJDNBWcTNWG`pz>6K#nI65+~0NqM|sD!f7=BF`bD^EADI(lUp=tF317tvdI`4ED zLBEfk?DUSJjq!yW{fYV2h5VOVm!3HdzQ>1CfRM^Enc8DH)uWUjuVpw zpq*?6#|vLt~88=P;K3Aq3(*t~rI2;dlB38$1WI-Hn13r1$(X8573N>G3NBXE4p&IDOu7 z89B)-yIKSUnnk#Upvl^i+wl)rX!_OVGGUB;({)zJ*f4IGp1DFsiE;b%o)t2Nj2ou! z15q!he^?8rqWwskU2 zjI*bEu9J~wd^|mMos7EpYHkH4eMSw?k`X3d1}*STCeX_3#p`5dGTxkSyI#hbYd+}i z6VU#)#nZdi%h+0iE|vvfA_^)z1g;A3aDv86cm&q-@NjTD-so-;a8w3O7dvhQG1Wj! zPy!WLKW&4Il)xcSYGu)6767fXlMvWAU2B7kFXPtf6&qyqG&h23Z_sEosBy{+G8@!{ z1Nn+Ui4kQGF@Y{jFt3BHU$pQ zHbPEbX3z|}f+A=;GY9B+)|1m~H_MnX-kiQ^vy7%FM4b(2`4xvF_%>^=#27Z+Ym1DOvI3W5gFvCH0;i){wj+~gVG-C`(0vPx8cZb`OdJB|rgv?T zkzt%Yefbs{8_9D_N-XeeTS4cZgHK)LP~w>G=r1Y3%BR5QXf|DdQBZ2S+jbdYp?6FQ zZ2F8YO3a`Ea1AC0MV9Hg+hvr*d$xB7INAtQgO7B9oJXj{EHG#K#H}*II;TLA+F(fq z(5`%DfjOWd5jG8`0yCx#M$m8z=u%h4EXVKLr(f7AlgZdK-D;bRl^D?NSp4jE3xgCL7k;b8#2c^J0HLxFd>0?Ty4?=r&E_puAIP4C|+!)XAjWw=3W z&{;rTS5WcG1e*9}0S#_}HVd=qGn!a42r95R3WJ8f%%*ea2x*JVV+PHlgI7?nfQBS7 zP3G2PQQ*L4F1Vk}0!me==GN^L(3V9p7jz*8s1#&!`~lkQ^J2QdE*UGvwbQ+K$;gPE zW&<_4c)+WDIULV2x+kpBzZu_LI1h{D9Cw~*g=Ju;}y{4Ab7w);0ccsJH!W|DJam+ zVFea}ThluZ$XGJon11Mhj1l9_>0b}XsIffY6xcpp{-8`eE)|56hSc-9e}a^>-Za zy_o*|u#7CzYk}!(M`VnUSeby%*C@9FbYT^q+Hj)=?Qd zkoc;jGBRLs5Pj#x^o56I#J~e+430>y1ND|1!R}-P6*$vnj>!};UYy>4OvaY+()6>( zWaJo^O@DJtMwfBRbgAPq8jM?}J0F)xLcTa1?BE|4rf)wkqrkXj`iiPs#){u9^Pvq>LihDq&a9rdvkt zzUeZjWF#5aPd7Uylg+q(`l3@Zu5z1r;bUr``VcgQxRn=F;en=?woYd~EfdUmaeCxw znL5UU)9;>^@n_sLUH6Pki1=DzM+Of^`H~{g>@|y{N|q9rBcs5k=`+sAs0tr+Wbgwm zXjkA;fQ%vtte<}MjEs`xdiW?YBghiSa&v(!f%Ve`&dL-pZkpbBR^|-j^67r(WaJqS zP0u?gW5c+7`s#BsYK(`bUpglf#<*&_=6RVa##PgooR{fj+%jG7f=oW+`swp7$ha`B zpZ*v^X?PgzC8}Bh9#a`--ce2--6J`!yMT#cZHB5XlQIJ zFLX!}viThxVIXOc-H@?LJ|-qc22h%ZS1_Q2Ah4Aea*_3nQWC)~z336-zCupooU?wxDECdh7fwsbf z$_fP*fpgOz-I5720-rCk33MnlrzW!j=u${l&<1u6*!{drZoHtmAO%jy&^N2Vx#?NA zWjv%&^aY^kn|}7TjJ(VRb_EWBSwad73Y?nEkozK8U?xxJxFe&=^?-9Nf)6MV5 zfbMqAyCY-Fcw_paJ2L8wcc!1ZBV)&SZaU{(8E-a-b4f_JU(+}K}QDU0MKK;o(8C_0ruY^(H zmB4hV`!e&`K-WqMoSVMyzKjp!x#`>wz%`%U1DW%PGd4Jo&e)heUHPF*CewR?>Gh9f z%=y?91QfVHXX%2L>9@Sf&X?-+LmX!q`6D?}A)7L+hi2`xCpUG%}X!B<> zb3io6J=1xf%kV<1I}Z|j{ai+uv3pjKgfhLo|sn~3B1`3Re9x`j0#BQuWvFl80Sq_{3>I?cyfBwR}4EAJm>&7 zT2bwIj%)`^306CvKbQ`(Lt!4XA~z^Zz%z>s;Ik&Vz=Z)9Xx2es%Jfa&Wn36bJ>)*!Hrqa>L~@# zy@3LAnLyPXg94+#yy^FT$(S+rOc(tvqt1A8y6tZnbH;PiOMinS^X_jM3+oLJI|Lks z!OjI;mk72^i5t{?~zg#Xqo@0{+Wb7(BY(0`n4R_=O8JAq4l(4@NW4U6)*r z9ZXq{-$5#+r#t?XNt!ApE6nuh`c!>cDaN@|y=0}CzF(i3F1v*B+;kpB*$U;K9MEuv z*o@UJ%Nb>z8E;O1%_yrG2swul6mp=d2Qp*vlUIQYT<>s$S1)jZYZ!0}1?^{0U^xty21ph;nvCbBPhgh?l`}usWeu1fE|{*xA)71wa6vO@#U3QtKu=Rx#Ub0rIB&Wor>rjH z!RhIoU^8ZM%1SZbn!c4&)`s!@^iQ0!4vaUZ8*|AzGxkl7-YgW00VTrrvk6Q%IODqLB>tvla*s!IbEJl))J5FBM}Y{A7}}wih}yBpmIck19ZO&q#L|&`dnv0N5<3B?>h@BGA^9X=pu+} zfJG|Ufc{iLRmLmR7di+^GoGJ*F;!5ManbbWsbH;|X@bg(i>7A@$=WdXP0us|hjF_J zIE&&=)dV#R4ALG2~`-NpK80Sv^AuMao_;R{|2)4i`CYa_R2h$u- zFo{jyBPz?oxOMtTQCTy_v(v<6Co^uIK1EDcf$`n+tzxpBvYSCy#WR2g2%7~#OKGea zzz4pAn*J?<(;te<3Qx}ymjxZf*CZ~h$9Q=9MsZot4C_sCSv$s-=@JsM0a_q4K$|we zmyUpn*uw$K4aS(JjH0d~+}s{*6I(dCmQWEmNor%#rYb!B>Sar!MuSx=@17pJRA z$>uQqxHx^Xl&l8Rhl|tqO3CWTnFvhf1dUcqVN_xU9|H|phb)lg_?tms>U4H#S-*O4 z%cYMCq-+gH86!7nQdNOT0W_j6FrN=JJ_Q={6POGV0-wsRz|8%h1=6(uU9Z5V0y;>A zNtc1emBj(H{s~kwg0z5+$OkPhV+9$`16kb)S`h>qriYG?%&dnFXoAj`1l=$M8pG`c zO)Y_!iYa6%FgnfzP4BY`Yy*2mMTrUQ69vTJK4iGXam&f60sFZ7YtVQ3@Hk1 zjtd#B7(gRG3|0&|kRjU$&=D!%8&|*!yIwG43ETj?s|ITF4+cmGK*xUI?in0g?=%ZIiV57{0r_hRBg6m&7Bi+fj0%b1D|u#shJHbJhkQ~0#HwsQG4{3R(}baSII?Gh?r>yRV0Yy3 zozA5!dynzq^q0!Ab&LAe(r{*h zhuc}zWF;6GA5WK4mz88(I^9BD)&|soQDSd| zbGa^ZfHqpPIj-0>-A_YSOLZDJTc&^){;+{=u7@fBCHDkKB3`j;`dkfJ1);Oxb=oyZ zN~fRKkd;e54^G%Eki|pDW`GWf2bDyiV@L(ggLU^{s?uQUU<9wc1*LW-1qlJrtiHK37v#lJVvA?V7S?jHjo+)07Ppc?&*P z0i35mhi!8yunTOT9-$=*8c(a&lAX&qbGodytOnz`=^yze;~3vf4-k+v<^Kz6*$N1N zdK;{umi$X?S!wVPy#z;m%+jo>*8s7AOP zX!PdDL&Thg$b68=hlb@`JVxL6y$D=?`>e z6PadBp6;wC+X$kM>B)M?Bc&bi*;O;%!uNC3KU>jTYV zGEL9>&n`AS-$0gw@xb&71KDuK1Jf@U$R;pupKfR+YDuAf<_RH zWUUzwPwz33RZ%$1E-+nK0CZa`XuyOO)W>H4kL9p}4nr3>GW~*)tcv0h_DKStcoY~w z6U#6);B&ryFlGrHWmn{yuHYssIbF|KmV@;u`vQSC)2)qVWf_l8k2IFmlsFE$Y@S1b zU4a!eg$ufD#F&vmV2<$g`);z*p!;M0xXH>g9-1!Y4o*If?y{nCGlW5xe=vcsfpz4` z0%y1$vMmGmJ-1x8&44$wk!(8Mtd=)_RaCQo*OL(`X- z$+|Nhn*PR2R+911bT)HYRmS<#b{H;83%dm#T>pWl zg^o@Sv6WS1J<7gF;LY?ZTX3Y#u$9#`I?k@ZrogSh1zI%?+IJ$61&Y)K!U}Al4WUZl z89vZj3Q(2;$$aLSK0g#3&4PBaYT}1LZ61Rxm|@rP4}~tm1KN9J;P2`PkJsN z=vF@P-L}lS3|!zt#lic_XKtLn(GFa=9ORQQna*b~tH=0mx__mZ()4(HS)qW&+Z_Uq zI?!UqAzO(N)GcKNEwFU}?ILFd?H+er4^qShQUp5kl^s+TfKK*h)nIZkV~TKO1l1a# z?dFcVZ%;pHFPkL^H5Fdmuqd#C4yc^VJpHe^gro$-IJhim1A#*}Xh6ZxLPA1hH%J=2 zC|c+syKs7Gq|93JW8kAm!IvL1X)+5avhc8i#=oaqM#)%0B`v@UE;N}96hRZCplQR- zC>b@T|E$wDM#-p(&0+&D*mYu5WC5)&xWJI50=h@X(NWfsxMG57x#09XLs@r7IpH*2 z-C5R%@xt^dXW1ae1Jlt$bu%84pcw@R8MF+%SEukE|@?mg%Q`WaAl+ zZrApeJ;TVjWBLz2Syjdz)8+hS%^BZK5A&C8VB9kOy1%R^05$j^*9d;fL0`0G4M?P(;z1?ojF9-33se4#EccWBkWTN#R=MS!u>C)5{}dBN#VNzYrnoDzHir)J$iF z1UsX^mg%aIvXP9Nr`Jcyx-)K`emqjvjq&Vsz9?AufhzspC|O0N!|X~-0@sC=m=%}= zrVA^8*b1PtB^d-(gJxf)1wKx{5CyIvUPj5vTOMIo;sZ~#-w;;fpQOMia6?#uUxC+| zfq{V`RvIIyZ-BZvH$V-8>5kE|@=izDl>`+86a)os3M&aM0MiOW3IZ-5?I4^iE%1g* zfdMp_3d=zsKt?!ZgO>j=W(gdhz9?E&i}CpMv(d6LjJKx0ik6iTpCb(F7lIlTpna0Y zjG)dIm%y#*vN5u%25UgxP-2u;U{~N#V3Zd43~IZ8X177LG6Q&I5`3`{Xn_nmP>4koYNcUN{CNCA1^DwxPAJAcv(Hh z9n<*}WStndO%F-jX02!uWFf%>-Fzk)47H3?Q$uC@?v)mSs6If*KwMiLx^p z-%UT2D61p7fmZ=mqG~9xI%pVAlTW1}U<87&lFSn<8t$eu2f2DO=#obe&Y$*_=<98971QPd-dn z*ezx>T}nubSI&{S1aycxXjV*tDN8^FRG%w=&)sAO-7j}elU-a2w0(&|V85UOGpJey z_ufGSi@?F@ifhC~>OuWu1IRWZ4FzVKs*b`UMB|- z+XmF2gP02Hh(VM`D1vuEg@6S`5;nV`ff4WeT}3n8VIKs&s^a`g&K(x4+d z6_})zSOg9+LUpi8D}Xj1f|Y<(2xvlOn4}e%6gZ(AR%u06_$lp7&`E31B?I6sL>!Pk zqa0a^tl$Hmm_hTf4Cc%k;2lp2;83XtZz=-0i~~gQfG6NUUjzk*`QZw z^MWcm=*8V!t_n<`i@TW|6hNnBf$z@da%AuWtxIGDEvy4C_+bStjAT~e2DRSVK4yTc&?6kj-G+GCiSCHi>cX^uvX+)?!G#TV`DbCtfZE2FHd|(?yD8 z6HNB9fpTyHXhcSV(eWI3MF@CDBm)l*sJ#kbufeR#ki^RY(Yc~XR#}MA5mdRj@-j#{ zf{wmn5Li8}L{@bA&m!3cLTiMf;w%cR;6il`SnhPOtg_H5bh%a2N@RT*k4*O~kyT`B zIW@hoL{>Pc;S{6?&d2~7i(znVLGZzAypZIro) zKs$AK1gd$|K?}~nEg=TS2awYV6_~*10fItB;L`LvWwP@6=a|5|4KEAvsDhgEpe45q zj!<3CAi6+n3}DKpYn98&bHI6l<+2hQM?eRmA}bQ&Q9`&YLV+3Fvza$-`owZsDaPLE zYszJ%t!8X!16|`)%_EOcTA%@@C)nnRzVETk=SrMjVg44q%3MuG8FQz&s2=~?lkR70dHD`m| z%P3F{nqUU)%V82YHvLPrtcK#(^=+VgE~-ISA24Nsb~7+5fM!@hC(3qkW;yP??HONg6s z|8(PKSzE?;(+itry_k+XoW8eN)}L|rblw)2LQBT|(=%JZYL>Ui$}+y3ezZkaTkr&= z9Ptr=<;CfYt+J9rCn3TNaABe8d~<~)7-vjZpDW}&eL|~jwAgh}q%aGBlI#aI(AMr3 zY*_-YrZcw5iZI@pF4iV%$#`*kK%1h!8MSsT>{_rWIvRD&vN*o|;2 z*cF%^SFmR}t^rB12vkoOoG&EF_;~u)HdzJ6p6O!kvi^)$r>C{c8i9sKUe6bjVZ1th zd%LVQoS>A$*!WI3SqNA7g_ zZXpG3(8?Kbb#YB-x>vW5Iwv%4t_w}CLglVP<=*NRQV>F~8LkUW=jjoW<(M}OyvQbZ zx?zuyEMxC<{vKHs4rq;#JG~mD1X>5=PG8U?B+GPo+Vpymnx&wcK~5ld`s*GcIgT|T zW5fh9HWe&C_jqWmP$rg6acaf!ygUdxcCG7fqknD=VW0 z_X_O(8F5ev2Ci?B%l-$wvg(ksUs?j(nm+@YtzdS1!IOLV)4rqa% zJN-HHIf^uZMY;{;hlj>$0LstH1BjMt|pPLNe*JUP8}f~+1w z%kK$7MxZL;{RCNArsIOs{U!?OGYU?xnr!SlccEYZSLI%drqCXcFA|lW_ z;2)?Z4?0wcR|FJBNJ;YN^t%&f&847~0bHXns48d568Oh4J#Uhb1Vkhn&UR;vI$0k-06Okg}hCn^#a^P zL9mIS3Ibsws18In@#XZs$+FTQ17A-T$~J{o5pW{}z(yQk26+M&9s-a~A)?Bd?mb0T zmhtuUl~aUd<)Bpqk_pU?usY(n;PfX`gxndQOn);))&`MO3Z@F#fr|QqsSu}}ohlT- zcw@TUG$AvNcQYD56>;wLm}x?x99L&H2sp9}LjtfqoKOJoBhv`C~lKI_qSqD&cVK771i}CyP>KU@05(nW?3@R~T^~Q0*=`u5g zOy!^z2i$nj0Qm%HU2t4*dj3oyQ(0&Y0#^g7S7BAj@#&2-Wz9ICHA(LD|1*U|83m_{ z&JxmM+&$f4mXIRHU6B9S1ahZm&Jwa_I><3SZI-M*~wZ z1gdX(XUn>Q6kVMy>yHSioH;`BpgD;-vL>$R)yZ`sSS*6h#D|ou6WA1(zz0<^J5FHB z5Ue<_TFbKA1jxo~$b4n&}7U$!an_p8k5CtR_bfsJRYO5;I>Y zkn!g9b@PR+;T6fv=^P7$Oc>Wq|2tpSm~qo|!v(S)9QWt8fi}nIPCvLnNP}_H^i>OF z%@J8%bD@wC^Rs^))8!V*ic4MxpTz>{%p+GU`P2Ir3du5m`PVT$Z=tLf#{p2)0SfyE z3x!lTx*;u>-03`vgmgHd)k^MkhebkS)5R9ari!*tXaILnz%zItf(0~}I$@D)xhhl& zJ~aVf%nP&LAWI;B`U5>#Jzb>EAGWDY*t&aAa&*X^KG9MxnCXtt_GgxITN#=D+?hVt zT26&=&-6pqa;l7d(_et7CDZwA80-~NykFt?dW%_+)dZUe;3ghPK%Rt;;3#Xq2 zaVJdw1X8qQx`eHqD&ytpcD8b=Oe-f&Pqmd(VOl(Kdbh2d3gf-$8$hxbre6b5pQir- zQTL}S*~zIg{rEB6(@su>Y3GmW`5^kmkLgoE^qn8mcZ29RkETDglT%?lJDts5PL*-` zbR7_NdU_Cu>YZKzqHau|XD_G9GlH#tYL|ILN6oootzY9whs2$@H%v+4a+<9OYD*j~s)aw<$S9!w7f(UZHTSA*z3|E4bl(W}2tKMtZd zJed9#ME6~oF6;tQzka$Uh<>tedc2FA3gf@&EiQ7ZOp`86U+p5N%XoeIO&2+H$z7nN z$SjZ!%dD0PkZa;jOxJXkQ)48cZ#$W=u;!CrpD@ z@G%RV7M#A;RZg1m?evqba;l7fr@seL$EJ(A$*D4)nr`hTr^hysI#&iL6 zGRpVd8e08N5|dTC;yqh!Rg9Cv`~M{Yzos)O!jW6Kh_Cp6vOOHP96 z*xKolUUDi-PZvzD_mb0MJU4xnmz=KDVvsg|gf=zM9YqjrA3@r-uAR>9EvF2M2Lo?8 z6~=SZL%rp6rA~shA=#!1iZzI~Io@&-OxM>=-v-ro+gr|sanf`JA31f9DV{!Zu1t4+ zOrPK*r^DDYeXoz4AxQXxkDQv+VMzH2PYX&4ERGx4vjiS-PdD(DlVChH-P2bt5~O>H zFIe|QUpWJT(}J+HqX;@Q`{r~(KRE-Y4!-Hmesao;2d1a{$tlS+q9rpW@XnzJ2*Z`y zK`Iyp(iIs%!x@~=_56ZvymAVxifjs;0=?7k`^jlCy;(h-%U@24>C(yRYW{L6j60@# z`^&k>?`Q;9-f*|eE3i2303H17xPc=}VD|J4{&KdAd#1nhmy={XFr6bn&W7o}&~&!| zxgZcdD?m=$_ByEk=K#Av29%~4%$OQDK_|jFAJ2@V7xeeexRH($3oNj!sK*BHw#-bckqHD zl@}Cw9lTirkA5cxM1z{%@J}DAQO2bkCF?JTHDtl;D}UafEL|e;L8$tA~d})N=}Ax*Yv(9IY}l6 zS7iF$C^-(sebbLc$*D5VoBjqwZJI6=EvL#f=l*odXgOU`r2GQD8s`USndtQOZ{?&_ zUxON#Oo(EQ!|@5|=nasH<80uCmQNV`r@wzIX9Z_#p|paR1&MowC65$IHEMub*w z1#VDi7sbdaF)jNueOip1a`0t#1yF(pZB1ZOU=vsg+Ca<*KKo1oRv$sjB+yYtT-+e9 zLhN>Y%HR*$%m|vgU@&8v0lKnn27i|0#pyh;a>|VFrW?e{Ne6&36)1VF;0Ntt*}xCZ z(~6*I*uamPLwK2ZP>u{hIz0e6=j?eeC&deLE;!qQoIL&UdpSK8i7bIj(?vfZGE(LT zIc25?Leq2OB;P2;gY%77Jh*_#i;WK8M&NBU+D2o_l`}BiLWRIb8)uwOo<(mf* z3-IFuDOo7a(kOpOfQc6}S+sx+av&WOXq^p<0;pZ7#5R3mhLC7I6KIWv0vl*vj2U!% z2LqS|IxY_s4ou+F@>m2wJGwQPKyr$p3j#nVGAMv1LYN#|Kzjs1$MizD4KF7Pm@#bu z?Z5z)%b;l(esksu@bWD1E;G=E|12}64U9|<3QUeWSedez>J_FdX30s^Gifq!U<6$< zcmkvybV?hO;|Y-Q`iuoitd5M?j;v*lj9K8>L4obepd(OjfR=|df!f*^7?rp{^c6-W z=**wMJJ8hyj8+V9K(634V|oFlpMYpC@De*FM@B1#A0R%bSp?G;5Q7VRqcRg{?VSP> zcyO9Ym!U|BU4d1gNl*c7*9%57rWVi)0h0#P1IV@^CeS9yH;mvTe>#{Hm_XORfQGl3 z9A_|1Z}ejkt)BoA1sxU*njxORl;y}$1PTC<{~S581UB%3{8Io5BTz7af?1cLNReHE zgRBZs6wt*=Nw4_g98=I5@6X;$l1y+Gep!o_$Go~{j1&mekmvy%-{u3 zpkRK(q{M5+^ao@nlLpfZCM7;IrXP?~rXgKNP!|$(nlZZqi@*f|M@FMUkY3PDLZG=h zaPJbT1a!)yz;Z$G6d@zGBa0%3W_er3c9 zWo2Y!Vq#_n-we)T#x#Q&G+zS_qYh>TCdaP~jxQM$K(x7^?pLvrNFJglMN|z&x2}6kZuNR#w{QdK|6oUm^OfzEDAiJDR?ucIUoff z6BR%Q&oLHCgEPwtkQye(8LU}mOlv?YR)9vY1kMY>f{(o(p0Rd-2zZ0C z8Pf%j{0Y`91s<@oK(T!UBm{Ea0T6@1n(+?EK8W*ffS62<2S8R|0ZCs#w_1TiflW7t zfujgiG=PE(n#do3R6k$^T@ZxGUmVHT1vtUD$X}BW-0I~ z@CwMmI9v*x5Y7TN&>B-_?mvOdoYTcN$hkn9PzuZfcZ8)Fnz)%IVsLHH=sh(KZ4A7x*En6zab~b`TI;eM8kC38**~Io6kYHiVQqC(-k($ zNpb#K2o;*%3DPiO35>fQq+!YKHqd={U<)&+_iUDv;=Q~FA(SzF1IUb(6I%ovSwWXG zE8dio<6I2qx`Vj)&VdJoe=<$ay(uThdjZ7d5C9vQGTmW|oD}CL(8M@c|4xvG`&Ym{ zho4N-?}Ie__|XjNNPsmYPG7S{PKtLYNQfO`M#A(9ATwTogt$O%2)rdH$9V_Dev{C&zi|3iz_XpG?!2gSaO^Q$e76k*1#o z8+oAx9<(9T8@9p{yf1VLt82XUcORzI1h{{JBuYNL3_q;17#|fRk`pGms|E`=IFLVh5I4(S=^X!n5;$42L1+=CB zEaWlWV27L(XD=urgX7{M$PDPL*-xhF?Dyp4IH5CZKbfZM-jkE#T?krBA`J1C%k%>v z{m}WbpG?y$K^iuLj04Bze2@m{L>bsZ$LTsd<)nD;Tn4YS0Sh@y57;Ru#W~{vRA{>3 zeK|SK$z9C?j^O*l%|YBh|5^kbB|yOvdtXkDclCGh@qu9dHq#$~^lt#^7XhhQ3DVGa zp$V$t{Czn&?iuSF1stWpj-I}8x18|w*j;j*yie9O3OGuFMR_cy7wnRg;`|2_n(p{O zPL6ldr4|840f-8->3kREq=Xtk`|ulBvmBva!=Fskr#+A}klF1rRK7?cn?;0-vnYDlwcD)qH^;!?*6l`H5kUgy6ngz6{j$MIS0GkmT zKt^m}&2oGT8e##LMr{w} zx11F3sbB4&QW+ecM$_f@$VqV?dk+;sH6~>8GE_nKL~Tn$GeRGAQb`UrvVUBK!2vr*itBUFeWQd(D_` zfJUBfuw@DS1&zY0Oy9U)PIUU_r*hKzNaLd?K;xq)5aXjinP9F}2Ax+8azFRg(4lRXP` zGu_)4px}ax)&69f&ihg>h!bwd^z@fW_u1A zwv(T}_pqF-EOeL_*;>#EPcIM_D?`U=L8%?IU<_%XRti2)JLi?0rrI0O$UP`|D>8r- zvViX8TLJ1>tl-FUymS(p1*gA!B`3!T9=!z>5Yzcz%ehN{x!~I$pyh=$Xj`(t%IUeU z6+&5#FF`le zg4IU7k(1`^`wGp#(`!N8U!bKYVA%z41C+XNh-EpdgKmU| zx>W(Ro&_uoPcuD`z5^myVljU=NwS#Diy;WZZUvhL=IP2IgKd(E4geh!TVt z_)dchd`JcAK9ELGW>;k35u5({yqpy0#AO`CLIfxWaxMhTJt1=8^t(UhWMNZK7X+s({*u$+glEI)fxqNr zc;PKcMFt*@=~>6+%Le z6o{C{n*pt{6d8EfLC#RON3JA5w_zxNhRH$k09tE{Q5b>RZjkv)kp1}QFOfp%;cq!P zP{L*WgAz1`f8=C1;XyM!^p9K++iOsiuui{wOwOF?wb1luf8=Z!_e?)VBv?RWofV)O<$)Mzh=PHe z2ekVIv;)DB!JS(G#9;*Om=XYO6m#dE%LqBVrGXJVQ^Di_nlWI2oC5AB02&lyaJ2Zt zRnGw0y6}Suw0Y_Vn*gYlslWs}eoG0o71-hr=>A2}BnMcxfGj9RK*MJoAc9BWGI+Nh z1L*!c1`Q?^1qMeA(Ec5PC@F5x{aB6+iVVySpc7Nv6_^zncp11s<1G!0pnDf0AkLfq zq<~)_PCyoHo+2~InYs*7;3H3L76h3GKBsR9 z)Vl^?--0Z4d;*^FVpL$7zJ4>Gc>UC+Z30>P44}hj6&M|vvK1M57`eHb>J=CqIY9%T zj0#Lyj`~>&42}j_pc~5=9U;fhfW`Gd;*2GZsD{XAaVs%^uXoOJR8(Nd2A5d^Hw6`# z91TEzTnlv+=*kB{CKa%UK(piEa~YVXH%?@g#N#X{&IpLYEXVVpT}{j34s*x`4gZ22 zb`Bzg;WYa!ZY4&j(+)ByFe1ARypNjOiixqFvCdVQ!BHSvi4k;HvnCURB9j96EJO~_ z0h6F>6qwAJK?#IefkEJnkdy+WBRI6TF+y%Yhg{3#C@_8DeLgW3rYuLk>4NV?aC*u? z0n$`dVul1c=wfUIMo`d#gB6;#wR(n>4} z%>FD6paiAB=myg3$_v`o$pG3-!IG^6-o&E7>?;jQyP%F^BOqX z72p}zE68J|y&Ungq`s)*OfW;0HhF1uDX3L+>w zed=0SQP8@_wX%tf6Q=X7lMRH(fRxW%C!5STVfx>7vSBR$*qI!r%WsvHn4Z60)|LGl zA849QWIE#uN#W_|*2|i*|6~V=q#fgr>4rNXhVy@vRAKxvy?TeN7UPfU@gF7489z?Hw?j4_ zWQhGvSzi#{w^KG0GO8UD)2WT3;wvW{p>DTSw{X1$f7H* zZvVMk_7!Lo{FA-1FZdTAOU}4D{q{atL&k~IdH2g6W!y3S@_yMyxfg<Y46xP*$Ju z()5ypvIdN=r!PGyTfz8dy4)ezR>l|8mmHGa%J_VG)L~g?#uwA)9hQ}41RLhY>By)6 z+N;UvJDvN8tP3TcoY|WFyr+3V~)F46+os6gU;w1i)$)Ks;2n3dd#h zWwy+oA^<8s6hRm7Z2)mV(?x6w%#LejPhWjpwvTbvbgL7xT5_L3HY#K(FljOyfF{iy zEkN6AfsaDdXP=ZU2JM$VCHtN6`E>Tvvd)YXr+c54jbmIfebs4MY3qloFleftMc~c! z%V%UW85d07a7oqz{rul=mt@tZuRJT;0+VM&Js%h>KkJ-qHRGGG2n3;}{oA-*-{glX1axj!Ux3 zAy&CEwom_dN%lPBg6YRD%jz+WuB%S6q|zW0HKy zs>B8vb_LB`^JFP;D6oQ720WV1e^b_y@#*vfK2iqLD{so0FrJvc?xt)E|MAL-s35y#CFrU(ekoWAastRCab>346*N-#c}&bL|$bc&R~ZP`wUd1BKy z+?LH_oI73qj%*0yl<9Bo$*M9=m_F-{tR~~6={EOeWv4&9Bdf`D<=J$eyRtTnQ>SM^ zR7|)hE5e`3$Xu_$2s%iY(UD2u<@5)yWTmFBxhq@Gx#KD5CS-vg{^=9$%bKES(*YT_ z@1CqK!aAY*Fze(%k}>yXwLvtdjnf{;+N+%GYY}i<@xBFgB$ddwU}-_nBMHi429?Hgp zY}@}(b{Erxr_-w+$?AjXHIHPS8DCES@JLot`Vk-WD1Bb=9)BJsCXlC@9N7e3PFHy> zyO2?R`mx8ds*F#kFASH~m@e`}R)BHBbk!%aS|F1HpU4{UTzNKG0JKq=Mc^XObpJcD zqSF^Wk=17UDmeZ86Im6;3Ddtkkq z=o1l{&i6u=jT3A&GsxZz+XTdTLB%0xOoc&#S)gUQ!wXq|#{JVLypUDmo5KQYk|?pM zFia23V-}u%`Gu?y=@ujT7^aU?vIj3)YDSMHN&7T*vznaBSeR}UJ zSqa7|)0e%HwPXY*g&^UjBHZAEB0()XCPx-WR)ICs9bd~v2^{1{geU06#ObSF%i8g* zc?P~0Sy9q)?X&4$U(04O?fEc0@r`U9)9nw_AHI>5XS_6>@h#ZHrf+3$GB!=;ePW((JR&gRHbx|I5gQ(1n8Zm2Bne?2A(%J|lCThuAWWE5VES}}_p)LV2+IX{ z_+hdP0@J6*y_Xf!N67N=@WN!-k&NW!;eiQr2uz>8_`R$$$0Zg81_dsGNz>20myOXp z!@}giR1Z!9N<1KnMM=PMA7r3f2Xd4Nn}U$QAHM0qA7mY*n&E)~9)T=T;szgN$tBP? zeeDO?e5S7(rptYlm1cZ1-Rh&PKGTwo)3ZOy?qyoOak|zggzyEhaN6{{pJWA?TAodR z|4BBM>FtN2MYe+R=yb2IvJ)7OPQU$CHkfhA zbp3C#U5rbo@A@Wd%Xo77k8iSC9H+ond_G~F{_v-)CL5b0PnN*h>5<=MZ?jJW%e2ZLB>PV%YVqaGPZ5s`$JZSk#WlOTR&x;8K+E_`z33~cxZa;FIiWPV?qin z`iyhfrXTz!t3CbdFWEDUQ>HKaE$hX2bo$%hvek@7r)U0=Rb^Z7YJP+k(k)%gLz%ifo|K92U?46HJaA*`TvM z1RhS^Be#IDZ+a}F+&#vJ+ijTSv>4epLJqH(E@;T2YwXCV$O=k?j*KM=ELloypmSR5 z6v5X8fG~GfTZw*-tLf9P{KxdM$3p|3INx}j;lLWf{5OgL9 zyTEQ{(AGfkqDs)7f6y7T0$^v}0A2g6!6X2(MMMDPPEbRYZF&Rfu4mTi-`V8sn0~WP zzbGTAIz5J6u9@-o^at#6s!Uh-rpw7oiceoJD``AEkV7t6@i+LQX&Xn#Nhr)3OdisT zps7t3&{A6_fh&B|uX4!EWBfh6gcG8FHmBSKiTP}dETF?;9x!Grv8#a2?g4GWU&lQ? z(O5=m`n+4xob@0FF@g`tP~s9;&!)f*I@yC!flJ^gKa+z3yFR0g5(h7+v7(Ws!0spk zYQ}*Yfb5{#E(Oe)6%^PV4L}=iIR$?5gOuAqm4l97XLqzfQEy<*?4ZC7x?Ma3bifbj zkhpqK8;>1yc#I>Hzz=>U4o6UR53*2!(eVyrmJ+8KQvyU!gaSKw9V?>(JLu9}c74VY zs9m6Yir7K-HGzk#K;C56WX=FBNo7=EcLYTOJLnc_$c^g=8$jz`>m5JbY!z?>E&4#R z8zcid!9?IE6Pl>weFo6{65J3*(Dk=UpbJ7F)^KDAY+^^(qk*ag?k0sS$9e{VHXd~K z@W~;B1i~W@*^p~_;Z9(8WH)1)zz7MX4v<37amyelO<>G&{Bv@$fWSRwP}Hnogof7v zMo?TXV9e5Fu4e#^rm%yu&lZpo%%Ekv3gFnC!D!CB10=?2#&iN?>d zP6J({10Jzv2c4TL@Rb!I1&-?rAiWnDvm9T7^nzv=kQCHAg06Cg=zPG8BmufAwF7eB zGCSyU1IITY-QY+)bTkhr86zbDM+QX>Pz}jp&B&v`4qE4{!oc0k3K`%7 zjXyH0Fo1?en5Ms~@nq|3nJ$dt{;$;1F!+Q-1o!8qM+FP~IB>|BnUpotM?P;!7= z%gYPe8_xln--q4E%dWr-8s>!^)^Q)C0pwRjCeYe6W>E17J~;=xq?y5*Q38CRpE;G7522=xggETGJoB0#~!qR0TE7(fS8fik-zhQAm<#WUC! z2@1^Myv?G(AfW$~31%BY0q9Jq7Yv}agCKu`HeiCsh838&&B3>|)GIIuJm*mWk3WNa z53-pBbXXMV26q((?uU#_tdRBi0^q}&nCc-%%5W&LDKPo-GDv||mxG47nLHJl!6Vp; z?4X3Gz{Uc)6q=crSqhv~6hH@eGL|@khTg&b5f%+57C~-?deAya@Lfobj0()WOj6vS zK?zVj%CQ`@rrVK0fdf>JYA`VfvN9+!yYezhDKI;(VpL%H!wvE#iz8#161WNmckNjO z9x*DgLJ!1(bVfl|gRW*~P+&G=VgThLCNm}uM+R;MR!0Fz&>%9r!O!T(GAX+aXBT% z{nJau<;)m2OkX1|=LglbO+wC;k#YKTGfBBQjE|>ZmXu3joHgA-N=}{Q1e*fy+8Tik z)6=EojHJ(i&VXgpXKdk6VrQvWU}puLSgyg;z#%Yi`W7iUBdB@P-$==QU_3YduC$y5 z(}ow*#bxB=86QtKmyye6d^vrwjGTk)BQ^!l$zTd>psmmfY>o|V;FbG~ptk1bX|i(8 zjMJyP$jZ%P+%WyVtegeohUpS=axIMKrq7j=1Kpv*C@*KlKex95v`Pzp=(3l*T(vG# z5V9v2)SLlLVSo!uHpdAZSpxas>duvy2{dmv{gb>L=o*Md1vz`r70U{8B6vOdK|$^) z(}ow@w=2qR0^JOpr7UO7xMBJ-Ww~C)-svVPa*m9Lr(xBo?g9+BcV*?#;#|*l#6?B%s1%@n51!mCU zdJv5vh<0Ft5<93R$gIH>qX@nhC}O&djHD*$HcQs&JN4z1{g7IxpwbOAu>kP~==?-P zD;jj;6sQ)(df*-CObZ6k93Sg+RRg(3#-8cR4dgr+yQY6Ikke3p={bgS_mK|9XA(F(J;F%N3`q!dR{m@wISwlbLeQDe0?pGoP0$1^P2`SG?=+PQHa#k! z$gRMuz$|c7K#@lQRE08?DDh<}@q^YiWh=3O#5qCdaAqk9DsU(;XDbN^9Gxy`CI>p} z)zD1Nnep9ppVe}b)BDWiBqSCJDKIMVWhwE2s(;WS`=FCp*#s6(-)APLDXZnz|G?I-TD_&X958bVmz0RmO|k(=Ft58Ml9r zm&;=0?q^}*tY@lIW^nv~IC4_nQ6xu!1+?oxfyI%z1T)JZojJJ_G>3$iWu_B4bMida zGbh2FAZ%w&LX_h?bCL~y<|G^F%t=;A_L=_Ek(rnA(De2sx!yE*)&M(~gscJTQ-azF zu(Ke!K{3kcC?YTiX{=V>an|Gp0fEWWGn3_P84pcgo-C)zxM}*?WI0L3P1Bzz%XKnt zpI(?ESHrk|`nwdld5oK;_omA2Wt>0VJ5A1zar^YzG`SwedDFSl5wIQ(1qOk+tka*V z%d0aTc{n|gOIE>Jbzb5TE?P^vO5Y=_Pe?>X0?L3<6(S!GTf#s1>}Y9mNdL00e`;dxCyk zjMa~eMYxwSK{_M`*-GI42>5h-$c2BPQ3ru0&{_u2q%jMl!*m@ZIVsSkVW7K!)a$Jn zBtVT?a3fnlff;m(kA?!XBcmCU3WSnT039GFlV!%Fpa42*Q~-1~m_nAoW?s-4qKK`3 zOwvlw>2(%wkkuMYpxwbtC7?b%3#hC44s_ivXoV#QxVfspD9|FPF#VCZtkCo|jdC2a zOpc7%C60{Qg`i;paGR1tpc#DjmO+-v^oC|RX~tdC`TY3h6dW7CTL~Q-nEa)c7_&iR zFra(OAOky$0-e)uHOr|o?wZyj*UPwjdS8p&Y{o;=wOZx;7!Pf)Y?YH_GJ1CpGW`WA zct8#X?FV9Ye1XC~P`2*6<X8d%n)7zLZLgdxi-Y%ic~FaAce=yGqJ% z`d*O0h5J(k1ZGTs*DGhq^g(dCW}loD8mHotzde|I=y_NoaXepNpf6_N2hmAl6%hha(dxpIdR5E(>o{2 zX)wOrzG<=?Gb7{E>D#8rc{2SKp3XQ`PLr{9y7p8#b;b+RgQv;`O20%Jj$qVa;sM=q z!4B?hKAOINs+=Uqo`Y)A!qeYQmCI**Iz4up+-k;)(>bThIWn%C?mb;jg>mZig6VQy zjAy6+o-TKvaoY6TGvq)MXpA%Elo;nvSD7hi&e%FVW~Q7LU7Roa;w-cg73kIna)35PC}Xu)csUp;6}Q{N78ZqEbt{hyB5oBW;{DRZ;6~8W9Rg> zOXQ3gPfvfaM9!A+>~y)Ma@zdoAl**TVon8CfwR*iunA6D3f6F8shlrJ-!eH@#+%bq zmdOPQT>_nO06vEUa^$YSx#`E3$ptaZ`7&L4xtt5*uIUNO<@^~>PT#OxE}HSube++i+oD~l7%-@8gqT;!;LB50nQ58k@s7dSfo#VT;?ieojbb!CXFVf6{pu=)gU zSn(+EA~mcOLol0EpD>$LpM;p|6ISCKfl=Cf2hxa)yi-Ax*4>)9u#E=`k*vp1W2qjs>*JZTk7O zat4f-rmd3;lHSh&YRIvIrfnF&Lwl^COR*gF1om@Ge>a^^i}Bj@8SCV#c`gZo8XlmP zHQdJ21+Gd<^E3qhF*QQI(;B%aQV4a-m^dr3dGSlx*;}hb!hH!<`^uWg= zG7?ZnB~WqD<@7rvsq3Fbimbz&2may(EWqeJQ>pqZ^}tAUYKsa zMNXOV)AVQ%b$@#O7CB|6A3vrq*&?R|I_c^Jh<@>7`Wp~^=f`w`t#V3S&<%jqJPFem z-jb7IJUcyntDG|9^y$?g>h$#aAgXuzVGwm=`tz-F%Agakc(%zYfljL__ z%@Z;G;%zx8(8*U7Ao=Ev)8}lHQ(}BDeg8H&WyTBBA8(UWW;)q2onyP464SdS({;AX zDKV~}?!R45nd=B>#~!ahHBZ2F#=CM-pcAlWfRrt5nZ5@^ubDRe0Z94H>C8Lil$n;_ zny#@!PKmL1y7vw_WyTrP^LNN8b3u2|Rr7dEH@qh&#kg?#c93lQ^xGh6^Ys59YUy<4 zopQ>cldwE?$|-?P!pZ^Ble?x**eR#P1>FHx&EqgV@xGiC)9UZjZ-C@CJed9mME6~o zuCNQFZ~b%^5dCD`^o(6{N{s)eckhx@2AzntVV9gXxh0HtOY&^`+# zHh~k<)pyIO8Qu~?oTr1nIjvfO#c>5|mJ*W!cwsFo=+yHq)9ZH2iHRX=Lu^Q!zG}Ce zGUMOr=Rnl4>7PKSHkd&0{n@?~$Ao2g*84qjR*c7{YweZOLpm;t5#_k7{JnB|jNhiu+bib}*$gHzoo%0-0prx^ z=KCOLV&(6X3t{>!H2vs4IX@6Bx?j#1bS4&P`xoPh>FN9BjKq-6#6s-;;+VdEznqyF zo=sn%(0KqloeFd@I3v>1|6^;XOCOL^0v(EFe?U%$@!a&B19G}b&`n(+ZJ@1O_t3R% zT|0gE0Xap+Ths3zkON(L%6?EzR|&e83#6-oJxc&X*Y&m2!w!OVRUDLaWt=p9-$6N5 zwp*ZO%F5HZUdTBz-T5(H@UWZ|cw-i0&-8#pa_Wq?rdJ%2lV?0UeFlg+Hhsq-xp1gL zWyYTAribO!Ic|aawwwYtr=NQ%r^mt|(7^}VSjF^3X!`EMa`H@#Kd0Y0ET^CV-8oea z+Hu2)INwShzHLh8h@7SjblVhI6?hjB(w?d5iAUs=7IU?u72i+u94H`laSULUP z5jh>Eo}bfsj>`3m?dRZutpQ&E+AFd^D9iD{sp%V!%1M|)H$PQ_W_Q7xvNnKJY!J$F zgzkf?hU{-*Fk{*QlG!1Y<@js*zoT-JPSCAT)u7QZuvYl?5Oo1a{Q(w*Zv}$xd#Y9d z)#IRzP6wvP9g|aK+0VfvH~rNgISbf|@GnBsZy%GhWcez@BQrhnkDMRVSE1<{$K^DQ zpv&5;!5g1I190d^a)II--%55~1zylv~s~OV?(D7X-ctCwJb_HgEThn;AIfg;%e+9cHgIe+WcIk_+n=mwJII1Jc=D)BjwOlh)Y+*;@iC3Xu-( zLSll>@B(ELP0;B#H)U6)hkfga6~tH{6uIhliT&Gc6w z`KQwbZa|KBHNOEq;xz`u{k3pnh3|lmfVH>-c4OQfIaMC$85JONAV*Yyn#8L?>ef%c za0hZ2?Dsp6!(e6a%Be6dZJF*2qSs8Do_QC17;N8NIaMC$ITIjrAjeEF_D;Wb7j!J_ zw0n?KVb$+}PlfflCkI+DTm+&vPoDvzmQLSyPfnEwdQ1e!2*@cBpjI*WeK{4T$z9V8 zLG+)0)5AgZ>hIHQLG*?P(-(v2z6;Y&g6J9Rr@y~1r@{k0=>cRCEOS4}aJJ3iqe~ekvykI$&1p8RURjn`d%bXa^-oLfh1E z-AW29j-bZ%{po9;$w@M8T|526GdX3(+tc4XlT&3pHyw050@KO0(;c77iOJlDZ>GmQ z4Pkomb2(+U+n{DNms4lFJ)QN1oEs>g`n~|47F+c~&XDo;^ffQ!)Fq+k z8^E0dJKmsudg3cNNycN-*Ot2hd@C)Pu$T0@e&|?gcOoyFf z05KnS*eoc`%YjcsU~-f%bQE;sMLokn4%!%pssq<;$j$M`ufPY;a=e!F6Nfg(;dUY& zS)d1Mm(P7IH$xtA>O0m`38sg{KD=>i$jNr%;I5b`Mt(*+gfm741-^wY2&Tt2vDF8dX6XY|{#&S?S1Z@n!4)27U zh5JMS(BYk0@8o=pi8)FDZXV<$f$1yXK~D5M`%Vsiq9^Xt0N`;?^l1Roo!`qThe1yP zfcuj^hXDMB90CA4!4vLvj57d0A^7V9czp!u1W(4>(;Yv`X)<1%p8XMgfM@?l$bqZd zKFX;uZkm4kBlt8{#!razT;XPeT8NwPfbsV98K323Kqt;_`z+_kczgN}5O=}a>0)0Xb+XMD z$dR{`zsMON)d`@cETp}L+|+*bMb1$Z9FQ(?S4?GM;mtv_IE{r<>Bvi$@33c9uYAL`cfUzl6VrNA`<)~)5!7ygx#-yY2@ z@4+Y!p639q=mD+t18-;5U}A8*2Dy_>;N0{>Eb{h@z0;Xkc;6G%7nCbnG=^t3-4J3ZzQq^EDFAi#3 zfabQD{{NU>#3rvPxn5X_bFULaFlhfFc-<3d0+h+|Uib7ZZ1Orxi%v~{#3rxF)ZaUu zmt9`l3VPxcJervtPduC=pv%DE%F76Lu?7=ly&n9GLj?sUM};iMYtxI^<&8mujqLKu za=pSZeIR?m3#UPu0Hm2!pl|wfcKI^KQ`6%(g3mS7*F7eHDkiALFU%KRM*r zfQ;dk?_oSO-Iz;Wh4Iw%2rhXO#%t4ix#aB_Pfb6=C11mIuY0-|H^c`O+z=lu=9br% zhx-6}bRwD$roZKumtwp&or4GLS{okxu4NPGn_kNUcI_7)uxllF!LD`S1-rJN7wlRa zKCo*~@_}9Zjt}fwWqz=0llbMkl^cY?Yv@=Ukot$9G`w3!hH+kPCGtbPz0=3UqoI>VFAc`P63c-O%T>U0PW~{zzA|= zAxPDMqb;DhXOJpmgsKl9RUg2rHjBtBF`Yg-{W`?xuORN91Jfl%!8VzT$}2IwJ1{*O z!mR{x_aB@-7sA~Q;&vXKejmd93*v4$I$co=tk+&lUP+-1WIqqsl{yIfAAn9Z2Hoj* zZF(a})e?{@&z^X2TjDB!r`ge%YqLT7TOq-5Px0D3i6b|A(JvzM_ z!kr1?HXWY67s9;_;;ub7{Xc{&DUaVdkaA9hf# zd16U4o~No0qfP3kym0`d1QJZgqtrTucR>lSPST`Pmn7`5cb1Q zhP^g@BS=+0NEIZ;g%PSgfc8{_&U?K!{WHkumxrf|%Ytn(m6caw+IDz)B!pWI;4X7c_oEc2>W>u_CEj}1^a*v z6iNp{s;+=kiGsrtbm9oKzW4xA^#QDkSzca=Y0jbP>hfTldX(fPr^m_5b242yG(8g{ zb`B)A03^2c(Dc<1F#%|gPv5qU#UnznebPyzVK2V;M@z`{2 zC9qa6C3z*rEz@%$ydEV`VwgF7gA!Q%IVE`|g-*~3jgUeL=@{S#prfB3aDZGape(PX za2a%PBNy1k>iF#FF8G1S)P+?&dUbS*{$h{%+ncdC>2l!y)wN{1*~fhh`X$9 z`T-SrS&vN{!DkL4wLKUV7zO6CLTAT$K?lA=>X#dwptI;WL3e&av_MwRJ>Uebyk(x= z*eoXnT}x$2U@g@VJ|#wQwdl$V8p>sMoWKM!gOPi|^b0KVhMc{V!8f-4WSY*&D(?ts zL`m}gWC9%oaDg4P#ynbGSJck1!nGM$WlC5 z`w3-{oC1SD%k+ACc?nLqUhs-KiT{ws)=wrBqZVjSR}_$!7hK;DJy{nPO4A=U%1KT? zP{f`+{fdBmDCf0qa1i}unrIutBbAE=)2BOLqfMn0Z zWgVSS^d>mVOV{6k$tp7Qm?84m0Z>EW0H`4Vb0#e2OyLb$@SP8!U1ZZhW`GX}X67+P zm~jDQ256@kJaS=Xn83}bzX4KogE7ky+H3&_7uI$QxD9cqRyQs9H+FJLo62X00^_@;>)Oj(Z5))+Y8ur|lg9ik04 z(6$=b46KbcbThQ3Ulx-W;+zcf0O+*i>2JkQf`~_4UYheSNEUQj?R0%{6j?uz>@iTv z1LuoERM`n2**|dEM0XUu4es*N^$%gPip)G}h=4l)YPNyyV!%kXs&GGp?@>6xoaNXH zDvCh%D>8$x9f#Jb;9C_gfSP{jepZ2-Q4hXM;RbV-BeanT4mhl>Omst(;fBEPP=Gc; z!De7>gQA;jN%(_wI<^e5BwLQfRE6FlXm>(_!(J}6ohnL$U`!c#uzI)?+G)+oArW#N840a9~< zCCl*?$PDnA;LJR-hzPs@lD~k>3>ml?^*2C@Zm?uILfg*ZfWz8&Mt6ub+z`;E6pk-g zvK*l;Yp@wuo7U)NNKOANB`?JJ3Lf~p(kMZsBP}n@xd#;Zpp)FE`=iR{fn>jc^nwC# z`b1RO4ItSgpuh*qHhQC&yue#ty8aqSRtR(?DKqE@4S3*#8rh(;x-n9|DBRB{Kx$5~ zW;w0|*)I+;Lli0SLGl+^vm7zZ5P_Rf54uOf@dj&_BeY8Z4mhlR0`!0rh8yw%WD)4b z2xx}^YzEdI1G*VP)0t)Eg*ab=0s$Pnva%>aWFspt&AAOE3og~;QDy5uvTcW;C&5o& zh$?#kBzq5*E~iiQMKO7Uue@~qCzz}vGY=mk;0}QL4hKMe2Xy!H!u@;#q~-)$mg5hQ z>p*T-WCoom0*k;4Ao&Z}%mD4)f;39&Z-5ltV9RoZ_H@7jhqbGN?hu4yUx3Vb!ItF+ z?FWI)z}gW)H-l@su)Mqw=NwP~f=|}hl}8C8A9;Cc&MP2U@M+)qsIq+^*{vX1IZ%bW z5mojANcJU27HslDe-x7s_{&SzPXz_OCM4CeA^dXy)CW2M>I0#>7j)wZB;Zbf)SO_? za{L9#m(XH|6=B8&ko*N~W+3jaslNeIbb~$15!zz|2OQQe8@fXfm+-s*nel=>%Mse2 z1Dk=hLx*k#({yD;P-_Qt8WZ?5XIn*-Ac|3xm*#x>qzSZ<=qJv>OW`ctVYd;faCCm(VxEV$- zK!&{F$Z~}CLcxY$?S{e(nI5SuFU<|@iGq}ayP`-zI9pj>nsW;1;uDa+rtecm38cFq z+4&$@P^CIe1w~R`MP8co_3>5#$T9dIf>C4>Rpg~LH-X{?e6a+|H8ux8-OvM^NJaAW zzz})q`ekj+(3?V-LF?1ug$PJF=!9WtM;E#K2kPm5Bci7Z@;j*S3GM1~=YdWFtx;xh zT)+Z8Jyn5Gn|XSsj=Y9G5yQPx1i)QX&>c!F(`%0LDM&(x|B+n=T9Do%4DN~=yvN!V zJs{7_QO{JzSgXw7IDr?uMGJi1f&$ZYMjlQPJJ2CDN-W^h7Qj2Bl-SLf8W=(Af0#gr zC4lEo1?~trG8TeP6mw+ERs@~X2O2A7DN^K|9@x$(!_4fUF#Tc*yK%h&i{okVC1@-f zObo0FtlW+#8M65_1=YZJO|f&SF{try^YZa4DJsaz$;wDeNeU>7i;0K|3o@#y32|_$ zaYK&(1UZM%@jqjsE3YbOp9tu*J<#OI8wNLNO(qs4Zgb`i(3uOMqlQ@pPB1GnDljW> z+to8o0Wm<^Y{BOwOaQ5wz?h}LroaR~WtG`+1|#SUKvo52&?z4ZOafbYK-(lVn0gq^ znC5^EMuaK_HEWqcTc0Z&85BW_O`-mRx#Tp1n>4E<1Ggg!=%UmMd^{@L3e5Fj4GQ4H z0YT0^!{8>Z#Hi26;)u9&17#KU6aj&of&w=Ld6YqOMBrFpaRgsEEO1Q_bZafd2?|g_ zB_0JHC;N6K?p6ivT19Th0}P5R%nl0NPEQy@K-&}QnH5;F9KUmbuG|6%HYkC}y4vMv z$Gw8~yk;wc_DO&)Y2$VRpNGrr_ye@7CQE@EdLAw)cd$4zW`j;@1<8SB(9ezKflM7i z)YU714asuUwPFB8BuE#?MWFNR;OFFm7Ar%&1)4r)04?-na6AtVDorL9bLIumI0Oq? zF{}VZ;sVAjfmUH21$ZzaX}ti~TF<-zRqqav-VG4F^4Ro#fXv33F&zM@JirLuUihC? z;Hn^~fsX0Wk1!o4KsrE|J~BHhWC_e-6SyW=4{DD?wJ?D%4umI#ix6!ZOdJB>r8oj@ z!aOo?7ojPJ&k=&IK7`~0&=sQ^ObiHZ;Gntyasy~P6C~BvL#}hc?t-fj7l2NpW>LT} zR2u9Su%S0VhTeeZ8qh@mU`aD3P+kBX*96K74v1uaoxx37k(FD4u^y6TB^8(zST&hg zoD@3EnIC{mdw^`3B+N8~2{%Ad$l}NY-u?%68;b)d-@^=f0W#zT+z=^lQ1nQE&4Fde zn+$H!^-3I|6SKL@m_C5g0SClD(2c7yPFG?GQw804ML;)Evw;sp;Q)0@Ah8E6b)dQ92`GHPB{dWHN>tE1 zDT}~L4v;SJ8OkiKykZJW^|}lUN(_#i*-DI#a=wlXphHzk949w4OkgRLR$y?v!{DaC z;CPoI8x&8DN}feU3Ji|_7z-U0K;jw@VG~b~s4|4Z;#pJ#Dp?o=I)p*%K^e0_mvMs5 zJZ0ix;AY@vb^xu!cLP-y3M`7*jNs$M#lR7RRECQxFhiCDffq%ALK3tr7$Szay;mAk ziaYKApIV{_I^>K+f!*;4gQp@V#1?lJ2L*OT1_e%#gV?7F#>k1+GegdL03Dsh0uFZ2 zZf4MNYT&tOft%nRygS%HH-&($Q{qry5dg0pW(F^p1#wq`)(%555a>2+#wwY@oscRO2uROkji58W{)JK&k5ln*!*LZbeXO%Ipa0sqC0KML>ZG<<4tv z1#YJXrYPyu;?0A6#WHhL%1v^rK1#|&1Gw4W-3*hs@Z*WY%7z_68 z4X{duEP+dGN<2>g8N+x%>FWYpmI6zb0*?&?XxB5$1K>zuc6`H;<;dg60Jc3$i3xo0 zEi-5*^b-yRW=F_%+q=316hJaBI6(e#yug-a#`FLr{eUA&pi`Jf3{;OO)Pub6gds{= z5fmPvnFy95uxG#rYl14G0A4W#P*%n83ac5@2atImIN(_ebk0wfB8vjI6JxC+H#jwc zvOFl+z)Q{dpmfjZ2+p<&jP)8!ECOA^0$sw87?C$)S^$a&NWBR<<_~1|Y0w>$piNVt z0i8>tz;4DghfM)$9w@abFgwlw`E?1XO|yUv;WlsptpSOi1#NYJ*iz55 z1;k)>TmjOv0VKNtp$BxFCbOfw8B+sD5vH4dfJ#n~n_i&1=?^QKn|^?9nrs1?-+(Y5 z)VyFcW9k73gB;ZXwPp&4W_IiVxe1gmA*cU98a$w*JdtY3myjv|bhs7hgs&H%U1gw* z2|7-SHA`SO$Rq|crZ1oaxR^DVUa*2xHh_vw1~aA)tiho3Q$SZ=f(=(>0G$X5kE-Vo zvq9ku8smWGo<|@fnKhVhutM6E;3fHx5)M-OPj9>-ELsoCt)P(ss4U3Yp!4%V$6G0JLM{0xRgOE!Hf7Zeh?E01nm0j0^&x>q<_5RG)yT=7ASWAhS8LwV4?#nGb*z z)E|JT;D)P!d+Py%o3s`aivqJ#1B*G+A8>2Y@dr#jF7q)?jMjg?kVA3KS%}>Y>H_eFisaeMX`!gYK2VWf}77Ft}ylTF;dil+WRQ zyjKrtR)cR`W_ASKt_&`R;KkevCQ$5s02MW$MG7oM;2wm)B{pb@2U?SWx5NXTSD=ZdgB4V$PGD64UA8S;4=&;+fHj~Oe4y(!7@-9p zBdAsaUp=9~?6`y#xzJ;T7J4k;%Vm)ZJ&?=-aB>GV*UXq^fRe)uR&b%m2`==w>%r}7 zXrTwXR2Ed|K?*x|aA61W1?Y5&6|7khPq3RY?EsmygB4MvFo4_#DuywNJ7}DM&Sbj5 z1nQoELtlv%9R7~=pu4GA1ujAZ=K_-%(;X(z&UJ7ulmczbgJuG#IiLdK1QV!60aaFv zU{fHO4b)h<1IeeLYqvq$Nf-rovVpGt1?^~)Rs_}O+yUTY0YN5!E5&*tDQsmlC=WrC z?QL*@1}UY@n2s=k&dt*R_og(MK=%b7041CQOj!aw!k{gJP}d?Ea0{0Hc7RmwfT&~v zrCD|}rur2i1Ht7f=$JtTX2%swSqT3;h4@Ec4;QHF0-cV%skaM~AffCx0htu_S z4c&F*y@*=jwnax?j{`bsUOk<0CFh*!ZMyPF>CjR2YWOG>3f`12lEq!IV0D0IZ#$T?9HH~Z z)jX!tAG(19>mNwtvE$Pvjj;vR3XsMX%vp|qj!zFT1_xG-vAm=ube6c9#|YsX^eJM5 zH<2cWr|&by=1ufj-)bHMguzG)3k6N&C7G@rovveo&6__!uKmHBt>)32&L|1? zW<5ybreo7*nPBtg4A8vi43;d%r$?t>Gy!|_t%28$H+O(! zcCchQu01&2-V|&~w5hzLC3Hr%nnx4iC-mu9gegdKu+!I?Vhb$v8P;kZb%eo)m5bBN zvpvk-$Y*~(Nho(DP zfV~-OAunkOoyG*84*+w`29Vkfh#5wNH+O(!Kw}o44o+VWG37MK6zEiCHIF1B=FsOU z5vCwbPEMD##1>fSQ;*f4(IlAjkQQJTSjtO+7GQQ;VhgMe&>UI^dzK?~segA$;&g{ zUOU~#PTq;JcY41aqV4+8PF{m)Y1?$aC9-1EC-AYeG97t1-NIhpfa&Lt=?S6oB8-#4 zD>Wh8Y#9Xd!LwtaX6VFFdEx12?d262*H3?MFaLrA8W_3Y#g|O~FHisFATNy&1+BW| zfVLNN!HX^ta%%^#ni`1-vHzi?i0Ijv;fUdR71uwNkcm=f5k^|b7%$*KeXo-*mt+V8S zuCvUY4q9f3kOQr7ezA2szO5M5g@*r+d4C9g^uPuf)`OaC$F-w1f9G@QT4t7YTySx(9!{gKELb$s@+};z@??bqMLEKfxrz?7Z72A8rD=9!1%7I+1 zj3`l&_G&bG$SZL`n^n2sRdER0L5t!zpo`*ir-RnSA>=?y;y9p7;&P{hR>UFXKnvoS znhsCT_5?em(^FoFY3;%3Ya!gzAnvJy)89k5yk7E3OzlUe8+w5i`+LbNDL_}4fn1Gs zf!XwlUh+yD(Dh`w(?QG05VnI>lW{;BKe^z=WC%IXS~3pkTC!a5QZj@bXeAlbk;Bug zy}=Hd=`F9sbnWo;y%6qg5O?K~>Hi^ISs!^NruoOFJNkeX$NR`DDL|KIfn1GsW!ChC zKJrQ&(Dq91bkI61gzYx6^3oj8Wmvh>L94J3a-c<69MDBrx!^Te2szLaET(%0r+52; z9kS9_UWw_`!Rcoq+}9xPkHgdX{lK!iexSWq$EF8DxcPqaN(#_5O(0hz)pAHpi;aHr zN*vG?Ou5rR3z!hLgVrx`KpPdg(?QFZ7_UvQ36htb4%+F#0bRS4I~}xiiSgR>eIOaw zevjGyAU915mY3w-0jjunfY$gNoPHN9JpH%7yb{x|W7FjWK&qzm?UR(`MrtKYPlSlq z2goZaKvxEVynuCK(DZ`=paKQbD9D`-S_Xvh0%#Qw2XqxsE_e|Tc9{UB~d;(gKA>er9_*4M}MuEjFuvJWqpku^f z4Gz$)jR#IOgYJ1rpZ;JDpZN5KaCr;Rw$NSSkZqwa!Xev2g(FDb7P=;a9@|3wx9?|^ zw_;@Ep8ktTK9S`EGb79N{1{g8>H9fongRroRi5mj{`_A1*Hi3t+_3B2WlJ!}lsXq45351P@@L z`f|va<^gu-<;S2Ic_zm{ph@RCf9ToFip-#~OARItGo~G&L3AcFrY#^EbZ-yzs$j{$woj?JLnHnSs_0;6LCqre6sMJ8s@z1K|c3QXYJhPf3O9S?xF`WQflX6r$dMk);8 z88rn4f#2Zc$~Zv7n>>!7y*r>=q!}TXErU+41)aYPnplU)ac4lBF906k1&zZhFlRaP zgRWBMFH~Z3G$>MFa=gxvrNHFKkr8wOGb2brJxdwrCKpG3Z^#HObC#n( zmLvG0Xi#{9tY?N;F9SN=yGIx_TFarp09mU5cZL9i0)yje25(S+30xKA=FUN0OU4Mg zRazT#fi!qpst0ubJ)>g>Xt2G0286?rWyaJ2TIex@B})M7ax+E^umTMx@a|&pQX~aN z#|12)eN$6G_fUge#sIzO40PW(=!O^2J>sA(+n|*OjG&bU%#L!tyr7K754yvJxg-X< z-T<^Bf)TvpfDw7cfdV6J!2zQqV!=U?0;3~h#Q|uc8F;+`18BJ!C?7LsDKdd(h^POF zkvC$x@o>69th@z?&We?nQdqKW3glu|(D2LxC`W-sKve;xcg42pOJe0uGR~Tw7bhRY zIBWXBIC*WxS<~Of$%it2p6(nkug@69{aILn88l;yWP*Gc zCN%&YN_&y zOhRvuPZa=Nk!!^OT9n4*_~!WZ$aHx#ByrFj5LkR+y1WFEI7mGUNd4(_d3hvpP?iI$ z|C27Sha}DbTEPKQZ<8UfCW^2Zl!d?(EpLubFVB!y7kvYF5=e?OOM%Vt?eXcGGvu|= zbg_V>-W;F)8Ph;6(3M?pj!)Oi#L&e9mCDPMS3)xoyz=7B@##x5<&~UKUC5WE!0d?Z zGA2iXB7V@+2#PRB06ZneSOoHFT$a2z3kQtF&fy3bmIOsbT$a2Hh_11x_>}#w-O^kX0YDKIoo5LYXiCKj~fmu+2QD6!vPfBDdu{nx3 zUT2x^o+Iz1uE6SeoyA{TfyGh8Lt2T&k)=e5)$zffDFUEdpFvl^F)Oe--uN?pbB_F3 z#;MaAbLC?gr%k_?E5Ds{D#$p4EP<)h=jF-!a!!MCr!D`IC(nrBPXC)PFV1*yx>SLD z;dH|o`7=!anWpy^$gl8%oPdg@bx{qv^b95r8v5bLa)j2&pmPA>b5;VNgFHZIqE4?b zl$WfBR?O8rNKIAf+!eHvMj1bWPG7-}MP*W8HDj_+U~;s`a)h>yszFyN2pr&muhW9e zID?m5fbQ*9Vc>qs47o|sAxmj`Sq!THq#<#T1Kg0{fI2!C+>QXX$heEaEi(OLc@++* zn{pxThJzf_LG1=(sMWADvUwR5SQHoq4sqaVF-$*KEMLbsWjfzg`TXg7O5`^&UfI5` zM_!YW@!2LY4w($veBi z?CDD;%I{#DH9dBc{8Gl*)0rpBpJ$vk{qkga&FN1{<<~IwZT~x2o|B1j)^zr1^5%@Q zrrS@GcVIj`y>*(ru?Wm8W(5|f|Nohr6+i}_nI=DGF#iXHOTHA-|My#`bM9 z{Sdkpk-m-Kg%w05-q^lyhpqdV|->D~p5VL1uv^B@mLmYvs+w=L>_R6qud(+ng8_ zK-1?J7_$WCPrtoZ-V!S5#Gt?kat=uH16WdfoxC68{OMKe0g?upM{Ow;?$m=jNHcZdxkhgX}=*Zy5;^0^hYDpbbU_9u-3vMm`XD$RS!~M@( z=+4iLkoDpPwfsQWhr(nPLHpUcH%$N7DX+_TaJovDyei|t>Hb~v2e(^pkauR}hTPSo zz$CC%czX3lc^SrK(`RgyU&uIXy45CmJ;qtnGdIakWt=_z|0emo=}evS4^6rpxrmYib067W^|Puqd#$v;?p?I5L216{cnG?hKM%$e#<=23s@_m*3E5=#dZ&k~$Vdj}748Bi_O@UQl_V!8j^7|N3 zg<=}zkAa0)KrJ-|Hi6mO^_u0EF;34bl`otA>a%<#%PwI>hUo=c<;AAQevy|E+b!Iq z$N(xaSzIJ^T|#~<{@lMnVE6R?FY?NayQXjcB5x|bTNtL|lA>{a%}t>L3j}rx_e?kZ zC9l9Fuxq;FS9xvEJ;IL6g?!A6Ow6EV6xz%jASDoej36G!T2N;a%mJIo$PL=P!7)n! zBn9eq2!L0FO#iS=-go-KZSn!zZMVzEGBWO+K6{6}o1!CVF9cJGVFG&tA|`MUBm(gmL1&8G= z85s|4zjZ`DfQfPMbnWBvzKn;aS00yl=RYJ2N}%8iDODI0n5J_dmY=#k`-J>%M)qyO z0{esoHcZz&C9g1DajU#9GCTji}8cTZovRbEnfmoN_#H!LFU6<8Ju?3#XktGqem z?&U+pu#=Sc$=QoCgX$2p_Cs7SuS1kibFVSs*h(SMg03JS8s*^72u* zmvcAE>ocyJK6kUcG~8p1DQ~uOpO)`tVtg}w`dRrqjLWB&oRilC zS$Gm=A>)$iN6*Q}3n@TuLUjex*JsrD7EU)mFK@>9W_#Xw`6fojx6?mfke6kAJDvZc zd>H1=GU&*XxIhQNyH z`d8%jAYoz0bU}D}!+&{&>HDt8`!ikwwIa5Q&l85+^(z3r+ZojVVpU)hm@)m%Re2M| zRl<;k`;c%I2K7s!mpy~ml?lw7Zg@>zf^o)luWRyhBCCW!%PHAGwFl@;JUm#Ju7jg`4TN{*y1XLe zyy@>CJiZ%XIjtM=@FD*0!q;&xks7K190J`FO-gLX0@@f$A4WR8vp#G@>tH9OiWjEzDU12@~ z)tI1r4?v|c*o+ULDpY|>fkR*=$VkxLcI=wW36T4#L4nAw$y}houE|^>Fk||YoAL&X zSEq~Kl2>P3HQn}>`~}7t)A?@8OERvUu5nvlnepm$pW9%$lH2mZj4P*~xGf)}w@R3$ z9@HuYH5;57z~I2{0}KN5K&eFmoLWHZCOJU$x4@O@L3iZuFwU5+e^*|Xaqe{AyYjN0 zb3v^g@I^mN3apyU1`4c>;L2J6ECEVz3M>j73Y=g*NQHm`t0TC^25}S=SRFOMhR>M3 z`L4Vl;8MU3;NAAcqv z$vAJi;&b_S#(CRUK9}cZ2IcF!!U7w%?|mij2huM;ou$MMjp~N~ z_5v$}9T~hp84z;6I;#Sk0=vMB>5`wprJwa@c_mSBMrU_ofa}^{FR*HQ(Pw#M#u?L> zeU^8be)^ldB;&U2Pru1)GBIwL&i_-sn(3wR^q!ycc8o7Ul|N{^h`>wX=^uZ}+ks>Z zf5|I@=&)b%N`iZY6&V$nw3#^+7#$f4LCQLR$$w>fEj+#9x4e?r0bxZZL?I8lR<2Os zwea>GzvZ8RYp_?au;DLdVytDZ2aWXoXD$?2H~rFI`4py2!qe+7$xAS9oIc}{yaFS* zm{XZP?Vr3GF)AXAa@*WDGguz$a zffKDcGY5zR-Z})z8Q_ZLa*O;8##z(Xw95ZxoHcz#n|v|jtm!iC@{<{7P2bQiZ^Srz z`r~%_s_6^<%THuHH`PcXj`7{pi3+osW^SCW$EeW7IB)t+MujV&Mx&~N#Pmx{3XyVh z;1(cwmn4+Mp~RrTRHDF;WyZuZ{S~u<3)5Hj=>{wc(o8?ur}KXm5f#*65&$pnmjDgS z3JQE>pMK!Ki0t$UEDE}e=ceytQAlEZH(ilc!HIFs^b}TwDU5Tc|72Cr;Gem%Nx)HD zK#zf2foHh_^K`~M5#jC5YzlUajGfb)*cE1g94f=1kjr>(dKU-Sl(!rT^FgA0oC=2- z-%StVQpjbTGyMRU!VJc_(<8YRG(eI9DFXvWxz&K}m2am#J#<|mFcooz^Qm(uT zg^cf}Z|7Aw2jYTNuI5u%4_Ye_!>=$KME~Sh2x07;?kAuyLjlQ&?4S;(3WEX%_hx1$ zCde(=52o*JV3eqT$He3Sy6%lfiJcd8d$0s(7?>jqynq$t-z|lA^_^dvw>V7Fo)TZ5q7J# zCbNSQhf_VM=gAN1c(OsSy4Fx&1LZ3=M~y56j^zT=8N`hx7BI||sX8CewAAeYBY|0SRxC+Y~kxt$Gc z5op|3fkEH_^YjHu3X-hM3QPh&r+W)3ge%Pf4UmH_o(CDi?8pylL^EcAwzp+FCKM{M z2+WzD*u*b3{g$AD3sWP@bOj-WQpR`FrwJ+Kfnu6XSV5C<&U6D|g_$7kX<-F(#<|m( zMUawFpoqdG#&gr3h=7xyyC^sjcZq@%vAmdq1}8j)O#jFzC^J1rOkpnL%jtp|3Ubpm z#1;HO=2nU;#4{avxc!#6f(#>L`}FS;3i|SJeca%kdyX^SP8NV=JI4ucr@Klj7&4xl zUM>l?_?DzXiqKyU(4JTU&?pxuSRIc%oE{*h(8kz4{koKbq0T=JSZJ|;ZcSv-U=jgO z8z?|71Vp`zyN^>rd3(6DLM0>Po9P#26apD9O;?sxFkpN=Jw#Sv7vr1h{BjCjjBln# z$teUeUYfpE4kWkzo}7XuBjcs%5(*&hbWa6^TE=(Nk1HrRgFMQks4y2quU1qr0A;Vc ziV92Qe=@-?U<2Q$3MN1+%Axz%L2)W;_LB)Tw8ILX08wD$R)>~744|X~*1@5~0J^@K zL4hd;H0Q!x0xphDD=GBDim#$@DJ{mA)8~duNiaT|Zm6Ol$@p^mg>WgI>E$X4{)|tj zAMlYfm|hbhB{IEOS)qzCdOE+dg6Z@ICIv2*oGgKZ)BRNyBp3^){}E7-S5HTBvn~T@ zLgpGbl8^=y3*=hZ1xgCC{OKreF9w^Sf#Pk55q~g^_{IwMKOd_9r>ChZIPmu%r-+uf z)3>TBSn;DLlYgoTCSquUlLbKK3)A#VY6_0-TXswq5ZKG6z@*Q}0cyfB=`wJE2OL!x z_(9jQ!;0W7jEszR3QQm+42~RGj_9^WsVmrEvt4oeF?9uDTuunmP?$A6SVut~k|&%Q zOQ&ymC@#l%YP!6Tg5q>tO$Ad<8_?DSCI!}#>6i2sOr|GkDl}}D*HT!(IQ^xzf;`JX zW+sQ}vB&wPrmN{F_%I%Y=$xXX;0Q5OZu%D;1;^<{ItohD9fTAlrx)ldaBg3#t6;`L zFq1JEDJ+o$jmUx078@v2DY0jRuPx_LVD+88-$A>wT zPAE7rPJd*sAkCJXTM%ECx?S~@f(Vxe$l+EB&Q7jY3Z;38IhpBssVNEtpg>MhC@v`~ zO)gPL%S_El*=}$}!I+CtV|&g$g}2Px;~yzB%54AmPvNELc2QA9F-CX@Y*!akJi^Ed zGBCo2Ad1pS>PMQuj50R@ie79WrHRGU zKh0P4kOZ+);~6wSbYfmgJVQLh0GI9I3l!UUrr%qmD9-HZ7cyOOl7i%Pv9*d@wx3?B zc!hoY>aB{e*rw0gqxhA_$H(8#ox#=LjX~FK`o=5;sp(#e75TPb->YcH#+sI2q@b}~ z_=w^nj_te8DC)9Ie{(@`J!__+kp;u{6&DqM@=jlQUvd6+{|Acxs?(RTDiv;*V^dlx zvi-D_5;NoWtI|rQ0^9j?l-9BE>80itlvKu-=4IyRDcEk`X`rOQ&Q_3EnUkNm{hGN_ z8L@U1 z80bI@D=tj}m9f<+wUA^`4C5K-C>Ur?XD?QoETo{ofab{Uvx=3NIhYg_wx6$7TFfMg zCSA?Npa6-f?KO2u*WKzhK_RM8U2Dz7pqG=H2MS>jyS5e_av`8F1H~Q}LwbHmK3FU% zCqFqGRGM0IF<5hf6e}cWBo-+^k^v-KthpG9QcFsU@~pWSkR-u2TXQiKlqNySHHGQT zN0m$kkPRp)Ey$U^<*1U0u0pK>G_0(-7&N1+Yh$gs7!+(3{6c+vthvAe4tI;ybcJI| z&5WAU=N(h>-u~^F(pkRDL^$5S9NMn(q5^*2GTWn^%C0A>g=I8L|)64}Y% zcmvE}WN>@}VsJ8oG&3+TFfus)0tv7%g4q*pcL{LMWS-~%nipVTc%#9<#G=i>z+nBD z0qi1>wpFWEt%B+Xhdjt9JWzq@eV3Jv)UQinWCE!K34UZ_Vqykc#K6#z$;bqXE0ELA ztYQF%0Z8t^QU)eY1_lO@N%vPVFmW+3Fo49Dq%bmZL*w^38xs>RG@cKwVqg-2stH{S zVT1HvUB$p8!oW}u)@;Vez|b!Pu>?k|$1yOuF*shi*CoI$dgeRCH87e_gn>z&!EwU< zE&*I8J!bCBUr! z@{aZvMsW1P=#xSWOd23>D=ZLTaJ&FgCl3mq@aqr*VYC#~JPrm92FD2xLCyoE64%cV z<6!htVTf@G^`Kb208*y_N>x>xA?jfC0;shLU~4Bl0yz(q+HB53tcB5^gdpDKP*@j1GXB$H2niIN=G%b)Yn$EC@+~F#3`p1Ctg50|S!+!x09@3m|oppj7zrGebQ% zls|rkWTu?s3{0jBjuW1C32;k-QjqHekOLSPVD#2w3`}MWju*fR_!t-%4!(vYM;N^> z9b&>WkO}+@3=DstF@V!Cj9z;R6!MN2zzPHy>KPb*h(Roe(az}%OePGD6P|;N5M*Fr zIP@BlGGX+&a0Vtl2FD9v1)#Kj_%+18FuDTjh!-Fegc%qZ{yl?u14gen$-tz^;CSH$ zDEuWw7#J9Sib0$SqrFcuFc~p8PIw72LX?4l;a)QXI8VW7CwoRFO9saaU=P!XQV$W#x?ogtZ!#IFv`ZOjk$ z7cw|r04r2rU|`6<$p|jp*aDEdc$(`R+4`F1d3fy846M)Sr01B8q6TcsvAm+MT7I`gfAdtK*iXxACL%u z(Gb@%Fo28!DUt&fVK1IQ6v1eiA_gV~#|d9yC78uVh$<*u&j44(!r*uTq)ZW1gsCit zD1*^(RUDvk7sm4LKqEK2yz=(6{sBJn+;I~qv5JR1N0NVgA4J^RR=@TM|E&LgpnW1z$C`tIN>MA2vCt${G5RaRC2XL>03sOO!f?p7eET+K@xsn zASnn&H->^70cu5n>d{}IiVsw-6ptS!qMkWyk#|eKxCV)z}pXrcL zk;s71m-`r)3_#@{SOutf^Ou3-D;S+B!w7E1{RJ5TD&8E|Lu#80C~f!+)Sz*^09F7h z*9zA$GJ!H6jD9B0$Rx+$IN=}21W>{^+XQK?!RUHb2}Y1dFMw5m^3RnY5EU?bGep6J z{~#kk<=VF=5Ct%LGDN`zumVuIR1|~%Y z#|t1ya3MMO8AJh$z64P)p#|gsP#hVqXJq22XJB9eGZ+{czAOV5!WY0QK&9j_8wPL< za={Klv*v>f$tkTM2drBI@gR&YoXEfw%HVhbBnfuFX&X@DU|?8i2cefLFfbV~I8JDT zIp9q*15-Vybod3W+b8)mFljS5UI42AIY8k%$U+7NUnngSsFXIg zg(%>(htRxyj7*83ObE$eV0ECv+Q*iG36#y0>>;!#FC$YlNL@Wx;e-y5fuI6g+!m6J z-`YXwrM!$x=^%w*br(SDBtb>??WGX;?@(HJ8Km9S3Gy7M$gZ`8G@63#A@nO&CU80j zD*zSOhs;4`T|EN>gu%d|X9>>H6S_b~fEt@gP$Mj$MyxCXH6tA_fE9oW=|Ve50qE`k zq3`BHLbw}b0;q6ygDQ}LD$vRUw}~!*6@Wrsw;rm38LHyWX$EkH?Ex79Dw36;3Vzr_ z?5H~pE|@NW6@W@(aj1f~Pz8G+3MTY|OaPU}JWvIXpbFw4R$Krps0S6tj8GNVp(^I* zGcegQI8NvT838Jd-`GMdKLk}^lh44E#Nc=VtN>IRyRU_06&PK02NDwfAQM34@U|JC z+{VBFqgUTyU@~NIyigBT0ZJMyhLEz24@$#~m;f>YR08ju3u%^YnhPmtQ|B>)nhY1f z3P2@rpdF-%s_p=x*$ct>b;3lD37`^ql`Xi>$IxI8q2D<%)`MLERskx3ou7kBTn2_v zC_TL$+zFg831kGQ1b)cQ2=0EqhSKeEj9?SM3P2_BJv&BlfAb@hX0c}kHJ&C+2AKdV zf!}>#U;@S4KQLX-z`*yBfk~Ia@d8){s07ZoWncnDi=#b+z8S&5q{iSlVG76yPzik4 z1tM`3N(Wjpf?AdrzzRSmu+Dc#fnoU_Qj4a2XJE2maGWp|WCExJo^A^%6LRbAAq=fZ zNDB?D0#pK@{lUNl3e-nGA@oBshe#|hIwMu1A-H#U$^J_HT*BY6x=pgI+-08|2h zV24CGjJ_kx1ggR(Ob3|&DuMT2WMrxbl_DSpi1w&t0N1Ht6`&IMyDcP5oVAD0+riB@ z#|bk)MyN3`F#HK-U;?G_=nx3~M352Gj=ca@pw7U+U;r&8VYJgkaNlXdOppm0p#Faa z6Qqf_6-w{uWCS%uE`U{NGB7YSD?-fP0;Ri;fm+p$6J~V_aO;A)m14gj2@^&)T!JJo zekOx^T8$kk3JGi-fy@nzIt-2%K$;{$qa+rPV5?`~gQixM`;4Ie!-Ux&m+3GtFr+gv zg8jAxN-s|YbzmJYfEDO6Ffe#RCEB2LcpoE^ID_MaIUp1C7#J9)+CkdT*$xnTFB=m$ zd_f9y^%)o#y1xiOoZkHfLT6u^zW=F`Wc`G>ATtaY7#M1xW&}aaD1({-R$$1$z;Fhd zsTM&qmCGbX@X*LSkO@W%3=9vUg~xVi;Ss>b1gNFtwV523l(m_Pw|0j$83fq|g|T5ZNT zKorb`D44JSWP%x}3kP-C8mPQO<{RSuNyoe$~5Ed(jBU|?X__Y+c~ zUH%23)qjJ_?+aiBmJAFGn{6Rs-)Rq_U6LV%(jt%vRtyXbtA0W}yZ;x2=K9URR4>Ee zcmb@!nt_4AA8JPm)D8|7aG5h_)3P9aoljo3J?hd6n zg}~Wl!V-`Pb_@&*zZ^iN6a&LjM=)K_VD^I%RAO8JtFUKaU}*XYF=F~J2+eH8$P~-q zIAJNs2nSGLu7dQ(u0d(7|BT=<2CxE01_p+{XAlK28meHzGLQ*Q3=9lj8VukbU8F`m zgrVjIX_0|dI5RLXgw6)FsS&ho8@SOmVL8YM7X}7~g)1QnU^G<01+W5F1_p+BJ4n%K z>Hwi#iWrz|7#t_80GZ$h8s`8NYoJ!y7DxtVXoi++U={8R3=D11LMaAXD5YkDvb*Di zl^`QL7#J8%tO0d*7#OCnh0vSVGJ;1bzzRG;+3p-Tlo`%mgU~OoL84<7$OJFYFh<}^ zh|Jmf5c4Yaq(agkG^+9w zl+G9!#C}2O$-fwx>KGg+tOYqDfPsO*#Su~&zK7D)?I4pJFMt&UGB7Y47lUNYD^U7J zFC%E!seZybkP$%)3=G9TA!(uG7le-a&d6lP;CKP7Aee!HAqqOoG960CCxaWY6V`)F z2w`AgXuZn_u5bnJLFkzbOyDXTtRR$ufuVXmBw=nj1EFU!)Pq$_*Z?vjjDdkc{U;=2 z+5CdgnwubX9aup)0|P_qPe^50^$SA3`~@znCu{_n5W&E}AQuABSqr6=i@*vlfE7eC zFfhFJg7gZ1fM`(qi~j*>LTv&W5yim3Fa=uw9EH;JPJ;$K94~+sfEv@inGpBSfzn=H zkfLTY$b=YB%6$cK#Ew^>-Zle+WtX(^Omaso0ov=wAR0;sT>3{l_#rOP8hiO2B* zSOKW-Fu|6go(a?#OS1=QWnf^4gjBfOKt?1nFfh1wLP9whN(V(kI!<5($qWn(Thk#m z3XHyc2i(M(upMMV3IhYfB3VeUU^SHHzXyp>u!2<3sBF0tB>yx*X}`OWl79!th%^QU z2Gayc+V+6bXYVpFIWjn204o4>S(dJbl=vH=H1}NwCMO2R2|Gb1WPs{#B}l6#7)rO@ z1vfS??Ch==;MU7zU|@JT5n_b+BnWMD2^6EC;XDRm1qK$Dgaif#MurI>!g0bbkhxi) ztp5WNJXfHJavujH*kxb^*$fN}hvz`z{}z<~Jr7*YPS_1HA%}s1A$ZjiNWOdur7gkT zEXNCA6}b!y4B?KDvW>|JLa%iM_l+j(=@#JD1I@D?p9pC+eTCA0rc1w5lCHl1QXrYn zz`&rN44Jrs(XM@<)ay85FUS!E3=9m@R3NE!Ka|$)V+4&dUH~g71l7F;kTIKmPBUTk$6-Xc!n){esYi(ctm@ z35P*O)G#nGh&n<%cmPUQi!gz^Eno$;3=9nYPzft1h{8jKGUpPMR~; zgG*u$3rI=)>JOwZ3swONnqp|Gl7pry0Y)ZJKXbxSP^xNRU|_fbO-*c*AgN2{637Gw z21X_UaRnxxl!OF^hMf!(8o;w!AYGD;3=9m*-$T;DmiP4##^alyRO>k57|3Z&3=9n4 zHbBO~VRTFiq*wzhXl7tw$Z~>|C@{LQ2;5(qa2#Yp3j+f~xjw`M7=3L%B%y#6v@$R- z1g>FZ;s<#W%wS+(5Y&R?x)UHH+87uZW#q#67dN(Y28GFdY?UH~g#0(G}m-2gd> zf#C&|77b-&vSM(Ya0X-qXv}F&FeHxjLLjtxFr;$}RsiZHwmt$C6buZP-#}>FC`Ryn z*;$YYpur|SsNZCu^vysJ42~DT3P8h4-R~H|gXHs}^tvc; zTurzLG66Kaba*`^wr@k}{Q6)>P=Hl{hL`gA8Nm}G-B6l6j1gQ(T>=>a8m4&Q0x2Os zL+OXXpcysC3t$DHuJ_SLkS4eATL}F(5mUj~KxuJ^f(u{;py4I21rPN5Xja~=hsb!`htR7dz$z|)Re*+< zo-0BI+L)Cf^ol@6@R;olkP)EarD>Xw?719D^Fp%c1+W6p@KVV|P|?r8Fda%y4q*fj z+1>=1APXvgu0h+6pP+PMAUM@r0IL8EFZG{?WYHy1T0R7lMQ?$O01Yo4)?)-uwK3>J z=>9N9CT9l63t$DH;iYNNi8OX|h&TRfL0WLP!5wSR@KVlUNUhicr58h!)oqX+phUtv z1JXT#(awR4pz3DA9gqUh@DkStNHsJ2JA^KZf)q7i1)$-jDUTo$kKRD&hDb)HdTR#9 z33ow8fQFZXKR`;WOelRCT3Ue>fQFa)^+25_28KgWdP5K+lRJasgnJ+pK*LLkk3fSq z3=HevKxnf_aJstyRsdSBaaI;GFbbvX8CDj9N-f7J_d#R$5{8gU14f^LwAinJC3na{ z+NdykTMcCB>H)}dP+@y~8Ds_kM&I`awU-?)fE9pd82&wmGzSEpKxm~W_29PlgooV% z+_Ip;_A5JRu#16#hXX=~l!FEeL91&VFMt$Df(qPXFGy6t=KrUWP8CxLglqWT}G6SH~Y96`;jP6CQ)i0+qb?pF{dVJ6}NP zCSyoL1*`y6@=C0OGz&GLw6z-}cw+tu$OKTy8#@hTHUmQ&lzw~|+#$RGQcy1mDtUd@ zKq_+>9hC>3@BIN%0d~z(kXfK2*w+RU#4!4QHmD46+zL{_$OKvl(k;L(2P%s%eup#^ zVRYy_NEQdp5l?sqQUxlI>+imUD1*^fNXjmNm4QlSuZIv-Fj^i-)r99D!$3uI`*nya z7|nvD>H=65sC-U53sD88LHYmXTO^mg02u}^BoiAE<(qGp|6Q4=S|(#6vcLcGgBn%7oF`)u4tk149FY<1dgR(9r=CUW1GS72q45L#G=zJcpR8?+0n4 zfmMJ?@CRv-=~Ebeq8QT3djm28RDvH)gXo0O>Sw?TE`Sw)3h*;aKna9_fqf~2o^l0} zY2SiOkOp-uEv+FXxEGXu*A0pe#|vN;pb|Xa3DPlw(Ph!#^!o#(0GvACfy@F;6x><^ zDO_MQ`y)p1!XU5$a7iu*X{F48(n(HCOrROQ3GYEhfQoV*aYzfn3QBLugmk4r3QRyn zIa?Sch+%Z~^t->66zeB^0GR+P$uDjMwICT7UP9?9d<>ul-371$P)WWQI+k?KS1mBiZq zs7fz@m4YT7c0vt>(dycu`X6K>1NhFh3Ex5P1I<8eo&c%jjzVdMN>GQ+@d8)@X#PQ} z5fW}1P+Fl9l;i3hC;R{z0h)k_nFVRgXG3X;1_tm5Ay@%u0wUf5!(!=&BgwM6mllgA{ZZRS(?L@e%FvQu zeF4PDpK`%Y-V9OzT3CGn#E=BdSv(Pk3~9X(hq!0SQBZ;DIN@Kn0Jj!s&SKtlNF}mw zI>e~iV530G06_g%(Aq1I{h&otAT5%hS&X=BNRO&N8#0^4nhqY){tQw84%+`9cYhC{;Ob1t|d6>mY_CXj;PrYNj95%;RYwGwU5cgH(VbVM0TX0JkJ) zg5!G@#Cln%(oV2a$1NZQU_(I+RnSbw6sXGQSrCWngH?k3!vHFC!K+ytLB@h6Jz}AX zJD`dM(ioU@YzywXQ+m7sD_Wz7ceS|)K6#vnFyK$F@`Fz zg(|p|3i6ZV7LWpPVg)fIL9-!WGa+8mgsR*OQTYX=034#tATvR;BHYk)#Scwaa>?MP z=4OxrMo=u(gIJQF`H@YJA+^%E$B?{#AqlMXGe`lrz-<8;3YsVhfd+mjG(?|)!^v?A zNCA@qsGJ3{BtcUqT2Q4PP^Bv%O22>S1sMyPJb4TCzc^HF7DVk9kOHO# z4$vYRkW%n8N+zT-KAZ`u_@||Uv&$EdQm|XwKq^7=DXy}Rwmgjfn+oa@I9>oN@B-r&5;WEFIThkZ82zUjZ2g2zkXfLq z7W9RG2NOV*tK$W*0`OGJ6G(Pi0i}h4LH#Gk30*y)IVI3si{K4NZidl1KHzMB0i;3_ zG}V#=?QFp4^}I~r>Ev#ZBS2FvOV%-fm$<>`+S{NGuj2)<0?<^;J4Z;#BH#p}cNBvg zSoITndIY#tL31sSxF8;d(Y+y{#VFwH=fD6SQg^%nQY;CYb_uh9q*54tOawCK)eCYE zXxhaWssKh?ih@T@FMt(*rd?dX3hEgcAPfeE{~}-&6Z(1txHUlYE>`OywRAX?{uu(Q z!oUs!HD*B!P!RWE=QfTm-Fr$g#jbtr8; z9n`mXoG=k&0%$tM-IkF_4Ak|A12GsF7)os+ojR}z(0t5gIY=#V8%lTAff@{s6DEO- zNM&GP;O>BQVP&E88Ac{1P*Vu305lzQ>KP>a!RY;m!F`1ZlR+kcreo^AuY=U`66+y! zdH`s^$MFJK1!z8|{tF})V6^*HP%Jo3m;y2aG#wLr7~)D8eX1IiZX7Rw6@X@5M3o>V zn-Y{>9SG{-I8K-fPXC~(7qe-Q=8OxJ-c$vePjkEgQXvVNdbvLhQb@dn(t$#t0XoMC z(?E^@&AddOh2$U@Eey>;UMs)&ok% zf362-^a(RTMu4VXHl{-S0i*MmK{{h#1)!-HpGS}e5{#at3a+JofE0j|@Pt_)qd;>o zqR9~FDnaRwQs7p{1+W6p983Z$BU3%7|60rn8O`jJ0-H5qHpmFj984Q4#E9)sdW95Z zr5;!TXbxt}Gf0#F0F<6p02=#poG=Gu0%#6K@;PW=hk-!@N>>&@vI$s06{!3d6Ni+J zFxn{>l0@f%i~!BSC_)v$=rz#(E?5C*4hDT(t(bucw6b8rydD8=1<)MKvqz9%{|cp_ zH-Q$`g31qudeDM!kU~k&Tuj9iNJ^azrMG!NVt78tEugG=20Anaqo;a<1_d22fE9qI zVi+Dl+Gj9Y3A);D0muZ8_lTfx0+#|vNupjj8W7)Y#WL1_hWx7%^TB9IB7Sr?`UkW}{kem$hA zl`R2E0gk&tDnQM*3m}FhX!=Dh8&WGMXG1*pS`0M0?)V#|0HkulVvwPrc^Iqf5JQ!( zL(GhTnh8<>Qh5QykOWQ0=s;UTnlBmZ!CN>si9iemsQ@XRumof%XlmvMs7M2?hB^Z= z)D>zdNC8OY1rS3LG(`jJg{-lHlvgScGe3b8aDm&8pa^dOEzSWM-p#M~veJ4k zRF42e&!(k40^D4nb}mQ@s4IN|BqG_vz`y|OpyUZbS`g5N!zYjeE^rC}=>b`@3}j6& zXnoEXNFf~b1yX0Wi-Lw;9XEj#)T8LR01{H^11+zE_HHVdLj1j1927wyqZvT%Vq|cf zupDH5KWK3Qw0lzsEvX|RQM4DN031djhU5g$CPb)81E|Vk(O7W6e+H=lHO42b02w+F zv=)FDQdiFBg_Q2Ug~4s9%^(F_OkjtAHr_EfUI2;cPGVqSfOVcappD*nBGZ5KC`-yr zSlJ`Mtvi{4fx#HM?JMOaBqS0AvF=x8c4|cfOUbr2z3-_!zLqWNfF3Nvlti{yr)96cua+) zZK#v}fy@LsX~Nnb0dAAoAbmWH{NSB+j69G)nJYSd3!k!r%muKLISdR8NznFvTn?n1 zu@j&EflpaMdcrzTG|dIAgn;({-b4F?kEd(%D@(~-04Xq;$H2f~0X4!4YJ`aR^hAD; z5$i$jn9sn#@Eh7SGJ~qPHhm7ivZBldu!02)3=A`%3Y4G<)`(5N$*-&+GhqYBh=rg< z22d3eP!-d~ri%)Id;(Unh=GB@9jby4s-j73dLT%}gpD9G7K0XqK~=CoRpg3I?**y2 z09LUCwC4@F%j^@hr{p3w{Uk`mgiRnbmV)-}Ks!0FpvlZYY&xqT$Q>XRy2}_C7+}3S zMPW#F$sjh}QBYYjaKdI#h%RSfVAuxj)IEZB@{WswrrH@8oFEB>fx!u$5HElp^QTp__c0p!D1VklBuhw%7Lva5Hi-I9>oN-2hsI zu?CVE`l0j^Uyz}WjXQb-xWV1v2_Tl_Mg|53#kG)@CXAl;kO4F{1XzVG{!b zgAvrFFnT}CrS;&IM@>6H&H$AxAco{-1_p*0=>A|B%?EWcNCD$Q2FD8^hU6B|k_D(k z+o5z3#KBFwKxTrLOMw`QTNxM_#G&qm(LN6u>X|^J1fU=jU~oJHQVAYv05K%DF)%P_ zLmdvId%+HOY}ySn9JG-I#E{(1z`!u+0;J&tqjheBd#+nS3K$PCI9>oTBzG_{Fie2< zZ8tw*V5$erCNX=0hb$-T0Y%qNP_f4d8A)big3w}2;2zZlu!3C-3=G#NKpJ09p!CNH zpdN|iguNgWb~7+AJcrJ0ajb{5e~$P;TBBeEdl(oPDons_lzJG00a`i#1*rh_>L=_2 znY9%lI76@X?q|Gj_=f&8n7 zGFWdxy30p;p!?*@ts!k17;UTy+P4m>H5nW)fK*9xgVvm01*JO%2EA(#TB8b_G$tGc zxdb$+xfiP7AC!)%0L`yCUH~iL1@&o8xq-R`3=D6eG^0CsvTDLHkP&z- z1s|Za3RpqC;{~t^&}5~30htMbLAu29_ReTN56Brokp!6nYCMHqP>MD>C zp!vy<6Cet>CPL^r5Cs>`fE0ihnAEL@q#PI>YXeytbrz%mG(FiO0cqodXz=d!AXjh| za{;UZG%e}97Gea9o&+`G9LNaJwB)_3khCCw4MKBQfd^GDfE9pdC8M80swNoyUj;m^ z^%bN5oZHTWj8Xtir%reX2@BT85D&?TfoC``fK`CzC!akA^*R|CK0;|%A8`8m4^jZi z)Y6Cu5GYbYI^2wvqs;S$IM&>ZC>14!8oqe~LOr!rgsD^LgJ zKiDKOj6SadD$N`xTm~5dntz-JU4R0kf8PT4u`hrXfTkS{GaxMn7~LufUPm+G3djV| z9HYt&Na+frGwy@dk~&_v0?vP+Nk)A^hzc0J_&&Hs{0A}u99>sItt8NFV-qwec0lP3 zu^+BJ~#>Oqr@@ld5Oni-;WFGvMQ z=>-r&5;WO(!VnUiFgo!T$acpG*Fk21CL5=!tfQ$f5Ha7Hu20s}XX257p$a*=j0?=e5Y!Vwr3uc28|0j?FaA4g883mek z#6Cmqcmb>cH0ijtUJFuFY=zQFIt)w`KnMANj4)$hV1P})!sricKs_PHO}9V|Akh2? zh#?7@Y~1SxX_MTC(n~8KVRIW~6ljX^PAnvYam7LC!<7t7^|B0(7r-h&Q;Rs}awpsY z83CG6j81~=Xv=}p4=W)V0;~WueJC~y(%w;n(mqv?osV}xCV*xQ+1^451_20N&v2sx zJaKmctO7J`h<(1;al$>25uiyz^y%V{ncxZe3t$DHxj|gh#rHubfV$?0>Ee1wy=j!i z09qDy0jvTv6Sy`Rk|AL9jC-K!*KxuFkP)D+Z=VDtCBo>7^B^GsRsfpxgG~X!Xy{bX zgohv#K$Co`vJ6aYpbj{g!N9<<2XveYgX1QU3Q!C10*E0An(q5N4U(5&v~Q!<0& zghwE=K(l-cXF^;9qo>>g7q=I{3P6*4b1y=wU>JS=9wc$pKL#1$!N9<9O9w?RT4@9=zNLtO7JO2b&jx z(fYT*ErSWKK}LY4<~q4SlNk&Q%b@fRSI7h+SOI8eE~X1opuy-HaSWg(&lBE&OaRTs zCA&dVB80AIV91LD7r+<5DnQe5uxSrCT>~-VEyxJabX-CL#0VIzeg_=K7r+WYvvIpO zK?<3pP+Hm*9H`$x3cx|~4rCN)9%x1G0?_o^#4iv9YoPRwI!L> zqZ7V@OaM*4O}+wAFds@YLlj&9D*#Qu32%WYkO$GA^0xsJ6cfIIjK~9R##sqb!LbTL zYeE!U04o4ZzinI$k=P5RFF}H8!gr7fpy{`jb07+~LTP`9f(u{;py@Z>>yWUlmw+;& zp@I1WWCUpbEd&~f2~he8G$gzTgWGC-cpFlf7K8peu0btO}p_ffJjI{>E#dw7r+WYvu@8OKotCk(kc)I z6MlnC0L{A1)`Aq+FuLp>B(H)MRD#<7u<1h>Jz*KR?K$BO$OzEZr*3G)1EW15M^jt? zD*(;9!R8lX^d!hU<5!RZaD(hG$SBbCTl!5%$pNF!)I*f+2B`qG{V#wRlAwt=FKJLC znSmi0N}EZ8I{l6l{(;N_O~7@`gA5L>g3^)F;Bxl@SOF+q=}LpPPcbmmKtW zK_-9(G*6#}E@wS`77|DsRl#lltsoU3hhG3Ow1A8P zO~YM#$XE}afVc)tVJaoydSWj~1v3k12=@YrDGQo_+XG$Scn(T$Dg?Fq!9B;nAVrLz z`JPsg(V(ff3C|!NoeQNq3&Gyl3sL}5c>%;;9t-%U`9o`E3=O1~)tuhRPsQo*bM zJ_WK3WGrZ^ZSPsgioRn|`aIYXj(b1~n3zE8oq0w71!!JvGBnY_=zx3RF5H9;kOI&wTG)Gt(-WX{ zuP|g00ayWO5^erNh=P?+It8L&LMO-s&>UL5$PI`JIVf!nQE>sR0yJ~B>>NZv{WS>9 zc?TRk6S_b~fF{n~Lr0fjG}C>sf(u{;ph>c6!H{MTjII;}ZIy7G(A^7K(*&9vlMDl` zTwq|Zg3{TQ3{2q+ju${GBti3F+m}EF$v#2p!&ku3IiUyS2+*w8ZVgb*Wnj1lrDZNd z(luBCXtHa~OUUw<<4}5`K6r(L%!J-v0d6DYxh}-o$LVU?%5u^dKq?GCb6v2hEci;s z>G9giveFa!dIh)*KyzKNnJoDF#p%v$V}+(72q}m z&2=S0CsGh=6{mCPD9hGg0I5&~&2_q2}5`Y1oQIkOEMKH~?ZWu^2EoUI4KqK{H}Mq4QmuS&W(BQ$Asf7eOkRSOgdx z8zzD*0_8jqLk%=Z_5wQ51EW>IOGqID6bzstQP83zkZJ?agc)q!3Pv-pRVKT^(9Sn{WKnzLHbQx^A3T^%3 z0gwVF0Y=a+Ll9FEG;s!7xCo=63mE@`6tHlBCgmI_Oz9QiRs_wV6@G6L<6J~3mO=lR* z(F)FZ6Q+TT0L_mrhfY|)Xz0YnA&>%4>bL-6NP_0amN`SJ${kR;-t9L7Xxr(8=^(Q} z(_&}WLFU@7KnU3RnB3b1b{%mg_AG^?>Q6j$S_C$Z`KAlh)Nq_I8{`Iqc2;7&!(9$A7lh*CIo$DtLJfWbL|3H z0Vvzg4TbaxVYIv@I0;Nx05Sozvup1=NRWYO@UXaH9;7w{tKb98bgl-^*)g<3>8)0v zR=(qeg}nmYa-f+IKWM1KX!bmiD;dCzgbN^53ZUr_cWAJ~=sUTfRyP9!17{;Btt+n^AOT1fYB?|Ahi_(14|%-%pgWZ(avc?+8~1+D@_rRMvpzOO`B!NWP7tldOAd^6|D54;f zK<6$%>DhT;Z(mpeax-WarSc-gQ!qLV9>4W03Ji`DR)Q3QW>c7<5e%az!y|+PRGWd+ zDT0=b6+j~xMrXp+34oVNtO7+HXi8-jG-hG64qTxE=&;)h^P~7=6nMvU7AT$OzE>F2-|^LK;SEq=4K07r+WYlPRIlgKc5- ze_!yt{DgI&JOY|b+4mb1x%CVT5C#K7{tQ?`QUD!V1hNSMlA? zSPxPFno_xM2I&C^nL}tRbMX4i3t$DHnH2P;p#6~Zktb{b<$v%L%1daK2BRA;f(G9l zFMw1?f+kM19YK~dFkFMuo=pr)t_+S7Hi8@hnlA~Gg>(Y)pfo!pD2qB?04o5^m)wAs zyfE526TGape!?b@5ugc^G<8VX45O!Xfp!ZzUH~fq&6jj&LKML0la-*l+;PHYkO`pq z5+Ue9R2Ur{4QeYpUI8f(J=+W^mSOb9e9#WaddCS{K-xi7=5BGw2px>htODg4#|vNu zpz89LJE*2-V6gOn(8`{W#`#uIoPui0CrOZ00iz{SK@}7z!-AS`AZ3c+I@1IarISq< zA&1IKJ!_$Vs4zQ^y_T1O^5eEqD^#^0@$309rz@B^5Hx0HgI!f^2l0 zupMLqsLnKUf;1Rl^h!ann=gPBaDcjnuqjFy{WumpaPk?X0@PrhumfZksP-)50+odf z3~Qh?LlU@Kz7?bZ)Zx7VVt}_zAAv6CfYH-ZAVV!XK}LaUQC4ULRu5yi7J~)aq*>DkrFSrZHWa`WPB;QG5mX&ZJ%>0_ z9ZJuwzYZE{fvda#Rtc((qqrb*w;516?>cBq5w38;QBY0>RmaLVAh{1l%U=gM((wXV z0jN5D13lqG9(uw@P83+dgkvBR>OuAKOX$oKjGh|_E( z!f}uhpz8R!7bI!3c|&L(15n^QUH~aj0#(OmcG0cjQ=fzpQH1EoP0 zfd(%a94~-Xf~sc6dZ?21P+AaKDJT=10yzt`%z^POBn)7*?r~72cf0^r0IH)8LN5n_ z(d|b;`O|U2X;4N7)zOnAATb4_pB@EI%Y)nn%IF|I~i$aG+V6;{)DAXN4ffO(~fKC=a3o^?X6#nv4AlsY_ zp|ngdq`wVPAq!gD(3}7%WnuKx1W?5dx!d3zD9AzU8%%;BK^_gI*Cv4y8OUo4kTL?K z61=HeZ8D^MN(a;R3=IAMAlq}!gPa6f-{2X{06tW?07~BvWnhwJaNGh?02=(h0Afgj z7CJ0T0H|~tK)!bpd)-eUm#yei9 z2de-ri)b}~Z26i1r8ikX29Ixmi~uc*nCbxP=P)oVg3@dmps{4f3m^rGpk)!T*%BCi zG8>fZz>y7FF*V^Ps2~6>kEnpgPlXtCt5N1rPzndB1Qi4zb&{Y(5>~SywS_yBesBT2 zs9?e^kdr`*ByKoDippP5TE+>)cf0^nAPZU~u?2d03yj{K0dW#2N#6#UQV-h93~R5$ z=mp?I>m09uREVbKFoJLWg3%E?OrYVm33ou+LCtp9wO}yXiiZi5T`zzX$bp*eAL1b< zz-S*%&{jA|xlljhF322E<2}a#q6|hy@-cx9n7sg20BW{xw}&Lw6Ht094-@Fv;tBUa zCa{7|(`1JzxB#V1Ag3Q)04o5s);GL>lsyNb^c0?YaNRQDKFA1AbG;e5<8%s?=Hmsq z)$syY0ceOT7JBeR9+bY%!vtDcG2sEo1W+SA(hOop7L+dHft;ZYRsb5|TJ{Z6yzKzd zp!UZP4)F1e-#{wByColj%mTI2CviZ61V*cYL(cI6SOI9P>)cgPGG<`lzXqYFT!rME zM<5eGZS%gDppkk81{iH71P-$cU9CP_%c6h?1igrtxsAQM0haP*ZGwjV(G-SNVcUV(aUNl?QZeQAZz zZ%|Lial%uO5uheF`qGLw;3XE07r+WYjcxRm6$Y;$COiX~02;(XUs*8~yuza1@d8){ zsDX{Xw4w#P#KLjHbC40BrZxJ~irwHP7LFId3P4Tj9_TuP8BqEO_!Jt)2`@kMED@pr zM%!>Ofe+mRE3g2?zmGViX91&^a4~@wp8WtB0cyqWgB}wAqyKS1F315Z0JU7-Lc1O? zdKy0ylQe_lXOIH$R?MHEi3?EkbvO2JFglu_33P$V1+W6p0^WyE1u(jdAMEhYAO&D|{RXE$P@B~Q zx)~8hKNA86!sg$gDOJ#EU?2wgm>>S<5VyhT^IVV`_z%cgpth?8bmkC7^YSo(jvc%J zRsd?d`tU+b@Zp8Fze2g0m^2t1C;SB&0cyN15rSlU7+u5(@flbFsLAU05i-UNqi+g; z>+mli1xya0RQL~M7O3$m^cWK4FuF*XiAjdRaT7=ZOML@#1L)dJ5C?pW51T6_xxi>g z0Z>QTal(I)aiEqgj}}A$jFyEcxBylFYRG1OgCr*yUB?YckqvzU+>)S%Y}G7?gFrNR zTbQU2xD|8(tOC@M?QVpqfYCw1;CTB4Qoz!{%mC^^HiC=;wPyeOLHdU+c+=Kx3k1&|8x{RTH*LYf#bdWQhm#S@xA zjsP`bTR9+IVi=tu$ix)E;J6K>06YT?Vt~)%*((W&d zkfESezjvW?Q80R^AQMwGgX1=k0+7lJAO`q6o;s-YFq$1={Wp*TM$q-XtspZ&>wvdG zt%uU}3@ahlZv&}d0v$qo0mPC7tq9Iehm4rQXr8O!ndt8z1>n`KZ6H%YON0NWK{~=P zde=Ng(8Vp=K?*<_?E;7aKD5Uq9nw>R()A4M&Vy$PzJpYNM`7DRhJw}ze@TN_52L;2 zfUVyKQUJ340*E0AS}d%S4zV6aA6@~r{u@XE$odH#ATuKw7#LoqL9B<-^;xsQ)^7u; z09k(l!~mb>Gd~>?r7-%|3(%mN^%Fy6}(Ok>G?%NJh0BT2H05K#%>xh>@ z-3Ox+UV+{By|b?#x_qSzWGHAwu?N&v80~x+Z0mNA0&tvz7~qqAra`TT(fyCX)_(^n z01Z=2=mwbyT3Ngly2WcVl+%RYd{Mg z94GXHOaOJQj?Ra4j9_#J=qdxyDWhN&puSbmG)Na5M)yO;wI)me83F2BG1Wr`gSnw} zF(bITz5rGL>RaWyLy}iHl%B*0F1RL41epM8>PACXl*8!y-={$X&yE+sDnMN-^w~Pt zfyk3UMu56hiL#Jhc`cL{vw^dLVNt=|P+qCH^>$OurE$``u797Z#BfhR~WfD~|odQ=u4K-Glf z6OaI_=XcOS9gb6`_6cyaegz-acC&0}KN^%>{fsJ1R7T9Q$+t??}1?dH)rwI(;3HBu*0oHm3aALXu z;?#43Qj^mah{YfgR>Lb0i|2tfgObv!Kj1-yB_ILTnSa0|YZpKqPEgOh&mZipCm;dV zT7R&!Cd}^>;N}E%%dJ%)J_ZS}YN*`o=EM5R{I6=MfNJ)sJK?1BEk`PBP1nC8J#pNzSd<+s`<-G{;F^Izn z>WABW0*~50Sy&Is5!#Uiaw^(Cmui6OaJwt`5k8hNU3Ape}cFJ$Qh1 z2}ppos2;Li2gKn7^|uf7KoT`bfOUNj*as7qfy#eQP-ok>9=vE_$udxQ*wllj?Hn(F zIGoB13=B_tKofqBPe1~!mwUi-I}?_JECzM6qb`Gs&?O)NR?o}emEadZ98ORldsiEH znDhxqfOTaXcxGwB3Xon<|GHk$0(9tz;}VbvtAGWh6a#TMLA~o|n;;g01X!^u~ zq#4w;PJI9lpd}yy){qB~tO(+8g65I#1%cLcI6eUhu$~P9`)|T3kX}%qTDcvZ0_vB5 zL|BE|!71PZh{Fl$QHMdUzj^`^V0DF#Ca(r*26d+|1%iu=B_ILTgMr|^U>86fPEc3+ zi76y2f&^GEn?kbU8jxO4KRW*tsE_No1SG&3SN{n#|L1rC#N!0@q9?8aOFsb#ur{m# z%T8De(hTZ^_@?H zuNiP$0uo@|brDkI)Ps1Opw4oW0NCg!AOY4w0kF{%)`K*Iddk~mK%<0?OF#mwOJzV4 zBaRn998OR#nGI5)J^=}^e$@a6&V­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 From 8558983c865b6d7ec8a692bd1027443109058cf6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 4 Mar 2023 14:41:17 -0700 Subject: [PATCH 042/294] std.os.abort: take advantage of `@trap` --- lib/std/os.zig | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/lib/std/os.zig b/lib/std/os.zig index 3a3433d819..f2bd8bda97 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -575,22 +575,12 @@ pub fn abort() noreturn { raise(SIG.KILL) catch {}; exit(127); // Pid 1 might not be signalled in some containers. } - if (builtin.os.tag == .uefi) { - exit(0); // TODO choose appropriate exit code + switch (builtin.os.tag) { + .uefi, .wasi, .cuda => @trap(), + else => system.abort(), } - if (builtin.os.tag == .wasi) { - exit(1); - } - if (builtin.os.tag == .cuda) { - // TODO: introduce `@trap` instead of abusing https://github.com/ziglang/zig/issues/2291 - @"llvm.trap"(); - } - - system.abort(); } -extern fn @"llvm.trap"() noreturn; - pub const RaiseError = UnexpectedError; pub fn raise(sig: u8) RaiseError!void { From c29c4c6f70ccde441ff8e40e94c2848fef1f86da Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sun, 5 Mar 2023 23:40:54 -0500 Subject: [PATCH 043/294] tools: add lldb pretty printer for std.MultiArrayList.Slice --- lib/std/multi_array_list.zig | 15 +++++++++++-- tools/lldb_pretty_printers.py | 41 +++++++++++++++++++++++++++++++---- 2 files changed, 50 insertions(+), 6 deletions(-) diff --git a/lib/std/multi_array_list.zig b/lib/std/multi_array_list.zig index 6965205b1e..f97e42cc89 100644 --- a/lib/std/multi_array_list.zig +++ b/lib/std/multi_array_list.zig @@ -68,6 +68,15 @@ pub fn MultiArrayList(comptime S: type) type { other.deinit(gpa); self.* = undefined; } + + /// This function is used in the debugger pretty formatters in tools/ to fetch the + /// child field order and entry type to facilitate fancy debug printing for this type. + fn dbHelper(self: *Slice, child: *S, field: *Field, entry: *Entry) void { + _ = self; + _ = child; + _ = field; + _ = entry; + } }; const Self = @This(); @@ -463,16 +472,18 @@ pub fn MultiArrayList(comptime S: type) type { } }); }; /// This function is used in the debugger pretty formatters in tools/ to fetch the - /// child type to facilitate fancy debug printing for this type. - fn dbHelper(self: *Self, child: *S, entry: *Entry) void { + /// child field order and entry type to facilitate fancy debug printing for this type. + fn dbHelper(self: *Self, child: *S, field: *Field, entry: *Entry) void { _ = self; _ = child; + _ = field; _ = entry; } comptime { if (builtin.mode == .Debug) { _ = dbHelper; + _ = Slice.dbHelper; } } }; diff --git a/tools/lldb_pretty_printers.py b/tools/lldb_pretty_printers.py index 3013fbb43e..a1db03b33a 100644 --- a/tools/lldb_pretty_printers.py +++ b/tools/lldb_pretty_printers.py @@ -201,7 +201,7 @@ class std_MultiArrayList_SynthProvider: value_type = self.value.type for helper in self.value.target.FindFunctions('%s.dbHelper' % value_type.name, lldb.eFunctionNameTypeFull): - ptr_self_type, ptr_child_type, ptr_entry_type = helper.function.type.GetFunctionArgumentTypes() + ptr_self_type, ptr_child_type, ptr_field_type, ptr_entry_type = helper.function.type.GetFunctionArgumentTypes() if ptr_self_type.GetPointeeType() == value_type: break else: return @@ -221,12 +221,44 @@ class std_MultiArrayList_SynthProvider: offset = 0 data = lldb.SBData() for field in self.entry_type.fields: - ptr_field_type = field.type - field_size = ptr_field_type.GetPointeeType().size - data.Append(self.bytes.CreateChildAtOffset(field.name, offset + index * field_size, ptr_field_type).address_of.data) + field_type = field.type.GetPointeeType() + field_size = field_type.size + data.Append(self.bytes.CreateChildAtOffset(field.name, offset + index * field_size, field_type).address_of.data) offset += self.capacity * field_size return self.bytes.CreateValueFromData('[%d]' % index, data, self.entry_type) except: return None +class std_MultiArrayList_Slice_SynthProvider: + def __init__(self, value, _=None): self.value = value + def update(self): + try: + self.len = 0 + + value_type = self.value.type + for helper in self.value.target.FindFunctions('%s.dbHelper' % value_type.name, lldb.eFunctionNameTypeFull): + ptr_self_type, ptr_child_type, ptr_field_type, ptr_entry_type = helper.function.type.GetFunctionArgumentTypes() + if ptr_self_type.GetPointeeType() == value_type: break + else: return + + self.fields = {member.name: index for index, member in enumerate(ptr_field_type.GetPointeeType().enum_members)} + self.entry_type = ptr_entry_type.GetPointeeType() + self.ptrs = self.value.GetChildMemberWithName('ptrs') + self.len = self.value.GetChildMemberWithName('len').unsigned + self.capacity = self.value.GetChildMemberWithName('capacity').unsigned + except: pass + def has_children(self): return True + def num_children(self): return self.len + def get_child_index(self, name): + try: return int(name.removeprefix('[').removesuffix(']')) + except: return -1 + def get_child_at_index(self, index): + try: + if index < 0 or index >= self.len: return None + data = lldb.SBData() + for field in self.entry_type.fields: + field_type = field.type.GetPointeeType() + data.Append(self.ptrs.child[self.fields[field.name.removesuffix('_ptr')]].CreateChildAtOffset(field.name, index * field_type.size, field_type).address_of.data) + return self.ptrs.CreateValueFromData('[%d]' % index, data, self.entry_type) + except: return None class std_HashMapUnmanaged_SynthProvider: def __init__(self, value, _=None): self.value = value @@ -556,6 +588,7 @@ def __lldb_init_module(debugger, _=None): add(debugger, category='zig.std', type='mem.Allocator', summary='${var.ptr}') add(debugger, category='zig.std', regex=True, type='^segmented_list\\.SegmentedList\\(.*\\)$', identifier='std_SegmentedList', synth=True, expand=True, summary='len=${var.len}') add(debugger, category='zig.std', regex=True, type='^multi_array_list\\.MultiArrayList\\(.*\\)$', identifier='std_MultiArrayList', synth=True, expand=True, summary='len=${var.len} capacity=${var.capacity}') + add(debugger, category='zig.std', regex=True, type='^multi_array_list\\.MultiArrayList\\(.*\\)\\.Slice$', identifier='std_MultiArrayList_Slice', synth=True, expand=True, summary='len=${var.len} capacity=${var.capacity}') add(debugger, category='zig.std', regex=True, type=MultiArrayList_Entry('.*'), identifier='std_Entry', synth=True, inline_children=True, summary=True) add(debugger, category='zig.std', regex=True, type='^hash_map\\.HashMapUnmanaged\\(.*\\)$', identifier='std_HashMapUnmanaged', synth=True, expand=True, summary=True) add(debugger, category='zig.std', regex=True, type='^hash_map\\.HashMapUnmanaged\\(.*\\)\\.Entry$', identifier = 'std_Entry', synth=True, inline_children=True, summary=True) From 27701596060c7a8018f9a9add21952a6d7f83d96 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sun, 5 Mar 2023 02:58:15 -0500 Subject: [PATCH 044/294] std: reenable vectorized code with the C backend --- lib/std/crypto/blake3.zig | 2 +- lib/std/crypto/gimli.zig | 2 +- lib/std/target.zig | 12 ++---------- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/lib/std/crypto/blake3.zig b/lib/std/crypto/blake3.zig index 5b8e21d922..36d717387f 100644 --- a/lib/std/crypto/blake3.zig +++ b/lib/std/crypto/blake3.zig @@ -200,7 +200,7 @@ const CompressGeneric = struct { } }; -const compress = if (builtin.cpu.arch == .x86_64 and builtin.zig_backend != .stage2_c) +const compress = if (builtin.cpu.arch == .x86_64) CompressVectorized.compress else CompressGeneric.compress; diff --git a/lib/std/crypto/gimli.zig b/lib/std/crypto/gimli.zig index 9443b97be7..0189f4c359 100644 --- a/lib/std/crypto/gimli.zig +++ b/lib/std/crypto/gimli.zig @@ -152,7 +152,7 @@ pub const State = struct { self.endianSwap(); } - pub const permute = if (builtin.cpu.arch == .x86_64 and builtin.zig_backend != .stage2_c) impl: { + pub const permute = if (builtin.cpu.arch == .x86_64) impl: { break :impl permute_vectorized; } else if (builtin.mode == .ReleaseSmall) impl: { break :impl permute_small; diff --git a/lib/std/target.zig b/lib/std/target.zig index 9e3a8d62f4..52f41a0563 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -719,11 +719,7 @@ pub const Target = struct { /// Adds the specified feature set but not its dependencies. pub fn addFeatureSet(set: *Set, other_set: Set) void { - if (builtin.zig_backend == .stage2_c) { - for (&set.ints, 0..) |*int, i| int.* |= other_set.ints[i]; - } else { - set.ints = @as(@Vector(usize_count, usize), set.ints) | @as(@Vector(usize_count, usize), other_set.ints); - } + set.ints = @as(@Vector(usize_count, usize), set.ints) | @as(@Vector(usize_count, usize), other_set.ints); } /// Removes the specified feature but not its dependents. @@ -735,11 +731,7 @@ pub const Target = struct { /// Removes the specified feature but not its dependents. pub fn removeFeatureSet(set: *Set, other_set: Set) void { - if (builtin.zig_backend == .stage2_c) { - for (&set.ints, 0..) |*int, i| int.* &= ~other_set.ints[i]; - } else { - set.ints = @as(@Vector(usize_count, usize), set.ints) & ~@as(@Vector(usize_count, usize), other_set.ints); - } + set.ints = @as(@Vector(usize_count, usize), set.ints) & ~@as(@Vector(usize_count, usize), other_set.ints); } pub fn populateDependencies(set: *Set, all_features_list: []const Cpu.Feature) void { From 6ab04b59417eb9c7fff81ea26424c2340dc72097 Mon Sep 17 00:00:00 2001 From: jim price Date: Sun, 5 Mar 2023 16:39:56 -0800 Subject: [PATCH 045/294] std.os: Allow write functions to return INVAL errors In Linux when interacting with the virtual file system when writing in invalid value to a file the OS will return errno 22 (INVAL). Instead of triggering an unreachable, this change now returns a newly introduced error.InvalidArgument. --- lib/std/http/Client.zig | 1 + lib/std/os.zig | 9 +++++---- src/link.zig | 1 + src/main.zig | 1 + 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/std/http/Client.zig b/lib/std/http/Client.zig index 7cf512d65f..7f62b2d597 100644 --- a/lib/std/http/Client.zig +++ b/lib/std/http/Client.zig @@ -658,6 +658,7 @@ pub const Request = struct { MissingEndCertificateMarker, InvalidPadding, EndOfStream, + InvalidArgument, }; pub fn read(req: *Request, buffer: []u8) ReadError!usize { diff --git a/lib/std/os.zig b/lib/std/os.zig index f2bd8bda97..821c544cc8 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -1027,6 +1027,7 @@ pub const WriteError = error{ InputOutput, NoSpaceLeft, DeviceBusy, + InvalidArgument, /// In WASI, this error may occur when the file descriptor does /// not hold the required rights to write to it. @@ -1113,7 +1114,7 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize { switch (errno(rc)) { .SUCCESS => return @intCast(usize, rc), .INTR => continue, - .INVAL => unreachable, + .INVAL => return error.InvalidArgument, .FAULT => unreachable, .AGAIN => return error.WouldBlock, .BADF => return error.NotOpenForWriting, // can be a race condition. @@ -1183,7 +1184,7 @@ pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!usize { switch (errno(rc)) { .SUCCESS => return @intCast(usize, rc), .INTR => continue, - .INVAL => unreachable, + .INVAL => return error.InvalidArgument, .FAULT => unreachable, .AGAIN => return error.WouldBlock, .BADF => return error.NotOpenForWriting, // Can be a race condition. @@ -1278,7 +1279,7 @@ pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize { switch (errno(rc)) { .SUCCESS => return @intCast(usize, rc), .INTR => continue, - .INVAL => unreachable, + .INVAL => return error.InvalidArgument, .FAULT => unreachable, .AGAIN => return error.WouldBlock, .BADF => return error.NotOpenForWriting, // Can be a race condition. @@ -1368,7 +1369,7 @@ pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) PWriteError!usiz switch (errno(rc)) { .SUCCESS => return @intCast(usize, rc), .INTR => continue, - .INVAL => unreachable, + .INVAL => return error.InvalidArgument, .FAULT => unreachable, .AGAIN => return error.WouldBlock, .BADF => return error.NotOpenForWriting, // Can be a race condition. diff --git a/src/link.zig b/src/link.zig index 24cc0a3861..1ecbeadd7f 100644 --- a/src/link.zig +++ b/src/link.zig @@ -461,6 +461,7 @@ pub const File = struct { LockViolation, NetNameDeleted, DeviceBusy, + InvalidArgument, }; /// Called from within the CodeGen to lower a local variable instantion as an unnamed diff --git a/src/main.zig b/src/main.zig index fb02628c61..b134b7183e 100644 --- a/src/main.zig +++ b/src/main.zig @@ -4622,6 +4622,7 @@ const FmtError = error{ ConnectionResetByPeer, LockViolation, NetNameDeleted, + InvalidArgument, } || fs.File.OpenError; fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool, dir: fs.Dir, sub_path: []const u8) FmtError!void { From ccf00ccdf71fae7ecd25d9b1bf11dc7eb52d5d1d Mon Sep 17 00:00:00 2001 From: Eric Milliken <11590808+mllken@users.noreply.github.com> Date: Mon, 6 Mar 2023 22:28:11 +0000 Subject: [PATCH 046/294] crypto.25519.field: de-inline mul for small builds (#14775) --- lib/std/crypto/25519/field.zig | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/std/crypto/25519/field.zig b/lib/std/crypto/25519/field.zig index 66a50bee70..1885f9286e 100644 --- a/lib/std/crypto/25519/field.zig +++ b/lib/std/crypto/25519/field.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const builtin = @import("builtin"); const crypto = std.crypto; const readIntLittle = std.mem.readIntLittle; const writeIntLittle = std.mem.writeIntLittle; @@ -6,6 +7,12 @@ const writeIntLittle = std.mem.writeIntLittle; const NonCanonicalError = crypto.errors.NonCanonicalError; const NotSquareError = crypto.errors.NotSquareError; +// Inline conditionally, when it can result in large code generation. +const bloaty_inline = switch (builtin.mode) { + .ReleaseSafe, .ReleaseFast => .Inline, + .Debug, .ReleaseSmall => .Unspecified, +}; + pub const Fe = struct { limbs: [5]u64, @@ -264,7 +271,7 @@ pub const Fe = struct { } /// Multiply two field elements - pub inline fn mul(a: Fe, b: Fe) Fe { + pub fn mul(a: Fe, b: Fe) callconv(bloaty_inline) Fe { var ax: [5]u128 = undefined; var bx: [5]u128 = undefined; var a19: [5]u128 = undefined; From 70cbe5ac7c96c107985e0246dacefe04ea283761 Mon Sep 17 00:00:00 2001 From: r00ster91 Date: Sun, 5 Mar 2023 17:00:09 +0100 Subject: [PATCH 047/294] AstGen: remove unnecessary `pub`s I think it helps when you know that something is entirely self-contained, which AstGen is. The only function public is `generate`. --- src/AstGen.zig | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/AstGen.zig b/src/AstGen.zig index 20f4fb6df3..f91845a1fd 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -205,7 +205,7 @@ pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir { }; } -pub fn deinit(astgen: *AstGen, gpa: Allocator) void { +fn deinit(astgen: *AstGen, gpa: Allocator) void { astgen.instructions.deinit(gpa); astgen.extra.deinit(gpa); astgen.string_table.deinit(gpa); @@ -216,7 +216,7 @@ pub fn deinit(astgen: *AstGen, gpa: Allocator) void { astgen.ref_table.deinit(gpa); } -pub const ResultInfo = struct { +const ResultInfo = struct { /// The semantics requested for the result location rl: Loc, @@ -245,7 +245,7 @@ pub const ResultInfo = struct { } } - pub const Loc = union(enum) { + const Loc = union(enum) { /// The expression is the right-hand side of assignment to `_`. Only the side-effects of the /// expression should be generated. The result instruction from the expression must /// be ignored. @@ -277,11 +277,11 @@ pub const ResultInfo = struct { src_node: ?Ast.Node.Index = null, }; - pub const Strategy = struct { + const Strategy = struct { elide_store_to_block_ptr_instructions: bool, tag: Tag, - pub const Tag = enum { + const Tag = enum { /// Both branches will use break_void; result location is used to communicate the /// result instruction. break_void, @@ -331,7 +331,7 @@ pub const ResultInfo = struct { } }; - pub const Context = enum { + const Context = enum { /// The expression is the operand to a return expression. @"return", /// The expression is the input to an error-handling operator (if-else, try, or catch). @@ -349,11 +349,11 @@ pub const ResultInfo = struct { }; }; -pub const align_ri: ResultInfo = .{ .rl = .{ .ty = .u29_type } }; -pub const coerced_align_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .u29_type } }; -pub const bool_ri: ResultInfo = .{ .rl = .{ .ty = .bool_type } }; -pub const type_ri: ResultInfo = .{ .rl = .{ .ty = .type_type } }; -pub const coerced_type_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .type_type } }; +const align_ri: ResultInfo = .{ .rl = .{ .ty = .u29_type } }; +const coerced_align_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .u29_type } }; +const bool_ri: ResultInfo = .{ .rl = .{ .ty = .bool_type } }; +const type_ri: ResultInfo = .{ .rl = .{ .ty = .type_type } }; +const coerced_type_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .type_type } }; fn typeExpr(gz: *GenZir, scope: *Scope, type_node: Ast.Node.Index) InnerError!Zir.Inst.Ref { const prev_force_comptime = gz.force_comptime; @@ -3507,7 +3507,7 @@ const WipMembers = struct { /// (4 for src_hash + line + name + value + doc_comment + align + link_section + address_space ) const max_decl_size = 11; - pub fn init(gpa: Allocator, payload: *ArrayListUnmanaged(u32), decl_count: u32, field_count: u32, comptime bits_per_field: u32, comptime max_field_size: u32) Allocator.Error!Self { + fn init(gpa: Allocator, payload: *ArrayListUnmanaged(u32), decl_count: u32, field_count: u32, comptime bits_per_field: u32, comptime max_field_size: u32) Allocator.Error!Self { const payload_top = @intCast(u32, payload.items.len); const decls_start = payload_top + (decl_count + decls_per_u32 - 1) / decls_per_u32; const field_bits_start = decls_start + decl_count * max_decl_size; @@ -3528,7 +3528,7 @@ const WipMembers = struct { }; } - pub fn nextDecl(self: *Self, is_pub: bool, is_export: bool, has_align: bool, has_section_or_addrspace: bool) void { + fn nextDecl(self: *Self, is_pub: bool, is_export: bool, has_align: bool, has_section_or_addrspace: bool) void { const index = self.payload_top + self.decl_index / decls_per_u32; assert(index < self.decls_start); const bit_bag: u32 = if (self.decl_index % decls_per_u32 == 0) 0 else self.payload.items[index]; @@ -3540,7 +3540,7 @@ const WipMembers = struct { self.decl_index += 1; } - pub fn nextField(self: *Self, comptime bits_per_field: u32, bits: [bits_per_field]bool) void { + fn nextField(self: *Self, comptime bits_per_field: u32, bits: [bits_per_field]bool) void { const fields_per_u32 = 32 / bits_per_field; const index = self.field_bits_start + self.field_index / fields_per_u32; assert(index < self.fields_start); @@ -3554,25 +3554,25 @@ const WipMembers = struct { self.field_index += 1; } - pub fn appendToDecl(self: *Self, data: u32) void { + fn appendToDecl(self: *Self, data: u32) void { assert(self.decls_end < self.field_bits_start); self.payload.items[self.decls_end] = data; self.decls_end += 1; } - pub fn appendToDeclSlice(self: *Self, data: []const u32) void { + fn appendToDeclSlice(self: *Self, data: []const u32) void { assert(self.decls_end + data.len <= self.field_bits_start); mem.copy(u32, self.payload.items[self.decls_end..], data); self.decls_end += @intCast(u32, data.len); } - pub fn appendToField(self: *Self, data: u32) void { + fn appendToField(self: *Self, data: u32) void { assert(self.fields_end < self.payload.items.len); self.payload.items[self.fields_end] = data; self.fields_end += 1; } - pub fn finishBits(self: *Self, comptime bits_per_field: u32) void { + fn finishBits(self: *Self, comptime bits_per_field: u32) void { const empty_decl_slots = decls_per_u32 - (self.decl_index % decls_per_u32); if (self.decl_index > 0 and empty_decl_slots < decls_per_u32) { const index = self.payload_top + self.decl_index / decls_per_u32; @@ -3588,15 +3588,15 @@ const WipMembers = struct { } } - pub fn declsSlice(self: *Self) []u32 { + fn declsSlice(self: *Self) []u32 { return self.payload.items[self.payload_top..self.decls_end]; } - pub fn fieldsSlice(self: *Self) []u32 { + fn fieldsSlice(self: *Self) []u32 { return self.payload.items[self.field_bits_start..self.fields_end]; } - pub fn deinit(self: *Self) void { + fn deinit(self: *Self) void { self.payload.items.len = self.payload_top; } }; @@ -10803,7 +10803,7 @@ const Scope = struct { /// ref of the capture for decls in this namespace captures: std.AutoArrayHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .{}, - pub fn deinit(self: *Namespace, gpa: Allocator) void { + fn deinit(self: *Namespace, gpa: Allocator) void { self.decls.deinit(gpa); self.captures.deinit(gpa); self.* = undefined; From 6218e4004608000ba2e42e07ed1bd56745626820 Mon Sep 17 00:00:00 2001 From: r00ster91 Date: Sun, 5 Mar 2023 17:37:56 +0100 Subject: [PATCH 048/294] Zir: fix outdated comment --- src/Zir.zig | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Zir.zig b/src/Zir.zig index b8ea2ea295..65e2f21cc9 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -2045,8 +2045,7 @@ pub const Inst = struct { /// A reference to a TypedValue or ZIR instruction. /// - /// If the Ref has a tag in this enum, it refers to a TypedValue which may be - /// retrieved with Ref.toTypedValue(). + /// If the Ref has a tag in this enum, it refers to a TypedValue. /// /// If the value of a Ref does not have a tag, it refers to a ZIR instruction. /// From c1d16a2b80e258a126ed496dab09a8a7c26f8468 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Tue, 7 Mar 2023 02:11:49 -0500 Subject: [PATCH 049/294] compiler_rt: fix rare case in udivei4 Unsigned integers are never less than zero, and so zig helpfully deleted the entire case. :D Closes #14816 --- lib/compiler_rt/udivmodei4.zig | 12 ++++++------ test/behavior/int_div.zig | 20 ++++++++++++++++++++ 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/lib/compiler_rt/udivmodei4.zig b/lib/compiler_rt/udivmodei4.zig index de2427b79f..38a9b66b78 100644 --- a/lib/compiler_rt/udivmodei4.zig +++ b/lib/compiler_rt/udivmodei4.zig @@ -79,16 +79,16 @@ fn divmod(q: ?[]u32, r: ?[]u32, u: []const u32, v: []const u32) !void { } break; } - var carry: u64 = 0; + var carry: i64 = 0; i = 0; while (i <= n) : (i += 1) { const p = qhat * limb(&vn, i); const t = limb(&un, i + j) - carry - @truncate(u32, p); - limb_set(&un, i + j, @truncate(u32, t)); - carry = @intCast(u64, p >> 32) - @intCast(u64, t >> 32); + limb_set(&un, i + j, @truncate(u32, @bitCast(u64, t))); + carry = @intCast(i64, p >> 32) - @intCast(i64, t >> 32); } - const t = limb(&un, j + n + 1) - carry; - limb_set(&un, j + n + 1, @truncate(u32, t)); + const t = limb(&un, j + n + 1) -% carry; + limb_set(&un, j + n + 1, @truncate(u32, @bitCast(u64, t))); if (q) |q_| limb_set(q_, j, @truncate(u32, qhat)); if (t < 0) { if (q) |q_| limb_set(q_, j, limb(q_, j) - 1); @@ -99,7 +99,7 @@ fn divmod(q: ?[]u32, r: ?[]u32, u: []const u32, v: []const u32) !void { limb_set(&un, i + j, @truncate(u32, t2)); carry2 = t2 >> 32; } - limb_set(un, j + n + 1, @truncate(u32, limb(&un, j + n + 1) + carry2)); + limb_set(&un, j + n + 1, @truncate(u32, limb(&un, j + n + 1) + carry2)); } if (j == 0) break; } diff --git a/test/behavior/int_div.zig b/test/behavior/int_div.zig index 6ae794d377..c8b600ba03 100644 --- a/test/behavior/int_div.zig +++ b/test/behavior/int_div.zig @@ -91,3 +91,23 @@ fn mod(comptime T: type, a: T, b: T) T { fn rem(comptime T: type, a: T, b: T) T { return @rem(a, b); } + +test "large integer division" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + + { + var numerator: u256 = 99999999999999999997315645440; + var divisor: u256 = 10000000000000000000000000000; + try expect(numerator / divisor == 9); + } + { + var numerator: u256 = 99999999999999999999000000000000000000000; + var divisor: u256 = 10000000000000000000000000000000000000000; + try expect(numerator / divisor == 9); + } +} From 77d06012c2465f7c4ac22cb4834a2535c4de6cea Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Tue, 7 Mar 2023 02:59:41 -0500 Subject: [PATCH 050/294] CBE: implement unsigned big int div and mod --- lib/zig.h | 38 +++++++++++++++++++++++++++++++++ src/codegen/c.zig | 45 ++++++++++++++++++++++----------------- test/behavior/int_div.zig | 1 - 3 files changed, 64 insertions(+), 20 deletions(-) diff --git a/lib/zig.h b/lib/zig.h index 65fb21f99a..10b5f546e0 100644 --- a/lib/zig.h +++ b/lib/zig.h @@ -2384,6 +2384,44 @@ static inline void zig_subw_big(void *res, const void *lhs, const void *rhs, boo (void)zig_subo_big(res, lhs, rhs, is_signed, bits); } +zig_extern void __udivei4(uint32_t *res, const uint32_t *lhs, const uint32_t *rhs, uintptr_t bits); +static inline void zig_div_trunc_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) { + if (!is_signed) { + __udivei4(res, lhs, rhs, bits); + return; + } + + zig_trap(); +} + +static inline void zig_div_floor_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) { + if (!is_signed) { + zig_div_trunc_big(res, lhs, rhs, is_signed, bits); + return; + } + + zig_trap(); +} + +zig_extern void __umodei4(uint32_t *res, const uint32_t *lhs, const uint32_t *rhs, uintptr_t bits); +static inline void zig_rem_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) { + if (!is_signed) { + __umodei4(res, lhs, rhs, bits); + return; + } + + zig_trap(); +} + +static inline void zig_mod_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) { + if (!is_signed) { + zig_rem_big(res, lhs, rhs, is_signed, bits); + return; + } + + zig_trap(); +} + static inline uint16_t zig_clz_big(const void *val, bool is_signed, uint16_t bits) { const uint8_t *val_bytes = val; uint16_t byte_offset = 0; diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 3d059adc15..519b2b45d5 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1885,25 +1885,28 @@ pub const DeclGen = struct { } fn renderBuiltinInfo(dg: *DeclGen, writer: anytype, ty: Type, info: BuiltinInfo) !void { + const cty = try dg.typeToCType(ty, .complete); + const is_big = cty.tag() == .array; + switch (info) { - .none => {}, - .bits => { - const target = dg.module.getTarget(); - const int_info = if (ty.isAbiInt()) ty.intInfo(target) else std.builtin.Type.Int{ - .signedness = .unsigned, - .bits = @intCast(u16, ty.bitSize(target)), - }; - - const cty = try dg.typeToCType(ty, .complete); - if (cty.tag() == .array) try writer.print(", {}", .{int_info.signedness == .signed}); - - var bits_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, .data = int_info.bits }; - try writer.print(", {}", .{try dg.fmtIntLiteral(switch (cty.tag()) { - else => Type.u8, - .array => Type.u16, - }, Value.initPayload(&bits_pl.base), .FunctionArgument)}); - }, + .none => if (!is_big) return, + .bits => {}, } + + const target = dg.module.getTarget(); + const int_info = if (ty.isAbiInt()) ty.intInfo(target) else std.builtin.Type.Int{ + .signedness = .unsigned, + .bits = @intCast(u16, ty.bitSize(target)), + }; + + if (is_big) try writer.print(", {}", .{int_info.signedness == .signed}); + + var bits_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, .data = int_info.bits }; + try writer.print(", {}", .{try dg.fmtIntLiteral( + if (is_big) Type.u16 else Type.u8, + Value.initPayload(&bits_pl.base), + .FunctionArgument, + )}); } fn fmtIntLiteral( @@ -6099,13 +6102,16 @@ fn airBinBuiltinCall( return .none; } + const operand_ty = f.air.typeOf(bin_op.lhs); + const operand_cty = try f.typeToCType(operand_ty, .complete); + const is_big = operand_cty.tag() == .array; + const lhs = try f.resolveInst(bin_op.lhs); const rhs = try f.resolveInst(bin_op.rhs); - try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); + if (!is_big) try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); const inst_ty = f.air.typeOfIndex(inst); const inst_scalar_ty = inst_ty.scalarType(); - const operand_ty = f.air.typeOf(bin_op.lhs); const scalar_ty = operand_ty.scalarType(); const inst_scalar_cty = try f.typeToCType(inst_scalar_ty, .complete); @@ -6113,6 +6119,7 @@ fn airBinBuiltinCall( const writer = f.object.writer(); const local = try f.allocLocal(inst, inst_ty); + if (is_big) try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); const v = try Vectorizer.start(f, inst, writer, operand_ty); if (!ref_ret) { try f.writeCValue(writer, local, .Other); diff --git a/test/behavior/int_div.zig b/test/behavior/int_div.zig index c8b600ba03..954f6be220 100644 --- a/test/behavior/int_div.zig +++ b/test/behavior/int_div.zig @@ -95,7 +95,6 @@ fn rem(comptime T: type, a: T, b: T) T { test "large integer division" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; From 36d47dd1991f0ccd7a9673075624f09500cc415e Mon Sep 17 00:00:00 2001 From: Frank Denis <124872+jedisct1@users.noreply.github.com> Date: Tue, 7 Mar 2023 10:04:45 +0100 Subject: [PATCH 051/294] std.crypto.hash.sha3: add TurboSHAKE (#14824) --- lib/std/crypto/benchmark.zig | 2 ++ lib/std/crypto/keccak_p.zig | 4 ++-- lib/std/crypto/sha3.zig | 33 +++++++++++++++++++++++++++++++-- 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/lib/std/crypto/benchmark.zig b/lib/std/crypto/benchmark.zig index e6e0e1fc39..47ca24aa66 100644 --- a/lib/std/crypto/benchmark.zig +++ b/lib/std/crypto/benchmark.zig @@ -27,6 +27,8 @@ const hashes = [_]Crypto{ Crypto{ .ty = crypto.hash.sha3.Sha3_512, .name = "sha3-512" }, Crypto{ .ty = crypto.hash.sha3.Shake128, .name = "shake-128" }, Crypto{ .ty = crypto.hash.sha3.Shake256, .name = "shake-256" }, + Crypto{ .ty = crypto.hash.sha3.TurboShake128(null), .name = "turboshake-128" }, + Crypto{ .ty = crypto.hash.sha3.TurboShake256(null), .name = "turboshake-256" }, Crypto{ .ty = crypto.hash.Gimli, .name = "gimli-hash" }, Crypto{ .ty = crypto.hash.blake2.Blake2s256, .name = "blake2s" }, Crypto{ .ty = crypto.hash.blake2.Blake2b512, .name = "blake2b" }, diff --git a/lib/std/crypto/keccak_p.zig b/lib/std/crypto/keccak_p.zig index 10367caffd..cef3b0cce4 100644 --- a/lib/std/crypto/keccak_p.zig +++ b/lib/std/crypto/keccak_p.zig @@ -175,12 +175,12 @@ pub fn KeccakF(comptime f: u11) type { /// Apply a (possibly) reduced-round permutation to the state. pub fn permuteR(self: *Self, comptime rounds: u5) void { var i = RC.len - rounds; - while (i < rounds - rounds % 3) : (i += 3) { + while (i < RC.len - RC.len % 3) : (i += 3) { self.round(RC[i]); self.round(RC[i + 1]); self.round(RC[i + 2]); } - while (i < rounds) : (i += 1) { + while (i < RC.len) : (i += 1) { self.round(RC[i]); } } diff --git a/lib/std/crypto/sha3.zig b/lib/std/crypto/sha3.zig index 985027f3ee..6fc4977f0b 100644 --- a/lib/std/crypto/sha3.zig +++ b/lib/std/crypto/sha3.zig @@ -18,6 +18,20 @@ pub const Keccak_512 = @compileError("Deprecated: use `Keccak512` instead"); pub const Shake128 = Shake(128); pub const Shake256 = Shake(256); +/// TurboSHAKE128 is a XOF (a secure hash function with a variable output length), with a 128 bit security level. +/// It is based on the same permutation as SHA3 and SHAKE128, but which much higher performance. +/// The delimiter is 0x01 by default, but can be changed for context-separation. +pub fn TurboShake128(comptime delim: ?u8) type { + return TurboShake(128, delim); +} + +/// TurboSHAKE256 is a XOF (a secure hash function with a variable output length), with a 256 bit security level. +/// It is based on the same permutation as SHA3 and SHAKE256, but which much higher performance. +/// The delimiter is 0x01 by default, but can be changed for context-separation. +pub fn TurboShake256(comptime delim: ?u8) type { + return TurboShake(256, delim); +} + /// A generic Keccak hash function. pub fn Keccak(comptime f: u11, comptime output_bits: u11, comptime delim: u8, comptime rounds: u5) type { comptime assert(output_bits > 0 and output_bits * 2 < f and output_bits % 8 == 0); // invalid output length @@ -76,9 +90,18 @@ pub fn Keccak(comptime f: u11, comptime output_bits: u11, comptime delim: u8, co /// The SHAKE extendable output hash function. pub fn Shake(comptime security_level: u11) type { + return ShakeLike(security_level, 0x1f, 24); +} + +/// The TurboSHAKE extendable output hash function. +/// https://datatracker.ietf.org/doc/draft-irtf-cfrg-kangarootwelve/ +pub fn TurboShake(comptime security_level: u11, comptime delim: ?u8) type { + return ShakeLike(security_level, delim orelse 0x01, 12); +} + +fn ShakeLike(comptime security_level: u11, comptime delim: u8, comptime rounds: u5) type { const f = 1600; - const rounds = 24; - const State = KeccakState(f, security_level * 2, 0x1f, rounds); + const State = KeccakState(f, security_level * 2, delim, rounds); return struct { const Self = @This(); @@ -348,3 +371,9 @@ test "SHAKE-256 single" { Shake256.hash("hello123", &out, .{}); try htest.assertEqual("ade612ba265f92de4a37", &out); } + +test "TurboSHAKE-128" { + var out: [32]u8 = undefined; + TurboShake(128, 0x06).hash("\xff", &out, .{}); + try htest.assertEqual("8ec9c66465ed0d4a6c35d13506718d687a25cb05c74cca1e42501abd83874a67", &out); +} From e33dfc300e144dcf3323f96b38d8c67163259bb1 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Tue, 7 Mar 2023 12:25:22 -0500 Subject: [PATCH 052/294] zig.h: implement zig_breakpoint and zig_trap for more targets --- lib/zig.h | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/lib/zig.h b/lib/zig.h index 10b5f546e0..59c3ddd695 100644 --- a/lib/zig.h +++ b/lib/zig.h @@ -190,10 +190,17 @@ typedef char bool; #if zig_has_builtin(trap) #define zig_trap() __builtin_trap() +#elif _MSC_VER && (_M_IX86 || _M_X64) +#define zig_trap() __ud2() +#elif _MSC_VER +#define zig_trap() __fastfail(0) #elif defined(__i386__) || defined(__x86_64__) #define zig_trap() __asm__ volatile("ud2"); +#elif defined(__arm__) || defined(__aarch64__) +#define zig_breakpoint() __asm__ volatile("udf #0"); #else -#define zig_trap() raise(SIGTRAP) +#include +#define zig_trap() abort() #endif #if zig_has_builtin(debugtrap) @@ -202,8 +209,17 @@ typedef char bool; #define zig_breakpoint() __debugbreak() #elif defined(__i386__) || defined(__x86_64__) #define zig_breakpoint() __asm__ volatile("int $0x03"); +#elif defined(__arm__) +#define zig_breakpoint() __asm__ volatile("bkpt #0"); +#elif defined(__aarch64__) +#define zig_breakpoint() __asm__ volatile("brk #0"); #else +#include +#if defined(SIGTRAP) #define zig_breakpoint() raise(SIGTRAP) +#else +#define zig_breakpoint() zig_breakpoint_unavailable +#endif #endif #if zig_has_builtin(return_address) || defined(zig_gnuc) From 8da6b393fb6f34ba8cbe3efaf1dac4d459c05d5b Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Tue, 7 Mar 2023 12:19:38 +0100 Subject: [PATCH 053/294] std.fmt: add bytesToHex() to encode bytes as hex digits We already had `hexToBytes()`, but not the reverse operation (at least not without using formatters). --- lib/std/fmt.zig | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index 0da25fde78..8167a2b252 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -2555,6 +2555,21 @@ test "bytes.hex" { try expectFmt("lowercase: 000ebabe\n", "lowercase: {x}\n", .{fmtSliceHexLower(bytes_with_zeros)}); } +/// Encodes a sequence of bytes as hexadecimal digits. +/// Returns an array containing the encoded bytes. +pub fn bytesToHex(input: anytype, case: Case) [input.len * 2]u8 { + if (input.len == 0) return [_]u8{}; + comptime assert(@TypeOf(input[0]) == u8); // elements to encode must be unsigned bytes + + const charset = "0123456789" ++ if (case == .upper) "ABCDEF" else "abcdef"; + var result: [input.len * 2]u8 = undefined; + for (input, 0..) |b, i| { + result[i * 2 + 0] = charset[b >> 4]; + result[i * 2 + 1] = charset[b & 15]; + } + return result; +} + /// Decodes the sequence of bytes represented by the specified string of /// hexadecimal characters. /// Returns a slice of the output buffer containing the decoded bytes. @@ -2575,6 +2590,13 @@ pub fn hexToBytes(out: []u8, input: []const u8) ![]u8 { return out[0 .. in_i / 2]; } +test "bytesToHex" { + const input = "input slice"; + const encoded = bytesToHex(input, .lower); + var decoded: [input.len]u8 = undefined; + try std.testing.expectEqualSlices(u8, input, try hexToBytes(&decoded, &encoded)); +} + test "hexToBytes" { var buf: [32]u8 = undefined; try expectFmt("90" ** 32, "{s}", .{fmtSliceHexUpper(try hexToBytes(&buf, "90" ** 32))}); From bbba701a41443cdca454e65e58e5dda2f4d3188a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan?= Date: Tue, 7 Mar 2023 14:59:12 +0100 Subject: [PATCH 054/294] std.os.windows.advapi32: Add RegCloseKey --- lib/std/os/windows/advapi32.zig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/std/os/windows/advapi32.zig b/lib/std/os/windows/advapi32.zig index 67234a26e0..bace7ce850 100644 --- a/lib/std/os/windows/advapi32.zig +++ b/lib/std/os/windows/advapi32.zig @@ -27,6 +27,8 @@ pub extern "advapi32" fn RegQueryValueExW( lpcbData: ?*DWORD, ) callconv(WINAPI) LSTATUS; +pub extern "advapi32" fn RegCloseKey(hKey: HKEY) callconv(WINAPI) LSTATUS; + // RtlGenRandom is known as SystemFunction036 under advapi32 // http://msdn.microsoft.com/en-us/library/windows/desktop/aa387694.aspx */ pub extern "advapi32" fn SystemFunction036(output: [*]u8, length: ULONG) callconv(WINAPI) BOOL; From e3cf9d165081533524927cebf8a92ac6fee097f2 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Mon, 6 Mar 2023 17:22:23 -0500 Subject: [PATCH 055/294] Module: rewrite zir caching logic Multiple processes can sit waiting for the exclusive lock at the same time, so we want to recheck whether it needs to be updated whenever we get an exclusive lock. This also fixes a race condition between one process truncating the cache file and another process reading it without atomic locking. --- src/Module.zig | 123 ++++++++++++++++++++++++------------------------- stage1/wasi.c | 2 - 2 files changed, 60 insertions(+), 65 deletions(-) diff --git a/src/Module.zig b/src/Module.zig index 7ea69a0a2e..f3f1aa44e2 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -3538,44 +3538,61 @@ pub fn astGenFile(mod: *Module, file: *File) !void { const cache_directory = if (want_local_cache) mod.local_zir_cache else mod.global_zir_cache; const zir_dir = cache_directory.handle; - var cache_file: ?std.fs.File = null; - defer if (cache_file) |f| f.close(); - // Determine whether we need to reload the file from disk and redo parsing and AstGen. - switch (file.status) { - .never_loaded, .retryable_failure => cached: { + var lock: std.fs.File.Lock = switch (file.status) { + .never_loaded, .retryable_failure => lock: { // First, load the cached ZIR code, if any. log.debug("AstGen checking cache: {s} (local={}, digest={s})", .{ file.sub_file_path, want_local_cache, &digest, }); - // We ask for a lock in order to coordinate with other zig processes. - // If another process is already working on this file, we will get the cached - // version. Likewise if we're working on AstGen and another process asks for - // the cached file, they'll get it. - cache_file = zir_dir.openFile(&digest, .{ .lock = .Shared }) catch |err| switch (err) { - error.PathAlreadyExists => unreachable, // opening for reading - error.NoSpaceLeft => unreachable, // opening for reading - error.NotDir => unreachable, // no dir components - error.InvalidUtf8 => unreachable, // it's a hex encoded name - error.BadPathName => unreachable, // it's a hex encoded name - error.NameTooLong => unreachable, // it's a fixed size name - error.PipeBusy => unreachable, // it's not a pipe - error.WouldBlock => unreachable, // not asking for non-blocking I/O + break :lock .Shared; + }, + .parse_failure, .astgen_failure, .success_zir => lock: { + const unchanged_metadata = + stat.size == file.stat.size and + stat.mtime == file.stat.mtime and + stat.inode == file.stat.inode; - error.SymLinkLoop, - error.FileNotFound, - error.Unexpected, - => break :cached, + if (unchanged_metadata) { + log.debug("unmodified metadata of file: {s}", .{file.sub_file_path}); + return; + } - else => |e| return e, // Retryable errors are handled at callsite. - }; + log.debug("metadata changed: {s}", .{file.sub_file_path}); + break :lock .Exclusive; + }, + }; + + // We ask for a lock in order to coordinate with other zig processes. + // If another process is already working on this file, we will get the cached + // version. Likewise if we're working on AstGen and another process asks for + // the cached file, they'll get it. + const cache_file = zir_dir.createFile(&digest, .{ + .read = true, + .truncate = false, + .lock = lock, + }) catch |err| switch (err) { + error.NotDir => unreachable, // no dir components + error.InvalidUtf8 => unreachable, // it's a hex encoded name + error.BadPathName => unreachable, // it's a hex encoded name + error.NameTooLong => unreachable, // it's a fixed size name + error.PipeBusy => unreachable, // it's not a pipe + error.WouldBlock => unreachable, // not asking for non-blocking I/O + error.FileNotFound => unreachable, // no dir components + + else => |e| return e, // Retryable errors are handled at callsite. + }; + defer cache_file.close(); + + while (true) { + update: { // First we read the header to determine the lengths of arrays. - const header = cache_file.?.reader().readStruct(Zir.Header) catch |err| switch (err) { + const header = cache_file.reader().readStruct(Zir.Header) catch |err| switch (err) { // This can happen if Zig bails out of this function between creating // the cached file and writing it. - error.EndOfStream => break :cached, + error.EndOfStream => break :update, else => |e| return e, }; const unchanged_metadata = @@ -3585,7 +3602,7 @@ pub fn astGenFile(mod: *Module, file: *File) !void { if (!unchanged_metadata) { log.debug("AstGen cache stale: {s}", .{file.sub_file_path}); - break :cached; + break :update; } log.debug("AstGen cache hit: {s} instructions_len={d}", .{ file.sub_file_path, header.instructions_len, @@ -3637,13 +3654,13 @@ pub fn astGenFile(mod: *Module, file: *File) !void { .iov_len = header.extra_len * 4, }, }; - const amt_read = try cache_file.?.readvAll(&iovecs); + const amt_read = try cache_file.readvAll(&iovecs); const amt_expected = zir.instructions.len * 9 + zir.string_bytes.len + zir.extra.len * 4; if (amt_read != amt_expected) { log.warn("unexpected EOF reading cached ZIR for {s}", .{file.sub_file_path}); - break :cached; + break :update; } if (data_has_safety_tag) { const tags = zir.instructions.items(.tag); @@ -3679,42 +3696,22 @@ pub fn astGenFile(mod: *Module, file: *File) !void { return error.AnalysisFail; } return; - }, - .parse_failure, .astgen_failure, .success_zir => { - const unchanged_metadata = - stat.size == file.stat.size and - stat.mtime == file.stat.mtime and - stat.inode == file.stat.inode; + } - if (unchanged_metadata) { - log.debug("unmodified metadata of file: {s}", .{file.sub_file_path}); - return; - } - - log.debug("metadata changed: {s}", .{file.sub_file_path}); - }, + // If we already have the exclusive lock then it is our job to update. + if (builtin.os.tag == .wasi or lock == .Exclusive) break; + // Otherwise, unlock to give someone a chance to get the exclusive lock + // and then upgrade to an exclusive lock. + cache_file.unlock(); + lock = .Exclusive; + try cache_file.lock(lock); } - if (cache_file) |f| { - f.close(); - cache_file = null; - } - cache_file = zir_dir.createFile(&digest, .{ .lock = .Exclusive }) catch |err| switch (err) { - error.NotDir => unreachable, // no dir components - error.InvalidUtf8 => unreachable, // it's a hex encoded name - error.BadPathName => unreachable, // it's a hex encoded name - error.NameTooLong => unreachable, // it's a fixed size name - error.PipeBusy => unreachable, // it's not a pipe - error.WouldBlock => unreachable, // not asking for non-blocking I/O - error.FileNotFound => unreachable, // no dir components - else => |e| { - const pkg_path = file.pkg.root_src_directory.path orelse "."; - const cache_path = cache_directory.path orelse "."; - log.warn("unable to save cached ZIR code for {s}/{s} to {s}/{s}: {s}", .{ - pkg_path, file.sub_file_path, cache_path, &digest, @errorName(e), - }); - return; - }, + // The cache is definitely stale so delete the contents to avoid an underwrite later. + cache_file.setEndPos(0) catch |err| switch (err) { + error.FileTooBig => unreachable, // 0 is not too big + + else => |e| return e, }; mod.lockAndClearFileCompileError(file); @@ -3871,7 +3868,7 @@ pub fn astGenFile(mod: *Module, file: *File) !void { .iov_len = file.zir.extra.len * 4, }, }; - cache_file.?.writevAll(&iovecs) catch |err| { + cache_file.writevAll(&iovecs) catch |err| { const pkg_path = file.pkg.root_src_directory.path orelse "."; const cache_path = cache_directory.path orelse "."; log.warn("unable to write cached ZIR code for {s}/{s} to {s}/{s}: {s}", .{ diff --git a/stage1/wasi.c b/stage1/wasi.c index 911ce6e520..6c4ac48a50 100644 --- a/stage1/wasi.c +++ b/stage1/wasi.c @@ -497,8 +497,6 @@ uint32_t wasi_snapshot_preview1_fd_read(uint32_t fd, uint32_t iovs, uint32_t iov size_t read_size = 0; if (fds[fd].stream != NULL) read_size = fread(&m[iovs_ptr[i].ptr], 1, iovs_ptr[i].len, fds[fd].stream); - else - panic("unimplemented"); size += read_size; if (read_size < iovs_ptr[i].len) break; } From fea14c78d1374af909a2f11e37fe057777f3985d Mon Sep 17 00:00:00 2001 From: Frank Denis <124872+jedisct1@users.noreply.github.com> Date: Wed, 8 Mar 2023 14:50:06 +0100 Subject: [PATCH 056/294] wasm-linker: emit build_id section (#14820) The Build ID is a value that uniquely identifies a build. It is intended to capture the "meaning" or inputs of the build, and is usually associated with debug info. Reference: https://github.com/WebAssembly/tool-conventions/blob/main/BuildId.md --- src/link/Wasm.zig | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 513b567210..b5c9ffa991 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -2635,6 +2635,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l try man.addOptionalFile(compiler_rt_path); man.hash.addOptionalBytes(options.entry); man.hash.addOptional(options.stack_size_override); + man.hash.add(wasm.base.options.build_id); man.hash.add(options.import_memory); man.hash.add(options.import_table); man.hash.add(options.export_table); @@ -3225,6 +3226,12 @@ fn writeToFile( } if (!wasm.base.options.strip) { + // The build id must be computed on the main sections only, + // so we have to do it now, before the debug sections. + if (wasm.base.options.build_id) { + try emitBuildIdSection(&binary_bytes); + } + // if (wasm.dwarf) |*dwarf| { // const mod = wasm.base.options.module.?; // try dwarf.writeDbgAbbrev(); @@ -3363,6 +3370,33 @@ fn emitProducerSection(binary_bytes: *std.ArrayList(u8)) !void { ); } +fn emitBuildIdSection(binary_bytes: *std.ArrayList(u8)) !void { + const header_offset = try reserveCustomSectionHeader(binary_bytes); + + const writer = binary_bytes.writer(); + const build_id = "build_id"; + try leb.writeULEB128(writer, @intCast(u32, build_id.len)); + try writer.writeAll(build_id); + + var id: [16]u8 = undefined; + std.crypto.hash.sha3.TurboShake128(null).hash(binary_bytes.items, &id, .{}); + var uuid: [36]u8 = undefined; + _ = try std.fmt.bufPrint(&uuid, "{s}-{s}-{s}-{s}-{s}", .{ + std.fmt.fmtSliceHexLower(id[0..4]), std.fmt.fmtSliceHexLower(id[4..6]), std.fmt.fmtSliceHexLower(id[6..8]), + std.fmt.fmtSliceHexLower(id[8..10]), std.fmt.fmtSliceHexLower(id[10..]), + }); + + try leb.writeULEB128(writer, @as(u32, 1)); + try leb.writeULEB128(writer, @as(u32, uuid.len)); + try writer.writeAll(&uuid); + + try writeCustomSectionHeader( + binary_bytes.items, + header_offset, + @intCast(u32, binary_bytes.items.len - header_offset - 6), + ); +} + fn emitFeaturesSection(binary_bytes: *std.ArrayList(u8), enabled_features: []const bool, features_count: u32) !void { const header_offset = try reserveCustomSectionHeader(binary_bytes); @@ -3594,6 +3628,7 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! try man.addOptionalFile(compiler_rt_path); man.hash.addOptionalBytes(wasm.base.options.entry); man.hash.addOptional(wasm.base.options.stack_size_override); + man.hash.add(wasm.base.options.build_id); man.hash.add(wasm.base.options.import_memory); man.hash.add(wasm.base.options.import_table); man.hash.add(wasm.base.options.export_table); @@ -3760,6 +3795,12 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! if (wasm.base.options.import_symbols) { try argv.append("--allow-undefined"); } + + // XXX - TODO: add when wasm-ld supports --build-id. + // if (wasm.base.options.build_id) { + // try argv.append("--build-id=tree"); + // } + try argv.appendSlice(&.{ "-o", full_out_path }); if (target.cpu.arch == .wasm64) { From ecc0108cea97772b6e921b36d8fdc8f90d5fc6cb Mon Sep 17 00:00:00 2001 From: John Schmidt Date: Wed, 1 Mar 2023 21:44:11 +0100 Subject: [PATCH 057/294] astgen: fill result location with `void` value if no other value With this change, `break` and `break :blk` will fill the result location with `.void_value`, ensuring that the value will be type checked. The same will happen for a for loop that contains no `break`s in it's body. Closes https://github.com/ziglang/zig/issues/14686. --- src/AstGen.zig | 17 ++++------ .../break_void_result_location.zig | 32 +++++++++++++++++++ 2 files changed, 38 insertions(+), 11 deletions(-) create mode 100644 test/cases/compile_errors/break_void_result_location.zig diff --git a/src/AstGen.zig b/src/AstGen.zig index f91845a1fd..7b2138a535 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -1960,7 +1960,10 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn else .@"break"; + block_gz.break_count += 1; if (rhs == 0) { + _ = try rvalue(parent_gz, block_gz.break_result_info, .void_value, node); + try genDefers(parent_gz, scope, parent_scope, .normal_only); // As our last action before the break, "pop" the error trace if needed @@ -1970,7 +1973,6 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn _ = try parent_gz.addBreak(break_tag, block_inst, .void_value); return Zir.Inst.Ref.unreachable_value; } - block_gz.break_count += 1; const operand = try reachableExpr(parent_gz, parent_scope, block_gz.break_result_info, rhs, node); const search_index = @intCast(Zir.Inst.Index, astgen.instructions.len); @@ -6584,6 +6586,9 @@ fn forExpr( cond_block, break_tag, ); + if (ri.rl.strategy(&loop_scope).tag == .break_void and loop_scope.break_count == 0) { + _ = try rvalue(parent_gz, ri, .void_value, node); + } if (is_statement) { _ = try parent_gz.addUnNode(.ensure_result_used, result, node); } @@ -8525,16 +8530,6 @@ fn builtinCall( } } -fn simpleNoOpVoid( - gz: *GenZir, - ri: ResultInfo, - node: Ast.Node.Index, - tag: Zir.Inst.Tag, -) InnerError!Zir.Inst.Ref { - _ = try gz.addNode(tag, node); - return rvalue(gz, ri, .void_value, node); -} - fn hasDeclOrField( gz: *GenZir, scope: *Scope, diff --git a/test/cases/compile_errors/break_void_result_location.zig b/test/cases/compile_errors/break_void_result_location.zig new file mode 100644 index 0000000000..696ea39667 --- /dev/null +++ b/test/cases/compile_errors/break_void_result_location.zig @@ -0,0 +1,32 @@ +export fn f1() void { + const x: usize = for ("hello") |_| {}; + _ = x; +} +export fn f2() void { + const x: usize = for ("hello") |_| { + break; + }; + _ = x; +} +export fn f3() void { + var t: bool = true; + const x: usize = while (t) { + break; + }; + _ = x; +} +export fn f4() void { + const x: usize = blk: { + break :blk; + }; + _ = x; +} + +// error +// backend=stage2 +// target=native +// +// :2:22: error: expected type 'usize', found 'void' +// :7:9: error: expected type 'usize', found 'void' +// :14:9: error: expected type 'usize', found 'void' +// :18:1: error: expected type 'usize', found 'void' From 06b263825a67e68cec128c640a6287fa1716dc63 Mon Sep 17 00:00:00 2001 From: Jan Philipp Hafer Date: Sun, 5 Mar 2023 18:41:52 +0100 Subject: [PATCH 058/294] std.os: add missing mmap errors Man page for posix lists EMFILE, man page for linux ENFILE. Also posix says "The mmap() function adds an extra reference to the file associated with the file descriptor fildes which is not removed by a subsequent close() on that file descriptor. This reference is removed when there are no more mappings to the file." It sounds counter-intuitive, that a process limit but no system limit can be exceeeded. As far as I understand, fildes is only used for file descriptor backed mmaps. --- lib/std/Thread.zig | 3 +++ lib/std/os.zig | 41 +++++++++++++++++++++++------------------ 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/lib/std/Thread.zig b/lib/std/Thread.zig index 8004f94d7f..27f7fa5030 100644 --- a/lib/std/Thread.zig +++ b/lib/std/Thread.zig @@ -945,6 +945,7 @@ const LinuxThreadImpl = struct { // map all memory needed without read/write permissions // to avoid committing the whole region right away + // anonymous mapping ensures file descriptor limits are not exceeded const mapped = os.mmap( null, map_bytes, @@ -956,6 +957,8 @@ const LinuxThreadImpl = struct { error.MemoryMappingNotSupported => unreachable, error.AccessDenied => unreachable, error.PermissionDenied => unreachable, + error.ProcessFdQuotaExceeded => unreachable, + error.SystemFdQuotaExceeded => unreachable, else => |e| return e, }; assert(mapped.len >= map_bytes); diff --git a/lib/std/os.zig b/lib/std/os.zig index 821c544cc8..6c680e9a38 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -253,6 +253,25 @@ pub var argv: [][*:0]u8 = if (builtin.link_libc) undefined else switch (builtin. else => undefined, }; +pub const have_sigpipe_support = @hasDecl(@This(), "SIG") and @hasDecl(SIG, "PIPE"); + +fn noopSigHandler(_: c_int) callconv(.C) void {} + +/// On default executed by posix startup code before main(), if SIGPIPE is supported. +pub fn maybeIgnoreSigpipe() void { + if (have_sigpipe_support and !std.options.keep_sigpipe) { + const act = Sigaction{ + // We set handler to a noop function instead of SIG.IGN so we don't leak our + // signal disposition to a child process + .handler = .{ .handler = noopSigHandler }, + .mask = empty_sigset, + .flags = 0, + }; + sigaction(SIG.PIPE, &act, null) catch |err| + std.debug.panic("failed to install noop SIGPIPE handler with '{s}'", .{@errorName(err)}); + } +} + /// To obtain errno, call this function with the return value of the /// system function call. For some systems this will obtain the value directly /// from the return code; for others it will use a thread-local errno variable. @@ -4306,6 +4325,8 @@ pub const MMapError = error{ /// a filesystem that was mounted no-exec. PermissionDenied, LockedMemoryLimitExceeded, + ProcessFdQuotaExceeded, + SystemFdQuotaExceeded, OutOfMemory, } || UnexpectedError; @@ -4347,6 +4368,8 @@ pub fn mmap( .OVERFLOW => unreachable, // The number of pages used for length + offset would overflow. .NODEV => return error.MemoryMappingNotSupported, .INVAL => unreachable, // Invalid parameters to mmap() + .MFILE => return error.ProcessFdQuotaExceeded, + .NFILE => return error.SystemFdQuotaExceeded, .NOMEM => return error.OutOfMemory, else => return unexpectedErrno(err), } @@ -7081,21 +7104,3 @@ pub fn timerfd_gettime(fd: i32) TimerFdGetError!linux.itimerspec { else => |err| return unexpectedErrno(err), }; } - -pub const have_sigpipe_support = @hasDecl(@This(), "SIG") and @hasDecl(SIG, "PIPE"); - -fn noopSigHandler(_: c_int) callconv(.C) void {} - -pub fn maybeIgnoreSigpipe() void { - if (have_sigpipe_support and !std.options.keep_sigpipe) { - const act = Sigaction{ - // We set handler to a noop function instead of SIG.IGN so we don't leak our - // signal disposition to a child process - .handler = .{ .handler = noopSigHandler }, - .mask = empty_sigset, - .flags = 0, - }; - sigaction(SIG.PIPE, &act, null) catch |err| - std.debug.panic("failed to install noop SIGPIPE handler with '{s}'", .{@errorName(err)}); - } -} From 3e99afdbfe9a66bc5461148a4754c1e88b33fb88 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Wed, 8 Mar 2023 14:21:33 +0100 Subject: [PATCH 059/294] build: add -Dpie option It is becoming increasingly common for distributions to want to enable PIE for all binaries and zig currently does not provide any way to do so aside from patching the build.zig. --- CMakeLists.txt | 7 +++++++ build.zig | 2 ++ 2 files changed, 9 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 925fd2d639..501d12889f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -827,6 +827,12 @@ else() set(ZIG_STATIC_ARG "") endif() +if(CMAKE_POSITION_INDEPENDENT_CODE) + set(ZIG_PIE_ARG="-Dpie") +else() + set(ZIG_PIE_ARG="") +endif() + set(ZIG_BUILD_ARGS --zig-lib-dir "${CMAKE_SOURCE_DIR}/lib" "-Dconfig_h=${ZIG_CONFIG_H_OUT}" @@ -835,6 +841,7 @@ set(ZIG_BUILD_ARGS ${ZIG_STATIC_ARG} ${ZIG_NO_LIB_ARG} ${ZIG_SINGLE_THREADED_ARG} + ${ZIG_PIE_ARG} "-Dtarget=${ZIG_TARGET_TRIPLE}" "-Dcpu=${ZIG_TARGET_MCPU}" "-Dversion-string=${RESOLVED_ZIG_VERSION}" diff --git a/build.zig b/build.zig index a95c9dfb58..12e5d014e2 100644 --- a/build.zig +++ b/build.zig @@ -152,6 +152,7 @@ pub fn build(b: *std.Build) !void { const link_libc = b.option(bool, "force-link-libc", "Force self-hosted compiler to link libc") orelse (enable_llvm or only_c); const sanitize_thread = b.option(bool, "sanitize-thread", "Enable thread-sanitization") orelse false; const strip = b.option(bool, "strip", "Omit debug information"); + const pie = b.option(bool, "pie", "Produce a Position Independent Executable"); const value_tracing = b.option(bool, "value-tracing", "Enable extra state tracking to help troubleshoot bugs in the compiler (using the std.debug.Trace API)") orelse false; const mem_leak_frames: u32 = b.option(u32, "mem-leak-frames", "How many stack frames to print when a memory leak occurs. Tests get 2x this amount.") orelse blk: { @@ -162,6 +163,7 @@ pub fn build(b: *std.Build) !void { const exe = addCompilerStep(b, optimize, target); exe.strip = strip; + exe.pie = pie; exe.sanitize_thread = sanitize_thread; exe.build_id = b.option(bool, "build-id", "Include a build id note") orelse false; exe.install(); From 6d7fb8f19c864f04d3472e5aec161957193e1e7c Mon Sep 17 00:00:00 2001 From: mlugg Date: Thu, 23 Feb 2023 14:07:06 +0000 Subject: [PATCH 060/294] Sema: check type of comptime try operand Resolves: #14693 --- src/Sema.zig | 7 ++++++- .../cases/compile_errors/comptime_try_non_error.zig | 13 +++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 test/cases/compile_errors/comptime_try_non_error.zig diff --git a/src/Sema.zig b/src/Sema.zig index 8c6e3cf05c..840e8a4c6c 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1612,6 +1612,12 @@ fn analyzeBodyInner( const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index); const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len]; const err_union = try sema.resolveInst(extra.data.operand); + const err_union_ty = sema.typeOf(err_union); + if (err_union_ty.zigTypeTag() != .ErrorUnion) { + return sema.fail(block, operand_src, "expected error union type, found '{}'", .{ + err_union_ty.fmt(sema.mod), + }); + } const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union); assert(is_non_err != .none); const is_non_err_tv = sema.resolveInstConst(block, operand_src, is_non_err, "try operand inside comptime block must be comptime-known") catch |err| { @@ -1619,7 +1625,6 @@ fn analyzeBodyInner( return err; }; if (is_non_err_tv.val.toBool()) { - const err_union_ty = sema.typeOf(err_union); break :blk try sema.analyzeErrUnionPayload(block, src, err_union_ty, err_union, operand_src, false); } const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse diff --git a/test/cases/compile_errors/comptime_try_non_error.zig b/test/cases/compile_errors/comptime_try_non_error.zig new file mode 100644 index 0000000000..9b342f7934 --- /dev/null +++ b/test/cases/compile_errors/comptime_try_non_error.zig @@ -0,0 +1,13 @@ +export fn foo() void { + try bar(); +} + +pub fn bar() u8 { + return 0; +} + +// error +// backend=stage2 +// target=native +// +// :2:12: error: expected error union type, found 'u8' From 134e5748e0b698d51b6daf1983d3aeb1affc142f Mon Sep 17 00:00:00 2001 From: Frank Denis <124872+jedisct1@users.noreply.github.com> Date: Thu, 9 Mar 2023 06:18:15 +0100 Subject: [PATCH 061/294] Fix incorrect SHA-3 computation with the streaming API (#14852) * Fix SHA3 with streaming Leftover bytes should be added to the buffer, not to the state. (or, always to the state; we can and probably should eventually get rid of the buffer) Fixes #14851 * Add a test for SHA-3 with streaming --- lib/std/crypto/keccak_p.zig | 2 +- lib/std/crypto/sha3.zig | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/std/crypto/keccak_p.zig b/lib/std/crypto/keccak_p.zig index cef3b0cce4..af7c12c8c2 100644 --- a/lib/std/crypto/keccak_p.zig +++ b/lib/std/crypto/keccak_p.zig @@ -231,7 +231,7 @@ pub fn State(comptime f: u11, comptime capacity: u11, comptime delim: u8, compti bytes = bytes[rate..]; } if (bytes.len > 0) { - self.st.addBytes(bytes[0..]); + mem.copy(u8, &self.buf, bytes); self.offset = bytes.len; } } diff --git a/lib/std/crypto/sha3.zig b/lib/std/crypto/sha3.zig index 6fc4977f0b..d286e40666 100644 --- a/lib/std/crypto/sha3.zig +++ b/lib/std/crypto/sha3.zig @@ -377,3 +377,17 @@ test "TurboSHAKE-128" { TurboShake(128, 0x06).hash("\xff", &out, .{}); try htest.assertEqual("8ec9c66465ed0d4a6c35d13506718d687a25cb05c74cca1e42501abd83874a67", &out); } + +test "SHA-3 with streaming" { + var msg: [613]u8 = [613]u8{ 0x97, 0xd1, 0x2d, 0x1a, 0x16, 0x2d, 0x36, 0x4d, 0x20, 0x62, 0x19, 0x0b, 0x14, 0x93, 0xbb, 0xf8, 0x5b, 0xea, 0x04, 0xc2, 0x61, 0x8e, 0xd6, 0x08, 0x81, 0xa1, 0x1d, 0x73, 0x27, 0x48, 0xbf, 0xa4, 0xba, 0xb1, 0x9a, 0x48, 0x9c, 0xf9, 0x9b, 0xff, 0x34, 0x48, 0xa9, 0x75, 0xea, 0xc8, 0xa3, 0x48, 0x24, 0x9d, 0x75, 0x27, 0x48, 0xec, 0x03, 0xb0, 0xbb, 0xdf, 0x33, 0x90, 0xe3, 0x93, 0xed, 0x68, 0x24, 0x39, 0x12, 0xdf, 0xea, 0xee, 0x8c, 0x9f, 0x96, 0xde, 0x42, 0x46, 0x8c, 0x2b, 0x17, 0x83, 0x36, 0xfb, 0xf4, 0xf7, 0xff, 0x79, 0xb9, 0x45, 0x41, 0xc9, 0x56, 0x1a, 0x6b, 0x0c, 0xa4, 0x1a, 0xdd, 0x6b, 0x95, 0xe8, 0x03, 0x0f, 0x09, 0x29, 0x40, 0x1b, 0xea, 0x87, 0xfa, 0xb9, 0x18, 0xa9, 0x95, 0x07, 0x7c, 0x2f, 0x7c, 0x33, 0xfb, 0xc5, 0x11, 0x5e, 0x81, 0x0e, 0xbc, 0xae, 0xec, 0xb3, 0xe1, 0x4a, 0x26, 0x56, 0xe8, 0x5b, 0x11, 0x9d, 0x37, 0x06, 0x9b, 0x34, 0x31, 0x6e, 0xa3, 0xba, 0x41, 0xbc, 0x11, 0xd8, 0xc5, 0x15, 0xc9, 0x30, 0x2c, 0x9b, 0xb6, 0x71, 0xd8, 0x7c, 0xbc, 0x38, 0x2f, 0xd5, 0xbd, 0x30, 0x96, 0xd4, 0xa3, 0x00, 0x77, 0x9d, 0x55, 0x4a, 0x33, 0x53, 0xb6, 0xb3, 0x35, 0x1b, 0xae, 0xe5, 0xdc, 0x22, 0x23, 0x85, 0x95, 0x88, 0xf9, 0x3b, 0xbf, 0x74, 0x13, 0xaa, 0xcb, 0x0a, 0x60, 0x79, 0x13, 0x79, 0xc0, 0x4a, 0x02, 0xdb, 0x1c, 0xc9, 0xff, 0x60, 0x57, 0x9a, 0x70, 0x28, 0x58, 0x60, 0xbc, 0x57, 0x07, 0xc7, 0x47, 0x1a, 0x45, 0x71, 0x76, 0x94, 0xfb, 0x05, 0xad, 0xec, 0x12, 0x29, 0x5a, 0x44, 0x6a, 0x81, 0xd9, 0xc6, 0xf0, 0xb6, 0x9b, 0x97, 0x83, 0x69, 0xfb, 0xdc, 0x0d, 0x4a, 0x67, 0xbc, 0x72, 0xf5, 0x43, 0x5e, 0x9b, 0x13, 0xf2, 0xe4, 0x6d, 0x49, 0xdb, 0x76, 0xcb, 0x42, 0x6a, 0x3c, 0x9f, 0xa1, 0xfe, 0x5e, 0xca, 0x0a, 0xfc, 0xfa, 0x39, 0x27, 0xd1, 0x3c, 0xcb, 0x9a, 0xde, 0x4c, 0x6b, 0x09, 0x8b, 0x49, 0xfd, 0x1e, 0x3d, 0x5e, 0x67, 0x7c, 0x57, 0xad, 0x90, 0xcc, 0x46, 0x5f, 0x5c, 0xae, 0x6a, 0x9c, 0xb2, 0xcd, 0x2c, 0x89, 0x78, 0xcf, 0xf1, 0x49, 0x96, 0x55, 0x1e, 0x04, 0xef, 0x0e, 0x1c, 0xde, 0x6c, 0x96, 0x51, 0x00, 0xee, 0x9a, 0x1f, 0x8d, 0x61, 0xbc, 0xeb, 0xb1, 0xa6, 0xa5, 0x21, 0x8b, 0xa7, 0xf8, 0x25, 0x41, 0x48, 0x62, 0x5b, 0x01, 0x6c, 0x7c, 0x2a, 0xe8, 0xff, 0xf9, 0xf9, 0x1f, 0xe2, 0x79, 0x2e, 0xd1, 0xff, 0xa3, 0x2e, 0x1c, 0x3a, 0x1a, 0x5d, 0x2b, 0x7b, 0x87, 0x25, 0x22, 0xa4, 0x90, 0xea, 0x26, 0x9d, 0xdd, 0x13, 0x60, 0x4c, 0x10, 0x03, 0xf6, 0x99, 0xd3, 0x21, 0x0c, 0x69, 0xc6, 0xd8, 0xc8, 0x9e, 0x94, 0x89, 0x51, 0x21, 0xe3, 0x9a, 0xcd, 0xda, 0x54, 0x72, 0x64, 0xae, 0x94, 0x79, 0x36, 0x81, 0x44, 0x14, 0x6d, 0x3a, 0x0e, 0xa6, 0x30, 0xbf, 0x95, 0x99, 0xa6, 0xf5, 0x7f, 0x4f, 0xef, 0xc6, 0x71, 0x2f, 0x36, 0x13, 0x14, 0xa2, 0x9d, 0xc2, 0x0c, 0x0d, 0x4e, 0xc0, 0x02, 0xd3, 0x6f, 0xee, 0x98, 0x5e, 0x24, 0x31, 0x74, 0x11, 0x96, 0x6e, 0x43, 0x57, 0xe8, 0x8e, 0xa0, 0x8d, 0x3d, 0x79, 0x38, 0x20, 0xc2, 0x0f, 0xb4, 0x75, 0x99, 0x3b, 0xb1, 0xf0, 0xe8, 0xe1, 0xda, 0xf9, 0xd4, 0xe6, 0xd6, 0xf4, 0x8a, 0x32, 0x4a, 0x4a, 0x25, 0xa8, 0xd9, 0x60, 0xd6, 0x33, 0x31, 0x97, 0xb9, 0xb6, 0xed, 0x5f, 0xfc, 0x15, 0xbd, 0x13, 0xc0, 0x3a, 0x3f, 0x1f, 0x2d, 0x09, 0x1d, 0xeb, 0x69, 0x6a, 0xfe, 0xd7, 0x95, 0x3e, 0x8a, 0x4e, 0xe1, 0x6e, 0x61, 0xb2, 0x6c, 0xe3, 0x2b, 0x70, 0x60, 0x7e, 0x8c, 0xe4, 0xdd, 0x27, 0x30, 0x7e, 0x0d, 0xc7, 0xb7, 0x9a, 0x1a, 0x3c, 0xcc, 0xa7, 0x22, 0x77, 0x14, 0x05, 0x50, 0x57, 0x31, 0x1b, 0xc8, 0xbf, 0xce, 0x52, 0xaf, 0x9c, 0x8e, 0x10, 0x2e, 0xd2, 0x16, 0xb6, 0x6e, 0x43, 0x10, 0xaf, 0x8b, 0xde, 0x1d, 0x60, 0xb2, 0x7d, 0xe6, 0x2f, 0x08, 0x10, 0x12, 0x7e, 0xb4, 0x76, 0x45, 0xb6, 0xd8, 0x9b, 0x26, 0x40, 0xa1, 0x63, 0x5c, 0x7a, 0x2a, 0xb1, 0x8c, 0xd6, 0xa4, 0x6f, 0x5a, 0xae, 0x33, 0x7e, 0x6d, 0x71, 0xf5, 0xc8, 0x6d, 0x80, 0x1c, 0x35, 0xfc, 0x3f, 0xc1, 0xa6, 0xc6, 0x1a, 0x15, 0x04, 0x6d, 0x76, 0x38, 0x32, 0x95, 0xb2, 0x51, 0x1a, 0xe9, 0x3e, 0x89, 0x9f, 0x0c, 0x79 }; + var out: [Sha3_256.digest_length]u8 = undefined; + + Sha3_256.hash(&msg, &out, .{}); + try htest.assertEqual("5780048dfa381a1d01c747906e4a08711dd34fd712ecd7c6801dd2b38fd81a89", &out); + + var h = Sha3_256.init(.{}); + h.update(msg[0..64]); + h.update(msg[64..613]); + h.final(&out); + try htest.assertEqual("5780048dfa381a1d01c747906e4a08711dd34fd712ecd7c6801dd2b38fd81a89", &out); +} From 12b74b2c0587e1f3f0ce8fdae47ab76e790b0d8e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 4 Mar 2023 21:04:22 -0700 Subject: [PATCH 062/294] CI: more aggressively check zig1 bootstrapping This would have caught the problem we are seeing in #14799. --- ci/aarch64-linux-debug.sh | 6 ++++++ ci/aarch64-linux-release.sh | 6 ++++++ ci/x86_64-linux-debug.sh | 6 ++++++ ci/x86_64-linux-release.sh | 6 ++++++ 4 files changed, 24 insertions(+) diff --git a/ci/aarch64-linux-debug.sh b/ci/aarch64-linux-debug.sh index ca085d2779..94f40c557b 100644 --- a/ci/aarch64-linux-debug.sh +++ b/ci/aarch64-linux-debug.sh @@ -98,3 +98,9 @@ unset CXX ninja install stage3/bin/zig test ../test/behavior.zig -I../test +stage3/bin/zig build -p stage4 \ + -Dstatic-llvm \ + -Dtarget=native-native-musl \ + --search-prefix "$PREFIX" \ + --zig-lib-dir "$(pwd)/../lib" +stage4/bin/zig test ../test/behavior.zig -I../test diff --git a/ci/aarch64-linux-release.sh b/ci/aarch64-linux-release.sh index e3dc4530a8..65d6063f25 100644 --- a/ci/aarch64-linux-release.sh +++ b/ci/aarch64-linux-release.sh @@ -98,3 +98,9 @@ unset CXX ninja install stage3/bin/zig test ../test/behavior.zig -I../test +stage3/bin/zig build -p stage4 \ + -Dstatic-llvm \ + -Dtarget=native-native-musl \ + --search-prefix "$PREFIX" \ + --zig-lib-dir "$(pwd)/../lib" +stage4/bin/zig test ../test/behavior.zig -I../test diff --git a/ci/x86_64-linux-debug.sh b/ci/x86_64-linux-debug.sh index d3e16a3954..7f2382f04a 100755 --- a/ci/x86_64-linux-debug.sh +++ b/ci/x86_64-linux-debug.sh @@ -97,3 +97,9 @@ unset CXX ninja install stage3/bin/zig test ../test/behavior.zig -I../test +stage3/bin/zig build -p stage4 \ + -Dstatic-llvm \ + -Dtarget=native-native-musl \ + --search-prefix "$PREFIX" \ + --zig-lib-dir "$(pwd)/../lib" +stage4/bin/zig test ../test/behavior.zig -I../test diff --git a/ci/x86_64-linux-release.sh b/ci/x86_64-linux-release.sh index cec08fae84..cdb24e4a6f 100755 --- a/ci/x86_64-linux-release.sh +++ b/ci/x86_64-linux-release.sh @@ -114,3 +114,9 @@ unset CXX ninja install stage3/bin/zig test ../test/behavior.zig -I../test +stage3/bin/zig build -p stage4 \ + -Dstatic-llvm \ + -Dtarget=native-native-musl \ + --search-prefix "$PREFIX" \ + --zig-lib-dir "$(pwd)/../lib" +stage4/bin/zig test ../test/behavior.zig -I../test From 87738cad8607a31537a5c16826ab315990bc73c6 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Thu, 9 Mar 2023 19:14:17 +0100 Subject: [PATCH 063/294] wasm-linker: store symbol's virtual address For data symbols we will now store its virtual address. This means we do no longer have to calculate it each time a relocation asks for the address. This is now done for all data symbols only once rather than every single relocation for that symbol. This now also allows us directly store the virtual address of synthetic symbols without having to create an atom for them. This means we also don't need to have a "synthetic" segment any longer and do not emit the synthetic symbols such as __heap_end and __heap_base into the final binary. --- src/link/Wasm.zig | 53 ++++++++++++++++++++++++++++------------ src/link/Wasm/Atom.zig | 24 +----------------- src/link/Wasm/Object.zig | 2 ++ src/link/Wasm/Symbol.zig | 3 +++ 4 files changed, 43 insertions(+), 39 deletions(-) diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 513b567210..9a4166b052 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -468,6 +468,7 @@ fn createSyntheticSymbol(wasm: *Wasm, name: []const u8, tag: Symbol.Tag) !Symbol .flags = 0, .tag = tag, .index = undefined, + .virtual_address = undefined, }); try wasm.resolved_symbols.putNoClobber(wasm.base.allocator, loc, {}); try wasm.globals.put(wasm.base.allocator, name_offset, loc); @@ -1011,6 +1012,7 @@ pub fn allocateSymbol(wasm: *Wasm) !u32 { .flags = @enumToInt(Symbol.Flag.WASM_SYM_BINDING_LOCAL), .tag = undefined, // will be set after updateDecl .index = undefined, // will be set after updateDecl + .virtual_address = undefined, // will be set during atom allocation }; if (wasm.symbols_free_list.popOrNull()) |index| { wasm.symbols.items[index] = symbol; @@ -1246,6 +1248,7 @@ pub fn lowerUnnamedConst(wasm: *Wasm, tv: TypedValue, decl_index: Module.Decl.In .flags = @enumToInt(Symbol.Flag.WASM_SYM_BINDING_LOCAL), .tag = .data, .index = undefined, + .virtual_address = undefined, }; try wasm.resolved_symbols.putNoClobber(wasm.base.allocator, atom.symbolLoc(), {}); @@ -1292,6 +1295,7 @@ pub fn getGlobalSymbol(wasm: *Wasm, name: []const u8) !u32 { .flags = 0, .index = undefined, // index to type will be set after merging function symbols .tag = .function, + .virtual_address = undefined, }; symbol.setGlobal(true); symbol.setUndefined(true); @@ -1788,6 +1792,30 @@ fn allocateAtoms(wasm: *Wasm) !void { } } +/// For each data symbol, sets the virtual address. +fn allocateVirtualAddresses(wasm: *Wasm) void { + for (wasm.resolved_symbols.keys()) |loc| { + const symbol = loc.getSymbol(wasm); + if (symbol.tag != .data) { + continue; // only data symbols have virtual addresses + } + const atom_index = wasm.symbol_atom.get(loc) orelse { + // synthetic symbol that does not contain an atom + continue; + }; + + const atom = wasm.getAtom(atom_index); + const merge_segment = wasm.base.options.output_mode != .Obj; + const segment_info = if (atom.file) |object_index| blk: { + break :blk wasm.objects.items[object_index].segment_info; + } else wasm.segment_info.values(); + const segment_name = segment_info[symbol.index].outputName(merge_segment); + const segment_index = wasm.data_segments.get(segment_name).?; + const segment = wasm.segments.items[segment_index]; + symbol.virtual_address = atom.offset + segment.offset; + } +} + fn sortDataSegments(wasm: *Wasm) !void { var new_mapping: std.StringArrayHashMapUnmanaged(u32) = .{}; try new_mapping.ensureUnusedCapacity(wasm.base.allocator, wasm.data_segments.count()); @@ -2137,13 +2165,10 @@ fn setupExports(wasm: *Wasm) !void { break :blk try wasm.string_table.put(wasm.base.allocator, sym_name); }; const exp: types.Export = if (symbol.tag == .data) exp: { - const atom_index = wasm.symbol_atom.get(sym_loc).?; - const atom = wasm.getAtom(atom_index); - const va = atom.getVA(wasm, symbol); const global_index = @intCast(u32, wasm.imported_globals_count + wasm.wasm_globals.items.len); try wasm.wasm_globals.append(wasm.base.allocator, .{ .global_type = .{ .valtype = .i32, .mutable = false }, - .init = .{ .i32_const = @intCast(i32, va) }, + .init = .{ .i32_const = @intCast(i32, symbol.virtual_address) }, }); break :exp .{ .name = export_name, @@ -2240,12 +2265,8 @@ fn setupMemory(wasm: *Wasm) !void { // One of the linked object files has a reference to the __heap_base symbol. // We must set its virtual address so it can be used in relocations. if (wasm.findGlobalSymbol("__heap_base")) |loc| { - const segment_index = wasm.data_segments.get(".synthetic").?; - const segment = &wasm.segments.items[segment_index]; - segment.offset = 0; // for simplicity we store the entire VA into atom's offset. - const atom_index = wasm.symbol_atom.get(loc).?; - const atom = wasm.getAtomPtr(atom_index); - atom.offset = @intCast(u32, mem.alignForwardGeneric(u64, memory_ptr, heap_alignment)); + const symbol = loc.getSymbol(wasm); + symbol.virtual_address = @intCast(u32, mem.alignForwardGeneric(u64, memory_ptr, heap_alignment)); } // Setup the max amount of pages @@ -2274,12 +2295,8 @@ fn setupMemory(wasm: *Wasm) !void { log.debug("Total memory pages: {d}", .{wasm.memories.limits.min}); if (wasm.findGlobalSymbol("__heap_end")) |loc| { - const segment_index = wasm.data_segments.get(".synthetic").?; - const segment = &wasm.segments.items[segment_index]; - segment.offset = 0; - const atom_index = wasm.symbol_atom.get(loc).?; - const atom = wasm.getAtomPtr(atom_index); - atom.offset = @intCast(u32, memory_ptr); + const symbol = loc.getSymbol(wasm); + symbol.virtual_address = @intCast(u32, memory_ptr); } if (wasm.base.options.max_memory) |max_memory| { @@ -2417,6 +2434,7 @@ pub fn getErrorTableSymbol(wasm: *Wasm) !u32 { .tag = .data, .flags = 0, .index = 0, + .virtual_address = undefined, }; symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); @@ -2449,6 +2467,7 @@ fn populateErrorNameTable(wasm: *Wasm) !void { .tag = .data, .flags = 0, .index = 0, + .virtual_address = undefined, }; names_symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); @@ -2748,6 +2767,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l try wasm.allocateAtoms(); try wasm.setupMemory(); + wasm.allocateVirtualAddresses(); wasm.mapFunctionTable(); try wasm.mergeSections(); try wasm.mergeTypes(); @@ -2866,6 +2886,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod try wasm.allocateAtoms(); try wasm.setupMemory(); + wasm.allocateVirtualAddresses(); wasm.mapFunctionTable(); try wasm.mergeSections(); try wasm.mergeTypes(); diff --git a/src/link/Wasm/Atom.zig b/src/link/Wasm/Atom.zig index e719f8dfcc..0c9d761f05 100644 --- a/src/link/Wasm/Atom.zig +++ b/src/link/Wasm/Atom.zig @@ -89,21 +89,6 @@ pub fn getSymbolIndex(atom: Atom) ?u32 { return atom.sym_index; } -/// Returns the virtual address of the `Atom`. This is the address starting -/// from the first entry within a section. -pub fn getVA(atom: Atom, wasm: *const Wasm, symbol: *const Symbol) u32 { - if (symbol.tag == .function) return atom.offset; - std.debug.assert(symbol.tag == .data); - const merge_segment = wasm.base.options.output_mode != .Obj; - const segment_info = if (atom.file) |object_index| blk: { - break :blk wasm.objects.items[object_index].segment_info; - } else wasm.segment_info.values(); - const segment_name = segment_info[symbol.index].outputName(merge_segment); - const segment_index = wasm.data_segments.get(segment_name).?; - const segment = wasm.segments.items[segment_index]; - return segment.offset + atom.offset; -} - /// Resolves the relocations within the atom, writing the new value /// at the calculated offset. pub fn resolveRelocs(atom: *Atom, wasm_bin: *const Wasm) void { @@ -186,14 +171,7 @@ fn relocationValue(atom: Atom, relocation: types.Relocation, wasm_bin: *const Wa if (symbol.isUndefined()) { return 0; } - const target_atom_index = wasm_bin.symbol_atom.get(target_loc) orelse { - // this can only occur during incremental-compilation when a relocation - // still points to a freed decl. It is fine to emit the value 0 here - // as no actual code will point towards it. - return 0; - }; - const target_atom = wasm_bin.getAtom(target_atom_index); - const va = @intCast(i32, target_atom.getVA(wasm_bin, symbol)); + const va = @intCast(i64, symbol.virtual_address); return @intCast(u32, va + relocation.addend); }, .R_WASM_EVENT_INDEX_LEB => return symbol.index, diff --git a/src/link/Wasm/Object.zig b/src/link/Wasm/Object.zig index 82cab2528a..45c9464ec8 100644 --- a/src/link/Wasm/Object.zig +++ b/src/link/Wasm/Object.zig @@ -270,6 +270,7 @@ fn checkLegacyIndirectFunctionTable(object: *Object) !?Symbol { .name = table_import.name, .tag = .table, .index = 0, + .virtual_address = undefined, }; table_symbol.setFlag(.WASM_SYM_UNDEFINED); table_symbol.setFlag(.WASM_SYM_NO_STRIP); @@ -758,6 +759,7 @@ fn Parser(comptime ReaderType: type) type { .tag = tag, .name = undefined, .index = undefined, + .virtual_address = undefined, }; switch (tag) { diff --git a/src/link/Wasm/Symbol.zig b/src/link/Wasm/Symbol.zig index 089eee289e..156b507a32 100644 --- a/src/link/Wasm/Symbol.zig +++ b/src/link/Wasm/Symbol.zig @@ -20,6 +20,9 @@ name: u32, index: u32, /// Represents the kind of the symbol, such as a function or global. tag: Tag, +/// Contains the virtual address of the symbol, relative to the start of its section. +/// This differs from the offset of an `Atom` which is relative to the start of a segment. +virtual_address: u32, pub const Tag = enum { function, From 95f6a5935a675efe6d30bc2388e7a0bc6b742c6d Mon Sep 17 00:00:00 2001 From: Frank Denis <124872+jedisct1@users.noreply.github.com> Date: Thu, 9 Mar 2023 20:20:57 +0100 Subject: [PATCH 064/294] TurboSHAKE: change default delimiter to 0x1F (#14857) The TurboSHAKE paper just got published: https://eprint.iacr.org/2023/342.pdf and unlike the previous K12 paper, suggests 0x1F instead of 0x01 as the default value for "D". --- lib/std/crypto/sha3.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/std/crypto/sha3.zig b/lib/std/crypto/sha3.zig index d286e40666..1f48f87c53 100644 --- a/lib/std/crypto/sha3.zig +++ b/lib/std/crypto/sha3.zig @@ -20,7 +20,7 @@ pub const Shake256 = Shake(256); /// TurboSHAKE128 is a XOF (a secure hash function with a variable output length), with a 128 bit security level. /// It is based on the same permutation as SHA3 and SHAKE128, but which much higher performance. -/// The delimiter is 0x01 by default, but can be changed for context-separation. +/// The delimiter is 0x1f by default, but can be changed for context-separation. pub fn TurboShake128(comptime delim: ?u8) type { return TurboShake(128, delim); } @@ -96,7 +96,7 @@ pub fn Shake(comptime security_level: u11) type { /// The TurboSHAKE extendable output hash function. /// https://datatracker.ietf.org/doc/draft-irtf-cfrg-kangarootwelve/ pub fn TurboShake(comptime security_level: u11, comptime delim: ?u8) type { - return ShakeLike(security_level, delim orelse 0x01, 12); + return ShakeLike(security_level, delim orelse 0x1f, 12); } fn ShakeLike(comptime security_level: u11, comptime delim: u8, comptime rounds: u5) type { From afb26f4e6b39431001eff75cc8ce19144cb5301a Mon Sep 17 00:00:00 2001 From: Nameless Date: Thu, 2 Mar 2023 12:45:34 -0600 Subject: [PATCH 065/294] std.http: add connection pooling and make keep-alive requests by default --- lib/std/http/Client.zig | 211 ++++++++++++++++++++++++++++++---------- 1 file changed, 161 insertions(+), 50 deletions(-) diff --git a/lib/std/http/Client.zig b/lib/std/http/Client.zig index 7f62b2d597..d4d8f85ad1 100644 --- a/lib/std/http/Client.zig +++ b/lib/std/http/Client.zig @@ -21,11 +21,27 @@ ca_bundle: std.crypto.Certificate.Bundle = .{}, /// it will first rescan the system for root certificates. next_https_rescan_certs: bool = true, +connection_pool: std.TailQueue(Connection) = .{}, + +const ConnectionPool = std.TailQueue(Connection); +const ConnectionNode = ConnectionPool.Node; + +pub fn release(client: *Client, node: *ConnectionNode) void { + if (node.data.unusable) return node.data.close(client); + + client.connection_pool.append(node); +} + pub const Connection = struct { stream: net.Stream, /// undefined unless protocol is tls. - tls_client: std.crypto.tls.Client, + tls_client: std.crypto.tls.Client, // TODO: allocate this, it's currently 16 KB. protocol: Protocol, + host: []u8, + port: u16, + + // This connection has been part of a non keepalive request and cannot be added to the pool. + unusable: bool = false, pub const Protocol = enum { plain, tls }; @@ -56,6 +72,17 @@ pub const Connection = struct { .tls => return conn.tls_client.write(conn.stream, buffer), } } + + pub fn close(conn: *Connection, client: *const Client) void { + if (conn.protocol == .tls) { + // try to cleanly close the TLS connection, for any server that cares. + _ = conn.tls_client.writeEnd(conn.stream, "", true) catch {}; + } + + conn.stream.close(); + + client.allocator.free(conn.host); + } }; /// TODO: emit error.UnexpectedEndOfStream or something like that when the read @@ -63,7 +90,7 @@ pub const Connection = struct { /// close_notify protection on underlying TLS streams. pub const Request = struct { client: *Client, - connection: Connection, + connection: *ConnectionNode, redirects_left: u32, response: Response, /// These are stored in Request so that they are available when following @@ -79,6 +106,7 @@ pub const Request = struct { header_bytes: std.ArrayListUnmanaged(u8), max_header_bytes: usize, next_chunk_length: u64, + done: bool, pub const Headers = struct { status: http.Status, @@ -86,6 +114,7 @@ pub const Request = struct { location: ?[]const u8 = null, content_length: ?u64 = null, transfer_encoding: ?http.TransferEncoding = null, + connection_close: bool = true, pub fn parse(bytes: []const u8) !Response.Headers { var it = mem.split(u8, bytes[0 .. bytes.len - 4], "\r\n"); @@ -126,6 +155,14 @@ pub const Request = struct { if (headers.transfer_encoding != null) return error.HttpHeadersInvalid; headers.transfer_encoding = std.meta.stringToEnum(http.TransferEncoding, header_value) orelse return error.HttpTransferEncodingUnsupported; + } else if (std.ascii.eqlIgnoreCase(header_name, "connection")) { + if (std.ascii.eqlIgnoreCase(header_value, "keep-alive")) { + headers.connection_close = false; + } else if (std.ascii.eqlIgnoreCase(header_value, "close")) { + headers.connection_close = true; + } else { + return error.HttpConnectionHeaderUnsupported; + } } } @@ -185,10 +222,10 @@ pub const Request = struct { chunk_r, chunk_data, - pub fn zeroMeansEnd(state: State) bool { - return switch (state) { - .finished, .chunk_data => true, - else => false, + pub fn isContent(self: State) bool { + return switch (self) { + .invalid, .start, .seen_r, .seen_rn, .seen_rnr => false, + .finished, .chunk_size_prefix_r, .chunk_size_prefix_n, .chunk_size, .chunk_r, .chunk_data => true, }; } }; @@ -201,6 +238,7 @@ pub const Request = struct { .max_header_bytes = max, .header_bytes_owned = true, .next_chunk_length = undefined, + .done = false, }; } @@ -212,6 +250,7 @@ pub const Request = struct { .max_header_bytes = buf.len, .header_bytes_owned = false, .next_chunk_length = undefined, + .done = false, }; } @@ -501,6 +540,7 @@ pub const Request = struct { pub const Headers = struct { version: http.Version = .@"HTTP/1.1", method: http.Method = .GET, + connection_close: bool = false, }; pub const Options = struct { @@ -545,6 +585,7 @@ pub const Request = struct { HttpHeadersExceededSizeLimit, HttpRedirectMissingLocation, HttpTransferEncodingUnsupported, + HttpConnectionHeaderUnsupported, HttpContentLengthUnknown, TooManyHttpRedirects, ShortHttpStatusLine, @@ -669,8 +710,9 @@ pub const Request = struct { assert(len <= buffer.len); var index: usize = 0; while (index < len) { - const zero_means_end = req.response.state.zeroMeansEnd(); const amt = try readAdvanced(req, buffer[index..]); + const zero_means_end = req.response.done and req.response.headers.status.class() != .redirect; + if (amt == 0 and zero_means_end) break; index += amt; } @@ -680,7 +722,29 @@ pub const Request = struct { /// This one can return 0 without meaning EOF. /// TODO change to readvAdvanced pub fn readAdvanced(req: *Request, buffer: []u8) !usize { - var in = buffer[0..try req.connection.read(buffer)]; + if (req.response.done) { + if (req.response.headers.status.class() == .redirect) { + if (req.redirects_left == 0) return error.TooManyHttpRedirects; + + const location = req.response.headers.location orelse + return error.HttpRedirectMissingLocation; + const new_url = try std.Uri.parse(location); + const new_req = try req.client.request(new_url, req.headers, .{ + .max_redirects = req.redirects_left - 1, + .header_strategy = if (req.response.header_bytes_owned) .{ + .dynamic = req.response.max_header_bytes, + } else .{ + .static = req.response.header_bytes.unusedCapacitySlice(), + }, + }); + req.deinit(); + req.* = new_req; + } else { + return 0; + } + } + + var in = buffer[0..try req.connection.data.read(buffer)]; var out_index: usize = 0; while (true) { switch (req.response.state) { @@ -698,24 +762,10 @@ pub const Request = struct { if (req.response.state == .finished) { req.response.headers = try Response.Headers.parse(req.response.header_bytes.items); - if (req.response.headers.status.class() == .redirect) { - if (req.redirects_left == 0) return error.TooManyHttpRedirects; - const location = req.response.headers.location orelse - return error.HttpRedirectMissingLocation; - const new_url = try std.Uri.parse(location); - const new_req = try req.client.request(new_url, req.headers, .{ - .max_redirects = req.redirects_left - 1, - .header_strategy = if (req.response.header_bytes_owned) .{ - .dynamic = req.response.max_header_bytes, - } else .{ - .static = req.response.header_bytes.unusedCapacitySlice(), - }, - }); - req.deinit(); - req.* = new_req; - assert(out_index == 0); - in = buffer[0..try req.connection.read(buffer)]; - continue; + if (req.response.headers.connection_close == true) { + req.connection.data.unusable = true; + } else { + req.connection.data.unusable = false; } if (req.response.headers.transfer_encoding) |transfer_encoding| { @@ -742,11 +792,29 @@ pub const Request = struct { return 0; }, .finished => { + const sub_amt = @intCast(usize, @min(req.response.next_chunk_length, in.len)); + req.response.next_chunk_length -= sub_amt; + + if (req.response.next_chunk_length == 0) { + req.client.release(req.connection); + req.connection = undefined; + + req.response.done = true; + assert(in.len == sub_amt); // TODO: figure out how to not read more than necessary. + + if (req.response.state.isContent() and req.response.headers.status.class() == .redirect) return 0; + + mem.copy(u8, buffer[out_index..], in[0..sub_amt]); + return out_index + sub_amt; + } + + if (req.response.state.isContent() and req.response.headers.status.class() == .redirect) return 0; + if (in.ptr == buffer.ptr) { - return in.len; + return sub_amt; } else { - mem.copy(u8, buffer[out_index..], in); - return out_index + in.len; + mem.copy(u8, buffer[out_index..], in[0..sub_amt]); + return out_index + sub_amt; } }, .chunk_size_prefix_r => switch (in.len) { @@ -793,7 +861,10 @@ pub const Request = struct { .invalid => return error.HttpHeadersInvalid, .chunk_data => { if (req.response.next_chunk_length == 0) { - req.response.state = .start; + req.response.done = true; + req.client.release(req.connection); + req.connection = undefined; + return out_index; } in = in[i..]; @@ -807,20 +878,27 @@ pub const Request = struct { // TODO https://github.com/ziglang/zig/issues/14039 const sub_amt = @intCast(usize, @min(req.response.next_chunk_length, in.len)); req.response.next_chunk_length -= sub_amt; - if (req.response.next_chunk_length > 0) { - if (in.ptr == buffer.ptr) { - return sub_amt; - } else { - mem.copy(u8, buffer[out_index..], in[0..sub_amt]); - out_index += sub_amt; - return out_index; - } + + if (req.response.next_chunk_length == 0) { + req.response.state = .chunk_size_prefix_r; + in = in[sub_amt..]; + + if (req.response.headers.status.class() == .redirect) continue; + + mem.copy(u8, buffer[out_index..], in[0..sub_amt]); + out_index += sub_amt; + continue; + } + + if (req.response.headers.status.class() == .redirect) return 0; + + if (in.ptr == buffer.ptr) { + return sub_amt; + } else { + mem.copy(u8, buffer[out_index..], in[0..sub_amt]); + out_index += sub_amt; + return out_index; } - mem.copy(u8, buffer[out_index..], in[0..sub_amt]); - out_index += sub_amt; - req.response.state = .chunk_size_prefix_r; - in = in[sub_amt..]; - continue; }, } } @@ -844,24 +922,52 @@ pub const Request = struct { }; pub fn deinit(client: *Client) void { + var next = client.connection_pool.first; + while (next) |node| { + next = node.next; + + node.data.close(client); + + client.allocator.destroy(node); + } + client.ca_bundle.deinit(client.allocator); client.* = undefined; } -pub fn connect(client: *Client, host: []const u8, port: u16, protocol: Connection.Protocol) !Connection { - var conn: Connection = .{ +pub fn connect(client: *Client, host: []const u8, port: u16, protocol: Connection.Protocol) !*ConnectionNode { + var potential = client.connection_pool.last; + while (potential) |node| { + const same_host = mem.eql(u8, node.data.host, host); + const same_port = node.data.port == port; + const same_protocol = node.data.protocol == protocol; + + if (same_host and same_port and same_protocol) { + client.connection_pool.remove(node); + return node; + } + + potential = node.prev; + } + + const conn = try client.allocator.create(ConnectionNode); + errdefer client.allocator.destroy(conn); + + conn.* = .{ .data = .{ .stream = try net.tcpConnectToHost(client.allocator, host, port), .tls_client = undefined, .protocol = protocol, - }; + .host = try client.allocator.dupe(u8, host), + .port = port, + } }; switch (protocol) { .plain => {}, .tls => { - conn.tls_client = try std.crypto.tls.Client.init(conn.stream, client.ca_bundle, host); + conn.data.tls_client = try std.crypto.tls.Client.init(conn.data.stream, client.ca_bundle, host); // This is appropriate for HTTPS because the HTTP headers contain // the content length which is used to detect truncation attacks. - conn.tls_client.allow_truncation_attacks = true; + conn.data.tls_client.allow_truncation_attacks = true; }, } @@ -908,10 +1014,15 @@ pub fn request(client: *Client, uri: Uri, headers: Request.Headers, options: Req try h.appendSlice(@tagName(headers.version)); try h.appendSlice("\r\nHost: "); try h.appendSlice(host); - try h.appendSlice("\r\nConnection: close\r\n\r\n"); + if (headers.connection_close) { + try h.appendSlice("\r\nConnection: close"); + } else { + try h.appendSlice("\r\nConnection: keep-alive"); + } + try h.appendSlice("\r\n\r\n"); const header_bytes = h.slice(); - try req.connection.writeAll(header_bytes); + try req.connection.data.writeAll(header_bytes); } return req; From 8d86194b6e31788263d2cbdd03e2a8cde4134c37 Mon Sep 17 00:00:00 2001 From: Nameless Date: Mon, 6 Mar 2023 20:11:56 -0600 Subject: [PATCH 066/294] add error sets to tcpConnect* and tls.Client.init --- lib/std/crypto/tls/Client.zig | 50 ++++++++++++++++++++++++++++++++++- lib/std/net.zig | 34 +++++++++++++++++++++--- 2 files changed, 80 insertions(+), 4 deletions(-) diff --git a/lib/std/crypto/tls/Client.zig b/lib/std/crypto/tls/Client.zig index 627ad7ea59..01bf957820 100644 --- a/lib/std/crypto/tls/Client.zig +++ b/lib/std/crypto/tls/Client.zig @@ -88,11 +88,59 @@ pub const StreamInterface = struct { } }; +pub fn InitError(comptime Stream: type) type { + return std.mem.Allocator.Error || Stream.WriteError || Stream.ReadError || error { + InsufficientEntropy, + DiskQuota, + LockViolation, + NotOpenForWriting, + TlsAlert, + TlsUnexpectedMessage, + TlsIllegalParameter, + TlsDecryptFailure, + TlsRecordOverflow, + TlsBadRecordMac, + CertificateFieldHasInvalidLength, + CertificateHostMismatch, + CertificatePublicKeyInvalid, + CertificateExpired, + CertificateFieldHasWrongDataType, + CertificateIssuerMismatch, + CertificateNotYetValid, + CertificateSignatureAlgorithmMismatch, + CertificateSignatureAlgorithmUnsupported, + CertificateSignatureInvalid, + CertificateSignatureInvalidLength, + CertificateSignatureNamedCurveUnsupported, + CertificateSignatureUnsupportedBitCount, + TlsCertificateNotVerified, + TlsBadSignatureScheme, + TlsBadRsaSignatureBitCount, + InvalidEncoding, + IdentityElement, + SignatureVerificationFailed, + TlsDecryptError, + TlsConnectionTruncated, + TlsDecodeError, + UnsupportedCertificateVersion, + CertificateTimeInvalid, + CertificateHasUnrecognizedObjectId, + CertificateHasInvalidBitString, + MessageTooLong, + NegativeIntoUnsigned, + TargetTooSmall, + BufferTooSmall, + InvalidSignature, + NotSquare, + NonCanonical, + }; +} + /// Initiates a TLS handshake and establishes a TLSv1.3 session with `stream`, which /// must conform to `StreamInterface`. /// /// `host` is only borrowed during this function call. -pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) !Client { +pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) InitError(@TypeOf(stream))!Client { const host_len = @intCast(u16, host.len); var random_buffer: [128]u8 = undefined; diff --git a/lib/std/net.zig b/lib/std/net.zig index 50a0f8b9d7..cf112cbab9 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -702,8 +702,10 @@ pub const AddressList = struct { } }; +pub const TcpConnectToHostError = GetAddressListError || TcpConnectToAddressError; + /// All memory allocated with `allocator` will be freed before this function returns. -pub fn tcpConnectToHost(allocator: mem.Allocator, name: []const u8, port: u16) !Stream { +pub fn tcpConnectToHost(allocator: mem.Allocator, name: []const u8, port: u16) TcpConnectToHostError!Stream { const list = try getAddressList(allocator, name, port); defer list.deinit(); @@ -720,7 +722,9 @@ pub fn tcpConnectToHost(allocator: mem.Allocator, name: []const u8, port: u16) ! return std.os.ConnectError.ConnectionRefused; } -pub fn tcpConnectToAddress(address: Address) !Stream { +pub const TcpConnectToAddressError = std.os.SocketError || std.os.ConnectError; + +pub fn tcpConnectToAddress(address: Address) TcpConnectToAddressError!Stream { const nonblock = if (std.io.is_async) os.SOCK.NONBLOCK else 0; const sock_flags = os.SOCK.STREAM | nonblock | (if (builtin.target.os.tag == .windows) 0 else os.SOCK.CLOEXEC); @@ -737,8 +741,32 @@ pub fn tcpConnectToAddress(address: Address) !Stream { return Stream{ .handle = sockfd }; } +const GetAddressListError = std.mem.Allocator.Error || std.fs.File.OpenError || std.fs.File.ReadError || std.os.SocketError || std.os.BindError || error { + // TODO: break this up into error sets from the various underlying functions + + TemporaryNameServerFailure, + NameServerFailure, + AddressFamilyNotSupported, + UnknownHostName, + ServiceUnavailable, + Unexpected, + + HostLacksNetworkAddresses, + + InvalidCharacter, + InvalidEnd, + NonCanonical, + Overflow, + Incomplete, + InvalidIpv4Mapping, + InvalidIPAddressFormat, + + InterfaceNotFound, + FileSystem, +}; + /// Call `AddressList.deinit` on the result. -pub fn getAddressList(allocator: mem.Allocator, name: []const u8, port: u16) !*AddressList { +pub fn getAddressList(allocator: mem.Allocator, name: []const u8, port: u16) GetAddressListError!*AddressList { const result = blk: { var arena = std.heap.ArenaAllocator.init(allocator); errdefer arena.deinit(); From fd2f906d1ede2b65ba21eec59137b2d4b676eedc Mon Sep 17 00:00:00 2001 From: Nameless Date: Mon, 6 Mar 2023 20:13:15 -0600 Subject: [PATCH 067/294] std.http: handle compressed payloads --- lib/std/http.zig | 10 + lib/std/http/Client.zig | 769 ++++++++++++++++++++++++++-------------- 2 files changed, 504 insertions(+), 275 deletions(-) diff --git a/lib/std/http.zig b/lib/std/http.zig index 7c2a2da605..d4cc259f19 100644 --- a/lib/std/http.zig +++ b/lib/std/http.zig @@ -253,6 +253,16 @@ pub const TransferEncoding = enum { gzip, }; +pub const Connection = enum { + keep_alive, + close, +}; + +pub const CustomHeader = struct { + name: []const u8, + value: []const u8, +}; + const std = @import("std.zig"); test { diff --git a/lib/std/http/Client.zig b/lib/std/http/Client.zig index d4d8f85ad1..cac6571798 100644 --- a/lib/std/http/Client.zig +++ b/lib/std/http/Client.zig @@ -21,27 +21,51 @@ ca_bundle: std.crypto.Certificate.Bundle = .{}, /// it will first rescan the system for root certificates. next_https_rescan_certs: bool = true, -connection_pool: std.TailQueue(Connection) = .{}, +connection_mutex: std.Thread.Mutex = .{}, +connection_pool: ConnectionPool = .{}, +connection_used: ConnectionPool = .{}, const ConnectionPool = std.TailQueue(Connection); const ConnectionNode = ConnectionPool.Node; -pub fn release(client: *Client, node: *ConnectionNode) void { - if (node.data.unusable) return node.data.close(client); +/// Acquires an existing connection from the connection pool. This function is threadsafe. +pub fn acquire(client: *Client, node: *ConnectionNode) void { + client.connection_mutex.lock(); + defer client.connection_mutex.unlock(); + client.connection_pool.remove(node); + client.connection_used.append(node); +} + +/// Tries to release a connection back to the connection pool. This function is threadsafe. +/// If the connection is marked as closing, it will be closed instead. +pub fn release(client: *Client, node: *ConnectionNode) void { + if (node.data.closing) { + node.data.close(client); + + return client.allocator.destroy(node); + } + + client.connection_mutex.lock(); + defer client.connection_mutex.unlock(); + + client.connection_used.remove(node); client.connection_pool.append(node); } +const DeflateDecompressor = std.compress.zlib.ZlibStream(Request.ReaderRaw); +const GzipDecompressor = std.compress.gzip.Decompress(Request.ReaderRaw); + pub const Connection = struct { stream: net.Stream, /// undefined unless protocol is tls. - tls_client: std.crypto.tls.Client, // TODO: allocate this, it's currently 16 KB. + tls_client: *std.crypto.tls.Client, // TODO: allocate this, it's currently 16 KB. protocol: Protocol, host: []u8, port: u16, // This connection has been part of a non keepalive request and cannot be added to the pool. - unusable: bool = false, + closing: bool = false, pub const Protocol = enum { plain, tls }; @@ -59,6 +83,24 @@ pub const Connection = struct { } } + pub const ReadError = std.net.Stream.ReadError || error{ + TlsConnectionTruncated, + TlsRecordOverflow, + TlsDecodeError, + TlsAlert, + TlsBadRecordMac, + Overflow, + TlsBadLength, + TlsIllegalParameter, + TlsUnexpectedMessage, + }; + + pub const Reader = std.io.Reader(*Connection, ReadError, read); + + pub fn reader(conn: *Connection) Reader { + return Reader{ .context = conn }; + } + pub fn writeAll(conn: *Connection, buffer: []const u8) !void { switch (conn.protocol) { .plain => return conn.stream.writeAll(buffer), @@ -73,10 +115,18 @@ pub const Connection = struct { } } + pub const WriteError = std.net.Stream.WriteError || error{}; + pub const Writer = std.io.Writer(*Connection, WriteError, write); + + pub fn writer(conn: *Connection) Writer { + return Writer{ .context = conn }; + } + pub fn close(conn: *Connection, client: *const Client) void { if (conn.protocol == .tls) { // try to cleanly close the TLS connection, for any server that cares. _ = conn.tls_client.writeEnd(conn.stream, "", true) catch {}; + client.allocator.destroy(conn.tls_client); } conn.stream.close(); @@ -85,10 +135,10 @@ pub const Connection = struct { } }; -/// TODO: emit error.UnexpectedEndOfStream or something like that when the read -/// data does not match the content length. This is necessary since HTTPS disables -/// close_notify protection on underlying TLS streams. pub const Request = struct { + const read_buffer_size = 8192; + const ReadBufferIndex = std.math.IntFittingRange(0, read_buffer_size); + client: *Client, connection: *ConnectionNode, redirects_left: u32, @@ -97,6 +147,11 @@ pub const Request = struct { /// redirects. headers: Headers, + /// Read buffer for the connection. This is used to pull in large amounts of data from the connection even if the user asks for a small amount. This can probably be removed with careful planning. + read_buffer: [read_buffer_size]u8 = undefined, + read_buffer_start: ReadBufferIndex = 0, + read_buffer_len: ReadBufferIndex = 0, + pub const Response = struct { headers: Response.Headers, state: State, @@ -106,15 +161,24 @@ pub const Request = struct { header_bytes: std.ArrayListUnmanaged(u8), max_header_bytes: usize, next_chunk_length: u64, - done: bool, + done: bool = false, + + compression: union(enum) { + deflate: DeflateDecompressor, + gzip: GzipDecompressor, + none: void, + } = .none, pub const Headers = struct { status: http.Status, version: http.Version, location: ?[]const u8 = null, content_length: ?u64 = null, - transfer_encoding: ?http.TransferEncoding = null, - connection_close: bool = true, + transfer_encoding: ?http.TransferEncoding = null, // This should only ever be chunked, compression is handled separately. + transfer_compression: ?http.TransferEncoding = null, + connection: http.Connection = .close, + + number_of_headers: usize = 0, pub fn parse(bytes: []const u8) !Response.Headers { var it = mem.split(u8, bytes[0 .. bytes.len - 4], "\r\n"); @@ -137,6 +201,8 @@ pub const Request = struct { }; while (it.next()) |line| { + headers.number_of_headers += 1; + if (line.len == 0) return error.HttpHeadersInvalid; switch (line[0]) { ' ', '\t' => return error.HttpHeaderContinuationsUnsupported, @@ -152,14 +218,65 @@ pub const Request = struct { if (headers.content_length != null) return error.HttpHeadersInvalid; headers.content_length = try std.fmt.parseInt(u64, header_value, 10); } else if (std.ascii.eqlIgnoreCase(header_name, "transfer-encoding")) { - if (headers.transfer_encoding != null) return error.HttpHeadersInvalid; - headers.transfer_encoding = std.meta.stringToEnum(http.TransferEncoding, header_value) orelse + if (headers.transfer_encoding != null or headers.transfer_compression != null) return error.HttpHeadersInvalid; + + // Transfer-Encoding: second, first + // Transfer-Encoding: deflate, chunked + var iter = std.mem.splitBackwards(u8, header_value, ","); + + if (iter.next()) |first| { + const kind = std.meta.stringToEnum( + http.TransferEncoding, + std.mem.trim(u8, first, " "), + ) orelse + return error.HttpTransferEncodingUnsupported; + + switch (kind) { + .chunked => headers.transfer_encoding = .chunked, + .compress => headers.transfer_compression = .compress, + .deflate => headers.transfer_compression = .deflate, + .gzip => headers.transfer_compression = .gzip, + } + } + + if (iter.next()) |second| { + if (headers.transfer_compression != null) return error.HttpTransferEncodingUnsupported; + + const kind = std.meta.stringToEnum( + http.TransferEncoding, + std.mem.trim(u8, second, " "), + ) orelse + return error.HttpTransferEncodingUnsupported; + + switch (kind) { + .chunked => return error.HttpHeadersInvalid, // chunked must come last + .compress => return error.HttpTransferEncodingUnsupported, // compress not supported + .deflate => headers.transfer_compression = .deflate, + .gzip => headers.transfer_compression = .gzip, + } + } + + if (iter.next()) |_| return error.HttpTransferEncodingUnsupported; + } else if (std.ascii.eqlIgnoreCase(header_name, "content-encoding")) { + if (headers.transfer_compression != null) return error.HttpHeadersInvalid; + + const kind = std.meta.stringToEnum( + http.TransferEncoding, + std.mem.trim(u8, header_value, " "), + ) orelse return error.HttpTransferEncodingUnsupported; + + switch (kind) { + .chunked => return error.HttpHeadersInvalid, // not transfer encoding + .compress => return error.HttpTransferEncodingUnsupported, // compress not supported + .deflate => headers.transfer_compression = .deflate, + .gzip => headers.transfer_compression = .gzip, + } } else if (std.ascii.eqlIgnoreCase(header_name, "connection")) { if (std.ascii.eqlIgnoreCase(header_value, "keep-alive")) { - headers.connection_close = false; + headers.connection = .keep_alive; } else if (std.ascii.eqlIgnoreCase(header_value, "close")) { - headers.connection_close = true; + headers.connection = .close; } else { return error.HttpConnectionHeaderUnsupported; } @@ -238,7 +355,6 @@ pub const Request = struct { .max_header_bytes = max, .header_bytes_owned = true, .next_chunk_length = undefined, - .done = false, }; } @@ -250,7 +366,6 @@ pub const Request = struct { .max_header_bytes = buf.len, .header_bytes_owned = false, .next_chunk_length = undefined, - .done = false, }; } @@ -537,10 +652,19 @@ pub const Request = struct { } }; + pub const RequestTransfer = union(enum) { + content_length: u64, + chunked: void, + none: void, + }; + pub const Headers = struct { version: http.Version = .@"HTTP/1.1", method: http.Method = .GET, - connection_close: bool = false, + connection: http.Connection = .keep_alive, + transfer_encoding: RequestTransfer = .none, + + custom: []const http.CustomHeader = &[_]http.CustomHeader{}, }; pub const Options = struct { @@ -561,167 +685,131 @@ pub const Request = struct { }; }; - /// May be skipped if header strategy is buffer. + /// Frees all resources associated with the request. pub fn deinit(req: *Request) void { + switch (req.response.compression) { + .none => {}, + .deflate => |*deflate| deflate.deinit(), + .gzip => |*gzip| gzip.deinit(), + } + if (req.response.header_bytes_owned) { req.response.header_bytes.deinit(req.client.allocator); } + + if (!req.response.done) { + // If the response wasn't fully read, then we need to close the connection. + req.connection.data.closing = true; + req.client.release(req.connection); + } + req.* = undefined; } - pub const Reader = std.io.Reader(*Request, ReadError, read); - - pub fn reader(req: *Request) Reader { - return .{ .context = req }; - } - - pub fn readAll(req: *Request, buffer: []u8) !usize { - return readAtLeast(req, buffer, buffer.len); - } - - pub const ReadError = net.Stream.ReadError || error{ - // From HTTP protocol - HttpHeadersInvalid, - HttpHeadersExceededSizeLimit, - HttpRedirectMissingLocation, - HttpTransferEncodingUnsupported, - HttpConnectionHeaderUnsupported, - HttpContentLengthUnknown, + const ReadRawError = Connection.ReadError || std.Uri.ParseError || RequestError || error{ + UnexpectedEndOfStream, TooManyHttpRedirects, - ShortHttpStatusLine, - BadHttpVersion, - HttpHeaderContinuationsUnsupported, - UnsupportedUrlScheme, - UriMissingHost, - UnknownHostName, - - // Network problems - NetworkUnreachable, - HostLacksNetworkAddresses, - TemporaryNameServerFailure, - NameServerFailure, - ProtocolFamilyNotAvailable, - ProtocolNotSupported, - - // System resource problems - ProcessFdQuotaExceeded, - SystemFdQuotaExceeded, - OutOfMemory, - - // TLS problems - InsufficientEntropy, - TlsConnectionTruncated, - TlsRecordOverflow, - TlsDecodeError, - TlsAlert, - TlsBadRecordMac, - TlsBadLength, - TlsIllegalParameter, - TlsUnexpectedMessage, - TlsDecryptFailure, - CertificateFieldHasInvalidLength, - CertificateHostMismatch, - CertificatePublicKeyInvalid, - CertificateExpired, - CertificateFieldHasWrongDataType, - CertificateIssuerMismatch, - CertificateNotYetValid, - CertificateSignatureAlgorithmMismatch, - CertificateSignatureAlgorithmUnsupported, - CertificateSignatureInvalid, - CertificateSignatureInvalidLength, - CertificateSignatureNamedCurveUnsupported, - CertificateSignatureUnsupportedBitCount, - TlsCertificateNotVerified, - TlsBadSignatureScheme, - TlsBadRsaSignatureBitCount, - TlsDecryptError, - UnsupportedCertificateVersion, - CertificateTimeInvalid, - CertificateHasUnrecognizedObjectId, - CertificateHasInvalidBitString, - CertificateAuthorityBundleTooBig, - - // TODO: convert to higher level errors - InvalidFormat, - InvalidPort, - UnexpectedCharacter, - Overflow, - InvalidCharacter, - AddressFamilyNotSupported, - AddressInUse, - AddressNotAvailable, - ConnectionPending, - ConnectionRefused, - FileNotFound, - PermissionDenied, - ServiceUnavailable, - SocketTypeNotSupported, - FileTooBig, - LockViolation, - NoSpaceLeft, - NotOpenForWriting, - InvalidEncoding, - IdentityElement, - NonCanonical, - SignatureVerificationFailed, - MessageTooLong, - NegativeIntoUnsigned, - TargetTooSmall, - BufferTooSmall, - InvalidSignature, - NotSquare, - DiskQuota, - InvalidEnd, - Incomplete, - InvalidIpv4Mapping, - InvalidIPAddressFormat, - BadPathName, - DeviceBusy, - FileBusy, - FileLocksNotSupported, - InvalidHandle, - InvalidUtf8, - NameTooLong, - NoDevice, - PathAlreadyExists, - PipeBusy, - SharingViolation, - SymLinkLoop, - FileSystem, - InterfaceNotFound, - AlreadyBound, - FileDescriptorNotASocket, - NetworkSubsystemFailed, - NotDir, - ReadOnlyFileSystem, - Unseekable, - MissingEndCertificateMarker, - InvalidPadding, - EndOfStream, - InvalidArgument, + HttpRedirectMissingLocation, + HttpHeadersInvalid, }; - pub fn read(req: *Request, buffer: []u8) ReadError!usize { - return readAtLeast(req, buffer, 1); - } + const ReaderRaw = std.io.Reader(*Request, ReadRawError, readRaw); + + /// Read from the underlying stream, without decompressing or parsing the headers. Must be called + /// after waitForCompleteHead() has returned successfully. + pub fn readRaw(req: *Request, buffer: []u8) ReadRawError!usize { + assert(req.response.state.isContent()); - pub fn readAtLeast(req: *Request, buffer: []u8, len: usize) !usize { - assert(len <= buffer.len); var index: usize = 0; - while (index < len) { - const amt = try readAdvanced(req, buffer[index..]); + while (index == 0) { + const amt = try req.readRawAdvanced(buffer[index..]); const zero_means_end = req.response.done and req.response.headers.status.class() != .redirect; if (amt == 0 and zero_means_end) break; index += amt; } + return index; } + fn checkForCompleteHead(req: *Request, buffer: []u8) !usize { + switch (req.response.state) { + .invalid => unreachable, + .start, .seen_r, .seen_rn, .seen_rnr => {}, + else => return 0, // No more headers to read. + } + + const i = req.response.findHeadersEnd(buffer[0..]); + if (req.response.state == .invalid) return error.HttpHeadersInvalid; + + const headers_data = buffer[0..i]; + if (req.response.header_bytes.items.len + headers_data.len > req.response.max_header_bytes) { + return error.HttpHeadersExceededSizeLimit; + } + try req.response.header_bytes.appendSlice(req.client.allocator, headers_data); + + if (req.response.state == .finished) { + req.response.headers = try Response.Headers.parse(req.response.header_bytes.items); + + if (req.response.headers.connection == .keep_alive) { + req.connection.data.closing = false; + } else { + req.connection.data.closing = true; + } + + if (req.response.headers.transfer_encoding) |transfer_encoding| { + switch (transfer_encoding) { + .chunked => { + req.response.next_chunk_length = 0; + req.response.state = .chunk_size; + }, + .compress => unreachable, + .deflate => unreachable, + .gzip => unreachable, + } + } else if (req.response.headers.content_length) |content_length| { + req.response.next_chunk_length = content_length; + } else { + req.response.done = true; + } + + return i; + } + + return 0; + } + + pub const WaitForCompleteHeadError = ReadRawError || error { + UnexpectedEndOfStream, + + HttpHeadersExceededSizeLimit, + ShortHttpStatusLine, + BadHttpVersion, + HttpHeaderContinuationsUnsupported, + HttpTransferEncodingUnsupported, + HttpConnectionHeaderUnsupported, + }; + + /// Reads a complete response head. Any leftover data is stored in the request. This function is idempotent. + pub fn waitForCompleteHead(req: *Request) WaitForCompleteHeadError!void { + if (req.response.state.isContent()) return; + + while (true) { + const nread = try req.connection.data.read(req.read_buffer[0..]); + const amt = try checkForCompleteHead(req, req.read_buffer[0..nread]); + + if (amt != 0) { + req.read_buffer_start = @intCast(ReadBufferIndex, amt); + req.read_buffer_len = @intCast(ReadBufferIndex, nread); + return; + } else if (nread == 0) { + return error.UnexpectedEndOfStream; + } + } + } + /// This one can return 0 without meaning EOF. - /// TODO change to readvAdvanced - pub fn readAdvanced(req: *Request, buffer: []u8) !usize { + fn readRawAdvanced(req: *Request, buffer: []u8) !usize { if (req.response.done) { if (req.response.headers.status.class() == .redirect) { if (req.redirects_left == 0) return error.TooManyHttpRedirects; @@ -744,82 +832,56 @@ pub const Request = struct { } } - var in = buffer[0..try req.connection.data.read(buffer)]; + // var in: []const u8 = undefined; + if (req.read_buffer_start == req.read_buffer_len) { + const nread = try req.connection.data.read(req.read_buffer[0..]); + if (nread == 0) return error.UnexpectedEndOfStream; + + req.read_buffer_start = 0; + req.read_buffer_len = @intCast(ReadBufferIndex, nread); + } + var out_index: usize = 0; while (true) { switch (req.response.state) { - .invalid => unreachable, - .start, .seen_r, .seen_rn, .seen_rnr => { - const i = req.response.findHeadersEnd(in); - if (req.response.state == .invalid) return error.HttpHeadersInvalid; - - const headers_data = in[0..i]; - if (req.response.header_bytes.items.len + headers_data.len > req.response.max_header_bytes) { - return error.HttpHeadersExceededSizeLimit; - } - try req.response.header_bytes.appendSlice(req.client.allocator, headers_data); - - if (req.response.state == .finished) { - req.response.headers = try Response.Headers.parse(req.response.header_bytes.items); - - if (req.response.headers.connection_close == true) { - req.connection.data.unusable = true; - } else { - req.connection.data.unusable = false; - } - - if (req.response.headers.transfer_encoding) |transfer_encoding| { - switch (transfer_encoding) { - .chunked => { - req.response.next_chunk_length = 0; - req.response.state = .chunk_size; - }, - .compress => return error.HttpTransferEncodingUnsupported, - .deflate => return error.HttpTransferEncodingUnsupported, - .gzip => return error.HttpTransferEncodingUnsupported, - } - } else if (req.response.headers.content_length) |content_length| { - req.response.next_chunk_length = content_length; - } else { - return error.HttpContentLengthUnknown; - } - - in = in[i..]; - continue; - } - - assert(out_index == 0); - return 0; - }, + .invalid, .start, .seen_r, .seen_rn, .seen_rnr => unreachable, .finished => { - const sub_amt = @intCast(usize, @min(req.response.next_chunk_length, in.len)); - req.response.next_chunk_length -= sub_amt; + // TODO https://github.com/ziglang/zig/issues/14039 + const buf_avail = req.read_buffer_len - req.read_buffer_start; + const data_avail = req.response.next_chunk_length; + const out_avail = buffer.len; + + if (req.response.state.isContent() and req.response.headers.status.class() == .redirect) { + const can_read = @intCast(usize, @min(buf_avail, data_avail)); + req.response.next_chunk_length -= can_read; + + if (req.response.next_chunk_length == 0) { + req.client.release(req.connection); + req.connection = undefined; + req.response.done = true; + continue; + } + + return 0; // skip over as much data as possible + } + + const can_read = @intCast(usize, @min(@min(buf_avail, data_avail), out_avail)); + req.response.next_chunk_length -= can_read; + + mem.copy(u8, buffer[0..], req.read_buffer[req.read_buffer_start..][0..can_read]); + req.read_buffer_start += @intCast(ReadBufferIndex, can_read); if (req.response.next_chunk_length == 0) { req.client.release(req.connection); req.connection = undefined; - req.response.done = true; - assert(in.len == sub_amt); // TODO: figure out how to not read more than necessary. - - if (req.response.state.isContent() and req.response.headers.status.class() == .redirect) return 0; - - mem.copy(u8, buffer[out_index..], in[0..sub_amt]); - return out_index + sub_amt; } - if (req.response.state.isContent() and req.response.headers.status.class() == .redirect) return 0; - - if (in.ptr == buffer.ptr) { - return sub_amt; - } else { - mem.copy(u8, buffer[out_index..], in[0..sub_amt]); - return out_index + sub_amt; - } + return can_read; }, - .chunk_size_prefix_r => switch (in.len) { + .chunk_size_prefix_r => switch (req.read_buffer_len - req.read_buffer_start) { 0 => return out_index, - 1 => switch (in[0]) { + 1 => switch (req.read_buffer[req.read_buffer_start]) { '\r' => { req.response.state = .chunk_size_prefix_n; return out_index; @@ -829,9 +891,9 @@ pub const Request = struct { return error.HttpHeadersInvalid; }, }, - else => switch (int16(in[0..2])) { + else => switch (int16(req.read_buffer[req.read_buffer_start..][0..2])) { int16("\r\n") => { - in = in[2..]; + req.read_buffer_start += 2; req.response.state = .chunk_size; continue; }, @@ -841,11 +903,11 @@ pub const Request = struct { }, }, }, - .chunk_size_prefix_n => switch (in.len) { + .chunk_size_prefix_n => switch (req.read_buffer_len - req.read_buffer_start) { 0 => return out_index, - else => switch (in[0]) { + else => switch (req.read_buffer[req.read_buffer_start]) { '\n' => { - in = in[1..]; + req.read_buffer_start += 1; req.response.state = .chunk_size; continue; }, @@ -856,7 +918,7 @@ pub const Request = struct { }, }, .chunk_size, .chunk_r => { - const i = req.response.findChunkedLen(in); + const i = req.response.findChunkedLen(req.read_buffer[req.read_buffer_start..req.read_buffer_len]); switch (req.response.state) { .invalid => return error.HttpHeadersInvalid, .chunk_data => { @@ -867,7 +929,8 @@ pub const Request = struct { return out_index; } - in = in[i..]; + + req.read_buffer_start += @intCast(ReadBufferIndex, i); continue; }, .chunk_size => return out_index, @@ -876,34 +939,129 @@ pub const Request = struct { }, .chunk_data => { // TODO https://github.com/ziglang/zig/issues/14039 - const sub_amt = @intCast(usize, @min(req.response.next_chunk_length, in.len)); - req.response.next_chunk_length -= sub_amt; + const buf_avail = req.read_buffer_len - req.read_buffer_start; + const data_avail = req.response.next_chunk_length; + const out_avail = buffer.len - out_index; + + if (req.response.state.isContent() and req.response.headers.status.class() == .redirect) { + const can_read = @intCast(usize, @min(buf_avail, data_avail)); + req.response.next_chunk_length -= can_read; + + if (req.response.next_chunk_length == 0) { + req.client.release(req.connection); + req.connection = undefined; + req.response.done = true; + continue; + } + + return 0; // skip over as much data as possible + } + + const can_read = @intCast(usize, @min(@min(buf_avail, data_avail), out_avail)); + req.response.next_chunk_length -= can_read; + + mem.copy(u8, buffer[out_index..], req.read_buffer[req.read_buffer_start..][0..can_read]); + req.read_buffer_start += @intCast(ReadBufferIndex, can_read); + out_index += can_read; if (req.response.next_chunk_length == 0) { req.response.state = .chunk_size_prefix_r; - in = in[sub_amt..]; - if (req.response.headers.status.class() == .redirect) continue; - - mem.copy(u8, buffer[out_index..], in[0..sub_amt]); - out_index += sub_amt; continue; } - if (req.response.headers.status.class() == .redirect) return 0; - - if (in.ptr == buffer.ptr) { - return sub_amt; - } else { - mem.copy(u8, buffer[out_index..], in[0..sub_amt]); - out_index += sub_amt; - return out_index; - } + return out_index; }, } } } + pub const ReadError = DeflateDecompressor.Error || GzipDecompressor.Error || WaitForCompleteHeadError || error{ + BadHeader, + InvalidCompression, + StreamTooLong, + InvalidWindowSize, + }; + + pub const Reader = std.io.Reader(*Request, ReadError, read); + + pub fn reader(req: *Request) Reader { + return .{ .context = req }; + } + + pub fn read(req: *Request, buffer: []u8) ReadError!usize { + if (!req.response.state.isContent()) try req.waitForCompleteHead(); + + if (req.response.compression == .none and req.response.state.isContent()) { + if (req.response.headers.transfer_compression) |compression| { + switch (compression) { + .compress => unreachable, + .deflate => req.response.compression = .{ + .deflate = try std.compress.zlib.zlibStream(req.client.allocator, ReaderRaw{ .context = req }), + }, + .gzip => req.response.compression = .{ + .gzip = try std.compress.gzip.decompress(req.client.allocator, ReaderRaw{ .context = req }), + }, + .chunked => unreachable, + } + } + } + + return switch (req.response.compression) { + .deflate => |*deflate| try deflate.read(buffer), + .gzip => |*gzip| try gzip.read(buffer), + else => try req.readRaw(buffer), + }; + } + + pub fn readAll(req: *Request, buffer: []u8) !usize { + var index: usize = 0; + while (index < buffer.len) { + const amt = try read(req, buffer[index..]); + if (amt == 0) break; + index += amt; + } + return index; + } + + pub const WriteError = Connection.WriteError || error{MessageTooLong}; + + pub const Writer = std.io.Writer(*Request, WriteError, write); + + pub fn writer(req: *Request) Writer { + return .{ .context = req }; + } + + /// Write `bytes` to the server. The `transfer_encoding` request header determines how data will be sent. + pub fn write(req: *Request, bytes: []const u8) !usize { + switch (req.headers.transfer_encoding) { + .chunked => { + try req.connection.data.writer().print("{x}\r\n", .{bytes.len}); + try req.connection.data.writeAll(bytes); + try req.connection.data.writeAll("\r\n"); + + return bytes.len; + }, + .content_length => |*len| { + if (len.* < bytes.len) return error.MessageTooLong; + + const amt = try req.connection.data.write(bytes); + len.* -= amt; + return amt; + }, + .none => return error.NotWriteable, + } + } + + /// Finish the body of a request. This notifies the server that you have no more data to send. + pub fn finish(req: *Request) !void { + switch (req.headers.transfer_encoding) { + .chunked => try req.connection.data.writeAll("0\r\n"), + .content_length => |len| if (len != 0) return error.MessageNotCompleted, + .none => {}, + } + } + inline fn int16(array: *const [2]u8) u16 { return @bitCast(u16, array.*); } @@ -917,6 +1075,10 @@ pub const Request = struct { } test { + const builtin = @import("builtin"); + + if (builtin.os.tag == .wasi) return error.SkipZigTest; + _ = Response; } }; @@ -931,23 +1093,39 @@ pub fn deinit(client: *Client) void { client.allocator.destroy(node); } + next = client.connection_used.first; + while (next) |node| { + next = node.next; + + node.data.close(client); + + client.allocator.destroy(node); + } + client.ca_bundle.deinit(client.allocator); client.* = undefined; } -pub fn connect(client: *Client, host: []const u8, port: u16, protocol: Connection.Protocol) !*ConnectionNode { - var potential = client.connection_pool.last; - while (potential) |node| { - const same_host = mem.eql(u8, node.data.host, host); - const same_port = node.data.port == port; - const same_protocol = node.data.protocol == protocol; +pub const ConnectError = std.mem.Allocator.Error || std.net.TcpConnectToHostError || std.crypto.tls.Client.InitError(std.net.Stream); - if (same_host and same_port and same_protocol) { - client.connection_pool.remove(node); - return node; +pub fn connect(client: *Client, host: []const u8, port: u16, protocol: Connection.Protocol) ConnectError!*ConnectionNode { + { // Search through the connection pool for a potential connection. + client.connection_mutex.lock(); + defer client.connection_mutex.unlock(); + + var potential = client.connection_pool.last; + while (potential) |node| { + const same_host = mem.eql(u8, node.data.host, host); + const same_port = node.data.port == port; + const same_protocol = node.data.protocol == protocol; + + if (same_host and same_port and same_protocol) { + client.acquire(node); + return node; + } + + potential = node.prev; } - - potential = node.prev; } const conn = try client.allocator.create(ConnectionNode); @@ -964,17 +1142,35 @@ pub fn connect(client: *Client, host: []const u8, port: u16, protocol: Connectio switch (protocol) { .plain => {}, .tls => { - conn.data.tls_client = try std.crypto.tls.Client.init(conn.data.stream, client.ca_bundle, host); + conn.data.tls_client = try client.allocator.create(std.crypto.tls.Client); + conn.data.tls_client.* = try std.crypto.tls.Client.init(conn.data.stream, client.ca_bundle, host); // This is appropriate for HTTPS because the HTTP headers contain // the content length which is used to detect truncation attacks. conn.data.tls_client.allow_truncation_attacks = true; }, } + { + client.connection_mutex.lock(); + defer client.connection_mutex.unlock(); + + client.connection_used.append(conn); + } + return conn; } -pub fn request(client: *Client, uri: Uri, headers: Request.Headers, options: Request.Options) !Request { +pub const RequestError = ConnectError || Connection.WriteError || error{ + UnsupportedUrlScheme, + UriMissingHost, + + CertificateAuthorityBundleTooBig, + InvalidPadding, + MissingEndCertificateMarker, + Unseekable, +}; + +pub fn request(client: *Client, uri: Uri, headers: Request.Headers, options: Request.Options) RequestError!Request { const protocol: Connection.Protocol = if (mem.eql(u8, uri.scheme, "http")) .plain else if (mem.eql(u8, uri.scheme, "https")) @@ -990,8 +1186,13 @@ pub fn request(client: *Client, uri: Uri, headers: Request.Headers, options: Req const host = uri.host orelse return error.UriMissingHost; if (client.next_https_rescan_certs and protocol == .tls) { - try client.ca_bundle.rescan(client.allocator); - client.next_https_rescan_certs = false; + client.connection_mutex.lock(); // TODO: this could be so much better than reusing the connection pool mutex. + defer client.connection_mutex.unlock(); + + if (client.next_https_rescan_certs) { + try client.ca_bundle.rescan(client.allocator); + client.next_https_rescan_certs = false; + } } var req: Request = .{ @@ -1006,23 +1207,39 @@ pub fn request(client: *Client, uri: Uri, headers: Request.Headers, options: Req }; { - var h = try std.BoundedArray(u8, 1000).init(0); - try h.appendSlice(@tagName(headers.method)); - try h.appendSlice(" "); - try h.appendSlice(uri.path); - try h.appendSlice(" "); - try h.appendSlice(@tagName(headers.version)); - try h.appendSlice("\r\nHost: "); - try h.appendSlice(host); - if (headers.connection_close) { - try h.appendSlice("\r\nConnection: close"); - } else { - try h.appendSlice("\r\nConnection: keep-alive"); - } - try h.appendSlice("\r\n\r\n"); + var buffered = std.io.bufferedWriter(req.connection.data.writer()); + const writer = buffered.writer(); - const header_bytes = h.slice(); - try req.connection.data.writeAll(header_bytes); + try writer.writeAll(@tagName(headers.method)); + try writer.writeByte(' '); + try writer.writeAll(uri.path); + try writer.writeByte(' '); + try writer.writeAll(@tagName(headers.version)); + try writer.writeAll("\r\nHost: "); + try writer.writeAll(host); + if (headers.connection == .close) { + try writer.writeAll("\r\nConnection: close"); + } else { + try writer.writeAll("\r\nConnection: keep-alive"); + } + try writer.writeAll("\r\nAccept-Encoding: gzip, deflate"); + + switch (headers.transfer_encoding) { + .chunked => try writer.writeAll("\r\nTransfer-Encoding: chunked"), + .content_length => |content_length| try writer.print("\r\nContent-Length: {d}", .{content_length}), + .none => {}, + } + + for (headers.custom) |header| { + try writer.writeAll("\r\n"); + try writer.writeAll(header.name); + try writer.writeAll(": "); + try writer.writeAll(header.value); + } + + try writer.writeAll("\r\n\r\n"); + + try buffered.flush(); } return req; @@ -1036,5 +1253,7 @@ test { return error.SkipZigTest; } + if (builtin.os.tag == .wasi) return error.SkipZigTest; + _ = Request; } From 0a4130f364c2714b206257d0cf589103da823407 Mon Sep 17 00:00:00 2001 From: Nameless Date: Mon, 6 Mar 2023 23:35:35 -0600 Subject: [PATCH 068/294] std.http: handle relative redirects --- lib/std/Uri.zig | 109 ++++++++++++++++++++++---- lib/std/crypto/tls/Client.zig | 2 +- lib/std/http/Client.zig | 140 +++++++++++++++++++++++----------- lib/std/net.zig | 6 +- 4 files changed, 196 insertions(+), 61 deletions(-) diff --git a/lib/std/Uri.zig b/lib/std/Uri.zig index 015b6c34f6..eb6311a19b 100644 --- a/lib/std/Uri.zig +++ b/lib/std/Uri.zig @@ -16,15 +16,27 @@ fragment: ?[]const u8, /// Applies URI encoding and replaces all reserved characters with their respective %XX code. pub fn escapeString(allocator: std.mem.Allocator, input: []const u8) error{OutOfMemory}![]const u8 { + return escapeStringWithFn(allocator, input, isUnreserved); +} + +pub fn escapePath(allocator: std.mem.Allocator, input: []const u8) error{OutOfMemory}![]const u8 { + return escapeStringWithFn(allocator, input, isPathChar); +} + +pub fn escapeQuery(allocator: std.mem.Allocator, input: []const u8) error{OutOfMemory}![]const u8 { + return escapeStringWithFn(allocator, input, isQueryChar); +} + +pub fn escapeStringWithFn(allocator: std.mem.Allocator, input: []const u8, comptime keepUnescaped: fn (c: u8) bool) std.mem.Allocator.Error![]const u8 { var outsize: usize = 0; for (input) |c| { - outsize += if (isUnreserved(c)) @as(usize, 1) else 3; + outsize += if (keepUnescaped(c)) @as(usize, 1) else 3; } var output = try allocator.alloc(u8, outsize); var outptr: usize = 0; for (input) |c| { - if (isUnreserved(c)) { + if (keepUnescaped(c)) { output[outptr] = c; outptr += 1; } else { @@ -94,13 +106,14 @@ pub fn unescapeString(allocator: std.mem.Allocator, input: []const u8) error{Out pub const ParseError = error{ UnexpectedCharacter, InvalidFormat, InvalidPort }; -/// Parses the URI or returns an error. +/// Parses the URI or returns an error. This function is not compliant, but is required to parse +/// some forms of URIs in the wild. Such as HTTP Location headers. /// The return value will contain unescaped strings pointing into the /// original `text`. Each component that is provided, will be non-`null`. -pub fn parse(text: []const u8) ParseError!Uri { +pub fn parseWithoutScheme(text: []const u8) ParseError!Uri { var reader = SliceReader{ .slice = text }; var uri = Uri{ - .scheme = reader.readWhile(isSchemeChar), + .scheme = "", .user = null, .password = null, .host = null, @@ -110,14 +123,6 @@ pub fn parse(text: []const u8) ParseError!Uri { .fragment = null, }; - // after the scheme, a ':' must appear - if (reader.get()) |c| { - if (c != ':') - return error.UnexpectedCharacter; - } else { - return error.InvalidFormat; - } - if (reader.peekPrefix("//")) { // authority part std.debug.assert(reader.get().? == '/'); std.debug.assert(reader.get().? == '/'); @@ -179,6 +184,76 @@ pub fn parse(text: []const u8) ParseError!Uri { return uri; } +/// Parses the URI or returns an error. +/// The return value will contain unescaped strings pointing into the +/// original `text`. Each component that is provided, will be non-`null`. +pub fn parse(text: []const u8) ParseError!Uri { + var reader = SliceReader{ .slice = text }; + const scheme = reader.readWhile(isSchemeChar); + + // after the scheme, a ':' must appear + if (reader.get()) |c| { + if (c != ':') + return error.UnexpectedCharacter; + } else { + return error.InvalidFormat; + } + + var uri = try parseWithoutScheme(reader.readUntilEof()); + uri.scheme = scheme; + + return uri; +} + +/// Resolves a URI against a base URI, conforming to RFC 3986, Section 5. +/// arena owns any memory allocated by this function. +pub fn resolve(Base: Uri, R: Uri, strict: bool, arena: std.mem.Allocator) !Uri { + var T: Uri = undefined; + + if (R.scheme.len > 0 and !((!strict) and (std.mem.eql(u8, R.scheme, Base.scheme)))) { + T.scheme = R.scheme; + T.user = R.user; + T.host = R.host; + T.port = R.port; + T.path = try std.fs.path.resolvePosix(arena, &.{ "/", R.path }); + T.query = R.query; + } else { + if (R.host) |host| { + T.user = R.user; + T.host = host; + T.port = R.port; + T.path = R.path; + T.path = try std.fs.path.resolvePosix(arena, &.{ "/", R.path }); + T.query = R.query; + } else { + if (R.path.len == 0) { + T.path = Base.path; + if (R.query) |query| { + T.query = query; + } else { + T.query = Base.query; + } + } else { + if (R.path[0] == '/') { + T.path = try std.fs.path.resolvePosix(arena, &.{ "/", R.path }); + } else { + T.path = try std.fs.path.resolvePosix(arena, &.{ "/", Base.path, R.path }); + } + T.query = R.query; + } + + T.user = Base.user; + T.host = Base.host; + T.port = Base.port; + } + T.scheme = Base.scheme; + } + + T.fragment = R.fragment; + + return T; +} + const SliceReader = struct { const Self = @This(); @@ -284,6 +359,14 @@ fn isPathSeparator(c: u8) bool { }; } +fn isPathChar(c: u8) bool { + return isUnreserved(c) or isSubLimit(c) or c == '/' or c == ':' or c == '@'; +} + +fn isQueryChar(c: u8) bool { + return isPathChar(c) or c == '?'; +} + fn isQuerySeparator(c: u8) bool { return switch (c) { '#' => true, diff --git a/lib/std/crypto/tls/Client.zig b/lib/std/crypto/tls/Client.zig index 01bf957820..bc59459ff9 100644 --- a/lib/std/crypto/tls/Client.zig +++ b/lib/std/crypto/tls/Client.zig @@ -89,7 +89,7 @@ pub const StreamInterface = struct { }; pub fn InitError(comptime Stream: type) type { - return std.mem.Allocator.Error || Stream.WriteError || Stream.ReadError || error { + return std.mem.Allocator.Error || Stream.WriteError || Stream.ReadError || error{ InsufficientEntropy, DiskQuota, LockViolation, diff --git a/lib/std/http/Client.zig b/lib/std/http/Client.zig index cac6571798..5b3a74d292 100644 --- a/lib/std/http/Client.zig +++ b/lib/std/http/Client.zig @@ -29,9 +29,10 @@ const ConnectionPool = std.TailQueue(Connection); const ConnectionNode = ConnectionPool.Node; /// Acquires an existing connection from the connection pool. This function is threadsafe. -pub fn acquire(client: *Client, node: *ConnectionNode) void { - client.connection_mutex.lock(); - defer client.connection_mutex.unlock(); +/// If the caller already holds the connection mutex, it should pass `true` for `held`. +pub fn acquire(client: *Client, node: *ConnectionNode, held: bool) void { + if (!held) client.connection_mutex.lock(); + defer if (!held) client.connection_mutex.unlock(); client.connection_pool.remove(node); client.connection_used.append(node); @@ -40,16 +41,17 @@ pub fn acquire(client: *Client, node: *ConnectionNode) void { /// Tries to release a connection back to the connection pool. This function is threadsafe. /// If the connection is marked as closing, it will be closed instead. pub fn release(client: *Client, node: *ConnectionNode) void { + client.connection_mutex.lock(); + defer client.connection_mutex.unlock(); + + client.connection_used.remove(node); + if (node.data.closing) { node.data.close(client); return client.allocator.destroy(node); } - client.connection_mutex.lock(); - defer client.connection_mutex.unlock(); - - client.connection_used.remove(node); client.connection_pool.append(node); } @@ -83,7 +85,7 @@ pub const Connection = struct { } } - pub const ReadError = std.net.Stream.ReadError || error{ + pub const ReadError = net.Stream.ReadError || error{ TlsConnectionTruncated, TlsRecordOverflow, TlsDecodeError, @@ -115,7 +117,7 @@ pub const Connection = struct { } } - pub const WriteError = std.net.Stream.WriteError || error{}; + pub const WriteError = net.Stream.WriteError || error{}; pub const Writer = std.io.Writer(*Connection, WriteError, write); pub fn writer(conn: *Connection) Writer { @@ -139,14 +141,21 @@ pub const Request = struct { const read_buffer_size = 8192; const ReadBufferIndex = std.math.IntFittingRange(0, read_buffer_size); + uri: Uri, client: *Client, connection: *ConnectionNode, - redirects_left: u32, response: Response, /// These are stored in Request so that they are available when following /// redirects. headers: Headers, + redirects_left: u32, + handle_redirects: bool, + compression_init: bool, + + /// Used as a allocator for resolving redirects locations. + arena: std.heap.ArenaAllocator, + /// Read buffer for the connection. This is used to pull in large amounts of data from the connection even if the user asks for a small amount. This can probably be removed with careful planning. read_buffer: [read_buffer_size]u8 = undefined, read_buffer_start: ReadBufferIndex = 0, @@ -661,6 +670,7 @@ pub const Request = struct { pub const Headers = struct { version: http.Version = .@"HTTP/1.1", method: http.Method = .GET, + user_agent: []const u8 = "Zig (std.http)", connection: http.Connection = .keep_alive, transfer_encoding: RequestTransfer = .none, @@ -668,6 +678,7 @@ pub const Request = struct { }; pub const Options = struct { + handle_redirects: bool = true, max_redirects: u32 = 3, header_strategy: HeaderStrategy = .{ .dynamic = 16 * 1024 }, @@ -703,10 +714,11 @@ pub const Request = struct { req.client.release(req.connection); } + req.arena.deinit(); req.* = undefined; } - const ReadRawError = Connection.ReadError || std.Uri.ParseError || RequestError || error{ + const ReadRawError = Connection.ReadError || Uri.ParseError || RequestError || error{ UnexpectedEndOfStream, TooManyHttpRedirects, HttpRedirectMissingLocation, @@ -723,9 +735,7 @@ pub const Request = struct { var index: usize = 0; while (index == 0) { const amt = try req.readRawAdvanced(buffer[index..]); - const zero_means_end = req.response.done and req.response.headers.status.class() != .redirect; - - if (amt == 0 and zero_means_end) break; + if (amt == 0 and req.response.done) break; index += amt; } @@ -769,6 +779,8 @@ pub const Request = struct { } } else if (req.response.headers.content_length) |content_length| { req.response.next_chunk_length = content_length; + + if (content_length == 0) req.response.done = true; } else { req.response.done = true; } @@ -779,7 +791,7 @@ pub const Request = struct { return 0; } - pub const WaitForCompleteHeadError = ReadRawError || error { + pub const WaitForCompleteHeadError = ReadRawError || error{ UnexpectedEndOfStream, HttpHeadersExceededSizeLimit, @@ -810,27 +822,8 @@ pub const Request = struct { /// This one can return 0 without meaning EOF. fn readRawAdvanced(req: *Request, buffer: []u8) !usize { - if (req.response.done) { - if (req.response.headers.status.class() == .redirect) { - if (req.redirects_left == 0) return error.TooManyHttpRedirects; - - const location = req.response.headers.location orelse - return error.HttpRedirectMissingLocation; - const new_url = try std.Uri.parse(location); - const new_req = try req.client.request(new_url, req.headers, .{ - .max_redirects = req.redirects_left - 1, - .header_strategy = if (req.response.header_bytes_owned) .{ - .dynamic = req.response.max_header_bytes, - } else .{ - .static = req.response.header_bytes.unusedCapacitySlice(), - }, - }); - req.deinit(); - req.* = new_req; - } else { - return 0; - } - } + assert(req.response.state.isContent()); + if (req.response.done) return 0; // var in: []const u8 = undefined; if (req.read_buffer_start == req.read_buffer_len) { @@ -851,7 +844,7 @@ pub const Request = struct { const data_avail = req.response.next_chunk_length; const out_avail = buffer.len; - if (req.response.state.isContent() and req.response.headers.status.class() == .redirect) { + if (req.handle_redirects and req.response.headers.status.class() == .redirect) { const can_read = @intCast(usize, @min(buf_avail, data_avail)); req.response.next_chunk_length -= can_read; @@ -859,7 +852,6 @@ pub const Request = struct { req.client.release(req.connection); req.connection = undefined; req.response.done = true; - continue; } return 0; // skip over as much data as possible @@ -943,7 +935,7 @@ pub const Request = struct { const data_avail = req.response.next_chunk_length; const out_avail = buffer.len - out_index; - if (req.response.state.isContent() and req.response.headers.status.class() == .redirect) { + if (req.handle_redirects and req.response.headers.status.class() == .redirect) { const can_read = @intCast(usize, @min(buf_avail, data_avail)); req.response.next_chunk_length -= can_read; @@ -990,9 +982,41 @@ pub const Request = struct { } pub fn read(req: *Request, buffer: []u8) ReadError!usize { - if (!req.response.state.isContent()) try req.waitForCompleteHead(); + while (true) { + if (!req.response.state.isContent()) try req.waitForCompleteHead(); - if (req.response.compression == .none and req.response.state.isContent()) { + if (req.handle_redirects and req.response.headers.status.class() == .redirect) { + assert(try req.readRaw(buffer) == 0); + + if (req.redirects_left == 0) return error.TooManyHttpRedirects; + + const location = req.response.headers.location orelse + return error.HttpRedirectMissingLocation; + const new_url = Uri.parse(location) catch try Uri.parseWithoutScheme(location); + + var new_arena = std.heap.ArenaAllocator.init(req.client.allocator); + const resolved_url = try req.uri.resolve(new_url, false, new_arena.allocator()); + errdefer new_arena.deinit(); + + req.arena.deinit(); + req.arena = new_arena; + + const new_req = try req.client.request(resolved_url, req.headers, .{ + .max_redirects = req.redirects_left - 1, + .header_strategy = if (req.response.header_bytes_owned) .{ + .dynamic = req.response.max_header_bytes, + } else .{ + .static = req.response.header_bytes.unusedCapacitySlice(), + }, + }); + req.deinit(); + req.* = new_req; + } else { + break; + } + } + + if (req.response.compression == .none) { if (req.response.headers.transfer_compression) |compression| { switch (compression) { .compress => unreachable, @@ -1084,6 +1108,8 @@ pub const Request = struct { }; pub fn deinit(client: *Client) void { + client.connection_mutex.lock(); + var next = client.connection_pool.first; while (next) |node| { next = node.next; @@ -1106,7 +1132,7 @@ pub fn deinit(client: *Client) void { client.* = undefined; } -pub const ConnectError = std.mem.Allocator.Error || std.net.TcpConnectToHostError || std.crypto.tls.Client.InitError(std.net.Stream); +pub const ConnectError = std.mem.Allocator.Error || net.TcpConnectToHostError || std.crypto.tls.Client.InitError(net.Stream); pub fn connect(client: *Client, host: []const u8, port: u16, protocol: Connection.Protocol) ConnectError!*ConnectionNode { { // Search through the connection pool for a potential connection. @@ -1120,7 +1146,7 @@ pub fn connect(client: *Client, host: []const u8, port: u16, protocol: Connectio const same_protocol = node.data.protocol == protocol; if (same_host and same_port and same_protocol) { - client.acquire(node); + client.acquire(node, true); return node; } @@ -1168,6 +1194,7 @@ pub const RequestError = ConnectError || Connection.WriteError || error{ InvalidPadding, MissingEndCertificateMarker, Unseekable, + EndOfStream, }; pub fn request(client: *Client, uri: Uri, headers: Request.Headers, options: Request.Options) RequestError!Request { @@ -1196,27 +1223,52 @@ pub fn request(client: *Client, uri: Uri, headers: Request.Headers, options: Req } var req: Request = .{ + .uri = uri, .client = client, .headers = headers, .connection = try client.connect(host, port, protocol), .redirects_left = options.max_redirects, + .handle_redirects = options.handle_redirects, + .compression_init = false, .response = switch (options.header_strategy) { .dynamic => |max| Request.Response.initDynamic(max), .static => |buf| Request.Response.initStatic(buf), }, + .arena = undefined, }; + req.arena = std.heap.ArenaAllocator.init(client.allocator); + { var buffered = std.io.bufferedWriter(req.connection.data.writer()); const writer = buffered.writer(); + const escaped_path = try Uri.escapePath(client.allocator, uri.path); + defer client.allocator.free(escaped_path); + + const escaped_query = if (uri.query) |q| try Uri.escapeQuery(client.allocator, q) else null; + defer if (escaped_query) |q| client.allocator.free(q); + + const escaped_fragment = if (uri.fragment) |f| try Uri.escapeQuery(client.allocator, f) else null; + defer if (escaped_fragment) |f| client.allocator.free(f); + try writer.writeAll(@tagName(headers.method)); try writer.writeByte(' '); - try writer.writeAll(uri.path); + try writer.writeAll(escaped_path); + if (escaped_query) |q| { + try writer.writeByte('?'); + try writer.writeAll(q); + } + if (escaped_fragment) |f| { + try writer.writeByte('#'); + try writer.writeAll(f); + } try writer.writeByte(' '); try writer.writeAll(@tagName(headers.version)); try writer.writeAll("\r\nHost: "); try writer.writeAll(host); + try writer.writeAll("\r\nUser-Agent: "); + try writer.writeAll(headers.user_agent); if (headers.connection == .close) { try writer.writeAll("\r\nConnection: close"); } else { diff --git a/lib/std/net.zig b/lib/std/net.zig index cf112cbab9..7222433fd5 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -741,9 +741,9 @@ pub fn tcpConnectToAddress(address: Address) TcpConnectToAddressError!Stream { return Stream{ .handle = sockfd }; } -const GetAddressListError = std.mem.Allocator.Error || std.fs.File.OpenError || std.fs.File.ReadError || std.os.SocketError || std.os.BindError || error { +const GetAddressListError = std.mem.Allocator.Error || std.fs.File.OpenError || std.fs.File.ReadError || std.os.SocketError || std.os.BindError || error{ // TODO: break this up into error sets from the various underlying functions - + TemporaryNameServerFailure, NameServerFailure, AddressFamilyNotSupported, @@ -760,7 +760,7 @@ const GetAddressListError = std.mem.Allocator.Error || std.fs.File.OpenError || Incomplete, InvalidIpv4Mapping, InvalidIPAddressFormat, - + InterfaceNotFound, FileSystem, }; From 634e7155048aeaf15553d866783930f3d22b375c Mon Sep 17 00:00:00 2001 From: Nameless Date: Wed, 8 Mar 2023 08:20:53 -0600 Subject: [PATCH 069/294] std.http: split Client's parts into their own files --- lib/std/http.zig | 5 + lib/std/http/Client.zig | 988 +------------------------------ lib/std/http/Client/Request.zig | 488 +++++++++++++++ lib/std/http/Client/Response.zig | 506 ++++++++++++++++ 4 files changed, 1010 insertions(+), 977 deletions(-) create mode 100644 lib/std/http/Client/Request.zig create mode 100644 lib/std/http/Client/Response.zig diff --git a/lib/std/http.zig b/lib/std/http.zig index d4cc259f19..ef89f09925 100644 --- a/lib/std/http.zig +++ b/lib/std/http.zig @@ -248,9 +248,14 @@ pub const Status = enum(u10) { pub const TransferEncoding = enum { chunked, + // compression is intentionally omitted here, as std.http.Client stores it as content-encoding +}; + +pub const ContentEncoding = enum { compress, deflate, gzip, + zstd, }; pub const Connection = enum { diff --git a/lib/std/http/Client.zig b/lib/std/http/Client.zig index 5b3a74d292..d44b1d098d 100644 --- a/lib/std/http/Client.zig +++ b/lib/std/http/Client.zig @@ -13,6 +13,9 @@ const Uri = std.Uri; const Allocator = std.mem.Allocator; const testing = std.testing; +pub const Request = @import("Client/Request.zig"); +pub const Response = @import("Client/Response.zig"); + /// Used for tcpConnectToHost and storing HTTP headers when an externally /// managed buffer is not provided. allocator: Allocator, @@ -25,8 +28,8 @@ connection_mutex: std.Thread.Mutex = .{}, connection_pool: ConnectionPool = .{}, connection_used: ConnectionPool = .{}, -const ConnectionPool = std.TailQueue(Connection); -const ConnectionNode = ConnectionPool.Node; +pub const ConnectionPool = std.TailQueue(Connection); +pub const ConnectionNode = ConnectionPool.Node; /// Acquires an existing connection from the connection pool. This function is threadsafe. /// If the caller already holds the connection mutex, it should pass `true` for `held`. @@ -55,8 +58,9 @@ pub fn release(client: *Client, node: *ConnectionNode) void { client.connection_pool.append(node); } -const DeflateDecompressor = std.compress.zlib.ZlibStream(Request.ReaderRaw); -const GzipDecompressor = std.compress.gzip.Decompress(Request.ReaderRaw); +pub const DeflateDecompressor = std.compress.zlib.ZlibStream(Request.ReaderRaw); +pub const GzipDecompressor = std.compress.gzip.Decompress(Request.ReaderRaw); +pub const ZstdDecompressor = std.compress.zstd.DecompressStream(Request.ReaderRaw, .{}); pub const Connection = struct { stream: net.Stream, @@ -137,976 +141,6 @@ pub const Connection = struct { } }; -pub const Request = struct { - const read_buffer_size = 8192; - const ReadBufferIndex = std.math.IntFittingRange(0, read_buffer_size); - - uri: Uri, - client: *Client, - connection: *ConnectionNode, - response: Response, - /// These are stored in Request so that they are available when following - /// redirects. - headers: Headers, - - redirects_left: u32, - handle_redirects: bool, - compression_init: bool, - - /// Used as a allocator for resolving redirects locations. - arena: std.heap.ArenaAllocator, - - /// Read buffer for the connection. This is used to pull in large amounts of data from the connection even if the user asks for a small amount. This can probably be removed with careful planning. - read_buffer: [read_buffer_size]u8 = undefined, - read_buffer_start: ReadBufferIndex = 0, - read_buffer_len: ReadBufferIndex = 0, - - pub const Response = struct { - headers: Response.Headers, - state: State, - header_bytes_owned: bool, - /// This could either be a fixed buffer provided by the API user or it - /// could be our own array list. - header_bytes: std.ArrayListUnmanaged(u8), - max_header_bytes: usize, - next_chunk_length: u64, - done: bool = false, - - compression: union(enum) { - deflate: DeflateDecompressor, - gzip: GzipDecompressor, - none: void, - } = .none, - - pub const Headers = struct { - status: http.Status, - version: http.Version, - location: ?[]const u8 = null, - content_length: ?u64 = null, - transfer_encoding: ?http.TransferEncoding = null, // This should only ever be chunked, compression is handled separately. - transfer_compression: ?http.TransferEncoding = null, - connection: http.Connection = .close, - - number_of_headers: usize = 0, - - pub fn parse(bytes: []const u8) !Response.Headers { - var it = mem.split(u8, bytes[0 .. bytes.len - 4], "\r\n"); - - const first_line = it.first(); - if (first_line.len < 12) - return error.ShortHttpStatusLine; - - const version: http.Version = switch (int64(first_line[0..8])) { - int64("HTTP/1.0") => .@"HTTP/1.0", - int64("HTTP/1.1") => .@"HTTP/1.1", - else => return error.BadHttpVersion, - }; - if (first_line[8] != ' ') return error.HttpHeadersInvalid; - const status = @intToEnum(http.Status, parseInt3(first_line[9..12].*)); - - var headers: Response.Headers = .{ - .version = version, - .status = status, - }; - - while (it.next()) |line| { - headers.number_of_headers += 1; - - if (line.len == 0) return error.HttpHeadersInvalid; - switch (line[0]) { - ' ', '\t' => return error.HttpHeaderContinuationsUnsupported, - else => {}, - } - var line_it = mem.split(u8, line, ": "); - const header_name = line_it.first(); - const header_value = line_it.rest(); - if (std.ascii.eqlIgnoreCase(header_name, "location")) { - if (headers.location != null) return error.HttpHeadersInvalid; - headers.location = header_value; - } else if (std.ascii.eqlIgnoreCase(header_name, "content-length")) { - if (headers.content_length != null) return error.HttpHeadersInvalid; - headers.content_length = try std.fmt.parseInt(u64, header_value, 10); - } else if (std.ascii.eqlIgnoreCase(header_name, "transfer-encoding")) { - if (headers.transfer_encoding != null or headers.transfer_compression != null) return error.HttpHeadersInvalid; - - // Transfer-Encoding: second, first - // Transfer-Encoding: deflate, chunked - var iter = std.mem.splitBackwards(u8, header_value, ","); - - if (iter.next()) |first| { - const kind = std.meta.stringToEnum( - http.TransferEncoding, - std.mem.trim(u8, first, " "), - ) orelse - return error.HttpTransferEncodingUnsupported; - - switch (kind) { - .chunked => headers.transfer_encoding = .chunked, - .compress => headers.transfer_compression = .compress, - .deflate => headers.transfer_compression = .deflate, - .gzip => headers.transfer_compression = .gzip, - } - } - - if (iter.next()) |second| { - if (headers.transfer_compression != null) return error.HttpTransferEncodingUnsupported; - - const kind = std.meta.stringToEnum( - http.TransferEncoding, - std.mem.trim(u8, second, " "), - ) orelse - return error.HttpTransferEncodingUnsupported; - - switch (kind) { - .chunked => return error.HttpHeadersInvalid, // chunked must come last - .compress => return error.HttpTransferEncodingUnsupported, // compress not supported - .deflate => headers.transfer_compression = .deflate, - .gzip => headers.transfer_compression = .gzip, - } - } - - if (iter.next()) |_| return error.HttpTransferEncodingUnsupported; - } else if (std.ascii.eqlIgnoreCase(header_name, "content-encoding")) { - if (headers.transfer_compression != null) return error.HttpHeadersInvalid; - - const kind = std.meta.stringToEnum( - http.TransferEncoding, - std.mem.trim(u8, header_value, " "), - ) orelse - return error.HttpTransferEncodingUnsupported; - - switch (kind) { - .chunked => return error.HttpHeadersInvalid, // not transfer encoding - .compress => return error.HttpTransferEncodingUnsupported, // compress not supported - .deflate => headers.transfer_compression = .deflate, - .gzip => headers.transfer_compression = .gzip, - } - } else if (std.ascii.eqlIgnoreCase(header_name, "connection")) { - if (std.ascii.eqlIgnoreCase(header_value, "keep-alive")) { - headers.connection = .keep_alive; - } else if (std.ascii.eqlIgnoreCase(header_value, "close")) { - headers.connection = .close; - } else { - return error.HttpConnectionHeaderUnsupported; - } - } - } - - return headers; - } - - test "parse headers" { - const example = - "HTTP/1.1 301 Moved Permanently\r\n" ++ - "Location: https://www.example.com/\r\n" ++ - "Content-Type: text/html; charset=UTF-8\r\n" ++ - "Content-Length: 220\r\n\r\n"; - const parsed = try Response.Headers.parse(example); - try testing.expectEqual(http.Version.@"HTTP/1.1", parsed.version); - try testing.expectEqual(http.Status.moved_permanently, parsed.status); - try testing.expectEqualStrings("https://www.example.com/", parsed.location orelse - return error.TestFailed); - try testing.expectEqual(@as(?u64, 220), parsed.content_length); - } - - test "header continuation" { - const example = - "HTTP/1.0 200 OK\r\n" ++ - "Content-Type: text/html;\r\n charset=UTF-8\r\n" ++ - "Content-Length: 220\r\n\r\n"; - try testing.expectError( - error.HttpHeaderContinuationsUnsupported, - Response.Headers.parse(example), - ); - } - - test "extra content length" { - const example = - "HTTP/1.0 200 OK\r\n" ++ - "Content-Length: 220\r\n" ++ - "Content-Type: text/html; charset=UTF-8\r\n" ++ - "content-length: 220\r\n\r\n"; - try testing.expectError( - error.HttpHeadersInvalid, - Response.Headers.parse(example), - ); - } - }; - - pub const State = enum { - /// Begin header parsing states. - invalid, - start, - seen_r, - seen_rn, - seen_rnr, - finished, - /// Begin transfer-encoding: chunked parsing states. - chunk_size_prefix_r, - chunk_size_prefix_n, - chunk_size, - chunk_r, - chunk_data, - - pub fn isContent(self: State) bool { - return switch (self) { - .invalid, .start, .seen_r, .seen_rn, .seen_rnr => false, - .finished, .chunk_size_prefix_r, .chunk_size_prefix_n, .chunk_size, .chunk_r, .chunk_data => true, - }; - } - }; - - pub fn initDynamic(max: usize) Response { - return .{ - .state = .start, - .headers = undefined, - .header_bytes = .{}, - .max_header_bytes = max, - .header_bytes_owned = true, - .next_chunk_length = undefined, - }; - } - - pub fn initStatic(buf: []u8) Response { - return .{ - .state = .start, - .headers = undefined, - .header_bytes = .{ .items = buf[0..0], .capacity = buf.len }, - .max_header_bytes = buf.len, - .header_bytes_owned = false, - .next_chunk_length = undefined, - }; - } - - /// Returns how many bytes are part of HTTP headers. Always less than or - /// equal to bytes.len. If the amount returned is less than bytes.len, it - /// means the headers ended and the first byte after the double \r\n\r\n is - /// located at `bytes[result]`. - pub fn findHeadersEnd(r: *Response, bytes: []const u8) usize { - var index: usize = 0; - - // TODO: https://github.com/ziglang/zig/issues/8220 - state: while (true) { - switch (r.state) { - .invalid => unreachable, - .finished => unreachable, - .start => while (true) { - switch (bytes.len - index) { - 0 => return index, - 1 => { - if (bytes[index] == '\r') - r.state = .seen_r; - return index + 1; - }, - 2 => { - if (int16(bytes[index..][0..2]) == int16("\r\n")) { - r.state = .seen_rn; - } else if (bytes[index + 1] == '\r') { - r.state = .seen_r; - } - return index + 2; - }, - 3 => { - if (int16(bytes[index..][0..2]) == int16("\r\n") and - bytes[index + 2] == '\r') - { - r.state = .seen_rnr; - } else if (int16(bytes[index + 1 ..][0..2]) == int16("\r\n")) { - r.state = .seen_rn; - } else if (bytes[index + 2] == '\r') { - r.state = .seen_r; - } - return index + 3; - }, - 4...15 => { - if (int32(bytes[index..][0..4]) == int32("\r\n\r\n")) { - r.state = .finished; - return index + 4; - } else if (int16(bytes[index + 1 ..][0..2]) == int16("\r\n") and - bytes[index + 3] == '\r') - { - r.state = .seen_rnr; - index += 4; - continue :state; - } else if (int16(bytes[index + 2 ..][0..2]) == int16("\r\n")) { - r.state = .seen_rn; - index += 4; - continue :state; - } else if (bytes[index + 3] == '\r') { - r.state = .seen_r; - index += 4; - continue :state; - } - index += 4; - continue; - }, - else => { - const chunk = bytes[index..][0..16]; - const v: @Vector(16, u8) = chunk.*; - const matches_r = v == @splat(16, @as(u8, '\r')); - const iota = std.simd.iota(u8, 16); - const default = @splat(16, @as(u8, 16)); - const sub_index = @reduce(.Min, @select(u8, matches_r, iota, default)); - switch (sub_index) { - 0...12 => { - index += sub_index + 4; - if (int32(chunk[sub_index..][0..4]) == int32("\r\n\r\n")) { - r.state = .finished; - return index; - } - continue; - }, - 13 => { - index += 16; - if (int16(chunk[14..][0..2]) == int16("\n\r")) { - r.state = .seen_rnr; - continue :state; - } - continue; - }, - 14 => { - index += 16; - if (chunk[15] == '\n') { - r.state = .seen_rn; - continue :state; - } - continue; - }, - 15 => { - r.state = .seen_r; - index += 16; - continue :state; - }, - 16 => { - index += 16; - continue; - }, - else => unreachable, - } - }, - } - }, - - .seen_r => switch (bytes.len - index) { - 0 => return index, - 1 => { - switch (bytes[index]) { - '\n' => r.state = .seen_rn, - '\r' => r.state = .seen_r, - else => r.state = .start, - } - return index + 1; - }, - 2 => { - if (int16(bytes[index..][0..2]) == int16("\n\r")) { - r.state = .seen_rnr; - return index + 2; - } - r.state = .start; - return index + 2; - }, - else => { - if (int16(bytes[index..][0..2]) == int16("\n\r") and - bytes[index + 2] == '\n') - { - r.state = .finished; - return index + 3; - } - index += 3; - r.state = .start; - continue :state; - }, - }, - .seen_rn => switch (bytes.len - index) { - 0 => return index, - 1 => { - switch (bytes[index]) { - '\r' => r.state = .seen_rnr, - else => r.state = .start, - } - return index + 1; - }, - else => { - if (int16(bytes[index..][0..2]) == int16("\r\n")) { - r.state = .finished; - return index + 2; - } - index += 2; - r.state = .start; - continue :state; - }, - }, - .seen_rnr => switch (bytes.len - index) { - 0 => return index, - else => { - if (bytes[index] == '\n') { - r.state = .finished; - return index + 1; - } - index += 1; - r.state = .start; - continue :state; - }, - }, - .chunk_size_prefix_r => unreachable, - .chunk_size_prefix_n => unreachable, - .chunk_size => unreachable, - .chunk_r => unreachable, - .chunk_data => unreachable, - } - - return index; - } - } - - pub fn findChunkedLen(r: *Response, bytes: []const u8) usize { - var i: usize = 0; - if (r.state == .chunk_size) { - while (i < bytes.len) : (i += 1) { - const digit = switch (bytes[i]) { - '0'...'9' => |b| b - '0', - 'A'...'Z' => |b| b - 'A' + 10, - 'a'...'z' => |b| b - 'a' + 10, - '\r' => { - r.state = .chunk_r; - i += 1; - break; - }, - else => { - r.state = .invalid; - return i; - }, - }; - const mul = @mulWithOverflow(r.next_chunk_length, 16); - if (mul[1] != 0) { - r.state = .invalid; - return i; - } - const add = @addWithOverflow(mul[0], digit); - if (add[1] != 0) { - r.state = .invalid; - return i; - } - r.next_chunk_length = add[0]; - } else { - return i; - } - } - assert(r.state == .chunk_r); - if (i == bytes.len) return i; - - if (bytes[i] == '\n') { - r.state = .chunk_data; - return i + 1; - } else { - r.state = .invalid; - return i; - } - } - - fn parseInt3(nnn: @Vector(3, u8)) u10 { - const zero: @Vector(3, u8) = .{ '0', '0', '0' }; - const mmm: @Vector(3, u10) = .{ 100, 10, 1 }; - return @reduce(.Add, @as(@Vector(3, u10), nnn -% zero) *% mmm); - } - - test parseInt3 { - const expectEqual = std.testing.expectEqual; - try expectEqual(@as(u10, 0), parseInt3("000".*)); - try expectEqual(@as(u10, 418), parseInt3("418".*)); - try expectEqual(@as(u10, 999), parseInt3("999".*)); - } - - test "find headers end basic" { - var buffer: [1]u8 = undefined; - var r = Response.initStatic(&buffer); - try testing.expectEqual(@as(usize, 10), r.findHeadersEnd("HTTP/1.1 4")); - try testing.expectEqual(@as(usize, 2), r.findHeadersEnd("18")); - try testing.expectEqual(@as(usize, 8), r.findHeadersEnd(" lol\r\n\r\nblah blah")); - } - - test "find headers end vectorized" { - var buffer: [1]u8 = undefined; - var r = Response.initStatic(&buffer); - const example = - "HTTP/1.1 301 Moved Permanently\r\n" ++ - "Location: https://www.example.com/\r\n" ++ - "Content-Type: text/html; charset=UTF-8\r\n" ++ - "Content-Length: 220\r\n" ++ - "\r\ncontent"; - try testing.expectEqual(@as(usize, 131), r.findHeadersEnd(example)); - } - - test "find headers end bug" { - var buffer: [1]u8 = undefined; - var r = Response.initStatic(&buffer); - const trail = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; - const example = - "HTTP/1.1 200 OK\r\n" ++ - "Access-Control-Allow-Origin: https://render.githubusercontent.com\r\n" ++ - "content-disposition: attachment; filename=zig-0.10.0.tar.gz\r\n" ++ - "Content-Security-Policy: default-src 'none'; style-src 'unsafe-inline'; sandbox\r\n" ++ - "Content-Type: application/x-gzip\r\n" ++ - "ETag: \"bfae0af6b01c7c0d89eb667cb5f0e65265968aeebda2689177e6b26acd3155ca\"\r\n" ++ - "Strict-Transport-Security: max-age=31536000\r\n" ++ - "Vary: Authorization,Accept-Encoding,Origin\r\n" ++ - "X-Content-Type-Options: nosniff\r\n" ++ - "X-Frame-Options: deny\r\n" ++ - "X-XSS-Protection: 1; mode=block\r\n" ++ - "Date: Fri, 06 Jan 2023 22:26:22 GMT\r\n" ++ - "Transfer-Encoding: chunked\r\n" ++ - "X-GitHub-Request-Id: 89C6:17E9:A7C9E:124B51:63B8A00E\r\n" ++ - "connection: close\r\n\r\n" ++ trail; - try testing.expectEqual(@as(usize, example.len - trail.len), r.findHeadersEnd(example)); - } - }; - - pub const RequestTransfer = union(enum) { - content_length: u64, - chunked: void, - none: void, - }; - - pub const Headers = struct { - version: http.Version = .@"HTTP/1.1", - method: http.Method = .GET, - user_agent: []const u8 = "Zig (std.http)", - connection: http.Connection = .keep_alive, - transfer_encoding: RequestTransfer = .none, - - custom: []const http.CustomHeader = &[_]http.CustomHeader{}, - }; - - pub const Options = struct { - handle_redirects: bool = true, - max_redirects: u32 = 3, - header_strategy: HeaderStrategy = .{ .dynamic = 16 * 1024 }, - - pub const HeaderStrategy = union(enum) { - /// In this case, the client's Allocator will be used to store the - /// entire HTTP header. This value is the maximum total size of - /// HTTP headers allowed, otherwise - /// error.HttpHeadersExceededSizeLimit is returned from read(). - dynamic: usize, - /// This is used to store the entire HTTP header. If the HTTP - /// header is too big to fit, `error.HttpHeadersExceededSizeLimit` - /// is returned from read(). When this is used, `error.OutOfMemory` - /// cannot be returned from `read()`. - static: []u8, - }; - }; - - /// Frees all resources associated with the request. - pub fn deinit(req: *Request) void { - switch (req.response.compression) { - .none => {}, - .deflate => |*deflate| deflate.deinit(), - .gzip => |*gzip| gzip.deinit(), - } - - if (req.response.header_bytes_owned) { - req.response.header_bytes.deinit(req.client.allocator); - } - - if (!req.response.done) { - // If the response wasn't fully read, then we need to close the connection. - req.connection.data.closing = true; - req.client.release(req.connection); - } - - req.arena.deinit(); - req.* = undefined; - } - - const ReadRawError = Connection.ReadError || Uri.ParseError || RequestError || error{ - UnexpectedEndOfStream, - TooManyHttpRedirects, - HttpRedirectMissingLocation, - HttpHeadersInvalid, - }; - - const ReaderRaw = std.io.Reader(*Request, ReadRawError, readRaw); - - /// Read from the underlying stream, without decompressing or parsing the headers. Must be called - /// after waitForCompleteHead() has returned successfully. - pub fn readRaw(req: *Request, buffer: []u8) ReadRawError!usize { - assert(req.response.state.isContent()); - - var index: usize = 0; - while (index == 0) { - const amt = try req.readRawAdvanced(buffer[index..]); - if (amt == 0 and req.response.done) break; - index += amt; - } - - return index; - } - - fn checkForCompleteHead(req: *Request, buffer: []u8) !usize { - switch (req.response.state) { - .invalid => unreachable, - .start, .seen_r, .seen_rn, .seen_rnr => {}, - else => return 0, // No more headers to read. - } - - const i = req.response.findHeadersEnd(buffer[0..]); - if (req.response.state == .invalid) return error.HttpHeadersInvalid; - - const headers_data = buffer[0..i]; - if (req.response.header_bytes.items.len + headers_data.len > req.response.max_header_bytes) { - return error.HttpHeadersExceededSizeLimit; - } - try req.response.header_bytes.appendSlice(req.client.allocator, headers_data); - - if (req.response.state == .finished) { - req.response.headers = try Response.Headers.parse(req.response.header_bytes.items); - - if (req.response.headers.connection == .keep_alive) { - req.connection.data.closing = false; - } else { - req.connection.data.closing = true; - } - - if (req.response.headers.transfer_encoding) |transfer_encoding| { - switch (transfer_encoding) { - .chunked => { - req.response.next_chunk_length = 0; - req.response.state = .chunk_size; - }, - .compress => unreachable, - .deflate => unreachable, - .gzip => unreachable, - } - } else if (req.response.headers.content_length) |content_length| { - req.response.next_chunk_length = content_length; - - if (content_length == 0) req.response.done = true; - } else { - req.response.done = true; - } - - return i; - } - - return 0; - } - - pub const WaitForCompleteHeadError = ReadRawError || error{ - UnexpectedEndOfStream, - - HttpHeadersExceededSizeLimit, - ShortHttpStatusLine, - BadHttpVersion, - HttpHeaderContinuationsUnsupported, - HttpTransferEncodingUnsupported, - HttpConnectionHeaderUnsupported, - }; - - /// Reads a complete response head. Any leftover data is stored in the request. This function is idempotent. - pub fn waitForCompleteHead(req: *Request) WaitForCompleteHeadError!void { - if (req.response.state.isContent()) return; - - while (true) { - const nread = try req.connection.data.read(req.read_buffer[0..]); - const amt = try checkForCompleteHead(req, req.read_buffer[0..nread]); - - if (amt != 0) { - req.read_buffer_start = @intCast(ReadBufferIndex, amt); - req.read_buffer_len = @intCast(ReadBufferIndex, nread); - return; - } else if (nread == 0) { - return error.UnexpectedEndOfStream; - } - } - } - - /// This one can return 0 without meaning EOF. - fn readRawAdvanced(req: *Request, buffer: []u8) !usize { - assert(req.response.state.isContent()); - if (req.response.done) return 0; - - // var in: []const u8 = undefined; - if (req.read_buffer_start == req.read_buffer_len) { - const nread = try req.connection.data.read(req.read_buffer[0..]); - if (nread == 0) return error.UnexpectedEndOfStream; - - req.read_buffer_start = 0; - req.read_buffer_len = @intCast(ReadBufferIndex, nread); - } - - var out_index: usize = 0; - while (true) { - switch (req.response.state) { - .invalid, .start, .seen_r, .seen_rn, .seen_rnr => unreachable, - .finished => { - // TODO https://github.com/ziglang/zig/issues/14039 - const buf_avail = req.read_buffer_len - req.read_buffer_start; - const data_avail = req.response.next_chunk_length; - const out_avail = buffer.len; - - if (req.handle_redirects and req.response.headers.status.class() == .redirect) { - const can_read = @intCast(usize, @min(buf_avail, data_avail)); - req.response.next_chunk_length -= can_read; - - if (req.response.next_chunk_length == 0) { - req.client.release(req.connection); - req.connection = undefined; - req.response.done = true; - } - - return 0; // skip over as much data as possible - } - - const can_read = @intCast(usize, @min(@min(buf_avail, data_avail), out_avail)); - req.response.next_chunk_length -= can_read; - - mem.copy(u8, buffer[0..], req.read_buffer[req.read_buffer_start..][0..can_read]); - req.read_buffer_start += @intCast(ReadBufferIndex, can_read); - - if (req.response.next_chunk_length == 0) { - req.client.release(req.connection); - req.connection = undefined; - req.response.done = true; - } - - return can_read; - }, - .chunk_size_prefix_r => switch (req.read_buffer_len - req.read_buffer_start) { - 0 => return out_index, - 1 => switch (req.read_buffer[req.read_buffer_start]) { - '\r' => { - req.response.state = .chunk_size_prefix_n; - return out_index; - }, - else => { - req.response.state = .invalid; - return error.HttpHeadersInvalid; - }, - }, - else => switch (int16(req.read_buffer[req.read_buffer_start..][0..2])) { - int16("\r\n") => { - req.read_buffer_start += 2; - req.response.state = .chunk_size; - continue; - }, - else => { - req.response.state = .invalid; - return error.HttpHeadersInvalid; - }, - }, - }, - .chunk_size_prefix_n => switch (req.read_buffer_len - req.read_buffer_start) { - 0 => return out_index, - else => switch (req.read_buffer[req.read_buffer_start]) { - '\n' => { - req.read_buffer_start += 1; - req.response.state = .chunk_size; - continue; - }, - else => { - req.response.state = .invalid; - return error.HttpHeadersInvalid; - }, - }, - }, - .chunk_size, .chunk_r => { - const i = req.response.findChunkedLen(req.read_buffer[req.read_buffer_start..req.read_buffer_len]); - switch (req.response.state) { - .invalid => return error.HttpHeadersInvalid, - .chunk_data => { - if (req.response.next_chunk_length == 0) { - req.response.done = true; - req.client.release(req.connection); - req.connection = undefined; - - return out_index; - } - - req.read_buffer_start += @intCast(ReadBufferIndex, i); - continue; - }, - .chunk_size => return out_index, - else => unreachable, - } - }, - .chunk_data => { - // TODO https://github.com/ziglang/zig/issues/14039 - const buf_avail = req.read_buffer_len - req.read_buffer_start; - const data_avail = req.response.next_chunk_length; - const out_avail = buffer.len - out_index; - - if (req.handle_redirects and req.response.headers.status.class() == .redirect) { - const can_read = @intCast(usize, @min(buf_avail, data_avail)); - req.response.next_chunk_length -= can_read; - - if (req.response.next_chunk_length == 0) { - req.client.release(req.connection); - req.connection = undefined; - req.response.done = true; - continue; - } - - return 0; // skip over as much data as possible - } - - const can_read = @intCast(usize, @min(@min(buf_avail, data_avail), out_avail)); - req.response.next_chunk_length -= can_read; - - mem.copy(u8, buffer[out_index..], req.read_buffer[req.read_buffer_start..][0..can_read]); - req.read_buffer_start += @intCast(ReadBufferIndex, can_read); - out_index += can_read; - - if (req.response.next_chunk_length == 0) { - req.response.state = .chunk_size_prefix_r; - - continue; - } - - return out_index; - }, - } - } - } - - pub const ReadError = DeflateDecompressor.Error || GzipDecompressor.Error || WaitForCompleteHeadError || error{ - BadHeader, - InvalidCompression, - StreamTooLong, - InvalidWindowSize, - }; - - pub const Reader = std.io.Reader(*Request, ReadError, read); - - pub fn reader(req: *Request) Reader { - return .{ .context = req }; - } - - pub fn read(req: *Request, buffer: []u8) ReadError!usize { - while (true) { - if (!req.response.state.isContent()) try req.waitForCompleteHead(); - - if (req.handle_redirects and req.response.headers.status.class() == .redirect) { - assert(try req.readRaw(buffer) == 0); - - if (req.redirects_left == 0) return error.TooManyHttpRedirects; - - const location = req.response.headers.location orelse - return error.HttpRedirectMissingLocation; - const new_url = Uri.parse(location) catch try Uri.parseWithoutScheme(location); - - var new_arena = std.heap.ArenaAllocator.init(req.client.allocator); - const resolved_url = try req.uri.resolve(new_url, false, new_arena.allocator()); - errdefer new_arena.deinit(); - - req.arena.deinit(); - req.arena = new_arena; - - const new_req = try req.client.request(resolved_url, req.headers, .{ - .max_redirects = req.redirects_left - 1, - .header_strategy = if (req.response.header_bytes_owned) .{ - .dynamic = req.response.max_header_bytes, - } else .{ - .static = req.response.header_bytes.unusedCapacitySlice(), - }, - }); - req.deinit(); - req.* = new_req; - } else { - break; - } - } - - if (req.response.compression == .none) { - if (req.response.headers.transfer_compression) |compression| { - switch (compression) { - .compress => unreachable, - .deflate => req.response.compression = .{ - .deflate = try std.compress.zlib.zlibStream(req.client.allocator, ReaderRaw{ .context = req }), - }, - .gzip => req.response.compression = .{ - .gzip = try std.compress.gzip.decompress(req.client.allocator, ReaderRaw{ .context = req }), - }, - .chunked => unreachable, - } - } - } - - return switch (req.response.compression) { - .deflate => |*deflate| try deflate.read(buffer), - .gzip => |*gzip| try gzip.read(buffer), - else => try req.readRaw(buffer), - }; - } - - pub fn readAll(req: *Request, buffer: []u8) !usize { - var index: usize = 0; - while (index < buffer.len) { - const amt = try read(req, buffer[index..]); - if (amt == 0) break; - index += amt; - } - return index; - } - - pub const WriteError = Connection.WriteError || error{MessageTooLong}; - - pub const Writer = std.io.Writer(*Request, WriteError, write); - - pub fn writer(req: *Request) Writer { - return .{ .context = req }; - } - - /// Write `bytes` to the server. The `transfer_encoding` request header determines how data will be sent. - pub fn write(req: *Request, bytes: []const u8) !usize { - switch (req.headers.transfer_encoding) { - .chunked => { - try req.connection.data.writer().print("{x}\r\n", .{bytes.len}); - try req.connection.data.writeAll(bytes); - try req.connection.data.writeAll("\r\n"); - - return bytes.len; - }, - .content_length => |*len| { - if (len.* < bytes.len) return error.MessageTooLong; - - const amt = try req.connection.data.write(bytes); - len.* -= amt; - return amt; - }, - .none => return error.NotWriteable, - } - } - - /// Finish the body of a request. This notifies the server that you have no more data to send. - pub fn finish(req: *Request) !void { - switch (req.headers.transfer_encoding) { - .chunked => try req.connection.data.writeAll("0\r\n"), - .content_length => |len| if (len != 0) return error.MessageNotCompleted, - .none => {}, - } - } - - inline fn int16(array: *const [2]u8) u16 { - return @bitCast(u16, array.*); - } - - inline fn int32(array: *const [4]u8) u32 { - return @bitCast(u32, array.*); - } - - inline fn int64(array: *const [8]u8) u64 { - return @bitCast(u64, array.*); - } - - test { - const builtin = @import("builtin"); - - if (builtin.os.tag == .wasi) return error.SkipZigTest; - - _ = Response; - } -}; - pub fn deinit(client: *Client) void { client.connection_mutex.lock(); @@ -1231,8 +265,8 @@ pub fn request(client: *Client, uri: Uri, headers: Request.Headers, options: Req .handle_redirects = options.handle_redirects, .compression_init = false, .response = switch (options.header_strategy) { - .dynamic => |max| Request.Response.initDynamic(max), - .static => |buf| Request.Response.initStatic(buf), + .dynamic => |max| Response.initDynamic(max), + .static => |buf| Response.initStatic(buf), }, .arena = undefined, }; @@ -1274,7 +308,7 @@ pub fn request(client: *Client, uri: Uri, headers: Request.Headers, options: Req } else { try writer.writeAll("\r\nConnection: keep-alive"); } - try writer.writeAll("\r\nAccept-Encoding: gzip, deflate"); + try writer.writeAll("\r\nAccept-Encoding: gzip, deflate, zstd"); switch (headers.transfer_encoding) { .chunked => try writer.writeAll("\r\nTransfer-Encoding: chunked"), diff --git a/lib/std/http/Client/Request.zig b/lib/std/http/Client/Request.zig new file mode 100644 index 0000000000..26ce5cb7bf --- /dev/null +++ b/lib/std/http/Client/Request.zig @@ -0,0 +1,488 @@ +const std = @import("std"); +const http = std.http; +const Uri = std.Uri; +const mem = std.mem; +const assert = std.debug.assert; + +const Client = @import("../Client.zig"); +const Connection = Client.Connection; +const ConnectionNode = Client.ConnectionNode; +const Response = @import("Response.zig"); + +const Request = @This(); + +const read_buffer_size = 8192; +const ReadBufferIndex = std.math.IntFittingRange(0, read_buffer_size); + +uri: Uri, +client: *Client, +connection: *ConnectionNode, +response: Response, +/// These are stored in Request so that they are available when following +/// redirects. +headers: Headers, + +redirects_left: u32, +handle_redirects: bool, +compression_init: bool, + +/// Used as a allocator for resolving redirects locations. +arena: std.heap.ArenaAllocator, + +/// Read buffer for the connection. This is used to pull in large amounts of data from the connection even if the user asks for a small amount. This can probably be removed with careful planning. +read_buffer: [read_buffer_size]u8 = undefined, +read_buffer_start: ReadBufferIndex = 0, +read_buffer_len: ReadBufferIndex = 0, + +pub const RequestTransfer = union(enum) { + content_length: u64, + chunked: void, + none: void, +}; + +pub const Headers = struct { + version: http.Version = .@"HTTP/1.1", + method: http.Method = .GET, + user_agent: []const u8 = "zig (std.http)", + connection: http.Connection = .keep_alive, + transfer_encoding: RequestTransfer = .none, + + custom: []const http.CustomHeader = &[_]http.CustomHeader{}, +}; + +pub const Options = struct { + handle_redirects: bool = true, + max_redirects: u32 = 3, + header_strategy: HeaderStrategy = .{ .dynamic = 16 * 1024 }, + + pub const HeaderStrategy = union(enum) { + /// In this case, the client's Allocator will be used to store the + /// entire HTTP header. This value is the maximum total size of + /// HTTP headers allowed, otherwise + /// error.HttpHeadersExceededSizeLimit is returned from read(). + dynamic: usize, + /// This is used to store the entire HTTP header. If the HTTP + /// header is too big to fit, `error.HttpHeadersExceededSizeLimit` + /// is returned from read(). When this is used, `error.OutOfMemory` + /// cannot be returned from `read()`. + static: []u8, + }; +}; + +/// Frees all resources associated with the request. +pub fn deinit(req: *Request) void { + switch (req.response.compression) { + .none => {}, + .deflate => |*deflate| deflate.deinit(), + .gzip => |*gzip| gzip.deinit(), + .zstd => |*zstd| zstd.deinit(), + } + + if (req.response.header_bytes_owned) { + req.response.header_bytes.deinit(req.client.allocator); + } + + if (!req.response.done) { + // If the response wasn't fully read, then we need to close the connection. + req.connection.data.closing = true; + req.client.release(req.connection); + } + + req.arena.deinit(); + req.* = undefined; +} + +pub const ReadRawError = Connection.ReadError || Uri.ParseError || Client.RequestError || error{ + UnexpectedEndOfStream, + TooManyHttpRedirects, + HttpRedirectMissingLocation, + HttpHeadersInvalid, +}; + +pub const ReaderRaw = std.io.Reader(*Request, ReadRawError, readRaw); + +/// Read from the underlying stream, without decompressing or parsing the headers. Must be called +/// after waitForCompleteHead() has returned successfully. +pub fn readRaw(req: *Request, buffer: []u8) ReadRawError!usize { + assert(req.response.state.isContent()); + + var index: usize = 0; + while (index == 0) { + const amt = try req.readRawAdvanced(buffer[index..]); + if (amt == 0 and req.response.done) break; + index += amt; + } + + return index; +} + +fn checkForCompleteHead(req: *Request, buffer: []u8) !usize { + switch (req.response.state) { + .invalid => unreachable, + .start, .seen_r, .seen_rn, .seen_rnr => {}, + else => return 0, // No more headers to read. + } + + const i = req.response.findHeadersEnd(buffer[0..]); + if (req.response.state == .invalid) return error.HttpHeadersInvalid; + + const headers_data = buffer[0..i]; + if (req.response.header_bytes.items.len + headers_data.len > req.response.max_header_bytes) { + return error.HttpHeadersExceededSizeLimit; + } + try req.response.header_bytes.appendSlice(req.client.allocator, headers_data); + + if (req.response.state == .finished) { + req.response.headers = try Response.Headers.parse(req.response.header_bytes.items); + + if (req.response.upgrade) |_| { + req.connection.data.closing = false; + req.response.done = true; + return i; + } + + if (req.response.headers.connection == .keep_alive) { + req.connection.data.closing = false; + } else { + req.connection.data.closing = true; + } + + if (req.response.headers.transfer_encoding) |transfer_encoding| { + switch (transfer_encoding) { + .chunked => { + req.response.next_chunk_length = 0; + req.response.state = .chunk_size; + }, + } + } else if (req.response.headers.content_length) |content_length| { + req.response.next_chunk_length = content_length; + + if (content_length == 0) req.response.done = true; + } else { + req.response.done = true; + } + + return i; + } + + return 0; +} + +pub const WaitForCompleteHeadError = ReadRawError || error{ + UnexpectedEndOfStream, + + HttpHeadersExceededSizeLimit, + ShortHttpStatusLine, + BadHttpVersion, + HttpHeaderContinuationsUnsupported, + HttpTransferEncodingUnsupported, + HttpConnectionHeaderUnsupported, +}; + +/// Reads a complete response head. Any leftover data is stored in the request. This function is idempotent. +pub fn waitForCompleteHead(req: *Request) WaitForCompleteHeadError!void { + if (req.response.state.isContent()) return; + + while (true) { + const nread = try req.connection.data.read(req.read_buffer[0..]); + const amt = try checkForCompleteHead(req, req.read_buffer[0..nread]); + + if (amt != 0) { + req.read_buffer_start = @intCast(ReadBufferIndex, amt); + req.read_buffer_len = @intCast(ReadBufferIndex, nread); + return; + } else if (nread == 0) { + return error.UnexpectedEndOfStream; + } + } +} + +/// This one can return 0 without meaning EOF. +fn readRawAdvanced(req: *Request, buffer: []u8) !usize { + assert(req.response.state.isContent()); + if (req.response.done) return 0; + + // var in: []const u8 = undefined; + if (req.read_buffer_start == req.read_buffer_len) { + const nread = try req.connection.data.read(req.read_buffer[0..]); + if (nread == 0) return error.UnexpectedEndOfStream; + + req.read_buffer_start = 0; + req.read_buffer_len = @intCast(ReadBufferIndex, nread); + } + + var out_index: usize = 0; + while (true) { + switch (req.response.state) { + .invalid, .start, .seen_r, .seen_rn, .seen_rnr => unreachable, + .finished => { + // TODO https://github.com/ziglang/zig/issues/14039 + const buf_avail = req.read_buffer_len - req.read_buffer_start; + const data_avail = req.response.next_chunk_length; + const out_avail = buffer.len; + + if (req.handle_redirects and req.response.headers.status.class() == .redirect) { + const can_read = @intCast(usize, @min(buf_avail, data_avail)); + req.response.next_chunk_length -= can_read; + + if (req.response.next_chunk_length == 0) { + req.client.release(req.connection); + req.connection = undefined; + req.response.done = true; + } + + return 0; // skip over as much data as possible + } + + const can_read = @intCast(usize, @min(@min(buf_avail, data_avail), out_avail)); + req.response.next_chunk_length -= can_read; + + mem.copy(u8, buffer[0..], req.read_buffer[req.read_buffer_start..][0..can_read]); + req.read_buffer_start += @intCast(ReadBufferIndex, can_read); + + if (req.response.next_chunk_length == 0) { + req.client.release(req.connection); + req.connection = undefined; + req.response.done = true; + } + + return can_read; + }, + .chunk_size_prefix_r => switch (req.read_buffer_len - req.read_buffer_start) { + 0 => return out_index, + 1 => switch (req.read_buffer[req.read_buffer_start]) { + '\r' => { + req.response.state = .chunk_size_prefix_n; + return out_index; + }, + else => { + req.response.state = .invalid; + return error.HttpHeadersInvalid; + }, + }, + else => switch (int16(req.read_buffer[req.read_buffer_start..][0..2])) { + int16("\r\n") => { + req.read_buffer_start += 2; + req.response.state = .chunk_size; + continue; + }, + else => { + req.response.state = .invalid; + return error.HttpHeadersInvalid; + }, + }, + }, + .chunk_size_prefix_n => switch (req.read_buffer_len - req.read_buffer_start) { + 0 => return out_index, + else => switch (req.read_buffer[req.read_buffer_start]) { + '\n' => { + req.read_buffer_start += 1; + req.response.state = .chunk_size; + continue; + }, + else => { + req.response.state = .invalid; + return error.HttpHeadersInvalid; + }, + }, + }, + .chunk_size, .chunk_r => { + const i = req.response.findChunkedLen(req.read_buffer[req.read_buffer_start..req.read_buffer_len]); + switch (req.response.state) { + .invalid => return error.HttpHeadersInvalid, + .chunk_data => { + if (req.response.next_chunk_length == 0) { + req.response.done = true; + req.client.release(req.connection); + req.connection = undefined; + + return out_index; + } + + req.read_buffer_start += @intCast(ReadBufferIndex, i); + continue; + }, + .chunk_size => return out_index, + else => unreachable, + } + }, + .chunk_data => { + // TODO https://github.com/ziglang/zig/issues/14039 + const buf_avail = req.read_buffer_len - req.read_buffer_start; + const data_avail = req.response.next_chunk_length; + const out_avail = buffer.len - out_index; + + if (req.handle_redirects and req.response.headers.status.class() == .redirect) { + const can_read = @intCast(usize, @min(buf_avail, data_avail)); + req.response.next_chunk_length -= can_read; + + if (req.response.next_chunk_length == 0) { + req.client.release(req.connection); + req.connection = undefined; + req.response.done = true; + continue; + } + + return 0; // skip over as much data as possible + } + + const can_read = @intCast(usize, @min(@min(buf_avail, data_avail), out_avail)); + req.response.next_chunk_length -= can_read; + + mem.copy(u8, buffer[out_index..], req.read_buffer[req.read_buffer_start..][0..can_read]); + req.read_buffer_start += @intCast(ReadBufferIndex, can_read); + out_index += can_read; + + if (req.response.next_chunk_length == 0) { + req.response.state = .chunk_size_prefix_r; + + continue; + } + + return out_index; + }, + } + } +} + +pub const ReadError = Client.DeflateDecompressor.Error || Client.GzipDecompressor.Error || Client.ZstdDecompressor.Error || WaitForCompleteHeadError || error{ + BadHeader, + InvalidCompression, + StreamTooLong, + InvalidWindowSize, + CompressionNotSupported +}; + +pub const Reader = std.io.Reader(*Request, ReadError, read); + +pub fn reader(req: *Request) Reader { + return .{ .context = req }; +} + +pub fn read(req: *Request, buffer: []u8) ReadError!usize { + while (true) { + if (!req.response.state.isContent()) try req.waitForCompleteHead(); + + if (req.handle_redirects and req.response.headers.status.class() == .redirect) { + assert(try req.readRaw(buffer) == 0); + + if (req.redirects_left == 0) return error.TooManyHttpRedirects; + + const location = req.response.headers.location orelse + return error.HttpRedirectMissingLocation; + const new_url = Uri.parse(location) catch try Uri.parseWithoutScheme(location); + + var new_arena = std.heap.ArenaAllocator.init(req.client.allocator); + const resolved_url = try req.uri.resolve(new_url, false, new_arena.allocator()); + errdefer new_arena.deinit(); + + req.arena.deinit(); + req.arena = new_arena; + + const new_req = try req.client.request(resolved_url, req.headers, .{ + .max_redirects = req.redirects_left - 1, + .header_strategy = if (req.response.header_bytes_owned) .{ + .dynamic = req.response.max_header_bytes, + } else .{ + .static = req.response.header_bytes.unusedCapacitySlice(), + }, + }); + req.deinit(); + req.* = new_req; + } else { + break; + } + } + + if (req.response.compression == .none) { + if (req.response.headers.transfer_compression) |compression| { + switch (compression) { + .compress => return error.CompressionNotSupported, + .deflate => req.response.compression = .{ + .deflate = try std.compress.zlib.zlibStream(req.client.allocator, ReaderRaw{ .context = req }), + }, + .gzip => req.response.compression = .{ + .gzip = try std.compress.gzip.decompress(req.client.allocator, ReaderRaw{ .context = req }), + }, + .zstd => req.response.compression = .{ + .zstd = std.compress.zstd.decompressStream(req.client.allocator, ReaderRaw{ .context = req }), + }, + } + } + } + + return switch (req.response.compression) { + .deflate => |*deflate| try deflate.read(buffer), + .gzip => |*gzip| try gzip.read(buffer), + .zstd => |*zstd| try zstd.read(buffer), + else => try req.readRaw(buffer), + }; +} + +pub fn readAll(req: *Request, buffer: []u8) !usize { + var index: usize = 0; + while (index < buffer.len) { + const amt = try read(req, buffer[index..]); + if (amt == 0) break; + index += amt; + } + return index; +} + +pub const WriteError = Connection.WriteError || error{MessageTooLong}; + +pub const Writer = std.io.Writer(*Request, WriteError, write); + +pub fn writer(req: *Request) Writer { + return .{ .context = req }; +} + +/// Write `bytes` to the server. The `transfer_encoding` request header determines how data will be sent. +pub fn write(req: *Request, bytes: []const u8) !usize { + switch (req.headers.transfer_encoding) { + .chunked => { + try req.connection.data.writer().print("{x}\r\n", .{bytes.len}); + try req.connection.data.writeAll(bytes); + try req.connection.data.writeAll("\r\n"); + + return bytes.len; + }, + .content_length => |*len| { + if (len.* < bytes.len) return error.MessageTooLong; + + const amt = try req.connection.data.write(bytes); + len.* -= amt; + return amt; + }, + .none => return error.NotWriteable, + } +} + +/// Finish the body of a request. This notifies the server that you have no more data to send. +pub fn finish(req: *Request) !void { + switch (req.headers.transfer_encoding) { + .chunked => try req.connection.data.writeAll("0\r\n"), + .content_length => |len| if (len != 0) return error.MessageNotCompleted, + .none => {}, + } +} + +inline fn int16(array: *const [2]u8) u16 { + return @bitCast(u16, array.*); +} + +inline fn int32(array: *const [4]u8) u32 { + return @bitCast(u32, array.*); +} + +inline fn int64(array: *const [8]u8) u64 { + return @bitCast(u64, array.*); +} + +test { + const builtin = @import("builtin"); + + if (builtin.os.tag == .wasi) return error.SkipZigTest; + + _ = Response; +} diff --git a/lib/std/http/Client/Response.zig b/lib/std/http/Client/Response.zig new file mode 100644 index 0000000000..bc064f9a20 --- /dev/null +++ b/lib/std/http/Client/Response.zig @@ -0,0 +1,506 @@ +const std = @import("std"); +const http = std.http; +const mem = std.mem; +const testing = std.testing; +const assert = std.debug.assert; + +const Client = @import("../Client.zig"); +const Response = @This(); + +headers: Headers, +state: State, +header_bytes_owned: bool, +/// This could either be a fixed buffer provided by the API user or it +/// could be our own array list. +header_bytes: std.ArrayListUnmanaged(u8), +max_header_bytes: usize, +next_chunk_length: u64, +done: bool = false, + +compression: union(enum) { + deflate: Client.DeflateDecompressor, + gzip: Client.GzipDecompressor, + zstd: Client.ZstdDecompressor, + none: void, +} = .none, + +pub const Headers = struct { + status: http.Status, + version: http.Version, + location: ?[]const u8 = null, + content_length: ?u64 = null, + transfer_encoding: ?http.TransferEncoding = null, + transfer_compression: ?http.ContentEncoding = null, + connection: http.Connection = .close, + + number_of_headers: usize = 0, + + pub fn parse(bytes: []const u8) !Headers { + var it = mem.split(u8, bytes[0 .. bytes.len - 4], "\r\n"); + + const first_line = it.first(); + if (first_line.len < 12) + return error.ShortHttpStatusLine; + + const version: http.Version = switch (int64(first_line[0..8])) { + int64("HTTP/1.0") => .@"HTTP/1.0", + int64("HTTP/1.1") => .@"HTTP/1.1", + else => return error.BadHttpVersion, + }; + if (first_line[8] != ' ') return error.HttpHeadersInvalid; + const status = @intToEnum(http.Status, parseInt3(first_line[9..12].*)); + + var headers: Headers = .{ + .version = version, + .status = status, + }; + + while (it.next()) |line| { + headers.number_of_headers += 1; + + if (line.len == 0) return error.HttpHeadersInvalid; + switch (line[0]) { + ' ', '\t' => return error.HttpHeaderContinuationsUnsupported, + else => {}, + } + var line_it = mem.split(u8, line, ": "); + const header_name = line_it.first(); + const header_value = line_it.rest(); + if (std.ascii.eqlIgnoreCase(header_name, "location")) { + if (headers.location != null) return error.HttpHeadersInvalid; + headers.location = header_value; + } else if (std.ascii.eqlIgnoreCase(header_name, "content-length")) { + if (headers.content_length != null) return error.HttpHeadersInvalid; + headers.content_length = try std.fmt.parseInt(u64, header_value, 10); + } else if (std.ascii.eqlIgnoreCase(header_name, "transfer-encoding")) { + if (headers.transfer_encoding != null or headers.transfer_compression != null) return error.HttpHeadersInvalid; + + // Transfer-Encoding: second, first + // Transfer-Encoding: deflate, chunked + var iter = std.mem.splitBackwards(u8, header_value, ","); + + if (iter.next()) |first| { + const trimmed = std.mem.trim(u8, first, " "); + + if (std.meta.stringToEnum(http.TransferEncoding, trimmed)) |te| { + headers.transfer_encoding = te; + } else if (std.meta.stringToEnum(http.ContentEncoding, trimmed)) |ce| { + headers.transfer_compression = ce; + } else { + return error.HttpTransferEncodingUnsupported; + } + } + + if (iter.next()) |second| { + if (headers.transfer_compression != null) return error.HttpTransferEncodingUnsupported; + + const trimmed = std.mem.trim(u8, second, " "); + + if (std.meta.stringToEnum(http.ContentEncoding, trimmed)) |ce| { + headers.transfer_compression = ce; + } else { + return error.HttpTransferEncodingUnsupported; + } + } + + if (iter.next()) |_| return error.HttpTransferEncodingUnsupported; + } else if (std.ascii.eqlIgnoreCase(header_name, "content-encoding")) { + if (headers.transfer_compression != null) return error.HttpHeadersInvalid; + + const trimmed = std.mem.trim(u8, header_value, " "); + + if (std.meta.stringToEnum(http.ContentEncoding, trimmed)) |ce| { + headers.transfer_compression = ce; + } else { + return error.HttpTransferEncodingUnsupported; + } + } else if (std.ascii.eqlIgnoreCase(header_name, "connection")) { + if (std.ascii.eqlIgnoreCase(header_value, "keep-alive")) { + headers.connection = .keep_alive; + } else if (std.ascii.eqlIgnoreCase(header_value, "close")) { + headers.connection = .close; + } else { + return error.HttpConnectionHeaderUnsupported; + } + } + } + + return headers; + } + + test "parse headers" { + const example = + "HTTP/1.1 301 Moved Permanently\r\n" ++ + "Location: https://www.example.com/\r\n" ++ + "Content-Type: text/html; charset=UTF-8\r\n" ++ + "Content-Length: 220\r\n\r\n"; + const parsed = try Headers.parse(example); + try testing.expectEqual(http.Version.@"HTTP/1.1", parsed.version); + try testing.expectEqual(http.Status.moved_permanently, parsed.status); + try testing.expectEqualStrings("https://www.example.com/", parsed.location orelse + return error.TestFailed); + try testing.expectEqual(@as(?u64, 220), parsed.content_length); + } + + test "header continuation" { + const example = + "HTTP/1.0 200 OK\r\n" ++ + "Content-Type: text/html;\r\n charset=UTF-8\r\n" ++ + "Content-Length: 220\r\n\r\n"; + try testing.expectError( + error.HttpHeaderContinuationsUnsupported, + Headers.parse(example), + ); + } + + test "extra content length" { + const example = + "HTTP/1.0 200 OK\r\n" ++ + "Content-Length: 220\r\n" ++ + "Content-Type: text/html; charset=UTF-8\r\n" ++ + "content-length: 220\r\n\r\n"; + try testing.expectError( + error.HttpHeadersInvalid, + Headers.parse(example), + ); + } +}; + +inline fn int16(array: *const [2]u8) u16 { + return @bitCast(u16, array.*); +} + +inline fn int32(array: *const [4]u8) u32 { + return @bitCast(u32, array.*); +} + +inline fn int64(array: *const [8]u8) u64 { + return @bitCast(u64, array.*); +} + +pub const State = enum { + /// Begin header parsing states. + invalid, + start, + seen_r, + seen_rn, + seen_rnr, + finished, + /// Begin transfer-encoding: chunked parsing states. + chunk_size_prefix_r, + chunk_size_prefix_n, + chunk_size, + chunk_r, + chunk_data, + + pub fn isContent(self: State) bool { + return switch (self) { + .invalid, .start, .seen_r, .seen_rn, .seen_rnr => false, + .finished, .chunk_size_prefix_r, .chunk_size_prefix_n, .chunk_size, .chunk_r, .chunk_data => true, + }; + } +}; + +pub fn initDynamic(max: usize) Response { + return .{ + .state = .start, + .headers = undefined, + .header_bytes = .{}, + .max_header_bytes = max, + .header_bytes_owned = true, + .next_chunk_length = undefined, + }; +} + +pub fn initStatic(buf: []u8) Response { + return .{ + .state = .start, + .headers = undefined, + .header_bytes = .{ .items = buf[0..0], .capacity = buf.len }, + .max_header_bytes = buf.len, + .header_bytes_owned = false, + .next_chunk_length = undefined, + }; +} + +/// Returns how many bytes are part of HTTP headers. Always less than or +/// equal to bytes.len. If the amount returned is less than bytes.len, it +/// means the headers ended and the first byte after the double \r\n\r\n is +/// located at `bytes[result]`. +pub fn findHeadersEnd(r: *Response, bytes: []const u8) usize { + var index: usize = 0; + + // TODO: https://github.com/ziglang/zig/issues/8220 + state: while (true) { + switch (r.state) { + .invalid => unreachable, + .finished => unreachable, + .start => while (true) { + switch (bytes.len - index) { + 0 => return index, + 1 => { + if (bytes[index] == '\r') + r.state = .seen_r; + return index + 1; + }, + 2 => { + if (int16(bytes[index..][0..2]) == int16("\r\n")) { + r.state = .seen_rn; + } else if (bytes[index + 1] == '\r') { + r.state = .seen_r; + } + return index + 2; + }, + 3 => { + if (int16(bytes[index..][0..2]) == int16("\r\n") and + bytes[index + 2] == '\r') + { + r.state = .seen_rnr; + } else if (int16(bytes[index + 1 ..][0..2]) == int16("\r\n")) { + r.state = .seen_rn; + } else if (bytes[index + 2] == '\r') { + r.state = .seen_r; + } + return index + 3; + }, + 4...15 => { + if (int32(bytes[index..][0..4]) == int32("\r\n\r\n")) { + r.state = .finished; + return index + 4; + } else if (int16(bytes[index + 1 ..][0..2]) == int16("\r\n") and + bytes[index + 3] == '\r') + { + r.state = .seen_rnr; + index += 4; + continue :state; + } else if (int16(bytes[index + 2 ..][0..2]) == int16("\r\n")) { + r.state = .seen_rn; + index += 4; + continue :state; + } else if (bytes[index + 3] == '\r') { + r.state = .seen_r; + index += 4; + continue :state; + } + index += 4; + continue; + }, + else => { + const chunk = bytes[index..][0..16]; + const v: @Vector(16, u8) = chunk.*; + const matches_r = v == @splat(16, @as(u8, '\r')); + const iota = std.simd.iota(u8, 16); + const default = @splat(16, @as(u8, 16)); + const sub_index = @reduce(.Min, @select(u8, matches_r, iota, default)); + switch (sub_index) { + 0...12 => { + index += sub_index + 4; + if (int32(chunk[sub_index..][0..4]) == int32("\r\n\r\n")) { + r.state = .finished; + return index; + } + continue; + }, + 13 => { + index += 16; + if (int16(chunk[14..][0..2]) == int16("\n\r")) { + r.state = .seen_rnr; + continue :state; + } + continue; + }, + 14 => { + index += 16; + if (chunk[15] == '\n') { + r.state = .seen_rn; + continue :state; + } + continue; + }, + 15 => { + r.state = .seen_r; + index += 16; + continue :state; + }, + 16 => { + index += 16; + continue; + }, + else => unreachable, + } + }, + } + }, + + .seen_r => switch (bytes.len - index) { + 0 => return index, + 1 => { + switch (bytes[index]) { + '\n' => r.state = .seen_rn, + '\r' => r.state = .seen_r, + else => r.state = .start, + } + return index + 1; + }, + 2 => { + if (int16(bytes[index..][0..2]) == int16("\n\r")) { + r.state = .seen_rnr; + return index + 2; + } + r.state = .start; + return index + 2; + }, + else => { + if (int16(bytes[index..][0..2]) == int16("\n\r") and + bytes[index + 2] == '\n') + { + r.state = .finished; + return index + 3; + } + index += 3; + r.state = .start; + continue :state; + }, + }, + .seen_rn => switch (bytes.len - index) { + 0 => return index, + 1 => { + switch (bytes[index]) { + '\r' => r.state = .seen_rnr, + else => r.state = .start, + } + return index + 1; + }, + else => { + if (int16(bytes[index..][0..2]) == int16("\r\n")) { + r.state = .finished; + return index + 2; + } + index += 2; + r.state = .start; + continue :state; + }, + }, + .seen_rnr => switch (bytes.len - index) { + 0 => return index, + else => { + if (bytes[index] == '\n') { + r.state = .finished; + return index + 1; + } + index += 1; + r.state = .start; + continue :state; + }, + }, + .chunk_size_prefix_r => unreachable, + .chunk_size_prefix_n => unreachable, + .chunk_size => unreachable, + .chunk_r => unreachable, + .chunk_data => unreachable, + } + + return index; + } +} + +pub fn findChunkedLen(r: *Response, bytes: []const u8) usize { + var i: usize = 0; + if (r.state == .chunk_size) { + while (i < bytes.len) : (i += 1) { + const digit = switch (bytes[i]) { + '0'...'9' => |b| b - '0', + 'A'...'Z' => |b| b - 'A' + 10, + 'a'...'z' => |b| b - 'a' + 10, + '\r' => { + r.state = .chunk_r; + i += 1; + break; + }, + else => { + r.state = .invalid; + return i; + }, + }; + const mul = @mulWithOverflow(r.next_chunk_length, 16); + if (mul[1] != 0) { + r.state = .invalid; + return i; + } + const add = @addWithOverflow(mul[0], digit); + if (add[1] != 0) { + r.state = .invalid; + return i; + } + r.next_chunk_length = add[0]; + } else { + return i; + } + } + assert(r.state == .chunk_r); + if (i == bytes.len) return i; + + if (bytes[i] == '\n') { + r.state = .chunk_data; + return i + 1; + } else { + r.state = .invalid; + return i; + } +} + +fn parseInt3(nnn: @Vector(3, u8)) u10 { + const zero: @Vector(3, u8) = .{ '0', '0', '0' }; + const mmm: @Vector(3, u10) = .{ 100, 10, 1 }; + return @reduce(.Add, @as(@Vector(3, u10), nnn -% zero) *% mmm); +} + +test parseInt3 { + const expectEqual = std.testing.expectEqual; + try expectEqual(@as(u10, 0), parseInt3("000".*)); + try expectEqual(@as(u10, 418), parseInt3("418".*)); + try expectEqual(@as(u10, 999), parseInt3("999".*)); +} + +test "find headers end basic" { + var buffer: [1]u8 = undefined; + var r = Response.initStatic(&buffer); + try testing.expectEqual(@as(usize, 10), r.findHeadersEnd("HTTP/1.1 4")); + try testing.expectEqual(@as(usize, 2), r.findHeadersEnd("18")); + try testing.expectEqual(@as(usize, 8), r.findHeadersEnd(" lol\r\n\r\nblah blah")); +} + +test "find headers end vectorized" { + var buffer: [1]u8 = undefined; + var r = Response.initStatic(&buffer); + const example = + "HTTP/1.1 301 Moved Permanently\r\n" ++ + "Location: https://www.example.com/\r\n" ++ + "Content-Type: text/html; charset=UTF-8\r\n" ++ + "Content-Length: 220\r\n" ++ + "\r\ncontent"; + try testing.expectEqual(@as(usize, 131), r.findHeadersEnd(example)); +} + +test "find headers end bug" { + var buffer: [1]u8 = undefined; + var r = Response.initStatic(&buffer); + const trail = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; + const example = + "HTTP/1.1 200 OK\r\n" ++ + "Access-Control-Allow-Origin: https://render.githubusercontent.com\r\n" ++ + "content-disposition: attachment; filename=zig-0.10.0.tar.gz\r\n" ++ + "Content-Security-Policy: default-src 'none'; style-src 'unsafe-inline'; sandbox\r\n" ++ + "Content-Type: application/x-gzip\r\n" ++ + "ETag: \"bfae0af6b01c7c0d89eb667cb5f0e65265968aeebda2689177e6b26acd3155ca\"\r\n" ++ + "Strict-Transport-Security: max-age=31536000\r\n" ++ + "Vary: Authorization,Accept-Encoding,Origin\r\n" ++ + "X-Content-Type-Options: nosniff\r\n" ++ + "X-Frame-Options: deny\r\n" ++ + "X-XSS-Protection: 1; mode=block\r\n" ++ + "Date: Fri, 06 Jan 2023 22:26:22 GMT\r\n" ++ + "Transfer-Encoding: chunked\r\n" ++ + "X-GitHub-Request-Id: 89C6:17E9:A7C9E:124B51:63B8A00E\r\n" ++ + "connection: close\r\n\r\n" ++ trail; + try testing.expectEqual(@as(usize, example.len - trail.len), r.findHeadersEnd(example)); +} From 524e0cd987a52a60ce1014aa27cd73f99a3b9958 Mon Sep 17 00:00:00 2001 From: Nameless Date: Wed, 8 Mar 2023 11:27:13 -0600 Subject: [PATCH 070/294] std.http: rework connection pool into its own type --- lib/std/http/Client.zig | 189 +++++++++++++++++++------------ lib/std/http/Client/Request.zig | 22 ++-- lib/std/http/Client/Response.zig | 5 +- lib/std/std.zig | 5 + 4 files changed, 134 insertions(+), 87 deletions(-) diff --git a/lib/std/http/Client.zig b/lib/std/http/Client.zig index d44b1d098d..baf0239388 100644 --- a/lib/std/http/Client.zig +++ b/lib/std/http/Client.zig @@ -16,6 +16,9 @@ const testing = std.testing; pub const Request = @import("Client/Request.zig"); pub const Response = @import("Client/Response.zig"); +pub const default_connection_pool_size = 32; +const connection_pool_size = std.options.http_connection_pool_size; + /// Used for tcpConnectToHost and storing HTTP headers when an externally /// managed buffer is not provided. allocator: Allocator, @@ -24,39 +27,115 @@ ca_bundle: std.crypto.Certificate.Bundle = .{}, /// it will first rescan the system for root certificates. next_https_rescan_certs: bool = true, -connection_mutex: std.Thread.Mutex = .{}, connection_pool: ConnectionPool = .{}, -connection_used: ConnectionPool = .{}, -pub const ConnectionPool = std.TailQueue(Connection); -pub const ConnectionNode = ConnectionPool.Node; +pub const ConnectionPool = struct { + pub const Criteria = struct { + host: []const u8, + port: u16, + is_tls: bool, + }; -/// Acquires an existing connection from the connection pool. This function is threadsafe. -/// If the caller already holds the connection mutex, it should pass `true` for `held`. -pub fn acquire(client: *Client, node: *ConnectionNode, held: bool) void { - if (!held) client.connection_mutex.lock(); - defer if (!held) client.connection_mutex.unlock(); + const Queue = std.TailQueue(Connection); + pub const Node = Queue.Node; - client.connection_pool.remove(node); - client.connection_used.append(node); -} + mutex: std.Thread.Mutex = .{}, + used: Queue = .{}, + free: Queue = .{}, + free_len: usize = 0, + free_size: usize = default_connection_pool_size, -/// Tries to release a connection back to the connection pool. This function is threadsafe. -/// If the connection is marked as closing, it will be closed instead. -pub fn release(client: *Client, node: *ConnectionNode) void { - client.connection_mutex.lock(); - defer client.connection_mutex.unlock(); + /// Finds and acquires a connection from the connection pool matching the criteria. This function is threadsafe. + /// If no connection is found, null is returned. + pub fn findConnection(pool: *ConnectionPool, criteria: Criteria) ?*Node { + pool.mutex.lock(); + defer pool.mutex.unlock(); - client.connection_used.remove(node); + var next = pool.free.last; + while (next) |node| : (next = node.prev) { + if ((node.data.protocol == .tls) != criteria.is_tls) continue; + if (node.data.port != criteria.port) continue; + if (std.mem.eql(u8, node.data.host, criteria.host)) continue; - if (node.data.closing) { - node.data.close(client); + pool.acquireUnsafe(node); + return node; + } - return client.allocator.destroy(node); + return null; } - client.connection_pool.append(node); -} + /// Acquires an existing connection from the connection pool. This function is not threadsafe. + pub fn acquireUnsafe(pool: *ConnectionPool, node: *Node) void { + pool.free.remove(node); + pool.free_len -= 1; + + pool.used.append(node); + } + + /// Acquires an existing connection from the connection pool. This function is threadsafe. + pub fn acquire(pool: *ConnectionPool, node: *Node) void { + pool.mutex.lock(); + defer pool.mutex.unlock(); + + return pool.acquireUnsafe(node); + } + + /// Tries to release a connection back to the connection pool. This function is threadsafe. + /// If the connection is marked as closing, it will be closed instead. + pub fn release(pool: *ConnectionPool, client: *Client, node: *Node) void { + pool.mutex.lock(); + defer pool.mutex.unlock(); + + pool.used.remove(node); + + if (node.data.closing) { + node.data.close(client); + + return client.allocator.destroy(node); + } + + if (pool.free_len + 1 >= pool.free_size) { + const popped = pool.free.popFirst() orelse unreachable; + + popped.data.close(client); + + return client.allocator.destroy(popped); + } + + pool.free.append(node); + pool.free_len += 1; + } + + /// Adds a newly created node to the pool of used connections. This function is threadsafe. + pub fn addUsed(pool: *ConnectionPool, node: *Node) void { + pool.mutex.lock(); + defer pool.mutex.unlock(); + + pool.used.append(node); + } + + pub fn deinit(pool: *ConnectionPool, client: *Client) void { + pool.mutex.lock(); + + var next = pool.free.first; + while (next) |node| { + defer client.allocator.destroy(node); + next = node.next; + + node.data.close(client); + } + + next = pool.used.first; + while (next) |node| { + defer client.allocator.destroy(node); + next = node.next; + + node.data.close(client); + } + + pool.* = undefined; + } +}; pub const DeflateDecompressor = std.compress.zlib.ZlibStream(Request.ReaderRaw); pub const GzipDecompressor = std.compress.gzip.Decompress(Request.ReaderRaw); @@ -142,25 +221,7 @@ pub const Connection = struct { }; pub fn deinit(client: *Client) void { - client.connection_mutex.lock(); - - var next = client.connection_pool.first; - while (next) |node| { - next = node.next; - - node.data.close(client); - - client.allocator.destroy(node); - } - - next = client.connection_used.first; - while (next) |node| { - next = node.next; - - node.data.close(client); - - client.allocator.destroy(node); - } + client.connection_pool.deinit(client); client.ca_bundle.deinit(client.allocator); client.* = undefined; @@ -168,36 +229,25 @@ pub fn deinit(client: *Client) void { pub const ConnectError = std.mem.Allocator.Error || net.TcpConnectToHostError || std.crypto.tls.Client.InitError(net.Stream); -pub fn connect(client: *Client, host: []const u8, port: u16, protocol: Connection.Protocol) ConnectError!*ConnectionNode { - { // Search through the connection pool for a potential connection. - client.connection_mutex.lock(); - defer client.connection_mutex.unlock(); +pub fn connect(client: *Client, host: []const u8, port: u16, protocol: Connection.Protocol) ConnectError!*ConnectionPool.Node { + if (client.connection_pool.findConnection(.{ + .host = host, + .port = port, + .is_tls = protocol == .tls, + })) |node| + return node; - var potential = client.connection_pool.last; - while (potential) |node| { - const same_host = mem.eql(u8, node.data.host, host); - const same_port = node.data.port == port; - const same_protocol = node.data.protocol == protocol; - - if (same_host and same_port and same_protocol) { - client.acquire(node, true); - return node; - } - - potential = node.prev; - } - } - - const conn = try client.allocator.create(ConnectionNode); + const conn = try client.allocator.create(ConnectionPool.Node); errdefer client.allocator.destroy(conn); + conn.* = .{ .data = undefined }; - conn.* = .{ .data = .{ + conn.data = .{ .stream = try net.tcpConnectToHost(client.allocator, host, port), .tls_client = undefined, .protocol = protocol, .host = try client.allocator.dupe(u8, host), .port = port, - } }; + }; switch (protocol) { .plain => {}, @@ -210,12 +260,7 @@ pub fn connect(client: *Client, host: []const u8, port: u16, protocol: Connectio }, } - { - client.connection_mutex.lock(); - defer client.connection_mutex.unlock(); - - client.connection_used.append(conn); - } + client.connection_pool.addUsed(conn); return conn; } @@ -247,8 +292,8 @@ pub fn request(client: *Client, uri: Uri, headers: Request.Headers, options: Req const host = uri.host orelse return error.UriMissingHost; if (client.next_https_rescan_certs and protocol == .tls) { - client.connection_mutex.lock(); // TODO: this could be so much better than reusing the connection pool mutex. - defer client.connection_mutex.unlock(); + client.connection_pool.mutex.lock(); // TODO: this could be so much better than reusing the connection pool mutex. + defer client.connection_pool.mutex.unlock(); if (client.next_https_rescan_certs) { try client.ca_bundle.rescan(client.allocator); diff --git a/lib/std/http/Client/Request.zig b/lib/std/http/Client/Request.zig index 26ce5cb7bf..9e2ebd2d6c 100644 --- a/lib/std/http/Client/Request.zig +++ b/lib/std/http/Client/Request.zig @@ -6,7 +6,7 @@ const assert = std.debug.assert; const Client = @import("../Client.zig"); const Connection = Client.Connection; -const ConnectionNode = Client.ConnectionNode; +const ConnectionNode = Client.ConnectionPool.Node; const Response = @import("Response.zig"); const Request = @This(); @@ -85,7 +85,7 @@ pub fn deinit(req: *Request) void { if (!req.response.done) { // If the response wasn't fully read, then we need to close the connection. req.connection.data.closing = true; - req.client.release(req.connection); + req.client.connection_pool.release(req.client, req.connection); } req.arena.deinit(); @@ -135,7 +135,7 @@ fn checkForCompleteHead(req: *Request, buffer: []u8) !usize { if (req.response.state == .finished) { req.response.headers = try Response.Headers.parse(req.response.header_bytes.items); - if (req.response.upgrade) |_| { + if (req.response.headers.upgrade) |_| { req.connection.data.closing = false; req.response.done = true; return i; @@ -226,7 +226,7 @@ fn readRawAdvanced(req: *Request, buffer: []u8) !usize { req.response.next_chunk_length -= can_read; if (req.response.next_chunk_length == 0) { - req.client.release(req.connection); + req.client.connection_pool.release(req.client, req.connection); req.connection = undefined; req.response.done = true; } @@ -241,7 +241,7 @@ fn readRawAdvanced(req: *Request, buffer: []u8) !usize { req.read_buffer_start += @intCast(ReadBufferIndex, can_read); if (req.response.next_chunk_length == 0) { - req.client.release(req.connection); + req.client.connection_pool.release(req.client, req.connection); req.connection = undefined; req.response.done = true; } @@ -293,7 +293,7 @@ fn readRawAdvanced(req: *Request, buffer: []u8) !usize { .chunk_data => { if (req.response.next_chunk_length == 0) { req.response.done = true; - req.client.release(req.connection); + req.client.connection_pool.release(req.client, req.connection); req.connection = undefined; return out_index; @@ -317,7 +317,7 @@ fn readRawAdvanced(req: *Request, buffer: []u8) !usize { req.response.next_chunk_length -= can_read; if (req.response.next_chunk_length == 0) { - req.client.release(req.connection); + req.client.connection_pool.release(req.client, req.connection); req.connection = undefined; req.response.done = true; continue; @@ -345,13 +345,7 @@ fn readRawAdvanced(req: *Request, buffer: []u8) !usize { } } -pub const ReadError = Client.DeflateDecompressor.Error || Client.GzipDecompressor.Error || Client.ZstdDecompressor.Error || WaitForCompleteHeadError || error{ - BadHeader, - InvalidCompression, - StreamTooLong, - InvalidWindowSize, - CompressionNotSupported -}; +pub const ReadError = Client.DeflateDecompressor.Error || Client.GzipDecompressor.Error || Client.ZstdDecompressor.Error || WaitForCompleteHeadError || error{ BadHeader, InvalidCompression, StreamTooLong, InvalidWindowSize, CompressionNotSupported }; pub const Reader = std.io.Reader(*Request, ReadError, read); diff --git a/lib/std/http/Client/Response.zig b/lib/std/http/Client/Response.zig index bc064f9a20..8b2a9a4918 100644 --- a/lib/std/http/Client/Response.zig +++ b/lib/std/http/Client/Response.zig @@ -32,6 +32,7 @@ pub const Headers = struct { transfer_encoding: ?http.TransferEncoding = null, transfer_compression: ?http.ContentEncoding = null, connection: http.Connection = .close, + upgrade: ?[]const u8 = null, number_of_headers: usize = 0, @@ -93,7 +94,7 @@ pub const Headers = struct { if (iter.next()) |second| { if (headers.transfer_compression != null) return error.HttpTransferEncodingUnsupported; - + const trimmed = std.mem.trim(u8, second, " "); if (std.meta.stringToEnum(http.ContentEncoding, trimmed)) |ce| { @@ -122,6 +123,8 @@ pub const Headers = struct { } else { return error.HttpConnectionHeaderUnsupported; } + } else if (std.ascii.eqlIgnoreCase(header_name, "upgrade")) { + headers.upgrade = header_value; } } diff --git a/lib/std/std.zig b/lib/std/std.zig index c1c682e224..e888ade659 100644 --- a/lib/std/std.zig +++ b/lib/std/std.zig @@ -185,6 +185,11 @@ pub const options = struct { options_override.keep_sigpipe else false; + + pub const http_connection_pool_size = if (@hasDecl(options_override, "http_connection_pool_size")) + options_override.http_connection_pool_size + else + http.Client.default_connection_pool_size; }; // This forces the start.zig file to be imported, and the comptime logic inside that From 14590e956e06903ac408af57874d3b5a0d697670 Mon Sep 17 00:00:00 2001 From: mlugg Date: Thu, 9 Mar 2023 15:35:54 +0000 Subject: [PATCH 071/294] Fix test case added in 6d7fb8f --- test/cases/compile_errors/comptime_try_non_error.zig | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/test/cases/compile_errors/comptime_try_non_error.zig b/test/cases/compile_errors/comptime_try_non_error.zig index 9b342f7934..935148414c 100644 --- a/test/cases/compile_errors/comptime_try_non_error.zig +++ b/test/cases/compile_errors/comptime_try_non_error.zig @@ -1,4 +1,8 @@ -export fn foo() void { +comptime { + foo(); +} + +fn foo() void { try bar(); } @@ -10,4 +14,5 @@ pub fn bar() u8 { // backend=stage2 // target=native // -// :2:12: error: expected error union type, found 'u8' +// :6:12: error: expected error union type, found 'u8' +// :2:8: note: called from here From b445bbfea205702770e4e9de4e456c3e8750f8ed Mon Sep 17 00:00:00 2001 From: antlilja Date: Thu, 9 Mar 2023 20:39:15 +0100 Subject: [PATCH 072/294] Add ability to import dependencies from build.zig --- src/Package.zig | 7 +++++-- src/main.zig | 12 ++++++------ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/Package.zig b/src/Package.zig index ed93500980..c238d3d567 100644 --- a/src/Package.zig +++ b/src/Package.zig @@ -215,6 +215,7 @@ pub const build_zig_basename = "build.zig"; pub fn fetchAndAddDependencies( pkg: *Package, + root_pkg: *Package, arena: Allocator, thread_pool: *ThreadPool, http_client: *std.http.Client, @@ -295,7 +296,8 @@ pub fn fetchAndAddDependencies( all_modules, ); - try pkg.fetchAndAddDependencies( + try sub_pkg.fetchAndAddDependencies( + root_pkg, arena, thread_pool, http_client, @@ -309,7 +311,8 @@ pub fn fetchAndAddDependencies( all_modules, ); - try add(pkg, gpa, fqn, sub_pkg); + try pkg.add(gpa, name, sub_pkg); + try root_pkg.add(gpa, fqn, sub_pkg); try dependencies_source.writer().print(" pub const {s} = @import(\"{}\");\n", .{ std.zig.fmtId(fqn), std.zig.fmtEscapes(fqn), diff --git a/src/main.zig b/src/main.zig index b134b7183e..95cfca1463 100644 --- a/src/main.zig +++ b/src/main.zig @@ -4228,6 +4228,10 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi .root_src_path = "build_runner.zig", }; + var build_pkg: Package = .{ + .root_src_directory = build_directory, + .root_src_path = build_zig_basename, + }; if (!build_options.omit_pkg_fetching_code) { var http_client: std.http.Client = .{ .allocator = gpa }; defer http_client.deinit(); @@ -4249,7 +4253,8 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi // Here we borrow main package's table and will replace it with a fresh // one after this process completes. - main_pkg.fetchAndAddDependencies( + build_pkg.fetchAndAddDependencies( + &main_pkg, arena, &thread_pool, &http_client, @@ -4280,11 +4285,6 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi mem.swap(Package.Table, &main_pkg.table, &deps_pkg.table); try main_pkg.add(gpa, "@dependencies", deps_pkg); } - - var build_pkg: Package = .{ - .root_src_directory = build_directory, - .root_src_path = build_zig_basename, - }; try main_pkg.add(gpa, "@build", &build_pkg); const comp = Compilation.create(gpa, .{ From 0ee9a52507fe30983f7933cb19a5bfde3b40a60c Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Fri, 10 Mar 2023 06:20:10 +0100 Subject: [PATCH 073/294] wasm-linker: remove synthetic segments & atoms --- src/link/Wasm.zig | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 9a4166b052..4292e00ed9 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -887,32 +887,12 @@ fn resolveLazySymbols(wasm: *Wasm) !void { const loc = try wasm.createSyntheticSymbol("__heap_base", .data); try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc); _ = wasm.resolved_symbols.swapRemove(loc); // we don't want to emit this symbol, only use it for relocations. - - // TODO: Can we use `createAtom` here while also re-using the symbol - // from `createSyntheticSymbol`. - const atom_index = @intCast(Atom.Index, wasm.managed_atoms.items.len); - const atom = try wasm.managed_atoms.addOne(wasm.base.allocator); - atom.* = Atom.empty; - atom.sym_index = loc.index; - atom.alignment = 1; - - try wasm.parseAtom(atom_index, .{ .data = .synthetic }); - try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom_index); } if (wasm.undefs.fetchSwapRemove("__heap_end")) |kv| { const loc = try wasm.createSyntheticSymbol("__heap_end", .data); try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc); _ = wasm.resolved_symbols.swapRemove(loc); - - const atom_index = @intCast(Atom.Index, wasm.managed_atoms.items.len); - const atom = try wasm.managed_atoms.addOne(wasm.base.allocator); - atom.* = Atom.empty; - atom.sym_index = loc.index; - atom.alignment = 1; - - try wasm.parseAtom(atom_index, .{ .data = .synthetic }); - try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom_index); } } @@ -1614,7 +1594,6 @@ const Kind = union(enum) { read_only, uninitialized, initialized, - synthetic, }, function: void, @@ -1625,7 +1604,6 @@ const Kind = union(enum) { .read_only => return ".rodata.", .uninitialized => return ".bss.", .initialized => return ".data.", - .synthetic => return ".synthetic", } } }; @@ -1833,7 +1811,6 @@ fn sortDataSegments(wasm: *Wasm) !void { if (mem.startsWith(u8, name, ".rodata")) return 0; if (mem.startsWith(u8, name, ".data")) return 1; if (mem.startsWith(u8, name, ".text")) return 2; - if (mem.startsWith(u8, name, ".synthetic")) return 100; // always at end return 3; } }; @@ -2245,10 +2222,6 @@ fn setupMemory(wasm: *Wasm) !void { var offset: u32 = @intCast(u32, memory_ptr); var data_seg_it = wasm.data_segments.iterator(); while (data_seg_it.next()) |entry| { - if (mem.eql(u8, entry.key_ptr.*, ".synthetic")) { - // do not update synthetic segments as they are not part of the output - continue; - } const segment = &wasm.segments.items[entry.value_ptr.*]; memory_ptr = std.mem.alignForwardGeneric(u64, memory_ptr, segment.alignment); memory_ptr += segment.size; @@ -3447,8 +3420,6 @@ fn emitNameSection(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), arena: std.mem // bss section is not emitted when this condition holds true, so we also // do not output a name for it. if (!wasm.base.options.import_memory and std.mem.eql(u8, key, ".bss")) continue; - // Synthetic segments are not emitted - if (std.mem.eql(u8, key, ".synthetic")) continue; segments.appendAssumeCapacity(.{ .index = data_segment_index, .name = key }); data_segment_index += 1; } From 023753b4693317055e8094059f6195d041311b5d Mon Sep 17 00:00:00 2001 From: mlugg Date: Fri, 10 Mar 2023 00:42:56 +0000 Subject: [PATCH 074/294] Sema: correctly detect use of undefined within slices in @Type Resolves: #14712 --- src/Sema.zig | 2 +- src/value.zig | 31 +++++++++++++++---- .../reify_type_with_undefined.zig | 13 ++++++++ 3 files changed, 39 insertions(+), 7 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 840e8a4c6c..41685890f7 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -18373,7 +18373,7 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in const union_val = val.cast(Value.Payload.Union).?.data; const target = mod.getTarget(); const tag_index = type_info_ty.unionTagFieldIndex(union_val.tag, mod).?; - if (union_val.val.anyUndef()) return sema.failWithUseOfUndef(block, src); + if (union_val.val.anyUndef(mod)) return sema.failWithUseOfUndef(block, src); switch (@intToEnum(std.builtin.TypeId, tag_index)) { .Type => return Air.Inst.Ref.type_type, .Void => return Air.Inst.Ref.void_type, diff --git a/src/value.zig b/src/value.zig index 00bf59ca38..b6d27d620d 100644 --- a/src/value.zig +++ b/src/value.zig @@ -3144,13 +3144,32 @@ pub const Value = extern union { /// TODO: check for cases such as array that is not marked undef but all the element /// values are marked undef, or struct that is not marked undef but all fields are marked /// undef, etc. - pub fn anyUndef(self: Value) bool { - if (self.castTag(.aggregate)) |aggregate| { - for (aggregate.data) |val| { - if (val.anyUndef()) return true; - } + pub fn anyUndef(self: Value, mod: *Module) bool { + switch (self.tag()) { + .slice => { + const payload = self.castTag(.slice).?; + const len = payload.data.len.toUnsignedInt(mod.getTarget()); + + var elem_value_buf: ElemValueBuffer = undefined; + var i: usize = 0; + while (i < len) : (i += 1) { + const elem_val = payload.data.ptr.elemValueBuffer(mod, i, &elem_value_buf); + if (elem_val.anyUndef(mod)) return true; + } + }, + + .aggregate => { + const payload = self.castTag(.aggregate).?; + for (payload.data) |val| { + if (val.anyUndef(mod)) return true; + } + }, + + .undef => return true, + else => {}, } - return self.isUndef(); + + return false; } /// Asserts the value is not undefined and not unreachable. diff --git a/test/cases/compile_errors/reify_type_with_undefined.zig b/test/cases/compile_errors/reify_type_with_undefined.zig index e5753fa420..59c0314773 100644 --- a/test/cases/compile_errors/reify_type_with_undefined.zig +++ b/test/cases/compile_errors/reify_type_with_undefined.zig @@ -11,6 +11,18 @@ comptime { }, }); } +comptime { + const std = @import("std"); + const fields: [1]std.builtin.Type.StructField = undefined; + _ = @Type(.{ + .Struct = .{ + .layout = .Auto, + .fields = &fields, + .decls = &.{}, + .is_tuple = false, + }, + }); +} // error // backend=stage2 @@ -18,3 +30,4 @@ comptime { // // :2:9: error: use of undefined value here causes undefined behavior // :5:9: error: use of undefined value here causes undefined behavior +// :17:9: error: use of undefined value here causes undefined behavior From 5a26d1b4268b2e4598e0c39d3703d184921cfa6d Mon Sep 17 00:00:00 2001 From: Lavt Niveau Date: Fri, 10 Mar 2023 06:36:43 -0700 Subject: [PATCH 075/294] Include `signal.h` to define SIGTRAP in Stage 1 compiler (#14867) copy lib/zig.h to stage1/zig.h In this case, it looks safe to backport over with no changes. Co-authored-by: Andrew Kelley --- stage1/zig.h | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/stage1/zig.h b/stage1/zig.h index 65fb21f99a..59c3ddd695 100644 --- a/stage1/zig.h +++ b/stage1/zig.h @@ -190,10 +190,17 @@ typedef char bool; #if zig_has_builtin(trap) #define zig_trap() __builtin_trap() +#elif _MSC_VER && (_M_IX86 || _M_X64) +#define zig_trap() __ud2() +#elif _MSC_VER +#define zig_trap() __fastfail(0) #elif defined(__i386__) || defined(__x86_64__) #define zig_trap() __asm__ volatile("ud2"); +#elif defined(__arm__) || defined(__aarch64__) +#define zig_breakpoint() __asm__ volatile("udf #0"); #else -#define zig_trap() raise(SIGTRAP) +#include +#define zig_trap() abort() #endif #if zig_has_builtin(debugtrap) @@ -202,8 +209,17 @@ typedef char bool; #define zig_breakpoint() __debugbreak() #elif defined(__i386__) || defined(__x86_64__) #define zig_breakpoint() __asm__ volatile("int $0x03"); +#elif defined(__arm__) +#define zig_breakpoint() __asm__ volatile("bkpt #0"); +#elif defined(__aarch64__) +#define zig_breakpoint() __asm__ volatile("brk #0"); #else +#include +#if defined(SIGTRAP) #define zig_breakpoint() raise(SIGTRAP) +#else +#define zig_breakpoint() zig_breakpoint_unavailable +#endif #endif #if zig_has_builtin(return_address) || defined(zig_gnuc) @@ -2384,6 +2400,44 @@ static inline void zig_subw_big(void *res, const void *lhs, const void *rhs, boo (void)zig_subo_big(res, lhs, rhs, is_signed, bits); } +zig_extern void __udivei4(uint32_t *res, const uint32_t *lhs, const uint32_t *rhs, uintptr_t bits); +static inline void zig_div_trunc_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) { + if (!is_signed) { + __udivei4(res, lhs, rhs, bits); + return; + } + + zig_trap(); +} + +static inline void zig_div_floor_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) { + if (!is_signed) { + zig_div_trunc_big(res, lhs, rhs, is_signed, bits); + return; + } + + zig_trap(); +} + +zig_extern void __umodei4(uint32_t *res, const uint32_t *lhs, const uint32_t *rhs, uintptr_t bits); +static inline void zig_rem_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) { + if (!is_signed) { + __umodei4(res, lhs, rhs, bits); + return; + } + + zig_trap(); +} + +static inline void zig_mod_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) { + if (!is_signed) { + zig_rem_big(res, lhs, rhs, is_signed, bits); + return; + } + + zig_trap(); +} + static inline uint16_t zig_clz_big(const void *val, bool is_signed, uint16_t bits) { const uint8_t *val_bytes = val; uint16_t byte_offset = 0; From 3169f0529b22fca2a387a841a62fec29b3d2096a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 9 Mar 2023 19:51:58 -0700 Subject: [PATCH 076/294] eliminate posix_spawn from the standard library Today I found out that posix_spawn is trash. It's actually implemented on top of fork/exec inside of libc (or libSystem in the case of macOS). So, anything posix_spawn can do, we can do better. In particular, what we can do better is handle spawning of child processes that are potentially foreign binaries. If you try to spawn a wasm binary, for example, posix spawn does the following: * Goes ahead and creates a child process. * The child process writes "foo.wasm: foo.wasm: cannot execute binary file" to stderr (yes, it prints the filename twice). * The child process then exits with code 126. This behavior is indistinguishable from the binary being successfully spawned, and then printing to stderr, and exiting with a failure - something that is an extremely common occurrence. Meanwhile, using the lower level fork/exec will simply return ENOEXEC code from the execve syscall (which is mapped to zig error.InvalidExe). The posix_spawn behavior means the zig build runner can't tell the difference between a failure to run a foreign binary, and a binary that did run, but failed in some other fashion. This is unacceptable, because attempting to excecve is the proper way to support things like Rosetta. --- CMakeLists.txt | 1 - lib/std/Build/CompileStep.zig | 2 - lib/std/child_process.zig | 127 +-------------- lib/std/os.zig | 5 +- lib/std/os/posix_spawn.zig | 290 ---------------------------------- 5 files changed, 3 insertions(+), 422 deletions(-) delete mode 100644 lib/std/os/posix_spawn.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index 501d12889f..46d9c701b0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -303,7 +303,6 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/lib/std/os/linux.zig" "${CMAKE_SOURCE_DIR}/lib/std/os/linux/io_uring.zig" "${CMAKE_SOURCE_DIR}/lib/std/os/linux/x86_64.zig" - "${CMAKE_SOURCE_DIR}/lib/std/os/posix_spawn.zig" "${CMAKE_SOURCE_DIR}/lib/std/os/windows.zig" "${CMAKE_SOURCE_DIR}/lib/std/os/windows/ntstatus.zig" "${CMAKE_SOURCE_DIR}/lib/std/os/windows/win32error.zig" diff --git a/lib/std/Build/CompileStep.zig b/lib/std/Build/CompileStep.zig index ea2320cc89..49d2fae68d 100644 --- a/lib/std/Build/CompileStep.zig +++ b/lib/std/Build/CompileStep.zig @@ -742,7 +742,6 @@ pub fn runPkgConfig(self: *CompileStep, lib_name: []const u8) ![]const []const u error.ExecNotSupported => return error.PkgConfigFailed, error.ExitCodeFailure => return error.PkgConfigFailed, error.FileNotFound => return error.PkgConfigNotInstalled, - error.ChildExecFailed => return error.PkgConfigFailed, else => return err, }; @@ -2042,7 +2041,6 @@ fn getPkgConfigList(self: *std.Build) ![]const PkgConfigPkg { error.FileNotFound => error.PkgConfigNotInstalled, error.InvalidName => error.PkgConfigNotInstalled, error.PkgConfigInvalidOutput => error.PkgConfigInvalidOutput, - error.ChildExecFailed => error.PkgConfigFailed, else => return err, }; self.pkg_config_pkg_list = result; diff --git a/lib/std/child_process.zig b/lib/std/child_process.zig index dba92ab998..3bdef3177a 100644 --- a/lib/std/child_process.zig +++ b/lib/std/child_process.zig @@ -90,8 +90,7 @@ pub const ChildProcess = struct { os.SetIdError || os.ChangeCurDirError || windows.CreateProcessError || - windows.WaitForSingleObjectError || - os.posix_spawn.Error; + windows.WaitForSingleObjectError; pub const Term = union(enum) { Exited: u8, @@ -143,10 +142,6 @@ pub const ChildProcess = struct { @compileError("the target operating system cannot spawn processes"); } - if (comptime builtin.target.isDarwin()) { - return self.spawnMacos(); - } - if (builtin.os.tag == .windows) { return self.spawnWindows(); } else { @@ -337,10 +332,7 @@ pub const ChildProcess = struct { } fn waitUnwrapped(self: *ChildProcess) !void { - const res: os.WaitPidResult = if (comptime builtin.target.isDarwin()) - try os.posix_spawn.waitpid(self.id, 0) - else - os.waitpid(self.id, 0); + const res: os.WaitPidResult = os.waitpid(self.id, 0); const status = res.status; self.cleanupStreams(); self.handleWaitResult(status); @@ -416,121 +408,6 @@ pub const ChildProcess = struct { Term{ .Unknown = status }; } - fn spawnMacos(self: *ChildProcess) SpawnError!void { - const pipe_flags = if (io.is_async) os.O.NONBLOCK else 0; - const stdin_pipe = if (self.stdin_behavior == StdIo.Pipe) try os.pipe2(pipe_flags) else undefined; - errdefer if (self.stdin_behavior == StdIo.Pipe) destroyPipe(stdin_pipe); - - const stdout_pipe = if (self.stdout_behavior == StdIo.Pipe) try os.pipe2(pipe_flags) else undefined; - errdefer if (self.stdout_behavior == StdIo.Pipe) destroyPipe(stdout_pipe); - - const stderr_pipe = if (self.stderr_behavior == StdIo.Pipe) try os.pipe2(pipe_flags) else undefined; - errdefer if (self.stderr_behavior == StdIo.Pipe) destroyPipe(stderr_pipe); - - const any_ignore = (self.stdin_behavior == StdIo.Ignore or self.stdout_behavior == StdIo.Ignore or self.stderr_behavior == StdIo.Ignore); - const dev_null_fd = if (any_ignore) - os.openZ("/dev/null", os.O.RDWR, 0) catch |err| switch (err) { - error.PathAlreadyExists => unreachable, - error.NoSpaceLeft => unreachable, - error.FileTooBig => unreachable, - error.DeviceBusy => unreachable, - error.FileLocksNotSupported => unreachable, - error.BadPathName => unreachable, // Windows-only - error.InvalidHandle => unreachable, // WASI-only - error.WouldBlock => unreachable, - else => |e| return e, - } - else - undefined; - defer if (any_ignore) os.close(dev_null_fd); - - var attr = try os.posix_spawn.Attr.init(); - defer attr.deinit(); - var flags: u16 = os.darwin.POSIX_SPAWN_SETSIGDEF | os.darwin.POSIX_SPAWN_SETSIGMASK; - if (self.disable_aslr) { - flags |= os.darwin._POSIX_SPAWN_DISABLE_ASLR; - } - if (self.start_suspended) { - flags |= os.darwin.POSIX_SPAWN_START_SUSPENDED; - } - try attr.set(flags); - - var actions = try os.posix_spawn.Actions.init(); - defer actions.deinit(); - - try setUpChildIoPosixSpawn(self.stdin_behavior, &actions, stdin_pipe, os.STDIN_FILENO, dev_null_fd); - try setUpChildIoPosixSpawn(self.stdout_behavior, &actions, stdout_pipe, os.STDOUT_FILENO, dev_null_fd); - try setUpChildIoPosixSpawn(self.stderr_behavior, &actions, stderr_pipe, os.STDERR_FILENO, dev_null_fd); - - if (self.cwd_dir) |cwd| { - try actions.fchdir(cwd.fd); - } else if (self.cwd) |cwd| { - try actions.chdir(cwd); - } - - var arena_allocator = std.heap.ArenaAllocator.init(self.allocator); - defer arena_allocator.deinit(); - const arena = arena_allocator.allocator(); - - const argv_buf = try arena.allocSentinel(?[*:0]u8, self.argv.len, null); - for (self.argv, 0..) |arg, i| argv_buf[i] = (try arena.dupeZ(u8, arg)).ptr; - - const envp = if (self.env_map) |env_map| m: { - const envp_buf = try createNullDelimitedEnvMap(arena, env_map); - break :m envp_buf.ptr; - } else std.c.environ; - - const pid = try os.posix_spawn.spawnp(self.argv[0], actions, attr, argv_buf, envp); - - if (self.stdin_behavior == StdIo.Pipe) { - self.stdin = File{ .handle = stdin_pipe[1] }; - } else { - self.stdin = null; - } - if (self.stdout_behavior == StdIo.Pipe) { - self.stdout = File{ .handle = stdout_pipe[0] }; - } else { - self.stdout = null; - } - if (self.stderr_behavior == StdIo.Pipe) { - self.stderr = File{ .handle = stderr_pipe[0] }; - } else { - self.stderr = null; - } - - self.id = pid; - self.term = null; - - if (self.stdin_behavior == StdIo.Pipe) { - os.close(stdin_pipe[0]); - } - if (self.stdout_behavior == StdIo.Pipe) { - os.close(stdout_pipe[1]); - } - if (self.stderr_behavior == StdIo.Pipe) { - os.close(stderr_pipe[1]); - } - } - - fn setUpChildIoPosixSpawn( - stdio: StdIo, - actions: *os.posix_spawn.Actions, - pipe_fd: [2]i32, - std_fileno: i32, - dev_null_fd: i32, - ) !void { - switch (stdio) { - .Pipe => { - const idx: usize = if (std_fileno == 0) 0 else 1; - try actions.dup2(pipe_fd[idx], std_fileno); - try actions.close(pipe_fd[1 - idx]); - }, - .Close => try actions.close(std_fileno), - .Inherit => {}, - .Ignore => try actions.dup2(dev_null_fd, std_fileno), - } - } - fn spawnPosix(self: *ChildProcess) SpawnError!void { const pipe_flags = if (io.is_async) os.O.NONBLOCK else 0; const stdin_pipe = if (self.stdin_behavior == StdIo.Pipe) try os.pipe2(pipe_flags) else undefined; diff --git a/lib/std/os.zig b/lib/std/os.zig index 6c680e9a38..069a910408 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -41,7 +41,6 @@ pub const plan9 = @import("os/plan9.zig"); pub const uefi = @import("os/uefi.zig"); pub const wasi = @import("os/wasi.zig"); pub const windows = @import("os/windows.zig"); -pub const posix_spawn = @import("os/posix_spawn.zig"); pub const ptrace = @import("os/ptrace.zig"); comptime { @@ -56,7 +55,6 @@ test { } _ = wasi; _ = windows; - _ = posix_spawn; _ = @import("os/test.zig"); } @@ -3998,8 +3996,7 @@ pub const WaitPidResult = struct { }; /// Use this version of the `waitpid` wrapper if you spawned your child process using explicit -/// `fork` and `execve` method. If you spawned your child process using `posix_spawn` method, -/// use `std.os.posix_spawn.waitpid` instead. +/// `fork` and `execve` method. pub fn waitpid(pid: pid_t, flags: u32) WaitPidResult { const Status = if (builtin.link_libc) c_int else u32; var status: Status = undefined; diff --git a/lib/std/os/posix_spawn.zig b/lib/std/os/posix_spawn.zig deleted file mode 100644 index 32904a9423..0000000000 --- a/lib/std/os/posix_spawn.zig +++ /dev/null @@ -1,290 +0,0 @@ -const std = @import("std"); -const builtin = @import("builtin"); - -const os = @import("../os.zig"); -const system = os.system; -const errno = system.getErrno; -const fd_t = system.fd_t; -const mode_t = system.mode_t; -const pid_t = system.pid_t; -const unexpectedErrno = os.unexpectedErrno; -const UnexpectedError = os.UnexpectedError; -const toPosixPath = os.toPosixPath; -const WaitPidResult = os.WaitPidResult; - -pub usingnamespace posix_spawn; - -pub const Error = error{ - SystemResources, - InvalidFileDescriptor, - NameTooLong, - TooBig, - PermissionDenied, - InputOutput, - FileSystem, - FileNotFound, - InvalidExe, - NotDir, - FileBusy, - - /// Returned when the child fails to execute either in the pre-exec() initialization step, or - /// when exec(3) is invoked. - ChildExecFailed, -} || UnexpectedError; - -const posix_spawn = if (builtin.target.isDarwin()) struct { - pub const Attr = struct { - attr: system.posix_spawnattr_t, - - pub fn init() Error!Attr { - var attr: system.posix_spawnattr_t = undefined; - switch (errno(system.posix_spawnattr_init(&attr))) { - .SUCCESS => return Attr{ .attr = attr }, - .NOMEM => return error.SystemResources, - .INVAL => unreachable, - else => |err| return unexpectedErrno(err), - } - } - - pub fn deinit(self: *Attr) void { - defer self.* = undefined; - switch (errno(system.posix_spawnattr_destroy(&self.attr))) { - .SUCCESS => return, - .INVAL => unreachable, // Invalid parameters. - else => unreachable, - } - } - - pub fn get(self: Attr) Error!u16 { - var flags: c_short = undefined; - switch (errno(system.posix_spawnattr_getflags(&self.attr, &flags))) { - .SUCCESS => return @bitCast(u16, flags), - .INVAL => unreachable, - else => |err| return unexpectedErrno(err), - } - } - - pub fn set(self: *Attr, flags: u16) Error!void { - switch (errno(system.posix_spawnattr_setflags(&self.attr, @bitCast(c_short, flags)))) { - .SUCCESS => return, - .INVAL => unreachable, - else => |err| return unexpectedErrno(err), - } - } - }; - - pub const Actions = struct { - actions: system.posix_spawn_file_actions_t, - - pub fn init() Error!Actions { - var actions: system.posix_spawn_file_actions_t = undefined; - switch (errno(system.posix_spawn_file_actions_init(&actions))) { - .SUCCESS => return Actions{ .actions = actions }, - .NOMEM => return error.SystemResources, - .INVAL => unreachable, - else => |err| return unexpectedErrno(err), - } - } - - pub fn deinit(self: *Actions) void { - defer self.* = undefined; - switch (errno(system.posix_spawn_file_actions_destroy(&self.actions))) { - .SUCCESS => return, - .INVAL => unreachable, // Invalid parameters. - else => unreachable, - } - } - - pub fn open(self: *Actions, fd: fd_t, path: []const u8, flags: u32, mode: mode_t) Error!void { - const posix_path = try toPosixPath(path); - return self.openZ(fd, &posix_path, flags, mode); - } - - pub fn openZ(self: *Actions, fd: fd_t, path: [*:0]const u8, flags: u32, mode: mode_t) Error!void { - switch (errno(system.posix_spawn_file_actions_addopen(&self.actions, fd, path, @bitCast(c_int, flags), mode))) { - .SUCCESS => return, - .BADF => return error.InvalidFileDescriptor, - .NOMEM => return error.SystemResources, - .NAMETOOLONG => return error.NameTooLong, - .INVAL => unreachable, // the value of file actions is invalid - else => |err| return unexpectedErrno(err), - } - } - - pub fn close(self: *Actions, fd: fd_t) Error!void { - switch (errno(system.posix_spawn_file_actions_addclose(&self.actions, fd))) { - .SUCCESS => return, - .BADF => return error.InvalidFileDescriptor, - .NOMEM => return error.SystemResources, - .INVAL => unreachable, // the value of file actions is invalid - .NAMETOOLONG => unreachable, - else => |err| return unexpectedErrno(err), - } - } - - pub fn dup2(self: *Actions, fd: fd_t, newfd: fd_t) Error!void { - switch (errno(system.posix_spawn_file_actions_adddup2(&self.actions, fd, newfd))) { - .SUCCESS => return, - .BADF => return error.InvalidFileDescriptor, - .NOMEM => return error.SystemResources, - .INVAL => unreachable, // the value of file actions is invalid - .NAMETOOLONG => unreachable, - else => |err| return unexpectedErrno(err), - } - } - - pub fn inherit(self: *Actions, fd: fd_t) Error!void { - switch (errno(system.posix_spawn_file_actions_addinherit_np(&self.actions, fd))) { - .SUCCESS => return, - .BADF => return error.InvalidFileDescriptor, - .NOMEM => return error.SystemResources, - .INVAL => unreachable, // the value of file actions is invalid - .NAMETOOLONG => unreachable, - else => |err| return unexpectedErrno(err), - } - } - - pub fn chdir(self: *Actions, path: []const u8) Error!void { - const posix_path = try toPosixPath(path); - return self.chdirZ(&posix_path); - } - - pub fn chdirZ(self: *Actions, path: [*:0]const u8) Error!void { - switch (errno(system.posix_spawn_file_actions_addchdir_np(&self.actions, path))) { - .SUCCESS => return, - .NOMEM => return error.SystemResources, - .NAMETOOLONG => return error.NameTooLong, - .BADF => unreachable, - .INVAL => unreachable, // the value of file actions is invalid - else => |err| return unexpectedErrno(err), - } - } - - pub fn fchdir(self: *Actions, fd: fd_t) Error!void { - switch (errno(system.posix_spawn_file_actions_addfchdir_np(&self.actions, fd))) { - .SUCCESS => return, - .BADF => return error.InvalidFileDescriptor, - .NOMEM => return error.SystemResources, - .INVAL => unreachable, // the value of file actions is invalid - .NAMETOOLONG => unreachable, - else => |err| return unexpectedErrno(err), - } - } - }; - - pub fn spawn( - path: []const u8, - actions: ?Actions, - attr: ?Attr, - argv: [*:null]?[*:0]const u8, - envp: [*:null]?[*:0]const u8, - ) Error!pid_t { - const posix_path = try toPosixPath(path); - return spawnZ(&posix_path, actions, attr, argv, envp); - } - - pub fn spawnZ( - path: [*:0]const u8, - actions: ?Actions, - attr: ?Attr, - argv: [*:null]?[*:0]const u8, - envp: [*:null]?[*:0]const u8, - ) Error!pid_t { - var pid: pid_t = undefined; - switch (errno(system.posix_spawn( - &pid, - path, - if (actions) |a| &a.actions else null, - if (attr) |a| &a.attr else null, - argv, - envp, - ))) { - .SUCCESS => return pid, - .@"2BIG" => return error.TooBig, - .NOMEM => return error.SystemResources, - .BADF => return error.InvalidFileDescriptor, - .ACCES => return error.PermissionDenied, - .IO => return error.InputOutput, - .LOOP => return error.FileSystem, - .NAMETOOLONG => return error.NameTooLong, - .NOENT => return error.FileNotFound, - .NOEXEC => return error.InvalidExe, - .NOTDIR => return error.NotDir, - .TXTBSY => return error.FileBusy, - .BADARCH => return error.InvalidExe, - .BADEXEC => return error.InvalidExe, - .FAULT => unreachable, - .INVAL => unreachable, - else => |err| return unexpectedErrno(err), - } - } - - pub fn spawnp( - file: []const u8, - actions: ?Actions, - attr: ?Attr, - argv: [*:null]?[*:0]const u8, - envp: [*:null]?[*:0]const u8, - ) Error!pid_t { - const posix_file = try toPosixPath(file); - return spawnpZ(&posix_file, actions, attr, argv, envp); - } - - pub fn spawnpZ( - file: [*:0]const u8, - actions: ?Actions, - attr: ?Attr, - argv: [*:null]?[*:0]const u8, - envp: [*:null]?[*:0]const u8, - ) Error!pid_t { - var pid: pid_t = undefined; - switch (errno(system.posix_spawnp( - &pid, - file, - if (actions) |a| &a.actions else null, - if (attr) |a| &a.attr else null, - argv, - envp, - ))) { - .SUCCESS => return pid, - .@"2BIG" => return error.TooBig, - .NOMEM => return error.SystemResources, - .BADF => return error.InvalidFileDescriptor, - .ACCES => return error.PermissionDenied, - .IO => return error.InputOutput, - .LOOP => return error.FileSystem, - .NAMETOOLONG => return error.NameTooLong, - .NOENT => return error.FileNotFound, - .NOEXEC => return error.InvalidExe, - .NOTDIR => return error.NotDir, - .TXTBSY => return error.FileBusy, - .BADARCH => return error.InvalidExe, - .BADEXEC => return error.InvalidExe, - .FAULT => unreachable, - .INVAL => unreachable, - else => |err| return unexpectedErrno(err), - } - } - - /// Use this version of the `waitpid` wrapper if you spawned your child process using `posix_spawn` - /// or `posix_spawnp` syscalls. - /// See also `std.os.waitpid` for an alternative if your child process was spawned via `fork` and - /// `execve` method. - pub fn waitpid(pid: pid_t, flags: u32) Error!WaitPidResult { - const Status = if (builtin.link_libc) c_int else u32; - var status: Status = undefined; - while (true) { - const rc = system.waitpid(pid, &status, if (builtin.link_libc) @intCast(c_int, flags) else flags); - switch (errno(rc)) { - .SUCCESS => return WaitPidResult{ - .pid = @intCast(pid_t, rc), - .status = @bitCast(u32, status), - }, - .INTR => continue, - .CHILD => return error.ChildExecFailed, - .INVAL => unreachable, // Invalid flags. - else => unreachable, - } - } - } -} else struct {}; From 4ea2f441df36cec61e1017f4d795d4037326c98c Mon Sep 17 00:00:00 2001 From: Andrius Bentkus Date: Sat, 16 Jul 2022 15:46:13 +0300 Subject: [PATCH 077/294] Module: retry ZIR cache file creation There are no dir components, so you would think that this was unreachable, however we have observed on macOS two processes racing to do openat() with O_CREAT manifest in ENOENT. closes #12138 --- src/Module.zig | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/src/Module.zig b/src/Module.zig index f3f1aa44e2..8c52176edd 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -3569,20 +3569,25 @@ pub fn astGenFile(mod: *Module, file: *File) !void { // If another process is already working on this file, we will get the cached // version. Likewise if we're working on AstGen and another process asks for // the cached file, they'll get it. - const cache_file = zir_dir.createFile(&digest, .{ - .read = true, - .truncate = false, - .lock = lock, - }) catch |err| switch (err) { - error.NotDir => unreachable, // no dir components - error.InvalidUtf8 => unreachable, // it's a hex encoded name - error.BadPathName => unreachable, // it's a hex encoded name - error.NameTooLong => unreachable, // it's a fixed size name - error.PipeBusy => unreachable, // it's not a pipe - error.WouldBlock => unreachable, // not asking for non-blocking I/O - error.FileNotFound => unreachable, // no dir components + const cache_file = while (true) { + break zir_dir.createFile(&digest, .{ + .read = true, + .truncate = false, + .lock = lock, + }) catch |err| switch (err) { + error.NotDir => unreachable, // no dir components + error.InvalidUtf8 => unreachable, // it's a hex encoded name + error.BadPathName => unreachable, // it's a hex encoded name + error.NameTooLong => unreachable, // it's a fixed size name + error.PipeBusy => unreachable, // it's not a pipe + error.WouldBlock => unreachable, // not asking for non-blocking I/O + // There are no dir components, so you would think that this was + // unreachable, however we have observed on macOS two processes racing + // to do openat() with O_CREAT manifest in ENOENT. + error.FileNotFound => continue, - else => |e| return e, // Retryable errors are handled at callsite. + else => |e| return e, // Retryable errors are handled at callsite. + }; }; defer cache_file.close(); From 817fb263b533f0a24476cabe43a6ee5826113d8d Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sun, 5 Mar 2023 17:40:53 +0100 Subject: [PATCH 078/294] x86_64: downstream table-driven instruction encoder --- src/arch/x86_64/CodeGen.zig | 175 +-- src/arch/x86_64/Emit.zig | 2639 +++++---------------------------- src/arch/x86_64/Encoding.zig | 521 +++++++ src/arch/x86_64/Mir.zig | 48 +- src/arch/x86_64/bits.zig | 1165 ++++----------- src/arch/x86_64/encoder.zig | 794 ++++++++++ src/arch/x86_64/encodings.zig | 542 +++++++ 7 files changed, 2622 insertions(+), 3262 deletions(-) create mode 100644 src/arch/x86_64/Encoding.zig create mode 100644 src/arch/x86_64/encoder.zig create mode 100644 src/arch/x86_64/encodings.zig diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index f8f6a773fa..c108ad6f32 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -303,7 +303,12 @@ pub fn generate( var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) { error.CodegenFail => return Result{ .fail = function.err_msg.? }, error.OutOfRegisters => return Result{ - .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), + .fail = try ErrorMsg.create( + bin_file.allocator, + src_loc, + "CodeGen ran out of registers. This is a bug in the Zig compiler.", + .{}, + ), }, else => |e| return e, }; @@ -342,6 +347,20 @@ pub fn generate( defer emit.deinit(); emit.lowerMir() catch |err| switch (err) { error.EmitFail => return Result{ .fail = emit.err_msg.? }, + error.InvalidInstruction, error.CannotEncode => |e| { + const msg = switch (e) { + error.InvalidInstruction => "CodeGen failed to find a viable instruction.", + error.CannotEncode => "CodeGen failed to encode the instruction.", + }; + return Result{ + .fail = try ErrorMsg.create( + bin_file.allocator, + src_loc, + "{s} This is a bug in the Zig compiler.", + .{msg}, + ), + }; + }, else => |e| return e, }; @@ -1687,7 +1706,7 @@ fn genIntMulDivOpMir( else => unreachable, }, }), - .data = .{ .imm = @bitCast(u32, -off) }, + .data = .{ .disp = -off }, }); }, else => unreachable, @@ -2191,7 +2210,7 @@ fn genSliceElemPtr(self: *Self, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref) !MCValue { .reg2 = .rbp, .flags = 0b01, }), - .data = .{ .imm = @bitCast(u32, -@intCast(i32, off)) }, + .data = .{ .disp = -@intCast(i32, off) }, }); }, else => return self.fail("TODO implement slice_elem_ptr when slice is {}", .{slice_mcv}), @@ -2275,7 +2294,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void { .reg1 = addr_reg.to64(), .reg2 = .rbp, }), - .data = .{ .imm = @bitCast(u32, -off) }, + .data = .{ .disp = -off }, }); }, .stack_offset => |off| { @@ -2286,7 +2305,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void { .reg1 = addr_reg.to64(), .reg2 = .rbp, }), - .data = .{ .imm = @bitCast(u32, -off) }, + .data = .{ .disp = -off }, }); }, .memory, .linker_load => { @@ -2352,7 +2371,7 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void { .reg2 = dst_mcv.register, .flags = 0b01, }), - .data = .{ .imm = 0 }, + .data = .{ .disp = 0 }, }); break :result .{ .register = registerAlias(dst_mcv.register, @intCast(u32, elem_abi_size)) }; } @@ -2615,7 +2634,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo .reg2 = reg, .flags = 0b01, }), - .data = .{ .imm = 0 }, + .data = .{ .disp = 0 }, }); }, .stack_offset => |off| { @@ -2842,7 +2861,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type .reg2 = addr_reg.to64(), .flags = 0b01, }), - .data = .{ .imm = 0 }, + .data = .{ .disp = 0 }, }); const new_ptr = MCValue{ .register = addr_reg.to64() }; @@ -2903,7 +2922,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type .reg2 = tmp_reg, .flags = 0b01, }), - .data = .{ .imm = 0 }, + .data = .{ .disp = 0 }, }); return self.store(new_ptr, .{ .register = tmp_reg }, ptr_ty, value_ty); } @@ -3542,25 +3561,13 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu if (intrinsicsAllowed(self.target.*, dst_ty)) { const actual_tag: Mir.Inst.Tag = switch (dst_ty.tag()) { .f32 => switch (mir_tag) { - .add => if (hasAvxSupport(self.target.*)) - Mir.Inst.Tag.add_f32_avx - else - Mir.Inst.Tag.add_f32_sse, - .cmp => if (hasAvxSupport(self.target.*)) - Mir.Inst.Tag.cmp_f32_avx - else - Mir.Inst.Tag.cmp_f32_sse, + .add => Mir.Inst.Tag.add_f32, + .cmp => Mir.Inst.Tag.cmp_f32, else => return self.fail("TODO genBinOpMir for f32 register-register with MIR tag {}", .{mir_tag}), }, .f64 => switch (mir_tag) { - .add => if (hasAvxSupport(self.target.*)) - Mir.Inst.Tag.add_f64_avx - else - Mir.Inst.Tag.add_f64_sse, - .cmp => if (hasAvxSupport(self.target.*)) - Mir.Inst.Tag.cmp_f64_avx - else - Mir.Inst.Tag.cmp_f64_sse, + .add => Mir.Inst.Tag.add_f64, + .cmp => Mir.Inst.Tag.cmp_f64, else => return self.fail("TODO genBinOpMir for f64 register-register with MIR tag {}", .{mir_tag}), }, else => return self.fail("TODO genBinOpMir for float register-register and type {}", .{dst_ty.fmtDebug()}), @@ -3618,7 +3625,7 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu .reg2 = .rbp, .flags = 0b01, }), - .data = .{ .imm = @bitCast(u32, -off) }, + .data = .{ .disp = -off }, }); }, } @@ -3644,7 +3651,7 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu .reg2 = registerAlias(src_reg, abi_size), .flags = 0b10, }), - .data = .{ .imm = @bitCast(u32, -off) }, + .data = .{ .disp = -off }, }); }, .immediate => |imm| { @@ -3665,7 +3672,7 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu else => unreachable, }; const payload = try self.addExtra(Mir.ImmPair{ - .dest_off = @bitCast(u32, -off), + .dest_off = -off, .operand = @truncate(u32, imm), }); _ = try self.addInst(.{ @@ -3756,7 +3763,7 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M .reg2 = .rbp, .flags = 0b01, }), - .data = .{ .imm = @bitCast(u32, -off) }, + .data = .{ .disp = -off }, }); }, .memory => { @@ -5360,7 +5367,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE // offset from rbp, which is at the top of the stack frame. // mov [rbp+offset], immediate const payload = try self.addExtra(Mir.ImmPair{ - .dest_off = @bitCast(u32, -stack_offset), + .dest_off = -stack_offset, .operand = @truncate(u32, imm), }); _ = try self.addInst(.{ @@ -5400,14 +5407,8 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE .Float => { if (intrinsicsAllowed(self.target.*, ty)) { const tag: Mir.Inst.Tag = switch (ty.tag()) { - .f32 => if (hasAvxSupport(self.target.*)) - Mir.Inst.Tag.mov_f32_avx - else - Mir.Inst.Tag.mov_f32_sse, - .f64 => if (hasAvxSupport(self.target.*)) - Mir.Inst.Tag.mov_f64_avx - else - Mir.Inst.Tag.mov_f64_sse, + .f32 => Mir.Inst.Tag.mov_f32, + .f64 => Mir.Inst.Tag.mov_f64, else => return self.fail("TODO genSetStackArg for register for type {}", .{ty.fmtDebug()}), }; _ = try self.addInst(.{ @@ -5421,7 +5422,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE .reg2 = reg.to128(), .flags = 0b01, }), - .data = .{ .imm = @bitCast(u32, -stack_offset) }, + .data = .{ .disp = -stack_offset }, }); return; } @@ -5436,7 +5437,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE .reg2 = registerAlias(reg, @intCast(u32, abi_size)), .flags = 0b10, }), - .data = .{ .imm = @bitCast(u32, -stack_offset) }, + .data = .{ .disp = -stack_offset }, }); }, } @@ -5516,7 +5517,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl 0 => { assert(ty.isError()); const payload = try self.addExtra(Mir.ImmPair{ - .dest_off = @bitCast(u32, -stack_offset), + .dest_off = -stack_offset, .operand = @truncate(u32, x_big), }); _ = try self.addInst(.{ @@ -5530,7 +5531,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl }, 1, 2, 4 => { const payload = try self.addExtra(Mir.ImmPair{ - .dest_off = @bitCast(u32, -stack_offset), + .dest_off = -stack_offset, .operand = @truncate(u32, x_big), }); _ = try self.addInst(.{ @@ -5552,7 +5553,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl // insted just use two 32 bit writes to avoid register allocation { const payload = try self.addExtra(Mir.ImmPair{ - .dest_off = @bitCast(u32, -stack_offset + 4), + .dest_off = -stack_offset + 4, .operand = @truncate(u32, x_big >> 32), }); _ = try self.addInst(.{ @@ -5566,7 +5567,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl } { const payload = try self.addExtra(Mir.ImmPair{ - .dest_off = @bitCast(u32, -stack_offset), + .dest_off = -stack_offset, .operand = @truncate(u32, x_big), }); _ = try self.addInst(.{ @@ -5595,14 +5596,8 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl .Float => { if (intrinsicsAllowed(self.target.*, ty)) { const tag: Mir.Inst.Tag = switch (ty.tag()) { - .f32 => if (hasAvxSupport(self.target.*)) - Mir.Inst.Tag.mov_f32_avx - else - Mir.Inst.Tag.mov_f32_sse, - .f64 => if (hasAvxSupport(self.target.*)) - Mir.Inst.Tag.mov_f64_avx - else - Mir.Inst.Tag.mov_f64_sse, + .f32 => Mir.Inst.Tag.mov_f32, + .f64 => Mir.Inst.Tag.mov_f64, else => return self.fail("TODO genSetStack for register for type {}", .{ty.fmtDebug()}), }; _ = try self.addInst(.{ @@ -5616,7 +5611,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl .reg2 = reg.to128(), .flags = 0b01, }), - .data = .{ .imm = @bitCast(u32, -stack_offset) }, + .data = .{ .disp = -stack_offset }, }); return; } @@ -5691,7 +5686,7 @@ fn genInlineMemcpyRegisterRegister( .reg2 = registerAlias(tmp_reg, nearest_power_of_two), .flags = 0b10, }), - .data = .{ .imm = @bitCast(u32, -next_offset) }, + .data = .{ .disp = -next_offset }, }); if (nearest_power_of_two > 1) { @@ -5711,7 +5706,7 @@ fn genInlineMemcpyRegisterRegister( .reg2 = registerAlias(src_reg, @intCast(u32, abi_size)), .flags = 0b10, }), - .data = .{ .imm = @bitCast(u32, -offset) }, + .data = .{ .disp = -offset }, }); } } @@ -5758,7 +5753,7 @@ fn genInlineMemcpy( .reg1 = dst_addr_reg.to64(), .reg2 = opts.dest_stack_base orelse .rbp, }), - .data = .{ .imm = @bitCast(u32, -off) }, + .data = .{ .disp = -off }, }); }, .register => |reg| { @@ -5787,7 +5782,7 @@ fn genInlineMemcpy( .reg1 = src_addr_reg.to64(), .reg2 = opts.source_stack_base orelse .rbp, }), - .data = .{ .imm = @bitCast(u32, -off) }, + .data = .{ .disp = -off }, }); }, .register => |reg| { @@ -5911,7 +5906,7 @@ fn genInlineMemset( .reg1 = addr_reg.to64(), .reg2 = opts.dest_stack_base orelse .rbp, }), - .data = .{ .imm = @bitCast(u32, -off) }, + .data = .{ .disp = -off }, }); }, .register => |reg| { @@ -5998,7 +5993,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void .reg1 = registerAlias(reg, abi_size), .reg2 = .rbp, }), - .data = .{ .imm = @bitCast(u32, -off) }, + .data = .{ .disp = -off }, }); }, .unreach, .none => return, // Nothing to do. @@ -6097,14 +6092,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void .Float => { if (intrinsicsAllowed(self.target.*, ty)) { const tag: Mir.Inst.Tag = switch (ty.tag()) { - .f32 => if (hasAvxSupport(self.target.*)) - Mir.Inst.Tag.mov_f32_avx - else - Mir.Inst.Tag.mov_f32_sse, - .f64 => if (hasAvxSupport(self.target.*)) - Mir.Inst.Tag.mov_f64_avx - else - Mir.Inst.Tag.mov_f64_sse, + .f32 => Mir.Inst.Tag.mov_f32, + .f64 => Mir.Inst.Tag.mov_f64, else => return self.fail("TODO genSetReg from register for {}", .{ty.fmtDebug()}), }; _ = try self.addInst(.{ @@ -6141,14 +6130,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void if (intrinsicsAllowed(self.target.*, ty)) { const tag: Mir.Inst.Tag = switch (ty.tag()) { - .f32 => if (hasAvxSupport(self.target.*)) - Mir.Inst.Tag.mov_f32_avx - else - Mir.Inst.Tag.mov_f32_sse, - .f64 => if (hasAvxSupport(self.target.*)) - Mir.Inst.Tag.mov_f64_avx - else - Mir.Inst.Tag.mov_f64_sse, + .f32 => Mir.Inst.Tag.mov_f32, + .f64 => Mir.Inst.Tag.mov_f64, else => return self.fail("TODO genSetReg from memory for {}", .{ty.fmtDebug()}), }; @@ -6162,7 +6145,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void else => unreachable, }, }), - .data = .{ .imm = 0 }, + .data = .{ .disp = 0 }, }); return; } @@ -6178,7 +6161,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void .reg2 = reg.to64(), .flags = 0b01, }), - .data = .{ .imm = 0 }, + .data = .{ .disp = 0 }, }); }, } @@ -6190,14 +6173,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void if (intrinsicsAllowed(self.target.*, ty)) { const tag: Mir.Inst.Tag = switch (ty.tag()) { - .f32 => if (hasAvxSupport(self.target.*)) - Mir.Inst.Tag.mov_f32_avx - else - Mir.Inst.Tag.mov_f32_sse, - .f64 => if (hasAvxSupport(self.target.*)) - Mir.Inst.Tag.mov_f64_avx - else - Mir.Inst.Tag.mov_f64_sse, + .f32 => Mir.Inst.Tag.mov_f32, + .f64 => Mir.Inst.Tag.mov_f64, else => return self.fail("TODO genSetReg from memory for {}", .{ty.fmtDebug()}), }; @@ -6211,7 +6188,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void else => unreachable, }, }), - .data = .{ .imm = 0 }, + .data = .{ .disp = 0 }, }); return; } @@ -6255,7 +6232,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void .reg2 = reg.to64(), .flags = 0b01, }), - .data = .{ .imm = 0 }, + .data = .{ .disp = 0 }, }); } } @@ -6283,7 +6260,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void .reg2 = .rbp, .flags = flags, }), - .data = .{ .imm = @bitCast(u32, -off) }, + .data = .{ .disp = -off }, }); return; } @@ -6302,7 +6279,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void .reg2 = .rbp, .flags = flags, }), - .data = .{ .imm = @bitCast(u32, -off) }, + .data = .{ .disp = -off }, }); return; } @@ -6311,14 +6288,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void .Float => { if (intrinsicsAllowed(self.target.*, ty)) { const tag: Mir.Inst.Tag = switch (ty.tag()) { - .f32 => if (hasAvxSupport(self.target.*)) - Mir.Inst.Tag.mov_f32_avx - else - Mir.Inst.Tag.mov_f32_sse, - .f64 => if (hasAvxSupport(self.target.*)) - Mir.Inst.Tag.mov_f64_avx - else - Mir.Inst.Tag.mov_f64_sse, + .f32 => Mir.Inst.Tag.mov_f32, + .f64 => Mir.Inst.Tag.mov_f64, else => return self.fail("TODO genSetReg from stack offset for {}", .{ty.fmtDebug()}), }; _ = try self.addInst(.{ @@ -6331,7 +6302,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void else => unreachable, }, }), - .data = .{ .imm = @bitCast(u32, -off) }, + .data = .{ .disp = -off }, }); return; } @@ -6347,7 +6318,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void .reg2 = .rbp, .flags = 0b01, }), - .data = .{ .imm = @bitCast(u32, -off) }, + .data = .{ .disp = -off }, }); }, } @@ -6436,7 +6407,7 @@ fn airFloatToInt(self: *Self, inst: Air.Inst.Index) !void { else => |size| return self.fail("TODO load ST(0) with abiSize={}", .{size}), }, }), - .data = .{ .imm = @bitCast(u32, -stack_offset) }, + .data = .{ .disp = -stack_offset }, }); // convert @@ -6452,7 +6423,7 @@ fn airFloatToInt(self: *Self, inst: Air.Inst.Index) !void { else => |size| return self.fail("TODO convert float with abiSize={}", .{size}), }, }), - .data = .{ .imm = @bitCast(u32, -stack_dst.stack_offset) }, + .data = .{ .disp = -stack_dst.stack_offset }, }); return self.finishAir(inst, stack_dst, .{ ty_op.operand, .none, .none }); @@ -6551,7 +6522,7 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void { .reg2 = reg, .flags = 0b01, }), - .data = .{ .imm = 0 }, + .data = .{ .disp = 0 }, }); break :blk MCValue{ .register = reg }; }, diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index e521de4bd4..1c540adc9d 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -1,3 +1,4 @@ +//! //! This file contains the functionality for lowering x86_64 MIR into //! machine code @@ -7,6 +8,7 @@ const std = @import("std"); const assert = std.debug.assert; const bits = @import("bits.zig"); const abi = @import("abi.zig"); +const encoder = @import("encoder.zig"); const link = @import("../../link.zig"); const log = std.log.scoped(.codegen); const math = std.math; @@ -19,12 +21,13 @@ const CodeGen = @import("CodeGen.zig"); const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; const Encoder = bits.Encoder; const ErrorMsg = Module.ErrorMsg; +const Instruction = encoder.Instruction; const MCValue = @import("CodeGen.zig").MCValue; +const Memory = bits.Memory; const Mir = @import("Mir.zig"); const Module = @import("../../Module.zig"); -const Instruction = bits.Instruction; -const Type = @import("../../type.zig").Type; const Register = bits.Register; +const Type = @import("../../type.zig").Type; mir: Mir, bin_file: *link.File, @@ -45,6 +48,8 @@ relocs: std.ArrayListUnmanaged(Reloc) = .{}, const InnerError = error{ OutOfMemory, EmitFail, + InvalidInstruction, + CannotEncode, }; const Reloc = struct { @@ -153,8 +158,8 @@ pub fn lowerMir(emit: *Emit) InnerError!void { .push => try emit.mirPushPop(.push, inst), .pop => try emit.mirPushPop(.pop, inst), - .jmp => try emit.mirJmpCall(.jmp_near, inst), - .call => try emit.mirJmpCall(.call_near, inst), + .jmp => try emit.mirJmpCall(.jmp, inst), + .call => try emit.mirJmpCall(.call, inst), .cond_jmp => try emit.mirCondJmp(inst), .cond_set_byte => try emit.mirCondSetByte(inst), @@ -170,25 +175,15 @@ pub fn lowerMir(emit: *Emit) InnerError!void { .interrupt => try emit.mirInterrupt(inst), .nop => {}, // just skip it - // SSE instructions - .mov_f64_sse => try emit.mirMovFloatSse(.movsd, inst), - .mov_f32_sse => try emit.mirMovFloatSse(.movss, inst), + // SSE/AVX instructions + .mov_f64 => try emit.mirMovFloat(.movsd, inst), + .mov_f32 => try emit.mirMovFloat(.movss, inst), - .add_f64_sse => try emit.mirAddFloatSse(.addsd, inst), - .add_f32_sse => try emit.mirAddFloatSse(.addss, inst), + .add_f64 => try emit.mirAddFloat(.addsd, inst), + .add_f32 => try emit.mirAddFloat(.addss, inst), - .cmp_f64_sse => try emit.mirCmpFloatSse(.ucomisd, inst), - .cmp_f32_sse => try emit.mirCmpFloatSse(.ucomiss, inst), - - // AVX instructions - .mov_f64_avx => try emit.mirMovFloatAvx(.vmovsd, inst), - .mov_f32_avx => try emit.mirMovFloatAvx(.vmovss, inst), - - .add_f64_avx => try emit.mirAddFloatAvx(.vaddsd, inst), - .add_f32_avx => try emit.mirAddFloatAvx(.vaddss, inst), - - .cmp_f64_avx => try emit.mirCmpFloatAvx(.vucomisd, inst), - .cmp_f32_avx => try emit.mirCmpFloatAvx(.vucomiss, inst), + .cmp_f64 => try emit.mirCmpFloat(.ucomisd, inst), + .cmp_f32 => try emit.mirCmpFloat(.ucomiss, inst), // Pseudo-instructions .call_extern => try emit.mirCallExtern(inst), @@ -235,8 +230,23 @@ fn fixupRelocs(emit: *Emit) InnerError!void { } } +fn encode(emit: *Emit, mnemonic: Instruction.Mnemonic, ops: struct { + op1: Instruction.Operand = .none, + op2: Instruction.Operand = .none, + op3: Instruction.Operand = .none, + op4: Instruction.Operand = .none, +}) InnerError!void { + const inst = try Instruction.new(mnemonic, .{ + .op1 = ops.op1, + .op2 = ops.op2, + .op3 = ops.op3, + .op4 = ops.op4, + }); + return inst.encode(emit.code.writer()); +} + fn mirUndefinedInstruction(emit: *Emit) InnerError!void { - return lowerToZoEnc(.ud2, emit.code); + return emit.encode(.ud2, .{}); } fn mirInterrupt(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { @@ -244,45 +254,43 @@ fn mirInterrupt(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { assert(tag == .interrupt); const ops = emit.mir.instructions.items(.ops)[inst].decode(); switch (ops.flags) { - 0b00 => return lowerToZoEnc(.int3, emit.code), + 0b00 => return emit.encode(.int3, .{}), else => return emit.fail("TODO handle variant 0b{b} of interrupt instruction", .{ops.flags}), } } fn mirSyscall(emit: *Emit) InnerError!void { - return lowerToZoEnc(.syscall, emit.code); + return emit.encode(.syscall, .{}); } -fn mirPushPop(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { +fn mirPushPop(emit: *Emit, mnemonic: Instruction.Mnemonic, inst: Mir.Inst.Index) InnerError!void { const ops = emit.mir.instructions.items(.ops)[inst].decode(); switch (ops.flags) { 0b00 => { - // PUSH/POP reg - return lowerToOEnc(tag, ops.reg1, emit.code); + return emit.encode(mnemonic, .{ + .op1 = .{ .reg = ops.reg1 }, + }); }, 0b01 => { - // PUSH/POP r/m64 - const imm = emit.mir.instructions.items(.data)[inst].imm; - const ptr_size: Memory.PtrSize = switch (immOpSize(imm)) { - 16 => .word_ptr, - else => .qword_ptr, - }; - return lowerToMEnc(tag, RegisterOrMemory.mem(ptr_size, .{ - .disp = imm, - .base = ops.reg1, - }), emit.code); + const disp = emit.mir.instructions.items(.data)[inst].disp; + return emit.encode(mnemonic, .{ + .op1 = .{ .mem = Memory.sib(.qword, .{ + .base = ops.reg1, + .disp = disp, + }) }, + }); }, 0b10 => { - // PUSH imm32 - assert(tag == .push); const imm = emit.mir.instructions.items(.data)[inst].imm; - return lowerToIEnc(.push, imm, emit.code); + return emit.encode(.push, .{ + .op1 = .{ .imm = imm }, + }); }, 0b11 => unreachable, } } -fn mirPushPopRegisterList(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { +fn mirPushPopRegisterList(emit: *Emit, mnemonic: Instruction.Mnemonic, inst: Mir.Inst.Index) InnerError!void { const ops = emit.mir.instructions.items(.ops)[inst].decode(); const payload = emit.mir.instructions.items(.data)[inst].payload; const save_reg_list = emit.mir.extraData(Mir.SaveRegisterList, payload).data; @@ -291,15 +299,20 @@ fn mirPushPopRegisterList(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerErro const callee_preserved_regs = abi.getCalleePreservedRegs(emit.target.*); for (callee_preserved_regs) |reg| { if (reg_list.isSet(callee_preserved_regs, reg)) { - switch (tag) { - .push => try lowerToMrEnc(.mov, RegisterOrMemory.mem(.qword_ptr, .{ - .disp = @bitCast(u32, disp), - .base = ops.reg1, - }), reg, emit.code), - .pop => try lowerToRmEnc(.mov, reg, RegisterOrMemory.mem(.qword_ptr, .{ - .disp = @bitCast(u32, disp), - .base = ops.reg1, - }), emit.code), + const op1: Instruction.Operand = .{ .mem = Memory.sib(.qword, .{ + .base = ops.reg1, + .disp = disp, + }) }; + const op2: Instruction.Operand = .{ .reg = reg }; + switch (mnemonic) { + .push => try emit.encode(.mov, .{ + .op1 = op1, + .op2 = op2, + }), + .pop => try emit.encode(.mov, .{ + .op1 = op2, + .op2 = op1, + }), else => unreachable, } disp += 8; @@ -307,13 +320,17 @@ fn mirPushPopRegisterList(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerErro } } -fn mirJmpCall(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { +fn mirJmpCall(emit: *Emit, mnemonic: Instruction.Mnemonic, inst: Mir.Inst.Index) InnerError!void { const ops = emit.mir.instructions.items(.ops)[inst].decode(); switch (ops.flags) { 0b00 => { const target = emit.mir.instructions.items(.data)[inst].inst; const source = emit.code.items.len; - try lowerToDEnc(tag, 0, emit.code); + try emit.encode(mnemonic, .{ + .op1 = .{ + .imm = 0, + }, + }); try emit.relocs.append(emit.bin_file.allocator, .{ .source = source, .target = target, @@ -323,34 +340,33 @@ fn mirJmpCall(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { }, 0b01 => { if (ops.reg1 == .none) { - // JMP/CALL [imm] const imm = emit.mir.instructions.items(.data)[inst].imm; - const ptr_size: Memory.PtrSize = switch (immOpSize(imm)) { - 16 => .word_ptr, - else => .qword_ptr, - }; - return lowerToMEnc(tag, RegisterOrMemory.mem(ptr_size, .{ .disp = imm }), emit.code); + return emit.encode(mnemonic, .{ + .op1 = .{ .imm = imm }, + }); } - // JMP/CALL reg - return lowerToMEnc(tag, RegisterOrMemory.reg(ops.reg1), emit.code); + return emit.encode(mnemonic, .{ + .op1 = .{ .reg = ops.reg1 }, + }); }, 0b10 => { - // JMP/CALL r/m64 - const imm = emit.mir.instructions.items(.data)[inst].imm; - return lowerToMEnc(tag, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg1.size()), .{ - .disp = imm, - .base = ops.reg1, - }), emit.code); + const disp = emit.mir.instructions.items(.data)[inst].disp; + return emit.encode(mnemonic, .{ + .op1 = .{ .mem = Memory.sib(.qword, .{ + .base = ops.reg1, + .disp = disp, + }) }, + }); }, 0b11 => return emit.fail("TODO unused variant jmp/call 0b11", .{}), } } fn mirCondJmp(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { - const mir_tag = emit.mir.instructions.items(.tag)[inst]; - assert(mir_tag == .cond_jmp); + const tag = emit.mir.instructions.items(.tag)[inst]; + assert(tag == .cond_jmp); const inst_cc = emit.mir.instructions.items(.data)[inst].inst_cc; - const tag: Tag = switch (inst_cc.cc) { + const mnemonic: Instruction.Mnemonic = switch (inst_cc.cc) { .a => .ja, .ae => .jae, .b => .jb, @@ -383,7 +399,9 @@ fn mirCondJmp(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { .z => .jz, }; const source = emit.code.items.len; - try lowerToDEnc(tag, 0, emit.code); + try emit.encode(mnemonic, .{ + .op1 = .{ .imm = 0 }, + }); try emit.relocs.append(emit.bin_file.allocator, .{ .source = source, .target = inst_cc.inst, @@ -393,11 +411,11 @@ fn mirCondJmp(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { } fn mirCondSetByte(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { - const mir_tag = emit.mir.instructions.items(.tag)[inst]; - assert(mir_tag == .cond_set_byte); + const tag = emit.mir.instructions.items(.tag)[inst]; + assert(tag == .cond_set_byte); const ops = emit.mir.instructions.items(.ops)[inst].decode(); const cc = emit.mir.instructions.items(.data)[inst].cc; - const tag: Tag = switch (cc) { + const mnemonic: Instruction.Mnemonic = switch (cc) { .a => .seta, .ae => .setae, .b => .setb, @@ -429,15 +447,15 @@ fn mirCondSetByte(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { .s => .sets, .z => .setz, }; - return lowerToMEnc(tag, RegisterOrMemory.reg(ops.reg1.to8()), emit.code); + return emit.encode(mnemonic, .{ .op1 = .{ .reg = ops.reg1 } }); } fn mirCondMov(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { - const mir_tag = emit.mir.instructions.items(.tag)[inst]; - assert(mir_tag == .cond_mov); + const tag = emit.mir.instructions.items(.tag)[inst]; + assert(tag == .cond_mov); const ops = emit.mir.instructions.items(.ops)[inst].decode(); const cc = emit.mir.instructions.items(.data)[inst].cc; - const tag: Tag = switch (cc) { + const mnemonic: Instruction.Mnemonic = switch (cc) { .a => .cmova, .ae => .cmovae, .b => .cmovb, @@ -469,21 +487,28 @@ fn mirCondMov(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { .s => .cmovs, .z => .cmovz, }; + const op1: Instruction.Operand = .{ .reg = ops.reg1 }; if (ops.flags == 0b00) { - return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code); + return emit.encode(mnemonic, .{ + .op1 = op1, + .op2 = .{ .reg = ops.reg2 }, + }); } - const imm = emit.mir.instructions.items(.data)[inst].imm; + const disp = emit.mir.instructions.items(.data)[inst].disp; const ptr_size: Memory.PtrSize = switch (ops.flags) { 0b00 => unreachable, - 0b01 => .word_ptr, - 0b10 => .dword_ptr, - 0b11 => .qword_ptr, + 0b01 => .word, + 0b10 => .dword, + 0b11 => .qword, }; - return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.mem(ptr_size, .{ - .disp = imm, - .base = ops.reg2, - }), emit.code); + return emit.encode(mnemonic, .{ + .op1 = op1, + .op2 = .{ .mem = Memory.sib(ptr_size, .{ + .base = ops.reg2, + .disp = disp, + }) }, + }); } fn mirTest(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { @@ -493,18 +518,16 @@ fn mirTest(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { switch (ops.flags) { 0b00 => { if (ops.reg2 == .none) { - // TEST r/m64, imm32 - // MI const imm = emit.mir.instructions.items(.data)[inst].imm; - if (ops.reg1.to64() == .rax) { - // TEST rax, imm32 - // I - return lowerToIEnc(.@"test", imm, emit.code); - } - return lowerToMiEnc(.@"test", RegisterOrMemory.reg(ops.reg1), imm, emit.code); + return emit.encode(.@"test", .{ + .op1 = .{ .reg = ops.reg1 }, + .op2 = .{ .imm = imm }, + }); } - // TEST r/m64, r64 - return lowerToMrEnc(.@"test", RegisterOrMemory.reg(ops.reg1), ops.reg2, emit.code); + return emit.encode(.@"test", .{ + .op1 = .{ .reg = ops.reg1 }, + .op2 = .{ .reg = ops.reg2 }, + }); }, else => return emit.fail("TODO more TEST alternatives", .{}), } @@ -515,62 +538,59 @@ fn mirRet(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { assert(tag == .ret); const ops = emit.mir.instructions.items(.ops)[inst].decode(); switch (ops.flags) { - 0b00 => { - // RETF imm16 - // I - const imm = emit.mir.instructions.items(.data)[inst].imm; - return lowerToIEnc(.ret_far, imm, emit.code); - }, - 0b01 => { - return lowerToZoEnc(.ret_far, emit.code); - }, + 0b00 => unreachable, + 0b01 => unreachable, 0b10 => { - // RET imm16 - // I const imm = emit.mir.instructions.items(.data)[inst].imm; - return lowerToIEnc(.ret_near, imm, emit.code); + return emit.encode(.ret, .{ + .op1 = .{ .imm = imm }, + }); }, 0b11 => { - return lowerToZoEnc(.ret_near, emit.code); + return emit.encode(.ret, .{}); }, } } -fn mirArith(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { +fn mirArith(emit: *Emit, mnemonic: Instruction.Mnemonic, inst: Mir.Inst.Index) InnerError!void { const ops = emit.mir.instructions.items(.ops)[inst].decode(); switch (ops.flags) { 0b00 => { if (ops.reg2 == .none) { - // mov reg1, imm32 - // MI const imm = emit.mir.instructions.items(.data)[inst].imm; - return lowerToMiEnc(tag, RegisterOrMemory.reg(ops.reg1), imm, emit.code); + return emit.encode(mnemonic, .{ + .op1 = .{ .reg = ops.reg1 }, + .op2 = .{ .imm = imm }, + }); } - // mov reg1, reg2 - // RM - return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code); + return emit.encode(mnemonic, .{ + .op1 = .{ .reg = ops.reg1 }, + .op2 = .{ .reg = ops.reg2 }, + }); }, 0b01 => { - // mov reg1, [reg2 + imm32] - // RM - const imm = emit.mir.instructions.items(.data)[inst].imm; - const src_reg: ?Register = if (ops.reg2 != .none) ops.reg2 else null; - return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg1.size()), .{ - .disp = imm, - .base = src_reg, - }), emit.code); + const disp = emit.mir.instructions.items(.data)[inst].disp; + const base: ?Register = if (ops.reg2 != .none) ops.reg2 else null; + return emit.encode(mnemonic, .{ + .op1 = .{ .reg = ops.reg1 }, + .op2 = .{ .mem = Memory.sib(Memory.PtrSize.fromSize(ops.reg1.size()), .{ + .base = base, + .disp = disp, + }) }, + }); }, 0b10 => { if (ops.reg2 == .none) { return emit.fail("TODO unused variant: mov reg1, none, 0b10", .{}); } - // mov [reg1 + imm32], reg2 - // MR - const imm = emit.mir.instructions.items(.data)[inst].imm; - return lowerToMrEnc(tag, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg2.size()), .{ - .disp = imm, - .base = ops.reg1, - }), ops.reg2, emit.code); + const disp = emit.mir.instructions.items(.data)[inst].disp; + return emit.encode(mnemonic, .{ + .op1 = .{ .mem = Memory.sib(Memory.PtrSize.fromSize(ops.reg2.size()), .{ + .base = ops.reg1, + .disp = disp, + }) }, + .op2 = .{ .reg = ops.reg2 }, + }); }, 0b11 => { return emit.fail("TODO unused variant: mov reg1, reg2, 0b11", .{}); @@ -578,169 +598,165 @@ fn mirArith(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { } } -fn mirArithMemImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { +fn mirArithMemImm(emit: *Emit, mnemonic: Instruction.Mnemonic, inst: Mir.Inst.Index) InnerError!void { const ops = emit.mir.instructions.items(.ops)[inst].decode(); assert(ops.reg2 == .none); const payload = emit.mir.instructions.items(.data)[inst].payload; const imm_pair = emit.mir.extraData(Mir.ImmPair, payload).data; const ptr_size: Memory.PtrSize = switch (ops.flags) { - 0b00 => .byte_ptr, - 0b01 => .word_ptr, - 0b10 => .dword_ptr, - 0b11 => .qword_ptr, + 0b00 => .byte, + 0b01 => .word, + 0b10 => .dword, + 0b11 => .qword, }; - return lowerToMiEnc(tag, RegisterOrMemory.mem(ptr_size, .{ - .disp = imm_pair.dest_off, - .base = ops.reg1, - }), imm_pair.operand, emit.code); + return emit.encode(mnemonic, .{ + .op1 = .{ .mem = Memory.sib(ptr_size, .{ + .disp = imm_pair.dest_off, + .base = ops.reg1, + }) }, + .op2 = .{ .imm = imm_pair.operand }, + }); } -inline fn setRexWRegister(reg: Register) bool { - if (reg.size() > 64) return false; - if (reg.size() == 64) return true; - return switch (reg) { - .ah, .ch, .dh, .bh => true, - else => false, - }; -} - -inline fn immOpSize(u_imm: u32) u6 { - const imm = @bitCast(i32, u_imm); - if (math.minInt(i8) <= imm and imm <= math.maxInt(i8)) { - return 8; - } - if (math.minInt(i16) <= imm and imm <= math.maxInt(i16)) { - return 16; - } - return 32; -} - -fn mirArithScaleSrc(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { +fn mirArithScaleSrc(emit: *Emit, mnemonic: Instruction.Mnemonic, inst: Mir.Inst.Index) InnerError!void { const ops = emit.mir.instructions.items(.ops)[inst].decode(); const scale = ops.flags; const payload = emit.mir.instructions.items(.data)[inst].payload; const index_reg_disp = emit.mir.extraData(Mir.IndexRegisterDisp, payload).data.decode(); - // OP reg1, [reg2 + scale*index + imm32] - const scale_index = ScaleIndex{ + const scale_index = Memory.ScaleIndex{ .scale = scale, .index = index_reg_disp.index, }; - return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg1.size()), .{ - .disp = index_reg_disp.disp, - .base = ops.reg2, - .scale_index = scale_index, - }), emit.code); + return emit.encode(mnemonic, .{ + .op1 = .{ .reg = ops.reg1 }, + .op2 = .{ .mem = Memory.sib(Memory.PtrSize.fromSize(ops.reg1.size()), .{ + .base = ops.reg2, + .scale_index = scale_index, + .disp = index_reg_disp.disp, + }) }, + }); } -fn mirArithScaleDst(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { +fn mirArithScaleDst(emit: *Emit, mnemonic: Instruction.Mnemonic, inst: Mir.Inst.Index) InnerError!void { const ops = emit.mir.instructions.items(.ops)[inst].decode(); const scale = ops.flags; const payload = emit.mir.instructions.items(.data)[inst].payload; const index_reg_disp = emit.mir.extraData(Mir.IndexRegisterDisp, payload).data.decode(); - const scale_index = ScaleIndex{ + const scale_index = Memory.ScaleIndex{ .scale = scale, .index = index_reg_disp.index, }; assert(ops.reg2 != .none); - // OP [reg1 + scale*index + imm32], reg2 - return lowerToMrEnc(tag, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg2.size()), .{ - .disp = index_reg_disp.disp, - .base = ops.reg1, - .scale_index = scale_index, - }), ops.reg2, emit.code); + return emit.encode(mnemonic, .{ + .op1 = .{ .mem = Memory.sib(Memory.PtrSize.fromSize(ops.reg2.size()), .{ + .base = ops.reg1, + .scale_index = scale_index, + .disp = index_reg_disp.disp, + }) }, + .op2 = .{ .reg = ops.reg2 }, + }); } -fn mirArithScaleImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { +fn mirArithScaleImm(emit: *Emit, mnemonic: Instruction.Mnemonic, inst: Mir.Inst.Index) InnerError!void { const ops = emit.mir.instructions.items(.ops)[inst].decode(); const scale = ops.flags; const payload = emit.mir.instructions.items(.data)[inst].payload; const index_reg_disp_imm = emit.mir.extraData(Mir.IndexRegisterDispImm, payload).data.decode(); - const scale_index = ScaleIndex{ + const scale_index = Memory.ScaleIndex{ .scale = scale, .index = index_reg_disp_imm.index, }; - // OP qword ptr [reg1 + scale*index + imm32], imm32 - return lowerToMiEnc(tag, RegisterOrMemory.mem(.qword_ptr, .{ - .disp = index_reg_disp_imm.disp, - .base = ops.reg1, - .scale_index = scale_index, - }), index_reg_disp_imm.imm, emit.code); + return emit.encode(mnemonic, .{ + .op1 = .{ .mem = Memory.sib(.qword, .{ + .base = ops.reg1, + .disp = index_reg_disp_imm.disp, + .scale_index = scale_index, + }) }, + .op2 = .{ .imm = index_reg_disp_imm.imm }, + }); } -fn mirArithMemIndexImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { +fn mirArithMemIndexImm(emit: *Emit, mnemonic: Instruction.Mnemonic, inst: Mir.Inst.Index) InnerError!void { const ops = emit.mir.instructions.items(.ops)[inst].decode(); assert(ops.reg2 == .none); const payload = emit.mir.instructions.items(.data)[inst].payload; const index_reg_disp_imm = emit.mir.extraData(Mir.IndexRegisterDispImm, payload).data.decode(); const ptr_size: Memory.PtrSize = switch (ops.flags) { - 0b00 => .byte_ptr, - 0b01 => .word_ptr, - 0b10 => .dword_ptr, - 0b11 => .qword_ptr, + 0b00 => .byte, + 0b01 => .word, + 0b10 => .dword, + 0b11 => .qword, }; - const scale_index = ScaleIndex{ + const scale_index = Memory.ScaleIndex{ .scale = 0, .index = index_reg_disp_imm.index, }; - // OP ptr [reg1 + index + imm32], imm32 - return lowerToMiEnc(tag, RegisterOrMemory.mem(ptr_size, .{ - .disp = index_reg_disp_imm.disp, - .base = ops.reg1, - .scale_index = scale_index, - }), index_reg_disp_imm.imm, emit.code); + return emit.encode(mnemonic, .{ + .op1 = .{ .mem = Memory.sib(ptr_size, .{ + .disp = index_reg_disp_imm.disp, + .base = ops.reg1, + .scale_index = scale_index, + }) }, + .op2 = .{ .imm = index_reg_disp_imm.imm }, + }); } fn mirMovSignExtend(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { - const mir_tag = emit.mir.instructions.items(.tag)[inst]; - assert(mir_tag == .mov_sign_extend); + const tag = emit.mir.instructions.items(.tag)[inst]; + assert(tag == .mov_sign_extend); const ops = emit.mir.instructions.items(.ops)[inst].decode(); - const imm = if (ops.flags != 0b00) emit.mir.instructions.items(.data)[inst].imm else undefined; + const disp = if (ops.flags != 0b00) emit.mir.instructions.items(.data)[inst].disp else undefined; switch (ops.flags) { 0b00 => { - const tag: Tag = if (ops.reg2.size() == 32) .movsxd else .movsx; - return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code); + const mnemonic: Instruction.Mnemonic = if (ops.reg2.size() == 32) .movsxd else .movsx; + return emit.encode(mnemonic, .{ + .op1 = .{ .reg = ops.reg1 }, + .op2 = .{ .reg = ops.reg2 }, + }); }, - 0b01 => { - return lowerToRmEnc(.movsx, ops.reg1, RegisterOrMemory.mem(.byte_ptr, .{ - .disp = imm, - .base = ops.reg2, - }), emit.code); - }, - 0b10 => { - return lowerToRmEnc(.movsx, ops.reg1, RegisterOrMemory.mem(.word_ptr, .{ - .disp = imm, - .base = ops.reg2, - }), emit.code); - }, - 0b11 => { - return lowerToRmEnc(.movsxd, ops.reg1, RegisterOrMemory.mem(.dword_ptr, .{ - .disp = imm, - .base = ops.reg2, - }), emit.code); + else => { + const ptr_size: Memory.PtrSize = switch (ops.flags) { + 0b01 => .byte, + 0b10 => .word, + 0b11 => .qword, + else => unreachable, + }; + return emit.encode(.movsx, .{ + .op1 = .{ .reg = ops.reg1 }, + .op2 = .{ .mem = Memory.sib(ptr_size, .{ + .disp = disp, + .base = ops.reg2, + }) }, + }); }, } } fn mirMovZeroExtend(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { - const mir_tag = emit.mir.instructions.items(.tag)[inst]; - assert(mir_tag == .mov_zero_extend); + const tag = emit.mir.instructions.items(.tag)[inst]; + assert(tag == .mov_zero_extend); const ops = emit.mir.instructions.items(.ops)[inst].decode(); - const imm = if (ops.flags != 0b00) emit.mir.instructions.items(.data)[inst].imm else undefined; + const disp = if (ops.flags != 0b00) emit.mir.instructions.items(.data)[inst].disp else undefined; switch (ops.flags) { 0b00 => { - return lowerToRmEnc(.movzx, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code); + return emit.encode(.movzx, .{ + .op1 = .{ .reg = ops.reg1 }, + .op2 = .{ .reg = ops.reg2 }, + }); }, - 0b01 => { - return lowerToRmEnc(.movzx, ops.reg1, RegisterOrMemory.mem(.byte_ptr, .{ - .disp = imm, - .base = ops.reg2, - }), emit.code); - }, - 0b10 => { - return lowerToRmEnc(.movzx, ops.reg1, RegisterOrMemory.mem(.word_ptr, .{ - .disp = imm, - .base = ops.reg2, - }), emit.code); + 0b01, 0b10 => { + const ptr_size: Memory.PtrSize = switch (ops.flags) { + 0b01 => .byte, + 0b10 => .word, + else => unreachable, + }; + return emit.encode(.movzx, .{ + .op1 = .{ .reg = ops.reg1 }, + .op2 = .{ .mem = Memory.sib(ptr_size, .{ + .disp = disp, + .base = ops.reg2, + }) }, + }); }, 0b11 => { return emit.fail("TODO unused variant: movzx 0b11", .{}); @@ -759,9 +775,10 @@ fn mirMovabs(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const imm = emit.mir.extraData(Mir.Imm64, payload).data; break :blk imm.decode(); } else emit.mir.instructions.items(.data)[inst].imm; - // movabs reg, imm64 - // OI - return lowerToOiEnc(.mov, ops.reg1, imm, emit.code); + return emit.encode(.mov, .{ + .op1 = .{ .reg = ops.reg1 }, + .op2 = .{ .imm = @bitCast(i64, imm) }, + }); }, 0b01 => { if (ops.reg1 == .none) { @@ -770,18 +787,20 @@ fn mirMovabs(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const imm = emit.mir.extraData(Mir.Imm64, payload).data; break :blk imm.decode(); } else emit.mir.instructions.items(.data)[inst].imm; - // movabs moffs64, rax - // TD - return lowerToTdEnc(.mov, imm, ops.reg2, emit.code); + return emit.encode(.mov, .{ + .op1 = .{ .mem = Memory.moffs(ops.reg2, imm) }, + .op2 = .{ .reg = .rax }, + }); } const imm: u64 = if (ops.reg1.size() == 64) blk: { const payload = emit.mir.instructions.items(.data)[inst].payload; const imm = emit.mir.extraData(Mir.Imm64, payload).data; break :blk imm.decode(); } else emit.mir.instructions.items(.data)[inst].imm; - // movabs rax, moffs64 - // FD - return lowerToFdEnc(.mov, ops.reg1, imm, emit.code); + return emit.encode(.mov, .{ + .op1 = .{ .reg = .rax }, + .op2 = .{ .mem = Memory.moffs(ops.reg1, imm) }, + }); }, else => return emit.fail("TODO unused movabs variant", .{}), } @@ -791,63 +810,58 @@ fn mirFisttp(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const tag = emit.mir.instructions.items(.tag)[inst]; assert(tag == .fisttp); const ops = emit.mir.instructions.items(.ops)[inst].decode(); - - // the selecting between operand sizes for this particular `fisttp` instruction - // is done via opcode instead of the usual prefixes. - - const opcode: Tag = switch (ops.flags) { - 0b00 => .fisttp16, - 0b01 => .fisttp32, - 0b10 => .fisttp64, + const ptr_size: Memory.PtrSize = switch (ops.flags) { + 0b00 => .word, + 0b01 => .dword, + 0b10 => .qword, else => unreachable, }; - const mem_or_reg = Memory{ - .base = ops.reg1, - .disp = emit.mir.instructions.items(.data)[inst].imm, - .ptr_size = Memory.PtrSize.dword_ptr, // to prevent any prefix from being used - }; - return lowerToMEnc(opcode, .{ .memory = mem_or_reg }, emit.code); + return emit.encode(.fisttp, .{ + .op1 = .{ .mem = Memory.sib(ptr_size, .{ + .base = ops.reg1, + .disp = emit.mir.instructions.items(.data)[inst].disp, + }) }, + }); } fn mirFld(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const tag = emit.mir.instructions.items(.tag)[inst]; assert(tag == .fld); const ops = emit.mir.instructions.items(.ops)[inst].decode(); - - // the selecting between operand sizes for this particular `fisttp` instruction - // is done via opcode instead of the usual prefixes. - - const opcode: Tag = switch (ops.flags) { - 0b01 => .fld32, - 0b10 => .fld64, + const ptr_size: Memory.PtrSize = switch (ops.flags) { + 0b01 => .dword, + 0b10 => .qword, else => unreachable, }; - const mem_or_reg = Memory{ - .base = ops.reg1, - .disp = emit.mir.instructions.items(.data)[inst].imm, - .ptr_size = Memory.PtrSize.dword_ptr, // to prevent any prefix from being used - }; - return lowerToMEnc(opcode, .{ .memory = mem_or_reg }, emit.code); + return emit.encode(.fld, .{ + .op1 = .{ .mem = Memory.sib(ptr_size, .{ + .base = ops.reg1, + .disp = emit.mir.instructions.items(.data)[inst].disp, + }) }, + }); } -fn mirShift(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { +fn mirShift(emit: *Emit, mnemonic: Instruction.Mnemonic, inst: Mir.Inst.Index) InnerError!void { const ops = emit.mir.instructions.items(.ops)[inst].decode(); switch (ops.flags) { 0b00 => { - // sal reg1, 1 - // M1 - return lowerToM1Enc(tag, RegisterOrMemory.reg(ops.reg1), emit.code); + return emit.encode(mnemonic, .{ + .op1 = .{ .reg = ops.reg1 }, + .op2 = .{ .imm = 1 }, + }); }, 0b01 => { - // sal reg1, .cl - // MC - return lowerToMcEnc(tag, RegisterOrMemory.reg(ops.reg1), emit.code); + return emit.encode(mnemonic, .{ + .op1 = .{ .reg = ops.reg1 }, + .op2 = .{ .reg = .cl }, + }); }, 0b10 => { - // sal reg1, imm8 - // MI const imm = @truncate(u8, emit.mir.instructions.items(.data)[inst].imm); - return lowerToMiImm8Enc(tag, RegisterOrMemory.reg(ops.reg1), imm, emit.code); + return emit.encode(mnemonic, .{ + .op1 = .{ .reg = ops.reg1 }, + .op2 = .{ .imm = imm }, + }); }, 0b11 => { return emit.fail("TODO unused variant: SHIFT reg1, 0b11", .{}); @@ -855,24 +869,28 @@ fn mirShift(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { } } -fn mirMulDiv(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { +fn mirMulDiv(emit: *Emit, mnemonic: Instruction.Mnemonic, inst: Mir.Inst.Index) InnerError!void { const ops = emit.mir.instructions.items(.ops)[inst].decode(); if (ops.reg1 != .none) { assert(ops.reg2 == .none); - return lowerToMEnc(tag, RegisterOrMemory.reg(ops.reg1), emit.code); + return emit.encode(mnemonic, .{ + .op1 = .{ .reg = ops.reg1 }, + }); } assert(ops.reg2 != .none); - const imm = emit.mir.instructions.items(.data)[inst].imm; + const disp = emit.mir.instructions.items(.data)[inst].disp; const ptr_size: Memory.PtrSize = switch (ops.flags) { - 0b00 => .byte_ptr, - 0b01 => .word_ptr, - 0b10 => .dword_ptr, - 0b11 => .qword_ptr, + 0b00 => .byte, + 0b01 => .word, + 0b10 => .dword, + 0b11 => .qword, }; - return lowerToMEnc(tag, RegisterOrMemory.mem(ptr_size, .{ - .disp = imm, - .base = ops.reg2, - }), emit.code); + return emit.encode(mnemonic, .{ + .op1 = .{ .mem = Memory.sib(ptr_size, .{ + .base = ops.reg2, + .disp = disp, + }) }, + }); } fn mirIMulComplex(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { @@ -881,40 +899,54 @@ fn mirIMulComplex(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const ops = emit.mir.instructions.items(.ops)[inst].decode(); switch (ops.flags) { 0b00 => { - return lowerToRmEnc(.imul, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code); + return emit.encode(.imul, .{ + .op1 = .{ .reg = ops.reg1 }, + .op2 = .{ .reg = ops.reg2 }, + }); }, 0b01 => { - const imm = emit.mir.instructions.items(.data)[inst].imm; + const disp = emit.mir.instructions.items(.data)[inst].disp; const src_reg: ?Register = if (ops.reg2 != .none) ops.reg2 else null; - return lowerToRmEnc(.imul, ops.reg1, RegisterOrMemory.mem(.qword_ptr, .{ - .disp = imm, - .base = src_reg, - }), emit.code); + return emit.encode(.imul, .{ + .op1 = .{ .reg = ops.reg1 }, + .op2 = .{ .mem = Memory.sib(.qword, .{ + .base = src_reg, + .disp = disp, + }) }, + }); }, 0b10 => { const imm = emit.mir.instructions.items(.data)[inst].imm; - return lowerToRmiEnc(.imul, ops.reg1, RegisterOrMemory.reg(ops.reg2), imm, emit.code); + return emit.encode(.imul, .{ + .op1 = .{ .reg = ops.reg1 }, + .op2 = .{ .reg = ops.reg2 }, + .op3 = .{ .imm = imm }, + }); }, 0b11 => { const payload = emit.mir.instructions.items(.data)[inst].payload; const imm_pair = emit.mir.extraData(Mir.ImmPair, payload).data; - return lowerToRmiEnc(.imul, ops.reg1, RegisterOrMemory.mem(.qword_ptr, .{ - .disp = imm_pair.dest_off, - .base = ops.reg2, - }), imm_pair.operand, emit.code); + return emit.encode(.imul, .{ + .op1 = .{ .reg = ops.reg1 }, + .op2 = .{ .mem = Memory.sib(.qword, .{ + .base = ops.reg2, + .disp = imm_pair.dest_off, + }) }, + .op3 = .{ .imm = imm_pair.operand }, + }); }, } } fn mirCwd(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const ops = emit.mir.instructions.items(.ops)[inst].decode(); - const tag: Tag = switch (ops.flags) { + const mnemonic: Instruction.Mnemonic = switch (ops.flags) { 0b00 => .cbw, 0b01 => .cwd, 0b10 => .cdq, 0b11 => .cqo, }; - return lowerToZoEnc(tag, emit.code); + return emit.encode(mnemonic, .{}); } fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { @@ -923,30 +955,22 @@ fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const ops = emit.mir.instructions.items(.ops)[inst].decode(); switch (ops.flags) { 0b00 => { - // lea reg1, [reg2 + imm32] - // RM - const imm = emit.mir.instructions.items(.data)[inst].imm; + const disp = emit.mir.instructions.items(.data)[inst].disp; const src_reg: ?Register = if (ops.reg2 != .none) ops.reg2 else null; - return lowerToRmEnc( - .lea, - ops.reg1, - RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg1.size()), .{ - .disp = imm, + return emit.encode(.lea, .{ + .op1 = .{ .reg = ops.reg1 }, + .op2 = .{ .mem = Memory.sib(Memory.PtrSize.fromSize(ops.reg1.size()), .{ .base = src_reg, - }), - emit.code, - ); + .disp = disp, + }) }, + }); }, 0b01 => { - // lea reg1, [rip + imm32] - // RM const start_offset = emit.code.items.len; - try lowerToRmEnc( - .lea, - ops.reg1, - RegisterOrMemory.rip(Memory.PtrSize.new(ops.reg1.size()), 0), - emit.code, - ); + try emit.encode(.lea, .{ + .op1 = .{ .reg = ops.reg1 }, + .op2 = .{ .mem = Memory.rip(Memory.PtrSize.fromSize(ops.reg1.size()), 0) }, + }); const end_offset = emit.code.items.len; // Backpatch the displacement const payload = emit.mir.instructions.items(.data)[inst].payload; @@ -955,24 +979,21 @@ fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { mem.writeIntLittle(i32, emit.code.items[end_offset - 4 ..][0..4], disp); }, 0b10 => { - // lea reg, [rbp + index + imm32] const payload = emit.mir.instructions.items(.data)[inst].payload; const index_reg_disp = emit.mir.extraData(Mir.IndexRegisterDisp, payload).data.decode(); const src_reg: ?Register = if (ops.reg2 != .none) ops.reg2 else null; - const scale_index = ScaleIndex{ + const scale_index = Memory.ScaleIndex{ .scale = 0, .index = index_reg_disp.index, }; - return lowerToRmEnc( - .lea, - ops.reg1, - RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg1.size()), .{ - .disp = index_reg_disp.disp, + return emit.encode(.lea, .{ + .op1 = .{ .reg = ops.reg1 }, + .op2 = .{ .mem = Memory.sib(Memory.PtrSize.fromSize(ops.reg1.size()), .{ .base = src_reg, .scale_index = scale_index, - }), - emit.code, - ); + .disp = index_reg_disp.disp, + }) }, + }); }, 0b11 => return emit.fail("TODO unused LEA variant 0b11", .{}), } @@ -989,14 +1010,10 @@ fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { else => return emit.fail("TODO unused LEA PIC variant 0b11", .{}), } - // lea reg1, [rip + reloc] - // RM - try lowerToRmEnc( - .lea, - ops.reg1, - RegisterOrMemory.rip(Memory.PtrSize.new(ops.reg1.size()), 0), - emit.code, - ); + try emit.encode(.lea, .{ + .op1 = .{ .reg = ops.reg1 }, + .op2 = .{ .mem = Memory.rip(Memory.PtrSize.fromSize(ops.reg1.size()), 0) }, + }); const end_offset = emit.code.items.len; @@ -1039,94 +1056,64 @@ fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { } } -// SSE instructions +// SSE/AVX instructions -fn mirMovFloatSse(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { +fn mirMovFloat(emit: *Emit, mnemonic: Instruction.Mnemonic, inst: Mir.Inst.Index) InnerError!void { const ops = emit.mir.instructions.items(.ops)[inst].decode(); switch (ops.flags) { 0b00 => { - const imm = emit.mir.instructions.items(.data)[inst].imm; - return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg2.size()), .{ - .disp = imm, - .base = ops.reg2, - }), emit.code); + const disp = emit.mir.instructions.items(.data)[inst].disp; + return emit.encode(mnemonic, .{ + .op1 = .{ .reg = ops.reg1 }, + .op2 = .{ .mem = Memory.sib(Memory.PtrSize.fromSize(ops.reg2.size()), .{ + .base = ops.reg2, + .disp = disp, + }) }, + }); }, 0b01 => { - const imm = emit.mir.instructions.items(.data)[inst].imm; - return lowerToMrEnc(tag, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg1.size()), .{ - .disp = imm, - .base = ops.reg1, - }), ops.reg2, emit.code); + const disp = emit.mir.instructions.items(.data)[inst].disp; + return emit.encode(mnemonic, .{ + .op1 = .{ .mem = Memory.sib(Memory.PtrSize.fromSize(ops.reg1.size()), .{ + .base = ops.reg1, + .disp = disp, + }) }, + .op2 = .{ .reg = ops.reg2 }, + }); }, 0b10 => { - return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code); + return emit.encode(mnemonic, .{ + .op1 = .{ .reg = ops.reg1 }, + .op2 = .{ .reg = ops.reg2 }, + }); }, - else => return emit.fail("TODO unused variant 0b{b} for {}", .{ ops.flags, tag }), + else => return emit.fail("TODO unused variant 0b{b} for {}", .{ ops.flags, mnemonic }), } } -fn mirAddFloatSse(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { +fn mirAddFloat(emit: *Emit, mnemonic: Instruction.Mnemonic, inst: Mir.Inst.Index) InnerError!void { const ops = emit.mir.instructions.items(.ops)[inst].decode(); switch (ops.flags) { 0b00 => { - return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code); + return emit.encode(mnemonic, .{ + .op1 = .{ .reg = ops.reg1 }, + .op2 = .{ .reg = ops.reg2 }, + }); }, - else => return emit.fail("TODO unused variant 0b{b} for {}", .{ ops.flags, tag }), + else => return emit.fail("TODO unused variant 0b{b} for {}", .{ ops.flags, mnemonic }), } } -fn mirCmpFloatSse(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { +fn mirCmpFloat(emit: *Emit, mnemonic: Instruction.Mnemonic, inst: Mir.Inst.Index) InnerError!void { const ops = emit.mir.instructions.items(.ops)[inst].decode(); switch (ops.flags) { 0b00 => { - return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code); + return emit.encode(mnemonic, .{ + .op1 = .{ .reg = ops.reg1 }, + .op2 = .{ .reg = ops.reg2 }, + }); }, - else => return emit.fail("TODO unused variant 0b{b} for {}", .{ ops.flags, tag }), - } -} -// AVX instructions - -fn mirMovFloatAvx(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { - const ops = emit.mir.instructions.items(.ops)[inst].decode(); - switch (ops.flags) { - 0b00 => { - const imm = emit.mir.instructions.items(.data)[inst].imm; - return lowerToVmEnc(tag, ops.reg1, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg2.size()), .{ - .disp = imm, - .base = ops.reg2, - }), emit.code); - }, - 0b01 => { - const imm = emit.mir.instructions.items(.data)[inst].imm; - return lowerToMvEnc(tag, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg1.size()), .{ - .disp = imm, - .base = ops.reg1, - }), ops.reg2, emit.code); - }, - 0b10 => { - return lowerToRvmEnc(tag, ops.reg1, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code); - }, - else => return emit.fail("TODO unused variant 0b{b} for {}", .{ ops.flags, tag }), - } -} - -fn mirAddFloatAvx(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { - const ops = emit.mir.instructions.items(.ops)[inst].decode(); - switch (ops.flags) { - 0b00 => { - return lowerToRvmEnc(tag, ops.reg1, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code); - }, - else => return emit.fail("TODO unused variant 0b{b} for {}", .{ ops.flags, tag }), - } -} - -fn mirCmpFloatAvx(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { - const ops = emit.mir.instructions.items(.ops)[inst].decode(); - switch (ops.flags) { - 0b00 => { - return lowerToVmEnc(tag, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code); - }, - else => return emit.fail("TODO unused variant 0b{b} for mov_f64", .{ops.flags}), + else => return emit.fail("TODO unused variant 0b{b} for {}", .{ ops.flags, mnemonic }), } } @@ -1139,7 +1126,9 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const offset = blk: { // callq - try lowerToDEnc(.call_near, 0, emit.code); + try emit.encode(.call, .{ + .op1 = .{ .imm = 0 }, + }); break :blk @intCast(u32, emit.code.items.len) - 4; }; @@ -1264,1841 +1253,3 @@ fn mirDbgEpilogueBegin(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { .none => {}, } } - -const Tag = enum { - adc, - add, - sub, - xor, - @"and", - @"or", - sbb, - cmp, - mov, - movsx, - movsxd, - movzx, - lea, - jmp_near, - call_near, - push, - pop, - @"test", - ud2, - int3, - nop, - imul, - mul, - idiv, - div, - syscall, - ret_near, - ret_far, - fisttp16, - fisttp32, - fisttp64, - fld32, - fld64, - jo, - jno, - jb, - jbe, - jc, - jnae, - jnc, - jae, - je, - jz, - jne, - jnz, - jna, - jnb, - jnbe, - ja, - js, - jns, - jpe, - jp, - jpo, - jnp, - jnge, - jl, - jge, - jnl, - jle, - jng, - jg, - jnle, - seto, - setno, - setb, - setc, - setnae, - setnb, - setnc, - setae, - sete, - setz, - setne, - setnz, - setbe, - setna, - seta, - setnbe, - sets, - setns, - setp, - setpe, - setnp, - setpo, - setl, - setnge, - setnl, - setge, - setle, - setng, - setnle, - setg, - cmovo, - cmovno, - cmovb, - cmovc, - cmovnae, - cmovnb, - cmovnc, - cmovae, - cmove, - cmovz, - cmovne, - cmovnz, - cmovbe, - cmovna, - cmova, - cmovnbe, - cmovs, - cmovns, - cmovp, - cmovpe, - cmovnp, - cmovpo, - cmovl, - cmovnge, - cmovnl, - cmovge, - cmovle, - cmovng, - cmovnle, - cmovg, - shl, - sal, - shr, - sar, - cbw, - cwd, - cdq, - cqo, - movsd, - movss, - addsd, - addss, - cmpsd, - cmpss, - ucomisd, - ucomiss, - vmovsd, - vmovss, - vaddsd, - vaddss, - vcmpsd, - vcmpss, - vucomisd, - vucomiss, - - fn isSse(tag: Tag) bool { - return switch (tag) { - .movsd, - .movss, - .addsd, - .addss, - .cmpsd, - .cmpss, - .ucomisd, - .ucomiss, - => true, - - else => false, - }; - } - - fn isAvx(tag: Tag) bool { - return switch (tag) { - .vmovsd, - .vmovss, - .vaddsd, - .vaddss, - .vcmpsd, - .vcmpss, - .vucomisd, - .vucomiss, - => true, - - else => false, - }; - } - - fn isSetCC(tag: Tag) bool { - return switch (tag) { - .seto, - .setno, - .setb, - .setc, - .setnae, - .setnb, - .setnc, - .setae, - .sete, - .setz, - .setne, - .setnz, - .setbe, - .setna, - .seta, - .setnbe, - .sets, - .setns, - .setp, - .setpe, - .setnp, - .setpo, - .setl, - .setnge, - .setnl, - .setge, - .setle, - .setng, - .setnle, - .setg, - => true, - else => false, - }; - } -}; - -const Encoding = enum { - /// OP - zo, - - /// OP rel32 - d, - - /// OP r/m64 - m, - - /// OP r64 - o, - - /// OP imm32 - i, - - /// OP r/m64, 1 - m1, - - /// OP r/m64, .cl - mc, - - /// OP r/m64, imm32 - mi, - - /// OP r/m64, imm8 - mi8, - - /// OP r/m64, r64 - mr, - - /// OP r64, r/m64 - rm, - - /// OP r64, imm64 - oi, - - /// OP al/ax/eax/rax, moffs - fd, - - /// OP moffs, al/ax/eax/rax - td, - - /// OP r64, r/m64, imm32 - rmi, - - /// OP xmm1, xmm2/m64 - vm, - - /// OP m64, xmm1 - mv, - - /// OP xmm1, xmm2, xmm3/m64 - rvm, - - /// OP xmm1, xmm2, xmm3/m64, imm8 - rvmi, -}; - -const OpCode = struct { - bytes: [3]u8, - count: usize, - - fn init(comptime in_bytes: []const u8) OpCode { - comptime assert(in_bytes.len <= 3); - comptime var bytes: [3]u8 = undefined; - inline for (in_bytes, 0..) |x, i| { - bytes[i] = x; - } - return .{ .bytes = bytes, .count = in_bytes.len }; - } - - fn encode(opc: OpCode, encoder: Encoder) void { - switch (opc.count) { - 1 => encoder.opcode_1byte(opc.bytes[0]), - 2 => encoder.opcode_2byte(opc.bytes[0], opc.bytes[1]), - 3 => encoder.opcode_3byte(opc.bytes[0], opc.bytes[1], opc.bytes[2]), - else => unreachable, - } - } - - fn encodeWithReg(opc: OpCode, encoder: Encoder, reg: Register) void { - assert(opc.count == 1); - encoder.opcode_withReg(opc.bytes[0], reg.lowEnc()); - } -}; - -inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) OpCode { - // zig fmt: off - switch (enc) { - .zo => return switch (tag) { - .ret_near => OpCode.init(&.{0xc3}), - .ret_far => OpCode.init(&.{0xcb}), - .ud2 => OpCode.init(&.{ 0x0F, 0x0B }), - .int3 => OpCode.init(&.{0xcc}), - .nop => OpCode.init(&.{0x90}), - .syscall => OpCode.init(&.{ 0x0f, 0x05 }), - .cbw => OpCode.init(&.{0x98}), - .cwd, - .cdq, - .cqo => OpCode.init(&.{0x99}), - else => unreachable, - }, - .d => return switch (tag) { - .jmp_near => OpCode.init(&.{0xe9}), - .call_near => OpCode.init(&.{0xe8}), - - .jo => if (is_one_byte) OpCode.init(&.{0x70}) else OpCode.init(&.{0x0f,0x80}), - - .jno => if (is_one_byte) OpCode.init(&.{0x71}) else OpCode.init(&.{0x0f,0x81}), - - .jb, - .jc, - .jnae => if (is_one_byte) OpCode.init(&.{0x72}) else OpCode.init(&.{0x0f,0x82}), - - .jnb, - .jnc, - .jae => if (is_one_byte) OpCode.init(&.{0x73}) else OpCode.init(&.{0x0f,0x83}), - - .je, - .jz => if (is_one_byte) OpCode.init(&.{0x74}) else OpCode.init(&.{0x0f,0x84}), - - .jne, - .jnz => if (is_one_byte) OpCode.init(&.{0x75}) else OpCode.init(&.{0x0f,0x85}), - - .jna, - .jbe => if (is_one_byte) OpCode.init(&.{0x76}) else OpCode.init(&.{0x0f,0x86}), - - .jnbe, - .ja => if (is_one_byte) OpCode.init(&.{0x77}) else OpCode.init(&.{0x0f,0x87}), - - .js => if (is_one_byte) OpCode.init(&.{0x78}) else OpCode.init(&.{0x0f,0x88}), - - .jns => if (is_one_byte) OpCode.init(&.{0x79}) else OpCode.init(&.{0x0f,0x89}), - - .jpe, - .jp => if (is_one_byte) OpCode.init(&.{0x7a}) else OpCode.init(&.{0x0f,0x8a}), - - .jpo, - .jnp => if (is_one_byte) OpCode.init(&.{0x7b}) else OpCode.init(&.{0x0f,0x8b}), - - .jnge, - .jl => if (is_one_byte) OpCode.init(&.{0x7c}) else OpCode.init(&.{0x0f,0x8c}), - - .jge, - .jnl => if (is_one_byte) OpCode.init(&.{0x7d}) else OpCode.init(&.{0x0f,0x8d}), - - .jle, - .jng => if (is_one_byte) OpCode.init(&.{0x7e}) else OpCode.init(&.{0x0f,0x8e}), - - .jg, - .jnle => if (is_one_byte) OpCode.init(&.{0x7f}) else OpCode.init(&.{0x0f,0x8f}), - - else => unreachable, - }, - .m => return switch (tag) { - .jmp_near, - .call_near, - .push => OpCode.init(&.{0xff}), - - .pop => OpCode.init(&.{0x8f}), - .seto => OpCode.init(&.{0x0f,0x90}), - .setno => OpCode.init(&.{0x0f,0x91}), - - .setb, - .setc, - .setnae => OpCode.init(&.{0x0f,0x92}), - - .setnb, - .setnc, - .setae => OpCode.init(&.{0x0f,0x93}), - - .sete, - .setz => OpCode.init(&.{0x0f,0x94}), - - .setne, - .setnz => OpCode.init(&.{0x0f,0x95}), - - .setbe, - .setna => OpCode.init(&.{0x0f,0x96}), - - .seta, - .setnbe => OpCode.init(&.{0x0f,0x97}), - - .sets => OpCode.init(&.{0x0f,0x98}), - .setns => OpCode.init(&.{0x0f,0x99}), - - .setp, - .setpe => OpCode.init(&.{0x0f,0x9a}), - - .setnp, - .setpo => OpCode.init(&.{0x0f,0x9b}), - - .setl, - .setnge => OpCode.init(&.{0x0f,0x9c}), - - .setnl, - .setge => OpCode.init(&.{0x0f,0x9d}), - - .setle, - .setng => OpCode.init(&.{0x0f,0x9e}), - - .setnle, - .setg => OpCode.init(&.{0x0f,0x9f}), - - .idiv, - .div, - .imul, - .mul => if (is_one_byte) OpCode.init(&.{0xf6}) else OpCode.init(&.{0xf7}), - - .fisttp16 => OpCode.init(&.{0xdf}), - .fisttp32 => OpCode.init(&.{0xdb}), - .fisttp64 => OpCode.init(&.{0xdd}), - .fld32 => OpCode.init(&.{0xd9}), - .fld64 => OpCode.init(&.{0xdd}), - else => unreachable, - }, - .o => return switch (tag) { - .push => OpCode.init(&.{0x50}), - .pop => OpCode.init(&.{0x58}), - else => unreachable, - }, - .i => return switch (tag) { - .push => if (is_one_byte) OpCode.init(&.{0x6a}) else OpCode.init(&.{0x68}), - .@"test" => if (is_one_byte) OpCode.init(&.{0xa8}) else OpCode.init(&.{0xa9}), - .ret_near => OpCode.init(&.{0xc2}), - .ret_far => OpCode.init(&.{0xca}), - else => unreachable, - }, - .m1 => return switch (tag) { - .shl, .sal, - .shr, .sar => if (is_one_byte) OpCode.init(&.{0xd0}) else OpCode.init(&.{0xd1}), - else => unreachable, - }, - .mc => return switch (tag) { - .shl, .sal, - .shr, .sar => if (is_one_byte) OpCode.init(&.{0xd2}) else OpCode.init(&.{0xd3}), - else => unreachable, - }, - .mi => return switch (tag) { - .adc, .add, - .sub, .xor, - .@"and", .@"or", - .sbb, .cmp => if (is_one_byte) OpCode.init(&.{0x80}) else OpCode.init(&.{0x81}), - .mov => if (is_one_byte) OpCode.init(&.{0xc6}) else OpCode.init(&.{0xc7}), - .@"test" => if (is_one_byte) OpCode.init(&.{0xf6}) else OpCode.init(&.{0xf7}), - else => unreachable, - }, - .mi8 => return switch (tag) { - .adc, .add, - .sub, .xor, - .@"and", .@"or", - .sbb, .cmp => OpCode.init(&.{0x83}), - .shl, .sal, - .shr, .sar => if (is_one_byte) OpCode.init(&.{0xc0}) else OpCode.init(&.{0xc1}), - else => unreachable, - }, - .mr => return switch (tag) { - .adc => if (is_one_byte) OpCode.init(&.{0x10}) else OpCode.init(&.{0x11}), - .add => if (is_one_byte) OpCode.init(&.{0x00}) else OpCode.init(&.{0x01}), - .sub => if (is_one_byte) OpCode.init(&.{0x28}) else OpCode.init(&.{0x29}), - .xor => if (is_one_byte) OpCode.init(&.{0x30}) else OpCode.init(&.{0x31}), - .@"and" => if (is_one_byte) OpCode.init(&.{0x20}) else OpCode.init(&.{0x21}), - .@"or" => if (is_one_byte) OpCode.init(&.{0x08}) else OpCode.init(&.{0x09}), - .sbb => if (is_one_byte) OpCode.init(&.{0x18}) else OpCode.init(&.{0x19}), - .cmp => if (is_one_byte) OpCode.init(&.{0x38}) else OpCode.init(&.{0x39}), - .mov => if (is_one_byte) OpCode.init(&.{0x88}) else OpCode.init(&.{0x89}), - .@"test" => if (is_one_byte) OpCode.init(&.{0x84}) else OpCode.init(&.{0x85}), - .movsd => OpCode.init(&.{0xf2,0x0f,0x11}), - .movss => OpCode.init(&.{0xf3,0x0f,0x11}), - else => unreachable, - }, - .rm => return switch (tag) { - .adc => if (is_one_byte) OpCode.init(&.{0x12}) else OpCode.init(&.{0x13}), - .add => if (is_one_byte) OpCode.init(&.{0x02}) else OpCode.init(&.{0x03}), - .sub => if (is_one_byte) OpCode.init(&.{0x2a}) else OpCode.init(&.{0x2b}), - .xor => if (is_one_byte) OpCode.init(&.{0x32}) else OpCode.init(&.{0x33}), - .@"and" => if (is_one_byte) OpCode.init(&.{0x22}) else OpCode.init(&.{0x23}), - .@"or" => if (is_one_byte) OpCode.init(&.{0x0a}) else OpCode.init(&.{0x0b}), - .sbb => if (is_one_byte) OpCode.init(&.{0x1a}) else OpCode.init(&.{0x1b}), - .cmp => if (is_one_byte) OpCode.init(&.{0x3a}) else OpCode.init(&.{0x3b}), - .mov => if (is_one_byte) OpCode.init(&.{0x8a}) else OpCode.init(&.{0x8b}), - .movsx => if (is_one_byte) OpCode.init(&.{0x0f,0xbe}) else OpCode.init(&.{0x0f,0xbf}), - .movsxd => OpCode.init(&.{0x63}), - .movzx => if (is_one_byte) OpCode.init(&.{0x0f,0xb6}) else OpCode.init(&.{0x0f,0xb7}), - .lea => if (is_one_byte) OpCode.init(&.{0x8c}) else OpCode.init(&.{0x8d}), - .imul => OpCode.init(&.{0x0f,0xaf}), - - .cmova, - .cmovnbe, => OpCode.init(&.{0x0f,0x47}), - - .cmovae, - .cmovnb, => OpCode.init(&.{0x0f,0x43}), - - .cmovb, - .cmovc, - .cmovnae => OpCode.init(&.{0x0f,0x42}), - - .cmovbe, - .cmovna, => OpCode.init(&.{0x0f,0x46}), - - .cmove, - .cmovz, => OpCode.init(&.{0x0f,0x44}), - - .cmovg, - .cmovnle, => OpCode.init(&.{0x0f,0x4f}), - - .cmovge, - .cmovnl, => OpCode.init(&.{0x0f,0x4d}), - - .cmovl, - .cmovnge, => OpCode.init(&.{0x0f,0x4c}), - - .cmovle, - .cmovng, => OpCode.init(&.{0x0f,0x4e}), - - .cmovne, - .cmovnz, => OpCode.init(&.{0x0f,0x45}), - - .cmovno => OpCode.init(&.{0x0f,0x41}), - - .cmovnp, - .cmovpo, => OpCode.init(&.{0x0f,0x4b}), - - .cmovns => OpCode.init(&.{0x0f,0x49}), - - .cmovo => OpCode.init(&.{0x0f,0x40}), - - .cmovp, - .cmovpe, => OpCode.init(&.{0x0f,0x4a}), - - .cmovs => OpCode.init(&.{0x0f,0x48}), - - .movsd => OpCode.init(&.{0xf2,0x0f,0x10}), - .movss => OpCode.init(&.{0xf3,0x0f,0x10}), - .addsd => OpCode.init(&.{0xf2,0x0f,0x58}), - .addss => OpCode.init(&.{0xf3,0x0f,0x58}), - .ucomisd => OpCode.init(&.{0x66,0x0f,0x2e}), - .ucomiss => OpCode.init(&.{0x0f,0x2e}), - else => unreachable, - }, - .oi => return switch (tag) { - .mov => if (is_one_byte) OpCode.init(&.{0xb0}) else OpCode.init(&.{0xb8}), - else => unreachable, - }, - .fd => return switch (tag) { - .mov => if (is_one_byte) OpCode.init(&.{0xa0}) else OpCode.init(&.{0xa1}), - else => unreachable, - }, - .td => return switch (tag) { - .mov => if (is_one_byte) OpCode.init(&.{0xa2}) else OpCode.init(&.{0xa3}), - else => unreachable, - }, - .rmi => return switch (tag) { - .imul => if (is_one_byte) OpCode.init(&.{0x6b}) else OpCode.init(&.{0x69}), - else => unreachable, - }, - .mv => return switch (tag) { - .vmovsd, - .vmovss => OpCode.init(&.{0x11}), - else => unreachable, - }, - .vm => return switch (tag) { - .vmovsd, - .vmovss => OpCode.init(&.{0x10}), - .vucomisd, - .vucomiss => OpCode.init(&.{0x2e}), - else => unreachable, - }, - .rvm => return switch (tag) { - .vaddsd, - .vaddss => OpCode.init(&.{0x58}), - .vmovsd, - .vmovss => OpCode.init(&.{0x10}), - else => unreachable, - }, - .rvmi => return switch (tag) { - .vcmpsd, - .vcmpss => OpCode.init(&.{0xc2}), - else => unreachable, - }, - } - // zig fmt: on -} - -inline fn getModRmExt(tag: Tag) u3 { - return switch (tag) { - .adc => 0x2, - .add => 0x0, - .sub => 0x5, - .xor => 0x6, - .@"and" => 0x4, - .@"or" => 0x1, - .sbb => 0x3, - .cmp => 0x7, - .mov => 0x0, - .jmp_near => 0x4, - .call_near => 0x2, - .push => 0x6, - .pop => 0x0, - .@"test" => 0x0, - .seto, - .setno, - .setb, - .setc, - .setnae, - .setnb, - .setnc, - .setae, - .sete, - .setz, - .setne, - .setnz, - .setbe, - .setna, - .seta, - .setnbe, - .sets, - .setns, - .setp, - .setpe, - .setnp, - .setpo, - .setl, - .setnge, - .setnl, - .setge, - .setle, - .setng, - .setnle, - .setg, - => 0x0, - .shl, - .sal, - => 0x4, - .shr => 0x5, - .sar => 0x7, - .mul => 0x4, - .imul => 0x5, - .div => 0x6, - .idiv => 0x7, - .fisttp16 => 0x1, - .fisttp32 => 0x1, - .fisttp64 => 0x1, - .fld32 => 0x0, - .fld64 => 0x0, - else => unreachable, - }; -} - -const VexEncoding = struct { - prefix: Encoder.Vex, - reg: ?enum { - ndd, - nds, - dds, - }, -}; - -inline fn getVexEncoding(tag: Tag, enc: Encoding) VexEncoding { - const desc: struct { - reg: enum { - none, - ndd, - nds, - dds, - } = .none, - len_256: bool = false, - wig: bool = false, - lig: bool = false, - lz: bool = false, - lead_opc: enum { - l_0f, - l_0f_3a, - l_0f_38, - } = .l_0f, - simd_prefix: enum { - none, - p_66, - p_f2, - p_f3, - } = .none, - } = blk: { - switch (enc) { - .mv => switch (tag) { - .vmovsd => break :blk .{ .lig = true, .simd_prefix = .p_f2, .wig = true }, - .vmovss => break :blk .{ .lig = true, .simd_prefix = .p_f3, .wig = true }, - else => unreachable, - }, - .vm => switch (tag) { - .vmovsd => break :blk .{ .lig = true, .simd_prefix = .p_f2, .wig = true }, - .vmovss => break :blk .{ .lig = true, .simd_prefix = .p_f3, .wig = true }, - .vucomisd => break :blk .{ .lig = true, .simd_prefix = .p_66, .wig = true }, - .vucomiss => break :blk .{ .lig = true, .wig = true }, - else => unreachable, - }, - .rvm => switch (tag) { - .vaddsd => break :blk .{ .reg = .nds, .lig = true, .simd_prefix = .p_f2, .wig = true }, - .vaddss => break :blk .{ .reg = .nds, .lig = true, .simd_prefix = .p_f3, .wig = true }, - .vmovsd => break :blk .{ .reg = .nds, .lig = true, .simd_prefix = .p_f2, .wig = true }, - .vmovss => break :blk .{ .reg = .nds, .lig = true, .simd_prefix = .p_f3, .wig = true }, - else => unreachable, - }, - .rvmi => switch (tag) { - .vcmpsd => break :blk .{ .reg = .nds, .lig = true, .simd_prefix = .p_f2, .wig = true }, - .vcmpss => break :blk .{ .reg = .nds, .lig = true, .simd_prefix = .p_f3, .wig = true }, - else => unreachable, - }, - else => unreachable, - } - }; - - var vex: Encoder.Vex = .{}; - - if (desc.len_256) vex.len_256(); - if (desc.wig) vex.wig(); - if (desc.lig) vex.lig(); - if (desc.lz) vex.lz(); - - switch (desc.lead_opc) { - .l_0f => {}, - .l_0f_3a => vex.lead_opc_0f_3a(), - .l_0f_38 => vex.lead_opc_0f_38(), - } - - switch (desc.simd_prefix) { - .none => {}, - .p_66 => vex.simd_prefix_66(), - .p_f2 => vex.simd_prefix_f2(), - .p_f3 => vex.simd_prefix_f3(), - } - - return VexEncoding{ .prefix = vex, .reg = switch (desc.reg) { - .none => null, - .nds => .nds, - .dds => .dds, - .ndd => .ndd, - } }; -} - -const ScaleIndex = packed struct { - scale: u2, - index: Register, -}; - -const Memory = struct { - base: ?Register, - rip: bool = false, - disp: u32, - ptr_size: PtrSize, - scale_index: ?ScaleIndex = null, - - const PtrSize = enum(u2) { - byte_ptr = 0b00, - word_ptr = 0b01, - dword_ptr = 0b10, - qword_ptr = 0b11, - - fn new(bit_size: u64) PtrSize { - return @intToEnum(PtrSize, math.log2_int(u4, @intCast(u4, @divExact(bit_size, 8)))); - } - - /// Returns size in bits. - fn size(ptr_size: PtrSize) u64 { - return 8 * (math.powi(u8, 2, @enumToInt(ptr_size)) catch unreachable); - } - }; - - fn encode(mem_op: Memory, encoder: Encoder, operand: u3) void { - if (mem_op.base) |base| { - const dst = base.lowEnc(); - const src = operand; - if (dst == 4 or mem_op.scale_index != null) { - if (mem_op.disp == 0 and dst != 5) { - encoder.modRm_SIBDisp0(src); - if (mem_op.scale_index) |si| { - encoder.sib_scaleIndexBase(si.scale, si.index.lowEnc(), dst); - } else { - encoder.sib_base(dst); - } - } else if (immOpSize(mem_op.disp) == 8) { - encoder.modRm_SIBDisp8(src); - if (mem_op.scale_index) |si| { - encoder.sib_scaleIndexBaseDisp8(si.scale, si.index.lowEnc(), dst); - } else { - encoder.sib_baseDisp8(dst); - } - encoder.disp8(@bitCast(i8, @truncate(u8, mem_op.disp))); - } else { - encoder.modRm_SIBDisp32(src); - if (mem_op.scale_index) |si| { - encoder.sib_scaleIndexBaseDisp32(si.scale, si.index.lowEnc(), dst); - } else { - encoder.sib_baseDisp32(dst); - } - encoder.disp32(@bitCast(i32, mem_op.disp)); - } - } else { - if (mem_op.disp == 0 and dst != 5) { - encoder.modRm_indirectDisp0(src, dst); - } else if (immOpSize(mem_op.disp) == 8) { - encoder.modRm_indirectDisp8(src, dst); - encoder.disp8(@bitCast(i8, @truncate(u8, mem_op.disp))); - } else { - encoder.modRm_indirectDisp32(src, dst); - encoder.disp32(@bitCast(i32, mem_op.disp)); - } - } - } else { - if (mem_op.rip) { - encoder.modRm_RIPDisp32(operand); - } else { - encoder.modRm_SIBDisp0(operand); - if (mem_op.scale_index) |si| { - encoder.sib_scaleIndexDisp32(si.scale, si.index.lowEnc()); - } else { - encoder.sib_disp32(); - } - } - encoder.disp32(@bitCast(i32, mem_op.disp)); - } - } - - /// Returns size in bits. - fn size(memory: Memory) u64 { - return memory.ptr_size.size(); - } -}; - -fn encodeImm(encoder: Encoder, imm: u32, size: u64) void { - switch (size) { - 8 => encoder.imm8(@bitCast(i8, @truncate(u8, imm))), - 16 => encoder.imm16(@bitCast(i16, @truncate(u16, imm))), - 32, 64 => encoder.imm32(@bitCast(i32, imm)), - else => unreachable, - } -} - -const RegisterOrMemory = union(enum) { - register: Register, - memory: Memory, - - fn reg(register: Register) RegisterOrMemory { - return .{ .register = register }; - } - - fn mem(ptr_size: Memory.PtrSize, args: struct { - disp: u32, - base: ?Register = null, - scale_index: ?ScaleIndex = null, - }) RegisterOrMemory { - return .{ - .memory = .{ - .base = args.base, - .disp = args.disp, - .ptr_size = ptr_size, - .scale_index = args.scale_index, - }, - }; - } - - fn rip(ptr_size: Memory.PtrSize, disp: u32) RegisterOrMemory { - return .{ - .memory = .{ - .base = null, - .rip = true, - .disp = disp, - .ptr_size = ptr_size, - }, - }; - } - - /// Returns size in bits. - fn size(reg_or_mem: RegisterOrMemory) u64 { - return switch (reg_or_mem) { - .register => |register| register.size(), - .memory => |memory| memory.size(), - }; - } -}; - -fn lowerToZoEnc(tag: Tag, code: *std.ArrayList(u8)) InnerError!void { - assert(!tag.isAvx()); - const opc = getOpCode(tag, .zo, false); - const encoder = try Encoder.init(code, 2); - switch (tag) { - .cqo => { - encoder.rex(.{ - .w = true, - }); - }, - else => {}, - } - opc.encode(encoder); -} - -fn lowerToIEnc(tag: Tag, imm: u32, code: *std.ArrayList(u8)) InnerError!void { - assert(!tag.isAvx()); - if (tag == .ret_far or tag == .ret_near) { - const encoder = try Encoder.init(code, 3); - const opc = getOpCode(tag, .i, false); - opc.encode(encoder); - encoder.imm16(@bitCast(i16, @truncate(u16, imm))); - return; - } - const opc = getOpCode(tag, .i, immOpSize(imm) == 8); - const encoder = try Encoder.init(code, 5); - if (immOpSize(imm) == 16) { - encoder.prefix16BitMode(); - } - opc.encode(encoder); - encodeImm(encoder, imm, immOpSize(imm)); -} - -fn lowerToOEnc(tag: Tag, reg: Register, code: *std.ArrayList(u8)) InnerError!void { - assert(!tag.isAvx()); - const opc = getOpCode(tag, .o, false); - const encoder = try Encoder.init(code, 3); - if (reg.size() == 16) { - encoder.prefix16BitMode(); - } - encoder.rex(.{ - .w = false, - .b = reg.isExtended(), - }); - opc.encodeWithReg(encoder, reg); -} - -fn lowerToDEnc(tag: Tag, imm: u32, code: *std.ArrayList(u8)) InnerError!void { - assert(!tag.isAvx()); - const opc = getOpCode(tag, .d, false); - const encoder = try Encoder.init(code, 6); - opc.encode(encoder); - encoder.imm32(@bitCast(i32, imm)); -} - -fn lowerToMxEnc(tag: Tag, reg_or_mem: RegisterOrMemory, enc: Encoding, code: *std.ArrayList(u8)) InnerError!void { - assert(!tag.isAvx()); - const opc = getOpCode(tag, enc, reg_or_mem.size() == 8); - const modrm_ext = getModRmExt(tag); - switch (reg_or_mem) { - .register => |reg| { - const encoder = try Encoder.init(code, 4); - if (reg.size() == 16) { - encoder.prefix16BitMode(); - } - const wide = if (tag == .jmp_near) false else setRexWRegister(reg); - encoder.rex(.{ - .w = wide, - .b = reg.isExtended(), - }); - opc.encode(encoder); - encoder.modRm_direct(modrm_ext, reg.lowEnc()); - }, - .memory => |mem_op| { - const encoder = try Encoder.init(code, 8); - if (mem_op.ptr_size == .word_ptr) { - encoder.prefix16BitMode(); - } - if (mem_op.base) |base| { - const wide = if (tag == .jmp_near) false else mem_op.ptr_size == .qword_ptr; - encoder.rex(.{ - .w = wide, - .b = base.isExtended(), - .x = if (mem_op.scale_index) |si| si.index.isExtended() else false, - }); - } - opc.encode(encoder); - mem_op.encode(encoder, modrm_ext); - }, - } -} - -fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) InnerError!void { - return lowerToMxEnc(tag, reg_or_mem, .m, code); -} - -fn lowerToM1Enc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) InnerError!void { - return lowerToMxEnc(tag, reg_or_mem, .m1, code); -} - -fn lowerToMcEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) InnerError!void { - return lowerToMxEnc(tag, reg_or_mem, .mc, code); -} - -fn lowerToTdEnc(tag: Tag, moffs: u64, reg: Register, code: *std.ArrayList(u8)) InnerError!void { - return lowerToTdFdEnc(tag, reg, moffs, code, true); -} - -fn lowerToFdEnc(tag: Tag, reg: Register, moffs: u64, code: *std.ArrayList(u8)) InnerError!void { - return lowerToTdFdEnc(tag, reg, moffs, code, false); -} - -fn lowerToTdFdEnc(tag: Tag, reg: Register, moffs: u64, code: *std.ArrayList(u8), td: bool) InnerError!void { - assert(!tag.isAvx()); - const opc = if (td) getOpCode(tag, .td, reg.size() == 8) else getOpCode(tag, .fd, reg.size() == 8); - const encoder = try Encoder.init(code, 10); - if (reg.size() == 16) { - encoder.prefix16BitMode(); - } - encoder.rex(.{ - .w = setRexWRegister(reg), - }); - opc.encode(encoder); - switch (reg.size()) { - 8 => encoder.imm8(@bitCast(i8, @truncate(u8, moffs))), - 16 => encoder.imm16(@bitCast(i16, @truncate(u16, moffs))), - 32 => encoder.imm32(@bitCast(i32, @truncate(u32, moffs))), - 64 => encoder.imm64(moffs), - else => unreachable, - } -} - -fn lowerToOiEnc(tag: Tag, reg: Register, imm: u64, code: *std.ArrayList(u8)) InnerError!void { - assert(!tag.isAvx()); - const opc = getOpCode(tag, .oi, reg.size() == 8); - const encoder = try Encoder.init(code, 10); - if (reg.size() == 16) { - encoder.prefix16BitMode(); - } - encoder.rex(.{ - .w = setRexWRegister(reg), - .b = reg.isExtended(), - }); - opc.encodeWithReg(encoder, reg); - switch (reg.size()) { - 8 => encoder.imm8(@bitCast(i8, @truncate(u8, imm))), - 16 => encoder.imm16(@bitCast(i16, @truncate(u16, imm))), - 32 => encoder.imm32(@bitCast(i32, @truncate(u32, imm))), - 64 => encoder.imm64(imm), - else => unreachable, - } -} - -fn lowerToMiXEnc( - tag: Tag, - reg_or_mem: RegisterOrMemory, - imm: u32, - enc: Encoding, - code: *std.ArrayList(u8), -) InnerError!void { - assert(!tag.isAvx()); - const modrm_ext = getModRmExt(tag); - const opc = getOpCode(tag, enc, reg_or_mem.size() == 8); - switch (reg_or_mem) { - .register => |dst_reg| { - const encoder = try Encoder.init(code, 7); - if (dst_reg.size() == 16) { - // 0x66 prefix switches to the non-default size; here we assume a switch from - // the default 32bits to 16bits operand-size. - // More info: https://www.cs.uni-potsdam.de/desn/lehre/ss15/64-ia-32-architectures-software-developer-instruction-set-reference-manual-325383.pdf#page=32&zoom=auto,-159,773 - encoder.prefix16BitMode(); - } - encoder.rex(.{ - .w = setRexWRegister(dst_reg), - .b = dst_reg.isExtended(), - }); - opc.encode(encoder); - encoder.modRm_direct(modrm_ext, dst_reg.lowEnc()); - encodeImm(encoder, imm, if (enc == .mi8) 8 else dst_reg.size()); - }, - .memory => |dst_mem| { - const encoder = try Encoder.init(code, 12); - if (dst_mem.ptr_size == .word_ptr) { - encoder.prefix16BitMode(); - } - if (dst_mem.base) |base| { - encoder.rex(.{ - .w = dst_mem.ptr_size == .qword_ptr, - .b = base.isExtended(), - .x = if (dst_mem.scale_index) |si| si.index.isExtended() else false, - }); - } else { - encoder.rex(.{ - .w = dst_mem.ptr_size == .qword_ptr, - .x = if (dst_mem.scale_index) |si| si.index.isExtended() else false, - }); - } - opc.encode(encoder); - dst_mem.encode(encoder, modrm_ext); - encodeImm(encoder, imm, if (enc == .mi8) 8 else dst_mem.ptr_size.size()); - }, - } -} - -fn lowerToMiImm8Enc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: u8, code: *std.ArrayList(u8)) InnerError!void { - return lowerToMiXEnc(tag, reg_or_mem, imm, .mi8, code); -} - -fn lowerToMiEnc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: u32, code: *std.ArrayList(u8)) InnerError!void { - return lowerToMiXEnc(tag, reg_or_mem, imm, .mi, code); -} - -fn lowerToRmEnc( - tag: Tag, - reg: Register, - reg_or_mem: RegisterOrMemory, - code: *std.ArrayList(u8), -) InnerError!void { - assert(!tag.isAvx()); - const opc = getOpCode(tag, .rm, reg.size() == 8 or reg_or_mem.size() == 8); - switch (reg_or_mem) { - .register => |src_reg| { - const encoder = try Encoder.init(code, 5); - if (reg.size() == 16) { - encoder.prefix16BitMode(); - } - encoder.rex(.{ - .w = setRexWRegister(reg) or setRexWRegister(src_reg), - .r = reg.isExtended(), - .b = src_reg.isExtended(), - }); - opc.encode(encoder); - encoder.modRm_direct(reg.lowEnc(), src_reg.lowEnc()); - }, - .memory => |src_mem| { - const encoder = try Encoder.init(code, 9); - if (reg.size() == 16) { - encoder.prefix16BitMode(); - } - if (src_mem.base) |base| { - // TODO handle 32-bit base register - requires prefix 0x67 - // Intel Manual, Vol 1, chapter 3.6 and 3.6.1 - encoder.rex(.{ - .w = setRexWRegister(reg), - .r = reg.isExtended(), - .b = base.isExtended(), - .x = if (src_mem.scale_index) |si| si.index.isExtended() else false, - }); - } else { - encoder.rex(.{ - .w = setRexWRegister(reg), - .r = reg.isExtended(), - .x = if (src_mem.scale_index) |si| si.index.isExtended() else false, - }); - } - opc.encode(encoder); - src_mem.encode(encoder, reg.lowEnc()); - }, - } -} - -fn lowerToMrEnc( - tag: Tag, - reg_or_mem: RegisterOrMemory, - reg: Register, - code: *std.ArrayList(u8), -) InnerError!void { - assert(!tag.isAvx()); - const opc = getOpCode(tag, .mr, reg.size() == 8 or reg_or_mem.size() == 8); - switch (reg_or_mem) { - .register => |dst_reg| { - const encoder = try Encoder.init(code, 4); - if (dst_reg.size() == 16) { - encoder.prefix16BitMode(); - } - encoder.rex(.{ - .w = setRexWRegister(dst_reg) or setRexWRegister(reg), - .r = reg.isExtended(), - .b = dst_reg.isExtended(), - }); - opc.encode(encoder); - encoder.modRm_direct(reg.lowEnc(), dst_reg.lowEnc()); - }, - .memory => |dst_mem| { - const encoder = try Encoder.init(code, 9); - if (reg.size() == 16) { - encoder.prefix16BitMode(); - } - if (dst_mem.base) |base| { - encoder.rex(.{ - .w = dst_mem.ptr_size == .qword_ptr or setRexWRegister(reg), - .r = reg.isExtended(), - .b = base.isExtended(), - .x = if (dst_mem.scale_index) |si| si.index.isExtended() else false, - }); - } else { - encoder.rex(.{ - .w = dst_mem.ptr_size == .qword_ptr or setRexWRegister(reg), - .r = reg.isExtended(), - .x = if (dst_mem.scale_index) |si| si.index.isExtended() else false, - }); - } - opc.encode(encoder); - dst_mem.encode(encoder, reg.lowEnc()); - }, - } -} - -fn lowerToRmiEnc( - tag: Tag, - reg: Register, - reg_or_mem: RegisterOrMemory, - imm: u32, - code: *std.ArrayList(u8), -) InnerError!void { - assert(!tag.isAvx()); - const opc = getOpCode(tag, .rmi, false); - const encoder = try Encoder.init(code, 13); - if (reg.size() == 16) { - encoder.prefix16BitMode(); - } - switch (reg_or_mem) { - .register => |src_reg| { - encoder.rex(.{ - .w = setRexWRegister(reg) or setRexWRegister(src_reg), - .r = reg.isExtended(), - .b = src_reg.isExtended(), - }); - opc.encode(encoder); - encoder.modRm_direct(reg.lowEnc(), src_reg.lowEnc()); - }, - .memory => |src_mem| { - if (src_mem.base) |base| { - // TODO handle 32-bit base register - requires prefix 0x67 - // Intel Manual, Vol 1, chapter 3.6 and 3.6.1 - encoder.rex(.{ - .w = setRexWRegister(reg), - .r = reg.isExtended(), - .b = base.isExtended(), - .x = if (src_mem.scale_index) |si| si.index.isExtended() else false, - }); - } else { - encoder.rex(.{ - .w = setRexWRegister(reg), - .r = reg.isExtended(), - .x = if (src_mem.scale_index) |si| si.index.isExtended() else false, - }); - } - opc.encode(encoder); - src_mem.encode(encoder, reg.lowEnc()); - }, - } - encodeImm(encoder, imm, reg.size()); -} - -/// Also referred to as XM encoding in Intel manual. -fn lowerToVmEnc( - tag: Tag, - reg: Register, - reg_or_mem: RegisterOrMemory, - code: *std.ArrayList(u8), -) InnerError!void { - const opc = getOpCode(tag, .vm, false); - var enc = getVexEncoding(tag, .vm); - const vex = &enc.prefix; - switch (reg_or_mem) { - .register => |src_reg| { - const encoder = try Encoder.init(code, 5); - vex.rex(.{ - .r = reg.isExtended(), - .b = src_reg.isExtended(), - }); - encoder.vex(enc.prefix); - opc.encode(encoder); - encoder.modRm_direct(reg.lowEnc(), src_reg.lowEnc()); - }, - .memory => |src_mem| { - const encoder = try Encoder.init(code, 10); - if (src_mem.base) |base| { - vex.rex(.{ - .r = reg.isExtended(), - .b = base.isExtended(), - .x = if (src_mem.scale_index) |si| si.index.isExtended() else false, - }); - } else { - vex.rex(.{ - .r = reg.isExtended(), - .x = if (src_mem.scale_index) |si| si.index.isExtended() else false, - }); - } - encoder.vex(enc.prefix); - opc.encode(encoder); - src_mem.encode(encoder, reg.lowEnc()); - }, - } -} - -/// Usually referred to as MR encoding with V/V in Intel manual. -fn lowerToMvEnc( - tag: Tag, - reg_or_mem: RegisterOrMemory, - reg: Register, - code: *std.ArrayList(u8), -) InnerError!void { - const opc = getOpCode(tag, .mv, false); - var enc = getVexEncoding(tag, .mv); - const vex = &enc.prefix; - switch (reg_or_mem) { - .register => |dst_reg| { - const encoder = try Encoder.init(code, 4); - vex.rex(.{ - .r = reg.isExtended(), - .b = dst_reg.isExtended(), - }); - encoder.vex(enc.prefix); - opc.encode(encoder); - encoder.modRm_direct(reg.lowEnc(), dst_reg.lowEnc()); - }, - .memory => |dst_mem| { - const encoder = try Encoder.init(code, 10); - if (dst_mem.base) |base| { - vex.rex(.{ - .r = reg.isExtended(), - .b = base.isExtended(), - .x = if (dst_mem.scale_index) |si| si.index.isExtended() else false, - }); - } else { - vex.rex(.{ - .r = reg.isExtended(), - .x = if (dst_mem.scale_index) |si| si.index.isExtended() else false, - }); - } - encoder.vex(enc.prefix); - opc.encode(encoder); - dst_mem.encode(encoder, reg.lowEnc()); - }, - } -} - -fn lowerToRvmEnc( - tag: Tag, - reg1: Register, - reg2: Register, - reg_or_mem: RegisterOrMemory, - code: *std.ArrayList(u8), -) InnerError!void { - const opc = getOpCode(tag, .rvm, false); - var enc = getVexEncoding(tag, .rvm); - const vex = &enc.prefix; - switch (reg_or_mem) { - .register => |reg3| { - if (enc.reg) |vvvv| { - switch (vvvv) { - .nds => vex.reg(reg2.enc()), - else => unreachable, // TODO - } - } - const encoder = try Encoder.init(code, 5); - vex.rex(.{ - .r = reg1.isExtended(), - .b = reg3.isExtended(), - }); - encoder.vex(enc.prefix); - opc.encode(encoder); - encoder.modRm_direct(reg1.lowEnc(), reg3.lowEnc()); - }, - .memory => |dst_mem| { - _ = dst_mem; - unreachable; // TODO - }, - } -} - -fn lowerToRvmiEnc( - tag: Tag, - reg1: Register, - reg2: Register, - reg_or_mem: RegisterOrMemory, - imm: u32, - code: *std.ArrayList(u8), -) InnerError!void { - const opc = getOpCode(tag, .rvmi, false); - var enc = getVexEncoding(tag, .rvmi); - const vex = &enc.prefix; - const encoder: Encoder = blk: { - switch (reg_or_mem) { - .register => |reg3| { - if (enc.reg) |vvvv| { - switch (vvvv) { - .nds => vex.reg(reg2.enc()), - else => unreachable, // TODO - } - } - const encoder = try Encoder.init(code, 5); - vex.rex(.{ - .r = reg1.isExtended(), - .b = reg3.isExtended(), - }); - encoder.vex(enc.prefix); - opc.encode(encoder); - encoder.modRm_direct(reg1.lowEnc(), reg3.lowEnc()); - break :blk encoder; - }, - .memory => |dst_mem| { - _ = dst_mem; - unreachable; // TODO - }, - } - }; - encodeImm(encoder, imm, 8); // TODO -} - -fn expectEqualHexStrings(expected: []const u8, given: []const u8, assembly: []const u8) !void { - assert(expected.len > 0); - if (mem.eql(u8, expected, given)) return; - const expected_fmt = try std.fmt.allocPrint(testing.allocator, "{x}", .{std.fmt.fmtSliceHexLower(expected)}); - defer testing.allocator.free(expected_fmt); - const given_fmt = try std.fmt.allocPrint(testing.allocator, "{x}", .{std.fmt.fmtSliceHexLower(given)}); - defer testing.allocator.free(given_fmt); - const idx = mem.indexOfDiff(u8, expected_fmt, given_fmt).?; - var padding = try testing.allocator.alloc(u8, idx + 5); - defer testing.allocator.free(padding); - mem.set(u8, padding, ' '); - std.debug.print("\nASM: {s}\nEXP: {s}\nGIV: {s}\n{s}^ -- first differing byte\n", .{ - assembly, - expected_fmt, - given_fmt, - padding, - }); - return error.TestFailed; -} - -const TestEmit = struct { - code_buffer: std.ArrayList(u8), - next: usize = 0, - - fn init() TestEmit { - return .{ - .code_buffer = std.ArrayList(u8).init(testing.allocator), - }; - } - - fn deinit(emit: *TestEmit) void { - emit.code_buffer.deinit(); - emit.next = undefined; - } - - fn code(emit: *TestEmit) *std.ArrayList(u8) { - emit.next = emit.code_buffer.items.len; - return &emit.code_buffer; - } - - fn lowered(emit: TestEmit) []const u8 { - return emit.code_buffer.items[emit.next..]; - } -}; - -test "lower MI encoding" { - var emit = TestEmit.init(); - defer emit.deinit(); - try lowerToMiEnc(.mov, RegisterOrMemory.reg(.rax), 0x10, emit.code()); - try expectEqualHexStrings("\x48\xc7\xc0\x10\x00\x00\x00", emit.lowered(), "mov rax, 0x10"); - try lowerToMiEnc(.mov, RegisterOrMemory.mem(.dword_ptr, .{ .disp = 0, .base = .r11 }), 0x10, emit.code()); - try expectEqualHexStrings("\x41\xc7\x03\x10\x00\x00\x00", emit.lowered(), "mov dword ptr [r11 + 0], 0x10"); - try lowerToMiEnc(.add, RegisterOrMemory.mem(.dword_ptr, .{ - .disp = @bitCast(u32, @as(i32, -8)), - .base = .rdx, - }), 0x10, emit.code()); - try expectEqualHexStrings("\x81\x42\xF8\x10\x00\x00\x00", emit.lowered(), "add dword ptr [rdx - 8], 0x10"); - try lowerToMiEnc(.sub, RegisterOrMemory.mem(.dword_ptr, .{ - .disp = 0x10000000, - .base = .r11, - }), 0x10, emit.code()); - try expectEqualHexStrings( - "\x41\x81\xab\x00\x00\x00\x10\x10\x00\x00\x00", - emit.lowered(), - "sub dword ptr [r11 + 0x10000000], 0x10", - ); - try lowerToMiEnc(.@"and", RegisterOrMemory.mem(.dword_ptr, .{ .disp = 0x10000000 }), 0x10, emit.code()); - try expectEqualHexStrings( - "\x81\x24\x25\x00\x00\x00\x10\x10\x00\x00\x00", - emit.lowered(), - "and dword ptr [ds:0x10000000], 0x10", - ); - try lowerToMiEnc(.@"and", RegisterOrMemory.mem(.dword_ptr, .{ - .disp = 0x10000000, - .base = .r12, - }), 0x10, emit.code()); - try expectEqualHexStrings( - "\x41\x81\xA4\x24\x00\x00\x00\x10\x10\x00\x00\x00", - emit.lowered(), - "and dword ptr [r12 + 0x10000000], 0x10", - ); - try lowerToMiEnc(.mov, RegisterOrMemory.rip(.qword_ptr, 0x10), 0x10, emit.code()); - try expectEqualHexStrings( - "\x48\xC7\x05\x10\x00\x00\x00\x10\x00\x00\x00", - emit.lowered(), - "mov qword ptr [rip + 0x10], 0x10", - ); - try lowerToMiEnc(.mov, RegisterOrMemory.mem(.qword_ptr, .{ - .disp = @bitCast(u32, @as(i32, -8)), - .base = .rbp, - }), 0x10, emit.code()); - try expectEqualHexStrings( - "\x48\xc7\x45\xf8\x10\x00\x00\x00", - emit.lowered(), - "mov qword ptr [rbp - 8], 0x10", - ); - try lowerToMiEnc(.mov, RegisterOrMemory.mem(.word_ptr, .{ - .disp = @bitCast(u32, @as(i32, -2)), - .base = .rbp, - }), 0x10, emit.code()); - try expectEqualHexStrings("\x66\xC7\x45\xFE\x10\x00", emit.lowered(), "mov word ptr [rbp - 2], 0x10"); - try lowerToMiEnc(.mov, RegisterOrMemory.mem(.byte_ptr, .{ - .disp = @bitCast(u32, @as(i32, -1)), - .base = .rbp, - }), 0x10, emit.code()); - try expectEqualHexStrings("\xC6\x45\xFF\x10", emit.lowered(), "mov byte ptr [rbp - 1], 0x10"); - try lowerToMiEnc(.mov, RegisterOrMemory.mem(.qword_ptr, .{ - .disp = 0x10000000, - .scale_index = .{ - .scale = 1, - .index = .rcx, - }, - }), 0x10, emit.code()); - try expectEqualHexStrings( - "\x48\xC7\x04\x4D\x00\x00\x00\x10\x10\x00\x00\x00", - emit.lowered(), - "mov qword ptr [rcx*2 + 0x10000000], 0x10", - ); - - try lowerToMiImm8Enc(.add, RegisterOrMemory.reg(.rax), 0x10, emit.code()); - try expectEqualHexStrings("\x48\x83\xC0\x10", emit.lowered(), "add rax, 0x10"); -} - -test "lower RM encoding" { - var emit = TestEmit.init(); - defer emit.deinit(); - try lowerToRmEnc(.mov, .rax, RegisterOrMemory.reg(.rbx), emit.code()); - try expectEqualHexStrings("\x48\x8b\xc3", emit.lowered(), "mov rax, rbx"); - try lowerToRmEnc(.mov, .rax, RegisterOrMemory.mem(.qword_ptr, .{ .disp = 0, .base = .r11 }), emit.code()); - try expectEqualHexStrings("\x49\x8b\x03", emit.lowered(), "mov rax, qword ptr [r11 + 0]"); - try lowerToRmEnc(.add, .r11, RegisterOrMemory.mem(.qword_ptr, .{ .disp = 0x10000000 }), emit.code()); - try expectEqualHexStrings( - "\x4C\x03\x1C\x25\x00\x00\x00\x10", - emit.lowered(), - "add r11, qword ptr [ds:0x10000000]", - ); - try lowerToRmEnc(.add, .r12b, RegisterOrMemory.mem(.byte_ptr, .{ .disp = 0x10000000 }), emit.code()); - try expectEqualHexStrings( - "\x44\x02\x24\x25\x00\x00\x00\x10", - emit.lowered(), - "add r11b, byte ptr [ds:0x10000000]", - ); - try lowerToRmEnc(.sub, .r11, RegisterOrMemory.mem(.qword_ptr, .{ - .disp = 0x10000000, - .base = .r13, - }), emit.code()); - try expectEqualHexStrings( - "\x4D\x2B\x9D\x00\x00\x00\x10", - emit.lowered(), - "sub r11, qword ptr [r13 + 0x10000000]", - ); - try lowerToRmEnc(.sub, .r11, RegisterOrMemory.mem(.qword_ptr, .{ - .disp = 0x10000000, - .base = .r12, - }), emit.code()); - try expectEqualHexStrings( - "\x4D\x2B\x9C\x24\x00\x00\x00\x10", - emit.lowered(), - "sub r11, qword ptr [r12 + 0x10000000]", - ); - try lowerToRmEnc(.mov, .rax, RegisterOrMemory.mem(.qword_ptr, .{ - .disp = @bitCast(u32, @as(i32, -4)), - .base = .rbp, - }), emit.code()); - try expectEqualHexStrings("\x48\x8B\x45\xFC", emit.lowered(), "mov rax, qword ptr [rbp - 4]"); - try lowerToRmEnc(.lea, .rax, RegisterOrMemory.rip(.qword_ptr, 0x10), emit.code()); - try expectEqualHexStrings("\x48\x8D\x05\x10\x00\x00\x00", emit.lowered(), "lea rax, [rip + 0x10]"); - try lowerToRmEnc(.mov, .rax, RegisterOrMemory.mem(.qword_ptr, .{ - .disp = @bitCast(u32, @as(i32, -8)), - .base = .rbp, - .scale_index = .{ - .scale = 0, - .index = .rcx, - }, - }), emit.code()); - try expectEqualHexStrings("\x48\x8B\x44\x0D\xF8", emit.lowered(), "mov rax, qword ptr [rbp + rcx*1 - 8]"); - try lowerToRmEnc(.mov, .eax, RegisterOrMemory.mem(.dword_ptr, .{ - .disp = @bitCast(u32, @as(i32, -4)), - .base = .rbp, - .scale_index = .{ - .scale = 2, - .index = .rdx, - }, - }), emit.code()); - try expectEqualHexStrings("\x8B\x44\x95\xFC", emit.lowered(), "mov eax, dword ptr [rbp + rdx*4 - 4]"); - try lowerToRmEnc(.mov, .rax, RegisterOrMemory.mem(.qword_ptr, .{ - .disp = @bitCast(u32, @as(i32, -8)), - .base = .rbp, - .scale_index = .{ - .scale = 3, - .index = .rcx, - }, - }), emit.code()); - try expectEqualHexStrings("\x48\x8B\x44\xCD\xF8", emit.lowered(), "mov rax, qword ptr [rbp + rcx*8 - 8]"); - try lowerToRmEnc(.mov, .r8b, RegisterOrMemory.mem(.byte_ptr, .{ - .disp = @bitCast(u32, @as(i32, -24)), - .base = .rsi, - .scale_index = .{ - .scale = 0, - .index = .rcx, - }, - }), emit.code()); - try expectEqualHexStrings("\x44\x8A\x44\x0E\xE8", emit.lowered(), "mov r8b, byte ptr [rsi + rcx*1 - 24]"); - try lowerToRmEnc(.lea, .rsi, RegisterOrMemory.mem(.qword_ptr, .{ - .disp = 0, - .base = .rbp, - .scale_index = .{ - .scale = 0, - .index = .rcx, - }, - }), emit.code()); - try expectEqualHexStrings("\x48\x8D\x74\x0D\x00", emit.lowered(), "lea rsi, qword ptr [rbp + rcx*1 + 0]"); -} - -test "lower MR encoding" { - var emit = TestEmit.init(); - defer emit.deinit(); - try lowerToMrEnc(.mov, RegisterOrMemory.reg(.rax), .rbx, emit.code()); - try expectEqualHexStrings("\x48\x89\xd8", emit.lowered(), "mov rax, rbx"); - try lowerToMrEnc(.mov, RegisterOrMemory.mem(.qword_ptr, .{ - .disp = @bitCast(u32, @as(i32, -4)), - .base = .rbp, - }), .r11, emit.code()); - try expectEqualHexStrings("\x4c\x89\x5d\xfc", emit.lowered(), "mov qword ptr [rbp - 4], r11"); - try lowerToMrEnc(.add, RegisterOrMemory.mem(.byte_ptr, .{ .disp = 0x10000000 }), .r12b, emit.code()); - try expectEqualHexStrings( - "\x44\x00\x24\x25\x00\x00\x00\x10", - emit.lowered(), - "add byte ptr [ds:0x10000000], r12b", - ); - try lowerToMrEnc(.add, RegisterOrMemory.mem(.dword_ptr, .{ .disp = 0x10000000 }), .r12d, emit.code()); - try expectEqualHexStrings( - "\x44\x01\x24\x25\x00\x00\x00\x10", - emit.lowered(), - "add dword ptr [ds:0x10000000], r12d", - ); - try lowerToMrEnc(.sub, RegisterOrMemory.mem(.qword_ptr, .{ - .disp = 0x10000000, - .base = .r11, - }), .r12, emit.code()); - try expectEqualHexStrings( - "\x4D\x29\xA3\x00\x00\x00\x10", - emit.lowered(), - "sub qword ptr [r11 + 0x10000000], r12", - ); - try lowerToMrEnc(.mov, RegisterOrMemory.rip(.qword_ptr, 0x10), .r12, emit.code()); - try expectEqualHexStrings("\x4C\x89\x25\x10\x00\x00\x00", emit.lowered(), "mov qword ptr [rip + 0x10], r12"); -} - -test "lower OI encoding" { - var emit = TestEmit.init(); - defer emit.deinit(); - try lowerToOiEnc(.mov, .rax, 0x1000000000000000, emit.code()); - try expectEqualHexStrings( - "\x48\xB8\x00\x00\x00\x00\x00\x00\x00\x10", - emit.lowered(), - "movabs rax, 0x1000000000000000", - ); - try lowerToOiEnc(.mov, .r11, 0x1000000000000000, emit.code()); - try expectEqualHexStrings( - "\x49\xBB\x00\x00\x00\x00\x00\x00\x00\x10", - emit.lowered(), - "movabs r11, 0x1000000000000000", - ); - try lowerToOiEnc(.mov, .r11d, 0x10000000, emit.code()); - try expectEqualHexStrings("\x41\xBB\x00\x00\x00\x10", emit.lowered(), "mov r11d, 0x10000000"); - try lowerToOiEnc(.mov, .r11w, 0x1000, emit.code()); - try expectEqualHexStrings("\x66\x41\xBB\x00\x10", emit.lowered(), "mov r11w, 0x1000"); - try lowerToOiEnc(.mov, .r11b, 0x10, emit.code()); - try expectEqualHexStrings("\x41\xB3\x10", emit.lowered(), "mov r11b, 0x10"); -} - -test "lower FD/TD encoding" { - var emit = TestEmit.init(); - defer emit.deinit(); - try lowerToFdEnc(.mov, .rax, 0x1000000000000000, emit.code()); - try expectEqualHexStrings( - "\x48\xa1\x00\x00\x00\x00\x00\x00\x00\x10", - emit.lowered(), - "mov rax, ds:0x1000000000000000", - ); - try lowerToFdEnc(.mov, .eax, 0x10000000, emit.code()); - try expectEqualHexStrings("\xa1\x00\x00\x00\x10", emit.lowered(), "mov eax, ds:0x10000000"); - try lowerToFdEnc(.mov, .ax, 0x1000, emit.code()); - try expectEqualHexStrings("\x66\xa1\x00\x10", emit.lowered(), "mov ax, ds:0x1000"); - try lowerToFdEnc(.mov, .al, 0x10, emit.code()); - try expectEqualHexStrings("\xa0\x10", emit.lowered(), "mov al, ds:0x10"); -} - -test "lower M encoding" { - var emit = TestEmit.init(); - defer emit.deinit(); - try lowerToMEnc(.jmp_near, RegisterOrMemory.reg(.r12), emit.code()); - try expectEqualHexStrings("\x41\xFF\xE4", emit.lowered(), "jmp r12"); - try lowerToMEnc(.jmp_near, RegisterOrMemory.reg(.r12w), emit.code()); - try expectEqualHexStrings("\x66\x41\xFF\xE4", emit.lowered(), "jmp r12w"); - try lowerToMEnc(.jmp_near, RegisterOrMemory.mem(.qword_ptr, .{ .disp = 0, .base = .r12 }), emit.code()); - try expectEqualHexStrings("\x41\xFF\x24\x24", emit.lowered(), "jmp qword ptr [r12]"); - try lowerToMEnc(.jmp_near, RegisterOrMemory.mem(.word_ptr, .{ .disp = 0, .base = .r12 }), emit.code()); - try expectEqualHexStrings("\x66\x41\xFF\x24\x24", emit.lowered(), "jmp word ptr [r12]"); - try lowerToMEnc(.jmp_near, RegisterOrMemory.mem(.qword_ptr, .{ .disp = 0x10, .base = .r12 }), emit.code()); - try expectEqualHexStrings("\x41\xFF\x64\x24\x10", emit.lowered(), "jmp qword ptr [r12 + 0x10]"); - try lowerToMEnc(.jmp_near, RegisterOrMemory.mem(.qword_ptr, .{ - .disp = 0x1000, - .base = .r12, - }), emit.code()); - try expectEqualHexStrings( - "\x41\xFF\xA4\x24\x00\x10\x00\x00", - emit.lowered(), - "jmp qword ptr [r12 + 0x1000]", - ); - try lowerToMEnc(.jmp_near, RegisterOrMemory.rip(.qword_ptr, 0x10), emit.code()); - try expectEqualHexStrings("\xFF\x25\x10\x00\x00\x00", emit.lowered(), "jmp qword ptr [rip + 0x10]"); - try lowerToMEnc(.jmp_near, RegisterOrMemory.mem(.qword_ptr, .{ .disp = 0x10 }), emit.code()); - try expectEqualHexStrings("\xFF\x24\x25\x10\x00\x00\x00", emit.lowered(), "jmp qword ptr [ds:0x10]"); - try lowerToMEnc(.seta, RegisterOrMemory.reg(.r11b), emit.code()); - try expectEqualHexStrings("\x41\x0F\x97\xC3", emit.lowered(), "seta r11b"); - try lowerToMEnc(.idiv, RegisterOrMemory.reg(.rax), emit.code()); - try expectEqualHexStrings("\x48\xF7\xF8", emit.lowered(), "idiv rax"); - try lowerToMEnc(.imul, RegisterOrMemory.reg(.al), emit.code()); - try expectEqualHexStrings("\xF6\xE8", emit.lowered(), "imul al"); -} - -test "lower M1 and MC encodings" { - var emit = TestEmit.init(); - defer emit.deinit(); - try lowerToM1Enc(.sal, RegisterOrMemory.reg(.r12), emit.code()); - try expectEqualHexStrings("\x49\xD1\xE4", emit.lowered(), "sal r12, 1"); - try lowerToM1Enc(.sal, RegisterOrMemory.reg(.r12d), emit.code()); - try expectEqualHexStrings("\x41\xD1\xE4", emit.lowered(), "sal r12d, 1"); - try lowerToM1Enc(.sal, RegisterOrMemory.reg(.r12w), emit.code()); - try expectEqualHexStrings("\x66\x41\xD1\xE4", emit.lowered(), "sal r12w, 1"); - try lowerToM1Enc(.sal, RegisterOrMemory.reg(.r12b), emit.code()); - try expectEqualHexStrings("\x41\xD0\xE4", emit.lowered(), "sal r12b, 1"); - try lowerToM1Enc(.sal, RegisterOrMemory.reg(.rax), emit.code()); - try expectEqualHexStrings("\x48\xD1\xE0", emit.lowered(), "sal rax, 1"); - try lowerToM1Enc(.sal, RegisterOrMemory.reg(.eax), emit.code()); - try expectEqualHexStrings("\xD1\xE0", emit.lowered(), "sal eax, 1"); - try lowerToM1Enc(.sal, RegisterOrMemory.mem(.qword_ptr, .{ - .disp = @bitCast(u32, @as(i32, -0x10)), - .base = .rbp, - }), emit.code()); - try expectEqualHexStrings("\x48\xD1\x65\xF0", emit.lowered(), "sal qword ptr [rbp - 0x10], 1"); - try lowerToM1Enc(.sal, RegisterOrMemory.mem(.dword_ptr, .{ - .disp = @bitCast(u32, @as(i32, -0x10)), - .base = .rbp, - }), emit.code()); - try expectEqualHexStrings("\xD1\x65\xF0", emit.lowered(), "sal dword ptr [rbp - 0x10], 1"); - - try lowerToMcEnc(.shr, RegisterOrMemory.reg(.r12), emit.code()); - try expectEqualHexStrings("\x49\xD3\xEC", emit.lowered(), "shr r12, cl"); - try lowerToMcEnc(.shr, RegisterOrMemory.reg(.rax), emit.code()); - try expectEqualHexStrings("\x48\xD3\xE8", emit.lowered(), "shr rax, cl"); - - try lowerToMcEnc(.sar, RegisterOrMemory.reg(.rsi), emit.code()); - try expectEqualHexStrings("\x48\xD3\xFE", emit.lowered(), "sar rsi, cl"); -} - -test "lower O encoding" { - var emit = TestEmit.init(); - defer emit.deinit(); - try lowerToOEnc(.pop, .r12, emit.code()); - try expectEqualHexStrings("\x41\x5c", emit.lowered(), "pop r12"); - try lowerToOEnc(.push, .r12w, emit.code()); - try expectEqualHexStrings("\x66\x41\x54", emit.lowered(), "push r12w"); -} - -test "lower RMI encoding" { - var emit = TestEmit.init(); - defer emit.deinit(); - try lowerToRmiEnc(.imul, .rax, RegisterOrMemory.mem(.qword_ptr, .{ - .disp = @bitCast(u32, @as(i32, -8)), - .base = .rbp, - }), 0x10, emit.code()); - try expectEqualHexStrings( - "\x48\x69\x45\xF8\x10\x00\x00\x00", - emit.lowered(), - "imul rax, qword ptr [rbp - 8], 0x10", - ); - try lowerToRmiEnc(.imul, .eax, RegisterOrMemory.mem(.dword_ptr, .{ - .disp = @bitCast(u32, @as(i32, -4)), - .base = .rbp, - }), 0x10, emit.code()); - try expectEqualHexStrings("\x69\x45\xFC\x10\x00\x00\x00", emit.lowered(), "imul eax, dword ptr [rbp - 4], 0x10"); - try lowerToRmiEnc(.imul, .ax, RegisterOrMemory.mem(.word_ptr, .{ - .disp = @bitCast(u32, @as(i32, -2)), - .base = .rbp, - }), 0x10, emit.code()); - try expectEqualHexStrings("\x66\x69\x45\xFE\x10\x00", emit.lowered(), "imul ax, word ptr [rbp - 2], 0x10"); - try lowerToRmiEnc(.imul, .r12, RegisterOrMemory.reg(.r12), 0x10, emit.code()); - try expectEqualHexStrings("\x4D\x69\xE4\x10\x00\x00\x00", emit.lowered(), "imul r12, r12, 0x10"); - try lowerToRmiEnc(.imul, .r12w, RegisterOrMemory.reg(.r12w), 0x10, emit.code()); - try expectEqualHexStrings("\x66\x45\x69\xE4\x10\x00", emit.lowered(), "imul r12w, r12w, 0x10"); -} - -test "lower MV encoding" { - var emit = TestEmit.init(); - defer emit.deinit(); - try lowerToMvEnc(.vmovsd, RegisterOrMemory.rip(.qword_ptr, 0x10), .xmm1, emit.code()); - try expectEqualHexStrings( - "\xC5\xFB\x11\x0D\x10\x00\x00\x00", - emit.lowered(), - "vmovsd qword ptr [rip + 0x10], xmm1", - ); -} - -test "lower VM encoding" { - var emit = TestEmit.init(); - defer emit.deinit(); - try lowerToVmEnc(.vmovsd, .xmm1, RegisterOrMemory.rip(.qword_ptr, 0x10), emit.code()); - try expectEqualHexStrings( - "\xC5\xFB\x10\x0D\x10\x00\x00\x00", - emit.lowered(), - "vmovsd xmm1, qword ptr [rip + 0x10]", - ); -} - -test "lower to RVM encoding" { - var emit = TestEmit.init(); - defer emit.deinit(); - try lowerToRvmEnc(.vaddsd, .xmm0, .xmm1, RegisterOrMemory.reg(.xmm2), emit.code()); - try expectEqualHexStrings("\xC5\xF3\x58\xC2", emit.lowered(), "vaddsd xmm0, xmm1, xmm2"); - try lowerToRvmEnc(.vaddsd, .xmm0, .xmm0, RegisterOrMemory.reg(.xmm1), emit.code()); - try expectEqualHexStrings("\xC5\xFB\x58\xC1", emit.lowered(), "vaddsd xmm0, xmm0, xmm1"); -} diff --git a/src/arch/x86_64/Encoding.zig b/src/arch/x86_64/Encoding.zig new file mode 100644 index 0000000000..2cccded7ec --- /dev/null +++ b/src/arch/x86_64/Encoding.zig @@ -0,0 +1,521 @@ +const Encoding = @This(); + +const std = @import("std"); +const assert = std.debug.assert; +const math = std.math; + +const bits = @import("bits.zig"); +const encoder = @import("encoder.zig"); +const Instruction = encoder.Instruction; +const Register = bits.Register; +const Rex = encoder.Rex; +const LegacyPrefixes = encoder.LegacyPrefixes; + +const table = @import("encodings.zig").table; + +mnemonic: Mnemonic, +op_en: OpEn, +op1: Op, +op2: Op, +op3: Op, +op4: Op, +opc_len: u2, +opc: [3]u8, +modrm_ext: u3, +mode: Mode, + +pub fn findByMnemonic(mnemonic: Mnemonic, args: struct { + op1: Instruction.Operand, + op2: Instruction.Operand, + op3: Instruction.Operand, + op4: Instruction.Operand, +}) ?Encoding { + const input_op1 = Op.fromOperand(args.op1); + const input_op2 = Op.fromOperand(args.op2); + const input_op3 = Op.fromOperand(args.op3); + const input_op4 = Op.fromOperand(args.op4); + + // TODO work out what is the maximum number of variants we can actually find in one swoop. + var candidates: [10]Encoding = undefined; + var count: usize = 0; + inline for (table) |entry| { + const enc = Encoding{ + .mnemonic = entry[0], + .op_en = entry[1], + .op1 = entry[2], + .op2 = entry[3], + .op3 = entry[4], + .op4 = entry[5], + .opc_len = entry[6], + .opc = .{ entry[7], entry[8], entry[9] }, + .modrm_ext = entry[10], + .mode = entry[11], + }; + if (enc.mnemonic == mnemonic and + input_op1.isSubset(enc.op1, enc.mode) and + input_op2.isSubset(enc.op2, enc.mode) and + input_op3.isSubset(enc.op3, enc.mode) and + input_op4.isSubset(enc.op4, enc.mode)) + { + candidates[count] = enc; + count += 1; + } + } + + if (count == 0) return null; + if (count == 1) return candidates[0]; + + const EncodingLength = struct { + fn estimate(encoding: Encoding, params: struct { + op1: Instruction.Operand, + op2: Instruction.Operand, + op3: Instruction.Operand, + op4: Instruction.Operand, + }) usize { + var inst = Instruction{ + .op1 = params.op1, + .op2 = params.op2, + .op3 = params.op3, + .op4 = params.op4, + .encoding = encoding, + }; + var cwriter = std.io.countingWriter(std.io.null_writer); + inst.encode(cwriter.writer()) catch unreachable; + return cwriter.bytes_written; + } + }; + + var shortest_encoding: ?struct { + index: usize, + len: usize, + } = null; + var i: usize = 0; + while (i < count) : (i += 1) { + const len = EncodingLength.estimate(candidates[i], .{ + .op1 = args.op1, + .op2 = args.op2, + .op3 = args.op3, + .op4 = args.op4, + }); + const current = shortest_encoding orelse { + shortest_encoding = .{ .index = i, .len = len }; + continue; + }; + if (len < current.len) { + shortest_encoding = .{ .index = i, .len = len }; + } + } + + return candidates[shortest_encoding.?.index]; +} + +/// Returns first matching encoding by opcode. +pub fn findByOpcode(opc: []const u8, prefixes: struct { + legacy: LegacyPrefixes, + rex: Rex, +}, modrm_ext: ?u3) ?Encoding { + inline for (table) |entry| { + const enc = Encoding{ + .mnemonic = entry[0], + .op_en = entry[1], + .op1 = entry[2], + .op2 = entry[3], + .op3 = entry[4], + .op4 = entry[5], + .opc_len = entry[6], + .opc = .{ entry[7], entry[8], entry[9] }, + .modrm_ext = entry[10], + .mode = entry[11], + }; + const match = match: { + if (modrm_ext) |ext| { + break :match ext == enc.modrm_ext and std.mem.eql(u8, enc.opcode(), opc); + } + break :match std.mem.eql(u8, enc.opcode(), opc); + }; + if (match) { + if (prefixes.rex.w) { + switch (enc.mode) { + .fpu, .sse, .sse2 => {}, + .long => return enc, + .none => { + // TODO this is a hack to allow parsing of instructions which contain + // spurious prefix bytes such as + // rex.W mov dil, 0x1 + // Here, rex.W is not needed. + const rex_w_allowed = blk: { + const bit_size = enc.operandSize(); + break :blk bit_size == 64 or bit_size == 8; + }; + if (rex_w_allowed) return enc; + }, + } + } else if (prefixes.legacy.prefix_66) { + switch (enc.operandSize()) { + 16 => return enc, + else => {}, + } + } else { + if (enc.mode == .none) { + switch (enc.operandSize()) { + 16 => {}, + else => return enc, + } + } + } + } + } + return null; +} + +pub fn opcode(encoding: *const Encoding) []const u8 { + return encoding.opc[0..encoding.opc_len]; +} + +pub fn mandatoryPrefix(encoding: *const Encoding) ?u8 { + const prefix = encoding.opc[0]; + return switch (prefix) { + 0x66, 0xf2, 0xf3 => prefix, + else => null, + }; +} + +pub fn modRmExt(encoding: Encoding) u3 { + return switch (encoding.op_en) { + .m, .mi, .m1, .mc => encoding.modrm_ext, + else => unreachable, + }; +} + +pub fn operandSize(encoding: Encoding) u32 { + if (encoding.mode == .long) return 64; + const bit_size: u32 = switch (encoding.op_en) { + .np => switch (encoding.op1) { + .o16 => 16, + .o32 => 32, + .o64 => 64, + else => 32, + }, + .td => encoding.op2.size(), + else => encoding.op1.size(), + }; + return bit_size; +} + +pub fn format( + encoding: Encoding, + comptime fmt: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, +) !void { + _ = options; + _ = fmt; + switch (encoding.mode) { + .long => try writer.writeAll("REX.W + "), + else => {}, + } + + for (encoding.opcode()) |byte| { + try writer.print("{x:0>2} ", .{byte}); + } + + switch (encoding.op_en) { + .np, .fd, .td, .i, .zi, .d => {}, + .o, .oi => { + const tag = switch (encoding.op1) { + .r8 => "rb", + .r16 => "rw", + .r32 => "rd", + .r64 => "rd", + else => unreachable, + }; + try writer.print("+{s} ", .{tag}); + }, + .m, .mi, .m1, .mc => try writer.print("/{d} ", .{encoding.modRmExt()}), + .mr, .rm, .rmi => try writer.writeAll("/r "), + } + + switch (encoding.op_en) { + .i, .d, .zi, .oi, .mi, .rmi => { + const op = switch (encoding.op_en) { + .i, .d => encoding.op1, + .zi, .oi, .mi => encoding.op2, + .rmi => encoding.op3, + else => unreachable, + }; + const tag = switch (op) { + .imm8 => "ib", + .imm16 => "iw", + .imm32 => "id", + .imm64 => "io", + .rel8 => "cb", + .rel16 => "cw", + .rel32 => "cd", + else => unreachable, + }; + try writer.print("{s} ", .{tag}); + }, + .np, .fd, .td, .o, .m, .m1, .mc, .mr, .rm => {}, + } + + try writer.print("{s} ", .{@tagName(encoding.mnemonic)}); + + const ops = &[_]Op{ encoding.op1, encoding.op2, encoding.op3, encoding.op4 }; + for (ops) |op| switch (op) { + .none, .o16, .o32, .o64 => break, + else => try writer.print("{s} ", .{@tagName(op)}), + }; + + const op_en = switch (encoding.op_en) { + .zi => .i, + else => |op_en| op_en, + }; + try writer.print("{s}", .{@tagName(op_en)}); +} + +pub const Mnemonic = enum { + // zig fmt: off + // General-purpose + adc, add, @"and", + call, cbw, cwde, cdqe, cwd, cdq, cqo, cmp, + cmova, cmovae, cmovb, cmovbe, cmovc, cmove, cmovg, cmovge, cmovl, cmovle, cmovna, + cmovnae, cmovnb, cmovnbe, cmovnc, cmovne, cmovng, cmovnge, cmovnl, cmovnle, cmovno, + cmovnp, cmovns, cmovnz, cmovo, cmovp, cmovpe, cmovpo, cmovs, cmovz, + div, + fisttp, fld, + idiv, imul, int3, + ja, jae, jb, jbe, jc, jrcxz, je, jg, jge, jl, jle, jna, jnae, jnb, jnbe, + jnc, jne, jng, jnge, jnl, jnle, jno, jnp, jns, jnz, jo, jp, jpe, jpo, js, jz, + jmp, + lea, + mov, movsx, movsxd, movzx, mul, + nop, + @"or", + pop, push, + ret, + sal, sar, sbb, shl, shr, sub, syscall, + seta, setae, setb, setbe, setc, sete, setg, setge, setl, setle, setna, setnae, + setnb, setnbe, setnc, setne, setng, setnge, setnl, setnle, setno, setnp, setns, + setnz, seto, setp, setpe, setpo, sets, setz, + @"test", + ud2, + xor, + // SSE + addss, + cmpss, + movss, + ucomiss, + // SSE2 + addsd, + cmpsd, + movq, movsd, + ucomisd, + // zig fmt: on +}; + +pub const OpEn = enum { + // zig fmt: off + np, + o, oi, + i, zi, + d, m, + fd, td, + m1, mc, mi, mr, rm, rmi, + // zig fmt: on +}; + +pub const Op = enum { + // zig fmt: off + none, + o16, o32, o64, + unity, + imm8, imm16, imm32, imm64, + al, ax, eax, rax, + cl, + r8, r16, r32, r64, + rm8, rm16, rm32, rm64, + m8, m16, m32, m64, m80, + rel8, rel16, rel32, + m, + moffs, + sreg, + xmm, xmm_m32, xmm_m64, + // zig fmt: on + + pub fn fromOperand(operand: Instruction.Operand) Op { + switch (operand) { + .none => return .none, + + .reg => |reg| { + switch (reg.class()) { + .segment => return .sreg, + .floating_point => return switch (reg.size()) { + 128 => .xmm, + else => unreachable, + }, + .general_purpose => { + if (reg.to64() == .rax) return switch (reg) { + .al => .al, + .ax => .ax, + .eax => .eax, + .rax => .rax, + else => unreachable, + }; + if (reg == .cl) return .cl; + return switch (reg.size()) { + 8 => .r8, + 16 => .r16, + 32 => .r32, + 64 => .r64, + else => unreachable, + }; + }, + } + }, + + .mem => |mem| switch (mem) { + .moffs => return .moffs, + .sib, .rip => { + const bit_size = mem.size(); + return switch (bit_size) { + 8 => .m8, + 16 => .m16, + 32 => .m32, + 64 => .m64, + 80 => .m80, + else => unreachable, + }; + }, + }, + + .imm => |imm| { + if (imm == 1) return .unity; + if (math.cast(i8, imm)) |_| return .imm8; + if (math.cast(i16, imm)) |_| return .imm16; + if (math.cast(i32, imm)) |_| return .imm32; + return .imm64; + }, + } + } + + pub fn size(op: Op) u32 { + return switch (op) { + .none, .o16, .o32, .o64, .moffs, .m, .sreg, .unity => unreachable, + .imm8, .al, .cl, .r8, .m8, .rm8, .rel8 => 8, + .imm16, .ax, .r16, .m16, .rm16, .rel16 => 16, + .imm32, .eax, .r32, .m32, .rm32, .rel32, .xmm_m32 => 32, + .imm64, .rax, .r64, .m64, .rm64, .xmm_m64 => 64, + .m80 => 80, + .xmm => 128, + }; + } + + pub fn isRegister(op: Op) bool { + // zig fmt: off + return switch (op) { + .cl, + .al, .ax, .eax, .rax, + .r8, .r16, .r32, .r64, + .rm8, .rm16, .rm32, .rm64, + .xmm, .xmm_m32, .xmm_m64, + => true, + else => false, + }; + // zig fmt: on + } + + pub fn isImmediate(op: Op) bool { + // zig fmt: off + return switch (op) { + .imm8, .imm16, .imm32, .imm64, + .rel8, .rel16, .rel32, + .unity, + => true, + else => false, + }; + // zig fmt: on + } + + pub fn isMemory(op: Op) bool { + // zig fmt: off + return switch (op) { + .rm8, .rm16, .rm32, .rm64, + .m8, .m16, .m32, .m64, .m80, + .m, + .xmm_m32, .xmm_m64, + => true, + else => false, + }; + // zig fmt: on + } + + pub fn isSegmentRegister(op: Op) bool { + return switch (op) { + .moffs, .sreg => true, + else => false, + }; + } + + pub fn isFloatingPointRegister(op: Op) bool { + return switch (op) { + .xmm, .xmm_m32, .xmm_m64 => true, + else => false, + }; + } + + /// Given an operand `op` checks if `target` is a subset for the purposes + /// of the encoding. + pub fn isSubset(op: Op, target: Op, mode: Mode) bool { + switch (op) { + .m, .o16, .o32, .o64 => unreachable, + .moffs, .sreg => return op == target, + .none => switch (target) { + .o16, .o32, .o64, .none => return true, + else => return false, + }, + else => { + if (op.isRegister() and target.isRegister()) { + switch (mode) { + .sse, .sse2 => return op.isFloatingPointRegister() and target.isFloatingPointRegister(), + else => switch (target) { + .cl, .al, .ax, .eax, .rax => return op == target, + else => return op.size() == target.size(), + }, + } + } + if (op.isMemory() and target.isMemory()) { + switch (target) { + .m => return true, + else => return op.size() == target.size(), + } + } + if (op.isImmediate() and target.isImmediate()) { + switch (target) { + .imm32, .rel32 => switch (op) { + .unity, .imm8, .imm16, .imm32 => return true, + else => return op == target, + }, + .imm16, .rel16 => switch (op) { + .unity, .imm8, .imm16 => return true, + else => return op == target, + }, + .imm8, .rel8 => switch (op) { + .unity, .imm8 => return true, + else => return op == target, + }, + else => return op == target, + } + } + return false; + }, + } + } +}; + +pub const Mode = enum { + none, + fpu, + long, + sse, + sse2, +}; diff --git a/src/arch/x86_64/Mir.zig b/src/arch/x86_64/Mir.zig index ba71f4cddd..b3be08e86b 100644 --- a/src/arch/x86_64/Mir.zig +++ b/src/arch/x86_64/Mir.zig @@ -339,41 +339,23 @@ pub const Inst = struct { /// Nop nop, - /// SSE instructions + /// SSE/AVX instructions /// ops flags: form: /// 0b00 reg1, qword ptr [reg2 + imm32] /// 0b01 qword ptr [reg1 + imm32], reg2 /// 0b10 reg1, reg2 - mov_f64_sse, - mov_f32_sse, + mov_f64, + mov_f32, /// ops flags: form: /// 0b00 reg1, reg2 - add_f64_sse, - add_f32_sse, + add_f64, + add_f32, /// ops flags: form: /// 0b00 reg1, reg2 - cmp_f64_sse, - cmp_f32_sse, - - /// AVX instructions - /// ops flags: form: - /// 0b00 reg1, qword ptr [reg2 + imm32] - /// 0b01 qword ptr [reg1 + imm32], reg2 - /// 0b10 reg1, reg1, reg2 - mov_f64_avx, - mov_f32_avx, - - /// ops flags: form: - /// 0b00 reg1, reg1, reg2 - add_f64_avx, - add_f32_avx, - - /// ops flags: form: - /// 0b00 reg1, reg1, reg2 - cmp_f64_avx, - cmp_f32_avx, + cmp_f64, + cmp_f32, /// Pseudo-instructions /// call extern function @@ -439,6 +421,8 @@ pub const Inst = struct { inst: Index, /// A 32-bit immediate value. imm: u32, + /// A 32-bit signed displacement value. + disp: i32, /// A condition code for use with EFLAGS register. cc: bits.Condition, /// Another instruction with condition code. @@ -476,9 +460,9 @@ pub const IndexRegisterDisp = struct { index: u32, /// Displacement value - disp: u32, + disp: i32, - pub fn encode(index: Register, disp: u32) IndexRegisterDisp { + pub fn encode(index: Register, disp: i32) IndexRegisterDisp { return .{ .index = @enumToInt(index), .disp = disp, @@ -487,7 +471,7 @@ pub const IndexRegisterDisp = struct { pub fn decode(this: IndexRegisterDisp) struct { index: Register, - disp: u32, + disp: i32, } { return .{ .index = @intToEnum(Register, this.index), @@ -503,12 +487,12 @@ pub const IndexRegisterDispImm = struct { index: u32, /// Displacement value - disp: u32, + disp: i32, /// Immediate imm: u32, - pub fn encode(index: Register, disp: u32, imm: u32) IndexRegisterDispImm { + pub fn encode(index: Register, disp: i32, imm: u32) IndexRegisterDispImm { return .{ .index = @enumToInt(index), .disp = disp, @@ -518,7 +502,7 @@ pub const IndexRegisterDispImm = struct { pub fn decode(this: IndexRegisterDispImm) struct { index: Register, - disp: u32, + disp: i32, imm: u32, } { return .{ @@ -576,7 +560,7 @@ pub const SaveRegisterList = struct { }; pub const ImmPair = struct { - dest_off: u32, + dest_off: i32, operand: u32, }; diff --git a/src/arch/x86_64/bits.zig b/src/arch/x86_64/bits.zig index cc123b96b6..9166550f16 100644 --- a/src/arch/x86_64/bits.zig +++ b/src/arch/x86_64/bits.zig @@ -1,9 +1,9 @@ const std = @import("std"); -const testing = std.testing; -const mem = std.mem; const assert = std.debug.assert; -const ArrayList = std.ArrayList; +const expect = std.testing.expect; + const Allocator = std.mem.Allocator; +const ArrayList = std.ArrayList; const DW = std.dwarf; /// EFLAGS condition codes @@ -135,960 +135,357 @@ pub const Condition = enum(u5) { } }; -/// Definitions of all of the general purpose x64 registers. The order is semantically meaningful. -/// The registers are defined such that IDs go in descending order of 64-bit, -/// 32-bit, 16-bit, and then 8-bit, and each set contains exactly sixteen -/// registers. This results in some useful properties: -/// -/// Any 64-bit register can be turned into its 32-bit form by adding 16, and -/// vice versa. This also works between 32-bit and 16-bit forms. With 8-bit, it -/// works for all except for sp, bp, si, and di, which do *not* have an 8-bit -/// form. -/// -/// If (register & 8) is set, the register is extended. -/// -/// The ID can be easily determined by figuring out what range the register is -/// in, and then subtracting the base. pub const Register = enum(u7) { // zig fmt: off - // 0 through 15, 64-bit registers. 8-15 are extended. - // id is just the int value. rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi, r8, r9, r10, r11, r12, r13, r14, r15, - // 16 through 31, 32-bit registers. 24-31 are extended. - // id is int value - 16. eax, ecx, edx, ebx, esp, ebp, esi, edi, r8d, r9d, r10d, r11d, r12d, r13d, r14d, r15d, - // 32-47, 16-bit registers. 40-47 are extended. - // id is int value - 32. ax, cx, dx, bx, sp, bp, si, di, r8w, r9w, r10w, r11w, r12w, r13w, r14w, r15w, - // 48-63, 8-bit registers. 56-63 are extended. - // id is int value - 48. - al, cl, dl, bl, ah, ch, dh, bh, + al, cl, dl, bl, spl, bpl, sil, dil, r8b, r9b, r10b, r11b, r12b, r13b, r14b, r15b, - // 64-79, 256-bit registers. - // id is int value - 64. + ah, ch, dh, bh, + ymm0, ymm1, ymm2, ymm3, ymm4, ymm5, ymm6, ymm7, ymm8, ymm9, ymm10, ymm11, ymm12, ymm13, ymm14, ymm15, - // 80-95, 128-bit registers. - // id is int value - 80. xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15, - // Pseudo-value for MIR instructions. + es, cs, ss, ds, fs, gs, + none, // zig fmt: on - pub fn id(self: Register) u7 { - return switch (@enumToInt(self)) { - 0...63 => @as(u7, @truncate(u4, @enumToInt(self))), - 64...79 => @enumToInt(self), + pub const Class = enum(u2) { + general_purpose, + floating_point, + segment, + }; + + pub fn class(reg: Register) Class { + return switch (@enumToInt(reg)) { + // zig fmt: off + @enumToInt(Register.rax) ... @enumToInt(Register.r15) => .general_purpose, + @enumToInt(Register.eax) ... @enumToInt(Register.r15d) => .general_purpose, + @enumToInt(Register.ax) ... @enumToInt(Register.r15w) => .general_purpose, + @enumToInt(Register.al) ... @enumToInt(Register.r15b) => .general_purpose, + @enumToInt(Register.ah) ... @enumToInt(Register.bh) => .general_purpose, + + @enumToInt(Register.ymm0) ... @enumToInt(Register.ymm15) => .floating_point, + @enumToInt(Register.xmm0) ... @enumToInt(Register.xmm15) => .floating_point, + + @enumToInt(Register.es) ... @enumToInt(Register.gs) => .segment, + + else => unreachable, + // zig fmt: on + }; + } + + pub fn id(reg: Register) u6 { + const base = switch (@enumToInt(reg)) { + // zig fmt: off + @enumToInt(Register.rax) ... @enumToInt(Register.r15) => @enumToInt(Register.rax), + @enumToInt(Register.eax) ... @enumToInt(Register.r15d) => @enumToInt(Register.eax), + @enumToInt(Register.ax) ... @enumToInt(Register.r15w) => @enumToInt(Register.ax), + @enumToInt(Register.al) ... @enumToInt(Register.r15b) => @enumToInt(Register.al), + @enumToInt(Register.ah) ... @enumToInt(Register.bh) => @enumToInt(Register.ah) - 4, + + @enumToInt(Register.ymm0) ... @enumToInt(Register.ymm15) => @enumToInt(Register.ymm0) - 16, + @enumToInt(Register.xmm0) ... @enumToInt(Register.xmm15) => @enumToInt(Register.xmm0) - 16, + + @enumToInt(Register.es) ... @enumToInt(Register.gs) => @enumToInt(Register.es) - 32, + + else => unreachable, + // zig fmt: on + }; + return @intCast(u6, @enumToInt(reg) - base); + } + + pub fn size(reg: Register) u32 { + return switch (@enumToInt(reg)) { + // zig fmt: off + @enumToInt(Register.rax) ... @enumToInt(Register.r15) => 64, + @enumToInt(Register.eax) ... @enumToInt(Register.r15d) => 32, + @enumToInt(Register.ax) ... @enumToInt(Register.r15w) => 16, + @enumToInt(Register.al) ... @enumToInt(Register.r15b) => 8, + @enumToInt(Register.ah) ... @enumToInt(Register.bh) => 8, + + @enumToInt(Register.ymm0) ... @enumToInt(Register.ymm15) => 256, + @enumToInt(Register.xmm0) ... @enumToInt(Register.xmm15) => 128, + + @enumToInt(Register.es) ... @enumToInt(Register.gs) => 16, + + else => unreachable, + // zig fmt: on + }; + } + + pub fn isExtended(reg: Register) bool { + return switch (@enumToInt(reg)) { + // zig fmt: off + @enumToInt(Register.r8) ... @enumToInt(Register.r15) => true, + @enumToInt(Register.r8d) ... @enumToInt(Register.r15d) => true, + @enumToInt(Register.r8w) ... @enumToInt(Register.r15w) => true, + @enumToInt(Register.r8b) ... @enumToInt(Register.r15b) => true, + + @enumToInt(Register.ymm8) ... @enumToInt(Register.ymm15) => true, + @enumToInt(Register.xmm8) ... @enumToInt(Register.xmm15) => true, + + else => false, + // zig fmt: on + }; + } + + pub fn isRexInvalid(reg: Register) bool { + return switch (@enumToInt(reg)) { + @enumToInt(Register.ah)...@enumToInt(Register.bh) => true, + else => false, + }; + } + + pub fn enc(reg: Register) u4 { + const base = switch (@enumToInt(reg)) { + // zig fmt: off + @enumToInt(Register.rax) ... @enumToInt(Register.r15) => @enumToInt(Register.rax), + @enumToInt(Register.eax) ... @enumToInt(Register.r15d) => @enumToInt(Register.eax), + @enumToInt(Register.ax) ... @enumToInt(Register.r15w) => @enumToInt(Register.ax), + @enumToInt(Register.al) ... @enumToInt(Register.r15b) => @enumToInt(Register.al), + @enumToInt(Register.ah) ... @enumToInt(Register.bh) => @enumToInt(Register.ah) - 4, + + @enumToInt(Register.ymm0) ... @enumToInt(Register.ymm15) => @enumToInt(Register.ymm0), + @enumToInt(Register.xmm0) ... @enumToInt(Register.xmm15) => @enumToInt(Register.xmm0), + + @enumToInt(Register.es) ... @enumToInt(Register.gs) => @enumToInt(Register.es), + + else => unreachable, + // zig fmt: on + }; + return @truncate(u4, @enumToInt(reg) - base); + } + + pub fn lowEnc(reg: Register) u3 { + return @truncate(u3, reg.enc()); + } + + pub fn toSize(reg: Register, bit_size: u32) Register { + return switch (bit_size) { + 8 => reg.to8(), + 16 => reg.to16(), + 32 => reg.to32(), + 64 => reg.to64(), + 128 => reg.to128(), + 256 => reg.to256(), else => unreachable, }; } - /// Returns the bit-width of the register. - pub fn size(self: Register) u9 { - return switch (@enumToInt(self)) { - 0...15 => 64, - 16...31 => 32, - 32...47 => 16, - 48...63 => 8, - 64...79 => 256, - 80...95 => 128, + fn gpBase(reg: Register) u7 { + assert(reg.class() == .general_purpose); + return switch (@enumToInt(reg)) { + // zig fmt: off + @enumToInt(Register.rax) ... @enumToInt(Register.r15) => @enumToInt(Register.rax), + @enumToInt(Register.eax) ... @enumToInt(Register.r15d) => @enumToInt(Register.eax), + @enumToInt(Register.ax) ... @enumToInt(Register.r15w) => @enumToInt(Register.ax), + @enumToInt(Register.al) ... @enumToInt(Register.r15b) => @enumToInt(Register.al), + @enumToInt(Register.ah) ... @enumToInt(Register.bh) => @enumToInt(Register.ah) - 4, + else => unreachable, + // zig fmt: on + }; + } + + pub fn to64(reg: Register) Register { + return @intToEnum(Register, @enumToInt(reg) - reg.gpBase() + @enumToInt(Register.rax)); + } + + pub fn to32(reg: Register) Register { + return @intToEnum(Register, @enumToInt(reg) - reg.gpBase() + @enumToInt(Register.eax)); + } + + pub fn to16(reg: Register) Register { + return @intToEnum(Register, @enumToInt(reg) - reg.gpBase() + @enumToInt(Register.ax)); + } + + pub fn to8(reg: Register) Register { + return @intToEnum(Register, @enumToInt(reg) - reg.gpBase() + @enumToInt(Register.al)); + } + + fn fpBase(reg: Register) u7 { + assert(reg.class() == .floating_point); + return switch (@enumToInt(reg)) { + @enumToInt(Register.ymm0)...@enumToInt(Register.ymm15) => @enumToInt(Register.ymm0), + @enumToInt(Register.xmm0)...@enumToInt(Register.xmm15) => @enumToInt(Register.xmm0), else => unreachable, }; } - /// Returns whether the register is *extended*. Extended registers are the - /// new registers added with amd64, r8 through r15. This also includes any - /// other variant of access to those registers, such as r8b, r15d, and so - /// on. This is needed because access to these registers requires special - /// handling via the REX prefix, via the B or R bits, depending on context. - pub fn isExtended(self: Register) bool { - return @enumToInt(self) & 0x08 != 0; + pub fn to256(reg: Register) Register { + return @intToEnum(Register, @enumToInt(reg) - reg.fpBase() + @enumToInt(Register.ymm0)); } - /// This returns the 4-bit register ID, which is used in practically every - /// opcode. Note that bit 3 (the highest bit) is *never* used directly in - /// an instruction (@see isExtended), and requires special handling. The - /// lower three bits are often embedded directly in instructions (such as - /// the B8 variant of moves), or used in R/M bytes. - pub fn enc(self: Register) u4 { - return @truncate(u4, @enumToInt(self)); + pub fn to128(reg: Register) Register { + return @intToEnum(Register, @enumToInt(reg) - reg.fpBase() + @enumToInt(Register.xmm0)); } - /// Like enc, but only returns the lower 3 bits. - pub fn lowEnc(self: Register) u3 { - return @truncate(u3, @enumToInt(self)); - } - - pub fn to256(self: Register) Register { - return @intToEnum(Register, @as(u8, self.enc()) + 64); - } - - pub fn to128(self: Register) Register { - return @intToEnum(Register, @as(u8, self.enc()) + 80); - } - - /// Convert from any register to its 64 bit alias. - pub fn to64(self: Register) Register { - return @intToEnum(Register, self.enc()); - } - - /// Convert from any register to its 32 bit alias. - pub fn to32(self: Register) Register { - return @intToEnum(Register, @as(u8, self.enc()) + 16); - } - - /// Convert from any register to its 16 bit alias. - pub fn to16(self: Register) Register { - return @intToEnum(Register, @as(u8, self.enc()) + 32); - } - - /// Convert from any register to its 8 bit alias. - pub fn to8(self: Register) Register { - return @intToEnum(Register, @as(u8, self.enc()) + 48); - } - - pub fn dwarfLocOp(self: Register) u8 { - switch (@enumToInt(self)) { - 0...63 => return switch (self.to64()) { - .rax => DW.OP.reg0, - .rdx => DW.OP.reg1, - .rcx => DW.OP.reg2, - .rbx => DW.OP.reg3, - .rsi => DW.OP.reg4, - .rdi => DW.OP.reg5, - .rbp => DW.OP.reg6, - .rsp => DW.OP.reg7, - - .r8 => DW.OP.reg8, - .r9 => DW.OP.reg9, - .r10 => DW.OP.reg10, - .r11 => DW.OP.reg11, - .r12 => DW.OP.reg12, - .r13 => DW.OP.reg13, - .r14 => DW.OP.reg14, - .r15 => DW.OP.reg15, - - else => unreachable, - }, - - 64...79 => return @as(u8, self.enc()) + DW.OP.reg17, - + pub fn dwarfLocOp(reg: Register) u8 { + return switch (reg.class()) { + .general_purpose => @intCast(u8, @enumToInt(reg) - reg.gpBase()) + DW.OP.reg0, + .floating_point => @intCast(u8, @enumToInt(reg) - reg.fpBase()) + DW.OP.reg17, else => unreachable, - } + }; } /// DWARF encodings that push a value onto the DWARF stack that is either /// the contents of a register or the result of adding the contents a given /// register to a given signed offset. - pub fn dwarfLocOpDeref(self: Register) u8 { - switch (@enumToInt(self)) { - 0...63 => return switch (self.to64()) { - .rax => DW.OP.breg0, - .rdx => DW.OP.breg1, - .rcx => DW.OP.breg2, - .rbx => DW.OP.breg3, - .rsi => DW.OP.breg4, - .rdi => DW.OP.breg5, - .rbp => DW.OP.breg6, - .rsp => DW.OP.fbreg, - - .r8 => DW.OP.breg8, - .r9 => DW.OP.breg9, - .r10 => DW.OP.breg10, - .r11 => DW.OP.breg11, - .r12 => DW.OP.breg12, - .r13 => DW.OP.breg13, - .r14 => DW.OP.breg14, - .r15 => DW.OP.breg15, - - else => unreachable, - }, - - 64...79 => return @as(u8, self.enc()) + DW.OP.breg17, - + pub fn dwarfLocOpDeref(reg: Register) u8 { + return switch (reg.class()) { + .general_purpose => @intCast(u8, @enumToInt(reg) - reg.gpBase()) + DW.OP.breg0, + .floating_point => @intCast(u8, @enumToInt(reg) - reg.fpBase()) + DW.OP.breg17, else => unreachable, - } + }; } }; -// zig fmt: on +test "Register id - different classes" { + try expect(Register.al.id() == Register.ax.id()); + try expect(Register.ah.id() == Register.spl.id()); + try expect(Register.ax.id() == Register.eax.id()); + try expect(Register.eax.id() == Register.rax.id()); -/// Encoding helper functions for x86_64 instructions -/// -/// Many of these helpers do very little, but they can help make things -/// slightly more readable with more descriptive field names / function names. -/// -/// Some of them also have asserts to ensure that we aren't doing dumb things. -/// For example, trying to use register 4 (esp) in an indirect modr/m byte is illegal, -/// you need to encode it with an SIB byte. -/// -/// Note that ALL of these helper functions will assume capacity, -/// so ensure that the `code` has sufficient capacity before using them. -/// The `init` method is the recommended way to ensure capacity. -pub const Encoder = struct { - /// Non-owning reference to the code array - code: *ArrayList(u8), + try expect(Register.ymm0.id() == 0b10000); + try expect(Register.ymm0.id() != Register.rax.id()); + try expect(Register.xmm0.id() == Register.ymm0.id()); - const Self = @This(); + try expect(Register.es.id() == 0b100000); +} - /// Wrap `code` in Encoder to make it easier to call these helper functions - /// - /// maximum_inst_size should contain the maximum number of bytes - /// that the encoded instruction will take. - /// This is because the helper functions will assume capacity - /// in order to avoid bounds checking. - pub fn init(code: *ArrayList(u8), maximum_inst_size: u8) !Self { - try code.ensureUnusedCapacity(maximum_inst_size); - return Self{ .code = code }; - } +test "Register enc - different classes" { + try expect(Register.al.enc() == Register.ax.enc()); + try expect(Register.ax.enc() == Register.eax.enc()); + try expect(Register.eax.enc() == Register.rax.enc()); + try expect(Register.ymm0.enc() == Register.rax.enc()); + try expect(Register.xmm0.enc() == Register.ymm0.enc()); + try expect(Register.es.enc() == Register.rax.enc()); +} - /// Directly write a number to the code array with big endianness - pub fn writeIntBig(self: Self, comptime T: type, value: T) void { - mem.writeIntBig( - T, - self.code.addManyAsArrayAssumeCapacity(@divExact(@typeInfo(T).Int.bits, 8)), - value, - ); - } +test "Register classes" { + try expect(Register.r11.class() == .general_purpose); + try expect(Register.ymm11.class() == .floating_point); + try expect(Register.fs.class() == .segment); +} - /// Directly write a number to the code array with little endianness - pub fn writeIntLittle(self: Self, comptime T: type, value: T) void { - mem.writeIntLittle( - T, - self.code.addManyAsArrayAssumeCapacity(@divExact(@typeInfo(T).Int.bits, 8)), - value, - ); - } +pub const Memory = union(enum) { + sib: Sib, + rip: Rip, + moffs: Moffs, - // -------- - // Prefixes - // -------- - - pub const LegacyPrefixes = packed struct { - /// LOCK - prefix_f0: bool = false, - /// REPNZ, REPNE, REP, Scalar Double-precision - prefix_f2: bool = false, - /// REPZ, REPE, REP, Scalar Single-precision - prefix_f3: bool = false, - - /// CS segment override or Branch not taken - prefix_2e: bool = false, - /// DS segment override - prefix_36: bool = false, - /// ES segment override - prefix_26: bool = false, - /// FS segment override - prefix_64: bool = false, - /// GS segment override - prefix_65: bool = false, - - /// Branch taken - prefix_3e: bool = false, - - /// Operand size override (enables 16 bit operation) - prefix_66: bool = false, - - /// Address size override (enables 16 bit address size) - prefix_67: bool = false, - - padding: u5 = 0, + pub const ScaleIndex = packed struct { + scale: u4, + index: Register, }; - /// Encodes legacy prefixes - pub fn legacyPrefixes(self: Self, prefixes: LegacyPrefixes) void { - if (@bitCast(u16, prefixes) != 0) { - // Hopefully this path isn't taken very often, so we'll do it the slow way for now + pub const PtrSize = enum { + byte, + word, + dword, + qword, + tbyte, - // LOCK - if (prefixes.prefix_f0) self.code.appendAssumeCapacity(0xf0); - // REPNZ, REPNE, REP, Scalar Double-precision - if (prefixes.prefix_f2) self.code.appendAssumeCapacity(0xf2); - // REPZ, REPE, REP, Scalar Single-precision - if (prefixes.prefix_f3) self.code.appendAssumeCapacity(0xf3); - - // CS segment override or Branch not taken - if (prefixes.prefix_2e) self.code.appendAssumeCapacity(0x2e); - // DS segment override - if (prefixes.prefix_36) self.code.appendAssumeCapacity(0x36); - // ES segment override - if (prefixes.prefix_26) self.code.appendAssumeCapacity(0x26); - // FS segment override - if (prefixes.prefix_64) self.code.appendAssumeCapacity(0x64); - // GS segment override - if (prefixes.prefix_65) self.code.appendAssumeCapacity(0x65); - - // Branch taken - if (prefixes.prefix_3e) self.code.appendAssumeCapacity(0x3e); - - // Operand size override - if (prefixes.prefix_66) self.code.appendAssumeCapacity(0x66); - - // Address size override - if (prefixes.prefix_67) self.code.appendAssumeCapacity(0x67); - } - } - - /// Use 16 bit operand size - /// - /// Note that this flag is overridden by REX.W, if both are present. - pub fn prefix16BitMode(self: Self) void { - self.code.appendAssumeCapacity(0x66); - } - - pub const Vex = struct { - rex_prefix: Rex = .{}, - lead_opc: u5 = 0b0_0001, - register: u4 = 0b1111, - length: u1 = 0b0, - simd_prefix: u2 = 0b00, - wig_desc: bool = false, - lig_desc: bool = false, - lz_desc: bool = false, - - pub fn rex(self: *Vex, r: Rex) void { - self.rex_prefix = r; - } - - pub fn lead_opc_0f(self: *Vex) void { - self.lead_opc = 0b0_0001; - } - - pub fn lead_opc_0f_38(self: *Vex) void { - self.lead_opc = 0b0_0010; - } - - pub fn lead_opc_0f_3a(self: *Vex) void { - self.lead_opc = 0b0_0011; - } - - pub fn reg(self: *Vex, register: u4) void { - self.register = ~register; - } - - pub fn len_128(self: *Vex) void { - self.length = 0; - } - - pub fn len_256(self: *Vex) void { - assert(!self.lz_desc); - self.length = 1; - } - - pub fn simd_prefix_66(self: *Vex) void { - self.simd_prefix = 0b01; - } - - pub fn simd_prefix_f3(self: *Vex) void { - self.simd_prefix = 0b10; - } - - pub fn simd_prefix_f2(self: *Vex) void { - self.simd_prefix = 0b11; - } - - pub fn wig(self: *Vex) void { - self.wig_desc = true; - } - - pub fn lig(self: *Vex) void { - self.lig_desc = true; - } - - pub fn lz(self: *Vex) void { - self.lz_desc = true; - } - - pub fn write(self: Vex, writer: anytype) usize { - var buf: [3]u8 = .{0} ** 3; - const form_3byte: bool = blk: { - if (self.rex_prefix.w and !self.wig_desc) break :blk true; - if (self.rex_prefix.x or self.rex_prefix.b) break :blk true; - break :blk self.lead_opc != 0b0_0001; + pub fn fromSize(bit_size: u32) PtrSize { + return switch (bit_size) { + 8 => .byte, + 16 => .word, + 32 => .dword, + 64 => .qword, + 80 => .tbyte, + else => unreachable, }; + } - if (self.lz_desc) { - assert(self.length == 0); - } - - if (form_3byte) { - // First byte - buf[0] = 0xc4; - // Second byte - const rxb_mask: u3 = @intCast(u3, @boolToInt(!self.rex_prefix.r)) << 2 | - @intCast(u2, @boolToInt(!self.rex_prefix.x)) << 1 | - @boolToInt(!self.rex_prefix.b); - buf[1] |= @intCast(u8, rxb_mask) << 5; - buf[1] |= self.lead_opc; - // Third byte - buf[2] |= @intCast(u8, @boolToInt(!self.rex_prefix.w)) << 7; - buf[2] |= @intCast(u7, self.register) << 3; - buf[2] |= @intCast(u3, self.length) << 2; - buf[2] |= self.simd_prefix; - } else { - // First byte - buf[0] = 0xc5; - // Second byte - buf[1] |= @intCast(u8, @boolToInt(!self.rex_prefix.r)) << 7; - buf[1] |= @intCast(u7, self.register) << 3; - buf[1] |= @intCast(u3, self.length) << 2; - buf[1] |= self.simd_prefix; - } - - const count: usize = if (form_3byte) 3 else 2; - _ = writer.writeAll(buf[0..count]) catch unreachable; - return count; + pub fn size(s: PtrSize) u32 { + return switch (s) { + .byte => 8, + .word => 16, + .dword => 32, + .qword => 64, + .tbyte => 80, + }; } }; - pub fn vex(self: Self, prefix: Vex) void { - _ = prefix.write(self.code.writer()); - } - - /// From section 2.2.1.2 of the manual, REX is encoded as b0100WRXB - pub const Rex = struct { - /// Wide, enables 64-bit operation - w: bool = false, - /// Extends the reg field in the ModR/M byte - r: bool = false, - /// Extends the index field in the SIB byte - x: bool = false, - /// Extends the r/m field in the ModR/M byte, - /// or the base field in the SIB byte, - /// or the reg field in the Opcode byte - b: bool = false, + pub const Sib = struct { + ptr_size: PtrSize, + base: ?Register, + scale_index: ?ScaleIndex, + disp: i32, }; - /// Encodes a REX prefix byte given all the fields - /// - /// Use this byte whenever you need 64 bit operation, - /// or one of reg, index, r/m, base, or opcode-reg might be extended. - /// - /// See struct `Rex` for a description of each field. - /// - /// Does not add a prefix byte if none of the fields are set! - pub fn rex(self: Self, byte: Rex) void { - var value: u8 = 0b0100_0000; + pub const Rip = struct { + ptr_size: PtrSize, + disp: i32, + }; - if (byte.w) value |= 0b1000; - if (byte.r) value |= 0b0100; - if (byte.x) value |= 0b0010; - if (byte.b) value |= 0b0001; + pub const Moffs = struct { + seg: Register, + offset: u64, + }; - if (value != 0b0100_0000) { - self.code.appendAssumeCapacity(value); - } + pub fn moffs(reg: Register, offset: u64) Memory { + assert(reg.class() == .segment); + return .{ .moffs = .{ .seg = reg, .offset = offset } }; } - // ------ - // Opcode - // ------ - - /// Encodes a 1 byte opcode - pub fn opcode_1byte(self: Self, opcode: u8) void { - self.code.appendAssumeCapacity(opcode); + pub fn sib(ptr_size: PtrSize, args: struct { + disp: i32, + base: ?Register = null, + scale_index: ?ScaleIndex = null, + }) Memory { + return .{ .sib = .{ + .base = args.base, + .disp = args.disp, + .ptr_size = ptr_size, + .scale_index = args.scale_index, + } }; } - /// Encodes a 2 byte opcode - /// - /// e.g. IMUL has the opcode 0x0f 0xaf, so you use - /// - /// encoder.opcode_2byte(0x0f, 0xaf); - pub fn opcode_2byte(self: Self, prefix: u8, opcode: u8) void { - self.code.appendAssumeCapacity(prefix); - self.code.appendAssumeCapacity(opcode); + pub fn rip(ptr_size: PtrSize, disp: i32) Memory { + return .{ .rip = .{ .ptr_size = ptr_size, .disp = disp } }; } - /// Encodes a 3 byte opcode - /// - /// e.g. MOVSD has the opcode 0xf2 0x0f 0x10 - /// - /// encoder.opcode_3byte(0xf2, 0x0f, 0x10); - pub fn opcode_3byte(self: Self, prefix_1: u8, prefix_2: u8, opcode: u8) void { - self.code.appendAssumeCapacity(prefix_1); - self.code.appendAssumeCapacity(prefix_2); - self.code.appendAssumeCapacity(opcode); + pub fn isSegmentRegister(mem: Memory) bool { + return switch (mem) { + .moffs => true, + .rip => false, + .sib => |s| if (s.base) |r| r.class() == .segment else false, + }; } - /// Encodes a 1 byte opcode with a reg field - /// - /// Remember to add a REX prefix byte if reg is extended! - pub fn opcode_withReg(self: Self, opcode: u8, reg: u3) void { - assert(opcode & 0b111 == 0); - self.code.appendAssumeCapacity(opcode | reg); + pub fn base(mem: Memory) ?Register { + return switch (mem) { + .moffs => |m| m.seg, + .sib => |s| s.base, + .rip => null, + }; } - // ------ - // ModR/M - // ------ - - /// Construct a ModR/M byte given all the fields - /// - /// Remember to add a REX prefix byte if reg or rm are extended! - pub fn modRm(self: Self, mod: u2, reg_or_opx: u3, rm: u3) void { - self.code.appendAssumeCapacity( - @as(u8, mod) << 6 | @as(u8, reg_or_opx) << 3 | rm, - ); + pub fn scaleIndex(mem: Memory) ?ScaleIndex { + return switch (mem) { + .moffs, .rip => null, + .sib => |s| s.scale_index, + }; } - /// Construct a ModR/M byte using direct r/m addressing - /// r/m effective address: r/m - /// - /// Note reg's effective address is always just reg for the ModR/M byte. - /// Remember to add a REX prefix byte if reg or rm are extended! - pub fn modRm_direct(self: Self, reg_or_opx: u3, rm: u3) void { - self.modRm(0b11, reg_or_opx, rm); - } - - /// Construct a ModR/M byte using indirect r/m addressing - /// r/m effective address: [r/m] - /// - /// Note reg's effective address is always just reg for the ModR/M byte. - /// Remember to add a REX prefix byte if reg or rm are extended! - pub fn modRm_indirectDisp0(self: Self, reg_or_opx: u3, rm: u3) void { - assert(rm != 4 and rm != 5); - self.modRm(0b00, reg_or_opx, rm); - } - - /// Construct a ModR/M byte using indirect SIB addressing - /// r/m effective address: [SIB] - /// - /// Note reg's effective address is always just reg for the ModR/M byte. - /// Remember to add a REX prefix byte if reg or rm are extended! - pub fn modRm_SIBDisp0(self: Self, reg_or_opx: u3) void { - self.modRm(0b00, reg_or_opx, 0b100); - } - - /// Construct a ModR/M byte using RIP-relative addressing - /// r/m effective address: [RIP + disp32] - /// - /// Note reg's effective address is always just reg for the ModR/M byte. - /// Remember to add a REX prefix byte if reg or rm are extended! - pub fn modRm_RIPDisp32(self: Self, reg_or_opx: u3) void { - self.modRm(0b00, reg_or_opx, 0b101); - } - - /// Construct a ModR/M byte using indirect r/m with a 8bit displacement - /// r/m effective address: [r/m + disp8] - /// - /// Note reg's effective address is always just reg for the ModR/M byte. - /// Remember to add a REX prefix byte if reg or rm are extended! - pub fn modRm_indirectDisp8(self: Self, reg_or_opx: u3, rm: u3) void { - assert(rm != 4); - self.modRm(0b01, reg_or_opx, rm); - } - - /// Construct a ModR/M byte using indirect SIB with a 8bit displacement - /// r/m effective address: [SIB + disp8] - /// - /// Note reg's effective address is always just reg for the ModR/M byte. - /// Remember to add a REX prefix byte if reg or rm are extended! - pub fn modRm_SIBDisp8(self: Self, reg_or_opx: u3) void { - self.modRm(0b01, reg_or_opx, 0b100); - } - - /// Construct a ModR/M byte using indirect r/m with a 32bit displacement - /// r/m effective address: [r/m + disp32] - /// - /// Note reg's effective address is always just reg for the ModR/M byte. - /// Remember to add a REX prefix byte if reg or rm are extended! - pub fn modRm_indirectDisp32(self: Self, reg_or_opx: u3, rm: u3) void { - assert(rm != 4); - self.modRm(0b10, reg_or_opx, rm); - } - - /// Construct a ModR/M byte using indirect SIB with a 32bit displacement - /// r/m effective address: [SIB + disp32] - /// - /// Note reg's effective address is always just reg for the ModR/M byte. - /// Remember to add a REX prefix byte if reg or rm are extended! - pub fn modRm_SIBDisp32(self: Self, reg_or_opx: u3) void { - self.modRm(0b10, reg_or_opx, 0b100); - } - - // --- - // SIB - // --- - - /// Construct a SIB byte given all the fields - /// - /// Remember to add a REX prefix byte if index or base are extended! - pub fn sib(self: Self, scale: u2, index: u3, base: u3) void { - self.code.appendAssumeCapacity( - @as(u8, scale) << 6 | @as(u8, index) << 3 | base, - ); - } - - /// Construct a SIB byte with scale * index + base, no frills. - /// r/m effective address: [base + scale * index] - /// - /// Remember to add a REX prefix byte if index or base are extended! - pub fn sib_scaleIndexBase(self: Self, scale: u2, index: u3, base: u3) void { - assert(base != 5); - - self.sib(scale, index, base); - } - - /// Construct a SIB byte with scale * index + disp32 - /// r/m effective address: [scale * index + disp32] - /// - /// Remember to add a REX prefix byte if index or base are extended! - pub fn sib_scaleIndexDisp32(self: Self, scale: u2, index: u3) void { - assert(index != 4); - - // scale is actually ignored - // index = 4 means no index - // base = 5 means no base, if mod == 0. - self.sib(scale, index, 5); - } - - /// Construct a SIB byte with just base - /// r/m effective address: [base] - /// - /// Remember to add a REX prefix byte if index or base are extended! - pub fn sib_base(self: Self, base: u3) void { - assert(base != 5); - - // scale is actually ignored - // index = 4 means no index - self.sib(0, 4, base); - } - - /// Construct a SIB byte with just disp32 - /// r/m effective address: [disp32] - /// - /// Remember to add a REX prefix byte if index or base are extended! - pub fn sib_disp32(self: Self) void { - // scale is actually ignored - // index = 4 means no index - // base = 5 means no base, if mod == 0. - self.sib(0, 4, 5); - } - - /// Construct a SIB byte with scale * index + base + disp8 - /// r/m effective address: [base + scale * index + disp8] - /// - /// Remember to add a REX prefix byte if index or base are extended! - pub fn sib_scaleIndexBaseDisp8(self: Self, scale: u2, index: u3, base: u3) void { - self.sib(scale, index, base); - } - - /// Construct a SIB byte with base + disp8, no index - /// r/m effective address: [base + disp8] - /// - /// Remember to add a REX prefix byte if index or base are extended! - pub fn sib_baseDisp8(self: Self, base: u3) void { - // scale is ignored - // index = 4 means no index - self.sib(0, 4, base); - } - - /// Construct a SIB byte with scale * index + base + disp32 - /// r/m effective address: [base + scale * index + disp32] - /// - /// Remember to add a REX prefix byte if index or base are extended! - pub fn sib_scaleIndexBaseDisp32(self: Self, scale: u2, index: u3, base: u3) void { - self.sib(scale, index, base); - } - - /// Construct a SIB byte with base + disp32, no index - /// r/m effective address: [base + disp32] - /// - /// Remember to add a REX prefix byte if index or base are extended! - pub fn sib_baseDisp32(self: Self, base: u3) void { - // scale is ignored - // index = 4 means no index - self.sib(0, 4, base); - } - - // ------------------------- - // Trivial (no bit fiddling) - // ------------------------- - - /// Encode an 8 bit immediate - /// - /// It is sign-extended to 64 bits by the cpu. - pub fn imm8(self: Self, imm: i8) void { - self.code.appendAssumeCapacity(@bitCast(u8, imm)); - } - - /// Encode an 8 bit displacement - /// - /// It is sign-extended to 64 bits by the cpu. - pub fn disp8(self: Self, disp: i8) void { - self.code.appendAssumeCapacity(@bitCast(u8, disp)); - } - - /// Encode an 16 bit immediate - /// - /// It is sign-extended to 64 bits by the cpu. - pub fn imm16(self: Self, imm: i16) void { - self.writeIntLittle(i16, imm); - } - - /// Encode an 32 bit immediate - /// - /// It is sign-extended to 64 bits by the cpu. - pub fn imm32(self: Self, imm: i32) void { - self.writeIntLittle(i32, imm); - } - - /// Encode an 32 bit displacement - /// - /// It is sign-extended to 64 bits by the cpu. - pub fn disp32(self: Self, disp: i32) void { - self.writeIntLittle(i32, disp); - } - - /// Encode an 64 bit immediate - /// - /// It is sign-extended to 64 bits by the cpu. - pub fn imm64(self: Self, imm: u64) void { - self.writeIntLittle(u64, imm); + pub fn size(mem: Memory) u32 { + return switch (mem) { + .rip => |r| r.ptr_size.size(), + .sib => |s| s.ptr_size.size(), + .moffs => unreachable, + }; } }; - -test "Encoder helpers - general purpose registers" { - var code = ArrayList(u8).init(testing.allocator); - defer code.deinit(); - - // simple integer multiplication - - // imul eax,edi - // 0faf c7 - { - try code.resize(0); - const encoder = try Encoder.init(&code, 4); - encoder.rex(.{ - .r = Register.eax.isExtended(), - .b = Register.edi.isExtended(), - }); - encoder.opcode_2byte(0x0f, 0xaf); - encoder.modRm_direct( - Register.eax.lowEnc(), - Register.edi.lowEnc(), - ); - - try testing.expectEqualSlices(u8, &[_]u8{ 0x0f, 0xaf, 0xc7 }, code.items); - } - - // simple mov - - // mov eax,edi - // 89 f8 - { - try code.resize(0); - const encoder = try Encoder.init(&code, 3); - encoder.rex(.{ - .r = Register.edi.isExtended(), - .b = Register.eax.isExtended(), - }); - encoder.opcode_1byte(0x89); - encoder.modRm_direct( - Register.edi.lowEnc(), - Register.eax.lowEnc(), - ); - - try testing.expectEqualSlices(u8, &[_]u8{ 0x89, 0xf8 }, code.items); - } - - // signed integer addition of 32-bit sign extended immediate to 64 bit register - - // add rcx, 2147483647 - // - // Using the following opcode: REX.W + 81 /0 id, we expect the following encoding - // - // 48 : REX.W set for 64 bit operand (*r*cx) - // 81 : opcode for " with immediate" - // c1 : id = rcx, - // : c1 = 11 <-- mod = 11 indicates r/m is register (rcx) - // : 000 <-- opcode_extension = 0 because opcode extension is /0. /0 specifies ADD - // : 001 <-- 001 is rcx - // ffffff7f : 2147483647 - { - try code.resize(0); - const encoder = try Encoder.init(&code, 7); - encoder.rex(.{ .w = true }); // use 64 bit operation - encoder.opcode_1byte(0x81); - encoder.modRm_direct( - 0, - Register.rcx.lowEnc(), - ); - encoder.imm32(2147483647); - - try testing.expectEqualSlices(u8, &[_]u8{ 0x48, 0x81, 0xc1, 0xff, 0xff, 0xff, 0x7f }, code.items); - } -} - -test "Encoder helpers - Vex prefix" { - var buf: [3]u8 = undefined; - var stream = std.io.fixedBufferStream(&buf); - const writer = stream.writer(); - - { - var vex_prefix = Encoder.Vex{}; - vex_prefix.rex(.{ - .r = true, - }); - const nwritten = vex_prefix.write(writer); - try testing.expectEqualSlices(u8, &[_]u8{ 0xc5, 0x78 }, buf[0..nwritten]); - } - - { - stream.reset(); - var vex_prefix = Encoder.Vex{}; - vex_prefix.reg(Register.xmm15.enc()); - const nwritten = vex_prefix.write(writer); - try testing.expectEqualSlices(u8, &[_]u8{ 0xc5, 0x80 }, buf[0..nwritten]); - } - - { - stream.reset(); - var vex_prefix = Encoder.Vex{}; - vex_prefix.rex(.{ - .w = true, - .x = true, - }); - const nwritten = vex_prefix.write(writer); - try testing.expectEqualSlices(u8, &[_]u8{ 0xc4, 0b101_0_0001, 0b0_1111_0_00 }, buf[0..nwritten]); - } - - { - stream.reset(); - var vex_prefix = Encoder.Vex{}; - vex_prefix.rex(.{ - .w = true, - .r = true, - }); - vex_prefix.len_256(); - vex_prefix.lead_opc_0f(); - vex_prefix.simd_prefix_66(); - const nwritten = vex_prefix.write(writer); - try testing.expectEqualSlices(u8, &[_]u8{ 0xc4, 0b011_0_0001, 0b0_1111_1_01 }, buf[0..nwritten]); - } - - var code = ArrayList(u8).init(testing.allocator); - defer code.deinit(); - - { - // vmovapd xmm1, xmm2 - const encoder = try Encoder.init(&code, 4); - var vex = Encoder.Vex{}; - vex.simd_prefix_66(); - encoder.vex(vex); // use 64 bit operation - encoder.opcode_1byte(0x28); - encoder.modRm_direct(0, Register.xmm1.lowEnc()); - try testing.expectEqualSlices(u8, &[_]u8{ 0xC5, 0xF9, 0x28, 0xC1 }, code.items); - } - - { - try code.resize(0); - - // vmovhpd xmm13, xmm1, qword ptr [rip] - const encoder = try Encoder.init(&code, 9); - var vex = Encoder.Vex{}; - vex.len_128(); - vex.simd_prefix_66(); - vex.lead_opc_0f(); - vex.rex(.{ .r = true }); - vex.reg(Register.xmm1.enc()); - encoder.vex(vex); - encoder.opcode_1byte(0x16); - encoder.modRm_RIPDisp32(Register.xmm13.lowEnc()); - encoder.disp32(0); - try testing.expectEqualSlices(u8, &[_]u8{ 0xC5, 0x71, 0x16, 0x2D, 0x00, 0x00, 0x00, 0x00 }, code.items); - } -} - -// TODO add these registers to the enum and populate dwarfLocOp -// // Return Address register. This is stored in `0(%rsp, "")` and is not a physical register. -// RA = (16, "RA"), -// -// XMM0 = (17, "xmm0"), -// XMM1 = (18, "xmm1"), -// XMM2 = (19, "xmm2"), -// XMM3 = (20, "xmm3"), -// XMM4 = (21, "xmm4"), -// XMM5 = (22, "xmm5"), -// XMM6 = (23, "xmm6"), -// XMM7 = (24, "xmm7"), -// -// XMM8 = (25, "xmm8"), -// XMM9 = (26, "xmm9"), -// XMM10 = (27, "xmm10"), -// XMM11 = (28, "xmm11"), -// XMM12 = (29, "xmm12"), -// XMM13 = (30, "xmm13"), -// XMM14 = (31, "xmm14"), -// XMM15 = (32, "xmm15"), -// -// ST0 = (33, "st0"), -// ST1 = (34, "st1"), -// ST2 = (35, "st2"), -// ST3 = (36, "st3"), -// ST4 = (37, "st4"), -// ST5 = (38, "st5"), -// ST6 = (39, "st6"), -// ST7 = (40, "st7"), -// -// MM0 = (41, "mm0"), -// MM1 = (42, "mm1"), -// MM2 = (43, "mm2"), -// MM3 = (44, "mm3"), -// MM4 = (45, "mm4"), -// MM5 = (46, "mm5"), -// MM6 = (47, "mm6"), -// MM7 = (48, "mm7"), -// -// RFLAGS = (49, "rFLAGS"), -// ES = (50, "es"), -// CS = (51, "cs"), -// SS = (52, "ss"), -// DS = (53, "ds"), -// FS = (54, "fs"), -// GS = (55, "gs"), -// -// FS_BASE = (58, "fs.base"), -// GS_BASE = (59, "gs.base"), -// -// TR = (62, "tr"), -// LDTR = (63, "ldtr"), -// MXCSR = (64, "mxcsr"), -// FCW = (65, "fcw"), -// FSW = (66, "fsw"), -// -// XMM16 = (67, "xmm16"), -// XMM17 = (68, "xmm17"), -// XMM18 = (69, "xmm18"), -// XMM19 = (70, "xmm19"), -// XMM20 = (71, "xmm20"), -// XMM21 = (72, "xmm21"), -// XMM22 = (73, "xmm22"), -// XMM23 = (74, "xmm23"), -// XMM24 = (75, "xmm24"), -// XMM25 = (76, "xmm25"), -// XMM26 = (77, "xmm26"), -// XMM27 = (78, "xmm27"), -// XMM28 = (79, "xmm28"), -// XMM29 = (80, "xmm29"), -// XMM30 = (81, "xmm30"), -// XMM31 = (82, "xmm31"), -// -// K0 = (118, "k0"), -// K1 = (119, "k1"), -// K2 = (120, "k2"), -// K3 = (121, "k3"), -// K4 = (122, "k4"), -// K5 = (123, "k5"), -// K6 = (124, "k6"), -// K7 = (125, "k7"), diff --git a/src/arch/x86_64/encoder.zig b/src/arch/x86_64/encoder.zig new file mode 100644 index 0000000000..3daffc7ad2 --- /dev/null +++ b/src/arch/x86_64/encoder.zig @@ -0,0 +1,794 @@ +const std = @import("std"); +const assert = std.debug.assert; +const math = std.math; + +const bits = @import("bits.zig"); +const Encoding = @import("Encoding.zig"); +const Memory = bits.Memory; +const Moffs = bits.Moffs; +const PtrSize = bits.PtrSize; +const Register = bits.Register; + +pub const Instruction = struct { + op1: Operand = .none, + op2: Operand = .none, + op3: Operand = .none, + op4: Operand = .none, + encoding: Encoding, + + pub const Mnemonic = Encoding.Mnemonic; + + pub const Operand = union(enum) { + none, + reg: Register, + mem: Memory, + imm: i64, + + /// Returns the bitsize of the operand. + /// Asserts the operand is either register or memory. + pub fn size(op: Operand) u64 { + return switch (op) { + .none => unreachable, + .reg => |reg| reg.size(), + .mem => |mem| mem.size(), + .imm => unreachable, + }; + } + + /// Returns true if the operand is a segment register. + /// Asserts the operand is either register or memory. + pub fn isSegmentRegister(op: Operand) bool { + return switch (op) { + .none => unreachable, + .reg => |reg| reg.class() == .segment, + .mem => |mem| mem.isSegmentRegister(), + .imm => unreachable, + }; + } + + pub fn fmtPrint(op: Operand, enc_op: Encoding.Op, writer: anytype) !void { + switch (op) { + .none => {}, + .reg => |reg| try writer.writeAll(@tagName(reg)), + .mem => |mem| switch (mem) { + .rip => |rip| { + try writer.print("{s} ptr [rip", .{@tagName(rip.ptr_size)}); + if (rip.disp != 0) { + const sign_bit = if (sign(rip.disp) < 0) "-" else "+"; + const disp_abs = try std.math.absInt(rip.disp); + try writer.print(" {s} 0x{x}", .{ sign_bit, disp_abs }); + } + try writer.writeByte(']'); + }, + .sib => |sib| { + try writer.print("{s} ptr ", .{@tagName(sib.ptr_size)}); + + if (mem.isSegmentRegister()) { + return writer.print("{s}:0x{x}", .{ @tagName(sib.base.?), sib.disp }); + } + + try writer.writeByte('['); + + if (sib.base) |base| { + try writer.print("{s}", .{@tagName(base)}); + } + if (sib.scale_index) |si| { + if (sib.base != null) { + try writer.writeAll(" + "); + } + try writer.print("{s} * {d}", .{ @tagName(si.index), si.scale }); + } + if (sib.disp != 0) { + if (sib.base != null or sib.scale_index != null) { + try writer.writeByte(' '); + } + try writer.writeByte(if (sign(sib.disp) < 0) '-' else '+'); + const disp_abs = try std.math.absInt(sib.disp); + try writer.print(" 0x{x}", .{disp_abs}); + } + + try writer.writeByte(']'); + }, + .moffs => |moffs| try writer.print("{s}:0x{x}", .{ @tagName(moffs.seg), moffs.offset }), + }, + .imm => |imm| { + if (enc_op == .imm64) { + return writer.print("0x{x}", .{@bitCast(u64, imm)}); + } + const imm_abs = try std.math.absInt(imm); + if (sign(imm) < 0) { + try writer.writeByte('-'); + } + try writer.print("0x{x}", .{imm_abs}); + }, + } + } + }; + + pub fn new(mnemonic: Mnemonic, args: struct { + op1: Operand = .none, + op2: Operand = .none, + op3: Operand = .none, + op4: Operand = .none, + }) !Instruction { + const encoding = Encoding.findByMnemonic(mnemonic, .{ + .op1 = args.op1, + .op2 = args.op2, + .op3 = args.op3, + .op4 = args.op4, + }) orelse return error.InvalidInstruction; + std.log.debug("{}", .{encoding}); + return .{ + .op1 = args.op1, + .op2 = args.op2, + .op3 = args.op3, + .op4 = args.op4, + .encoding = encoding, + }; + } + + pub fn fmtPrint(inst: Instruction, writer: anytype) !void { + try writer.print("{s}", .{@tagName(inst.encoding.mnemonic)}); + const ops = [_]struct { Operand, Encoding.Op }{ + .{ inst.op1, inst.encoding.op1 }, + .{ inst.op2, inst.encoding.op2 }, + .{ inst.op3, inst.encoding.op3 }, + .{ inst.op4, inst.encoding.op4 }, + }; + for (&ops, 0..) |op, i| { + if (op[0] == .none) break; + if (i > 0) { + try writer.writeByte(','); + } + try writer.writeByte(' '); + try op[0].fmtPrint(op[1], writer); + } + } + + pub fn encode(inst: Instruction, writer: anytype) !void { + const encoder = Encoder(@TypeOf(writer)){ .writer = writer }; + const encoding = inst.encoding; + + try inst.encodeLegacyPrefixes(encoder); + try inst.encodeMandatoryPrefix(encoder); + try inst.encodeRexPrefix(encoder); + try inst.encodeOpcode(encoder); + + switch (encoding.op_en) { + .np, .o => {}, + .i, .d => try encodeImm(inst.op1.imm, encoding.op1, encoder), + .zi, .oi => try encodeImm(inst.op2.imm, encoding.op2, encoder), + .fd => try encoder.imm64(inst.op2.mem.moffs.offset), + .td => try encoder.imm64(inst.op1.mem.moffs.offset), + else => { + const mem_op = switch (encoding.op_en) { + .m, .mi, .m1, .mc, .mr => inst.op1, + .rm, .rmi => inst.op2, + else => unreachable, + }; + switch (mem_op) { + .reg => |reg| { + const rm = switch (encoding.op_en) { + .m, .mi, .m1, .mc => encoding.modRmExt(), + .mr => inst.op2.reg.lowEnc(), + .rm, .rmi => inst.op1.reg.lowEnc(), + else => unreachable, + }; + try encoder.modRm_direct(rm, reg.lowEnc()); + }, + .mem => |mem| { + const op = switch (encoding.op_en) { + .m, .mi, .m1, .mc => .none, + .mr => inst.op2, + .rm, .rmi => inst.op1, + else => unreachable, + }; + try encodeMemory(encoding, mem, op, encoder); + }, + else => unreachable, + } + + switch (encoding.op_en) { + .mi => try encodeImm(inst.op2.imm, encoding.op2, encoder), + .rmi => try encodeImm(inst.op3.imm, encoding.op3, encoder), + else => {}, + } + }, + } + } + + fn encodeOpcode(inst: Instruction, encoder: anytype) !void { + const opcode = inst.encoding.opcode(); + switch (inst.encoding.op_en) { + .o, .oi => try encoder.opcode_withReg(opcode[0], inst.op1.reg.lowEnc()), + else => { + const index: usize = if (inst.encoding.mandatoryPrefix()) |_| 1 else 0; + for (opcode[index..]) |byte| { + try encoder.opcode_1byte(byte); + } + }, + } + } + + fn encodeLegacyPrefixes(inst: Instruction, encoder: anytype) !void { + const enc = inst.encoding; + const op_en = enc.op_en; + + var legacy = LegacyPrefixes{}; + if (enc.mode == .none) { + const bit_size = enc.operandSize(); + if (bit_size == 16) { + legacy.set16BitOverride(); + } + } + + const segment_override: ?Register = switch (op_en) { + .i, .zi, .o, .oi, .d, .np => null, + .fd => inst.op2.mem.base().?, + .td => inst.op1.mem.base().?, + .rm, .rmi => if (inst.op2.isSegmentRegister()) blk: { + break :blk switch (inst.op2) { + .reg => |r| r, + .mem => |m| m.base().?, + else => unreachable, + }; + } else null, + .m, .mi, .m1, .mc, .mr => if (inst.op1.isSegmentRegister()) blk: { + break :blk switch (inst.op1) { + .reg => |r| r, + .mem => |m| m.base().?, + else => unreachable, + }; + } else null, + }; + if (segment_override) |seg| { + legacy.setSegmentOverride(seg); + } + + try encoder.legacyPrefixes(legacy); + } + + fn encodeRexPrefix(inst: Instruction, encoder: anytype) !void { + const op_en = inst.encoding.op_en; + + // Check if we need REX and can actually encode it + const is_rex_invalid = for (&[_]Operand{ inst.op1, inst.op2, inst.op3, inst.op4 }) |op| switch (op) { + .reg => |r| if (r.isRexInvalid()) break true, + else => {}, + } else false; + + var rex = Rex{}; + rex.w = inst.encoding.mode == .long; + + switch (op_en) { + .np, .i, .zi, .fd, .td, .d => {}, + .o, .oi => { + rex.b = inst.op1.reg.isExtended(); + }, + .m, .mi, .m1, .mc, .mr, .rm, .rmi => { + const r_op = switch (op_en) { + .rm, .rmi => inst.op1, + .mr => inst.op2, + else => null, + }; + if (r_op) |op| { + rex.r = op.reg.isExtended(); + } + + const b_x_op = switch (op_en) { + .rm, .rmi => inst.op2, + .m, .mi, .m1, .mc, .mr => inst.op1, + else => unreachable, + }; + switch (b_x_op) { + .reg => |r| { + rex.b = r.isExtended(); + }, + .mem => |mem| { + rex.b = if (mem.base()) |base| base.isExtended() else false; + rex.x = if (mem.scaleIndex()) |si| si.index.isExtended() else false; + }, + else => unreachable, + } + }, + } + + if (rex.isSet() and is_rex_invalid) return error.CannotEncode; + + try encoder.rex(rex); + } + + fn encodeMandatoryPrefix(inst: Instruction, encoder: anytype) !void { + const prefix = inst.encoding.mandatoryPrefix() orelse return; + try encoder.opcode_1byte(prefix); + } + + fn encodeMemory(encoding: Encoding, mem: Memory, operand: Operand, encoder: anytype) !void { + const operand_enc = switch (operand) { + .reg => |reg| reg.lowEnc(), + .none => encoding.modRmExt(), + else => unreachable, + }; + + switch (mem) { + .moffs => unreachable, + .sib => |sib| { + if (sib.base) |base| { + if (base.class() == .segment) { + // TODO audit this wrt SIB + try encoder.modRm_SIBDisp0(operand_enc); + if (sib.scale_index) |si| { + const scale = math.log2_int(u4, si.scale); + try encoder.sib_scaleIndexDisp32(scale, si.index.lowEnc()); + } else { + try encoder.sib_disp32(); + } + try encoder.disp32(sib.disp); + } else { + assert(base.class() == .general_purpose); + const dst = base.lowEnc(); + const src = operand_enc; + if (dst == 4 or sib.scale_index != null) { + if (sib.disp == 0 and dst != 5) { + try encoder.modRm_SIBDisp0(src); + if (sib.scale_index) |si| { + const scale = math.log2_int(u4, si.scale); + try encoder.sib_scaleIndexBase(scale, si.index.lowEnc(), dst); + } else { + try encoder.sib_base(dst); + } + } else if (math.cast(i8, sib.disp)) |_| { + try encoder.modRm_SIBDisp8(src); + if (sib.scale_index) |si| { + const scale = math.log2_int(u4, si.scale); + try encoder.sib_scaleIndexBaseDisp8(scale, si.index.lowEnc(), dst); + } else { + try encoder.sib_baseDisp8(dst); + } + try encoder.disp8(@truncate(i8, sib.disp)); + } else { + try encoder.modRm_SIBDisp32(src); + if (sib.scale_index) |si| { + const scale = math.log2_int(u4, si.scale); + try encoder.sib_scaleIndexBaseDisp32(scale, si.index.lowEnc(), dst); + } else { + try encoder.sib_baseDisp32(dst); + } + try encoder.disp32(sib.disp); + } + } else { + if (sib.disp == 0 and dst != 5) { + try encoder.modRm_indirectDisp0(src, dst); + } else if (math.cast(i8, sib.disp)) |_| { + try encoder.modRm_indirectDisp8(src, dst); + try encoder.disp8(@truncate(i8, sib.disp)); + } else { + try encoder.modRm_indirectDisp32(src, dst); + try encoder.disp32(sib.disp); + } + } + } + } else { + try encoder.modRm_SIBDisp0(operand_enc); + if (sib.scale_index) |si| { + const scale = math.log2_int(u4, si.scale); + try encoder.sib_scaleIndexDisp32(scale, si.index.lowEnc()); + } else { + try encoder.sib_disp32(); + } + try encoder.disp32(sib.disp); + } + }, + .rip => |rip| { + try encoder.modRm_RIPDisp32(operand_enc); + try encoder.disp32(rip.disp); + }, + } + } + + fn encodeImm(imm: i64, kind: Encoding.Op, encoder: anytype) !void { + switch (kind) { + .imm8, .rel8 => try encoder.imm8(@truncate(i8, imm)), + .imm16, .rel16 => try encoder.imm16(@truncate(i16, imm)), + .imm32, .rel32 => try encoder.imm32(@truncate(i32, imm)), + .imm64 => try encoder.imm64(@bitCast(u64, imm)), + else => unreachable, + } + } +}; + +inline fn sign(i: anytype) @TypeOf(i) { + return @as(@TypeOf(i), @boolToInt(i > 0)) - @boolToInt(i < 0); +} + +pub const LegacyPrefixes = packed struct { + /// LOCK + prefix_f0: bool = false, + /// REPNZ, REPNE, REP, Scalar Double-precision + prefix_f2: bool = false, + /// REPZ, REPE, REP, Scalar Single-precision + prefix_f3: bool = false, + + /// CS segment override or Branch not taken + prefix_2e: bool = false, + /// SS segment override + prefix_36: bool = false, + /// ES segment override + prefix_26: bool = false, + /// FS segment override + prefix_64: bool = false, + /// GS segment override + prefix_65: bool = false, + + /// Branch taken + prefix_3e: bool = false, + + /// Address size override (enables 16 bit address size) + prefix_67: bool = false, + + /// Operand size override (enables 16 bit operation) + prefix_66: bool = false, + + padding: u5 = 0, + + pub fn setSegmentOverride(self: *LegacyPrefixes, reg: Register) void { + assert(reg.class() == .segment); + switch (reg) { + .cs => self.prefix_2e = true, + .ss => self.prefix_36 = true, + .es => self.prefix_26 = true, + .fs => self.prefix_64 = true, + .gs => self.prefix_65 = true, + .ds => {}, + else => unreachable, + } + } + + pub fn set16BitOverride(self: *LegacyPrefixes) void { + self.prefix_66 = true; + } +}; + +fn Encoder(comptime T: type) type { + return struct { + writer: T, + + const Self = @This(); + + // -------- + // Prefixes + // -------- + + /// Encodes legacy prefixes + pub fn legacyPrefixes(self: Self, prefixes: LegacyPrefixes) !void { + if (@bitCast(u16, prefixes) != 0) { + // Hopefully this path isn't taken very often, so we'll do it the slow way for now + + // LOCK + if (prefixes.prefix_f0) try self.writer.writeByte(0xf0); + // REPNZ, REPNE, REP, Scalar Double-precision + if (prefixes.prefix_f2) try self.writer.writeByte(0xf2); + // REPZ, REPE, REP, Scalar Single-precision + if (prefixes.prefix_f3) try self.writer.writeByte(0xf3); + + // CS segment override or Branch not taken + if (prefixes.prefix_2e) try self.writer.writeByte(0x2e); + // DS segment override + if (prefixes.prefix_36) try self.writer.writeByte(0x36); + // ES segment override + if (prefixes.prefix_26) try self.writer.writeByte(0x26); + // FS segment override + if (prefixes.prefix_64) try self.writer.writeByte(0x64); + // GS segment override + if (prefixes.prefix_65) try self.writer.writeByte(0x65); + + // Branch taken + if (prefixes.prefix_3e) try self.writer.writeByte(0x3e); + + // Operand size override + if (prefixes.prefix_66) try self.writer.writeByte(0x66); + + // Address size override + if (prefixes.prefix_67) try self.writer.writeByte(0x67); + } + } + + /// Use 16 bit operand size + /// + /// Note that this flag is overridden by REX.W, if both are present. + pub fn prefix16BitMode(self: Self) !void { + try self.writer.writeByte(0x66); + } + + /// Encodes a REX prefix byte given all the fields + /// + /// Use this byte whenever you need 64 bit operation, + /// or one of reg, index, r/m, base, or opcode-reg might be extended. + /// + /// See struct `Rex` for a description of each field. + /// + /// Does not add a prefix byte if none of the fields are set! + pub fn rex(self: Self, byte: Rex) !void { + var value: u8 = 0b0100_0000; + + if (byte.w) value |= 0b1000; + if (byte.r) value |= 0b0100; + if (byte.x) value |= 0b0010; + if (byte.b) value |= 0b0001; + + if (value != 0b0100_0000) { + try self.writer.writeByte(value); + } + } + + // ------ + // Opcode + // ------ + + /// Encodes a 1 byte opcode + pub fn opcode_1byte(self: Self, opcode: u8) !void { + try self.writer.writeByte(opcode); + } + + /// Encodes a 2 byte opcode + /// + /// e.g. IMUL has the opcode 0x0f 0xaf, so you use + /// + /// encoder.opcode_2byte(0x0f, 0xaf); + pub fn opcode_2byte(self: Self, prefix: u8, opcode: u8) !void { + try self.writer.writeAll(&.{ prefix, opcode }); + } + + /// Encodes a 3 byte opcode + /// + /// e.g. MOVSD has the opcode 0xf2 0x0f 0x10 + /// + /// encoder.opcode_3byte(0xf2, 0x0f, 0x10); + pub fn opcode_3byte(self: Self, prefix_1: u8, prefix_2: u8, opcode: u8) !void { + try self.writer.writeAll(&.{ prefix_1, prefix_2, opcode }); + } + + /// Encodes a 1 byte opcode with a reg field + /// + /// Remember to add a REX prefix byte if reg is extended! + pub fn opcode_withReg(self: Self, opcode: u8, reg: u3) !void { + assert(opcode & 0b111 == 0); + try self.writer.writeByte(opcode | reg); + } + + // ------ + // ModR/M + // ------ + + /// Construct a ModR/M byte given all the fields + /// + /// Remember to add a REX prefix byte if reg or rm are extended! + pub fn modRm(self: Self, mod: u2, reg_or_opx: u3, rm: u3) !void { + try self.writer.writeByte(@as(u8, mod) << 6 | @as(u8, reg_or_opx) << 3 | rm); + } + + /// Construct a ModR/M byte using direct r/m addressing + /// r/m effective address: r/m + /// + /// Note reg's effective address is always just reg for the ModR/M byte. + /// Remember to add a REX prefix byte if reg or rm are extended! + pub fn modRm_direct(self: Self, reg_or_opx: u3, rm: u3) !void { + try self.modRm(0b11, reg_or_opx, rm); + } + + /// Construct a ModR/M byte using indirect r/m addressing + /// r/m effective address: [r/m] + /// + /// Note reg's effective address is always just reg for the ModR/M byte. + /// Remember to add a REX prefix byte if reg or rm are extended! + pub fn modRm_indirectDisp0(self: Self, reg_or_opx: u3, rm: u3) !void { + assert(rm != 4 and rm != 5); + try self.modRm(0b00, reg_or_opx, rm); + } + + /// Construct a ModR/M byte using indirect SIB addressing + /// r/m effective address: [SIB] + /// + /// Note reg's effective address is always just reg for the ModR/M byte. + /// Remember to add a REX prefix byte if reg or rm are extended! + pub fn modRm_SIBDisp0(self: Self, reg_or_opx: u3) !void { + try self.modRm(0b00, reg_or_opx, 0b100); + } + + /// Construct a ModR/M byte using RIP-relative addressing + /// r/m effective address: [RIP + disp32] + /// + /// Note reg's effective address is always just reg for the ModR/M byte. + /// Remember to add a REX prefix byte if reg or rm are extended! + pub fn modRm_RIPDisp32(self: Self, reg_or_opx: u3) !void { + try self.modRm(0b00, reg_or_opx, 0b101); + } + + /// Construct a ModR/M byte using indirect r/m with a 8bit displacement + /// r/m effective address: [r/m + disp8] + /// + /// Note reg's effective address is always just reg for the ModR/M byte. + /// Remember to add a REX prefix byte if reg or rm are extended! + pub fn modRm_indirectDisp8(self: Self, reg_or_opx: u3, rm: u3) !void { + assert(rm != 4); + try self.modRm(0b01, reg_or_opx, rm); + } + + /// Construct a ModR/M byte using indirect SIB with a 8bit displacement + /// r/m effective address: [SIB + disp8] + /// + /// Note reg's effective address is always just reg for the ModR/M byte. + /// Remember to add a REX prefix byte if reg or rm are extended! + pub fn modRm_SIBDisp8(self: Self, reg_or_opx: u3) !void { + try self.modRm(0b01, reg_or_opx, 0b100); + } + + /// Construct a ModR/M byte using indirect r/m with a 32bit displacement + /// r/m effective address: [r/m + disp32] + /// + /// Note reg's effective address is always just reg for the ModR/M byte. + /// Remember to add a REX prefix byte if reg or rm are extended! + pub fn modRm_indirectDisp32(self: Self, reg_or_opx: u3, rm: u3) !void { + assert(rm != 4); + try self.modRm(0b10, reg_or_opx, rm); + } + + /// Construct a ModR/M byte using indirect SIB with a 32bit displacement + /// r/m effective address: [SIB + disp32] + /// + /// Note reg's effective address is always just reg for the ModR/M byte. + /// Remember to add a REX prefix byte if reg or rm are extended! + pub fn modRm_SIBDisp32(self: Self, reg_or_opx: u3) !void { + try self.modRm(0b10, reg_or_opx, 0b100); + } + + // --- + // SIB + // --- + + /// Construct a SIB byte given all the fields + /// + /// Remember to add a REX prefix byte if index or base are extended! + pub fn sib(self: Self, scale: u2, index: u3, base: u3) !void { + try self.writer.writeByte(@as(u8, scale) << 6 | @as(u8, index) << 3 | base); + } + + /// Construct a SIB byte with scale * index + base, no frills. + /// r/m effective address: [base + scale * index] + /// + /// Remember to add a REX prefix byte if index or base are extended! + pub fn sib_scaleIndexBase(self: Self, scale: u2, index: u3, base: u3) !void { + assert(base != 5); + + try self.sib(scale, index, base); + } + + /// Construct a SIB byte with scale * index + disp32 + /// r/m effective address: [scale * index + disp32] + /// + /// Remember to add a REX prefix byte if index or base are extended! + pub fn sib_scaleIndexDisp32(self: Self, scale: u2, index: u3) !void { + // scale is actually ignored + // index = 4 means no index if and only if we haven't extended the register + // TODO enforce this + // base = 5 means no base, if mod == 0. + try self.sib(scale, index, 5); + } + + /// Construct a SIB byte with just base + /// r/m effective address: [base] + /// + /// Remember to add a REX prefix byte if index or base are extended! + pub fn sib_base(self: Self, base: u3) !void { + assert(base != 5); + + // scale is actually ignored + // index = 4 means no index + try self.sib(0, 4, base); + } + + /// Construct a SIB byte with just disp32 + /// r/m effective address: [disp32] + /// + /// Remember to add a REX prefix byte if index or base are extended! + pub fn sib_disp32(self: Self) !void { + // scale is actually ignored + // index = 4 means no index + // base = 5 means no base, if mod == 0. + try self.sib(0, 4, 5); + } + + /// Construct a SIB byte with scale * index + base + disp8 + /// r/m effective address: [base + scale * index + disp8] + /// + /// Remember to add a REX prefix byte if index or base are extended! + pub fn sib_scaleIndexBaseDisp8(self: Self, scale: u2, index: u3, base: u3) !void { + try self.sib(scale, index, base); + } + + /// Construct a SIB byte with base + disp8, no index + /// r/m effective address: [base + disp8] + /// + /// Remember to add a REX prefix byte if index or base are extended! + pub fn sib_baseDisp8(self: Self, base: u3) !void { + // scale is ignored + // index = 4 means no index + try self.sib(0, 4, base); + } + + /// Construct a SIB byte with scale * index + base + disp32 + /// r/m effective address: [base + scale * index + disp32] + /// + /// Remember to add a REX prefix byte if index or base are extended! + pub fn sib_scaleIndexBaseDisp32(self: Self, scale: u2, index: u3, base: u3) !void { + try self.sib(scale, index, base); + } + + /// Construct a SIB byte with base + disp32, no index + /// r/m effective address: [base + disp32] + /// + /// Remember to add a REX prefix byte if index or base are extended! + pub fn sib_baseDisp32(self: Self, base: u3) !void { + // scale is ignored + // index = 4 means no index + try self.sib(0, 4, base); + } + + // ------------------------- + // Trivial (no bit fiddling) + // ------------------------- + + /// Encode an 8 bit immediate + /// + /// It is sign-extended to 64 bits by the cpu. + pub fn imm8(self: Self, imm: i8) !void { + try self.writer.writeByte(@bitCast(u8, imm)); + } + + /// Encode an 8 bit displacement + /// + /// It is sign-extended to 64 bits by the cpu. + pub fn disp8(self: Self, disp: i8) !void { + try self.writer.writeByte(@bitCast(u8, disp)); + } + + /// Encode an 16 bit immediate + /// + /// It is sign-extended to 64 bits by the cpu. + pub fn imm16(self: Self, imm: i16) !void { + try self.writer.writeIntLittle(i16, imm); + } + + /// Encode an 32 bit immediate + /// + /// It is sign-extended to 64 bits by the cpu. + pub fn imm32(self: Self, imm: i32) !void { + try self.writer.writeIntLittle(i32, imm); + } + + /// Encode an 32 bit displacement + /// + /// It is sign-extended to 64 bits by the cpu. + pub fn disp32(self: Self, disp: i32) !void { + try self.writer.writeIntLittle(i32, disp); + } + + /// Encode an 64 bit immediate + /// + /// It is sign-extended to 64 bits by the cpu. + pub fn imm64(self: Self, imm: u64) !void { + try self.writer.writeIntLittle(u64, imm); + } + }; +} + +pub const Rex = struct { + w: bool = false, + r: bool = false, + x: bool = false, + b: bool = false, + + pub fn isSet(rex: Rex) bool { + return rex.w or rex.r or rex.x or rex.b; + } +}; diff --git a/src/arch/x86_64/encodings.zig b/src/arch/x86_64/encodings.zig new file mode 100644 index 0000000000..b7099d2ad2 --- /dev/null +++ b/src/arch/x86_64/encodings.zig @@ -0,0 +1,542 @@ +const Encoding = @import("Encoding.zig"); +const Mnemonic = Encoding.Mnemonic; +const OpEn = Encoding.OpEn; +const Op = Encoding.Op; +const Mode = Encoding.Mode; + +const opcode_len = u2; +const modrm_ext = u3; + +const Entry = struct { Mnemonic, OpEn, Op, Op, Op, Op, opcode_len, u8, u8, u8, modrm_ext, Mode }; + +// TODO move this into a .zon file when Zig is capable of importing .zon files +// zig fmt: off +pub const table = &[_]Entry{ + // General-purpose + .{ .adc, .zi, .al, .imm8, .none, .none, 1, 0x14, 0x00, 0x00, 0, .none }, + .{ .adc, .zi, .ax, .imm16, .none, .none, 1, 0x15, 0x00, 0x00, 0, .none }, + .{ .adc, .zi, .eax, .imm32, .none, .none, 1, 0x15, 0x00, 0x00, 0, .none }, + .{ .adc, .zi, .rax, .imm32, .none, .none, 1, 0x15, 0x00, 0x00, 0, .long }, + .{ .adc, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 2, .none }, + .{ .adc, .mi, .rm16, .imm16, .none, .none, 1, 0x81, 0x00, 0x00, 2, .none }, + .{ .adc, .mi, .rm32, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 2, .none }, + .{ .adc, .mi, .rm64, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 2, .long }, + .{ .adc, .mi, .rm16, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 2, .none }, + .{ .adc, .mi, .rm32, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 2, .none }, + .{ .adc, .mi, .rm64, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 2, .long }, + .{ .adc, .mr, .rm8, .r8, .none, .none, 1, 0x10, 0x00, 0x00, 0, .none }, + .{ .adc, .mr, .rm16, .r16, .none, .none, 1, 0x11, 0x00, 0x00, 0, .none }, + .{ .adc, .mr, .rm32, .r32, .none, .none, 1, 0x11, 0x00, 0x00, 0, .none }, + .{ .adc, .mr, .rm64, .r64, .none, .none, 1, 0x11, 0x00, 0x00, 0, .long }, + .{ .adc, .rm, .r8, .rm8, .none, .none, 1, 0x12, 0x00, 0x00, 0, .none }, + .{ .adc, .rm, .r16, .rm16, .none, .none, 1, 0x13, 0x00, 0x00, 0, .none }, + .{ .adc, .rm, .r32, .rm32, .none, .none, 1, 0x13, 0x00, 0x00, 0, .none }, + .{ .adc, .rm, .r64, .rm64, .none, .none, 1, 0x13, 0x00, 0x00, 0, .long }, + + .{ .add, .zi, .al, .imm8, .none, .none, 1, 0x04, 0x00, 0x00, 0, .none }, + .{ .add, .zi, .ax, .imm16, .none, .none, 1, 0x05, 0x00, 0x00, 0, .none }, + .{ .add, .zi, .eax, .imm32, .none, .none, 1, 0x05, 0x00, 0x00, 0, .none }, + .{ .add, .zi, .rax, .imm32, .none, .none, 1, 0x05, 0x00, 0x00, 0, .long }, + .{ .add, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 0, .none }, + .{ .add, .mi, .rm16, .imm16, .none, .none, 1, 0x81, 0x00, 0x00, 0, .none }, + .{ .add, .mi, .rm32, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 0, .none }, + .{ .add, .mi, .rm64, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 0, .long }, + .{ .add, .mi, .rm16, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 0, .none }, + .{ .add, .mi, .rm32, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 0, .none }, + .{ .add, .mi, .rm64, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 0, .long }, + .{ .add, .mr, .rm8, .r8, .none, .none, 1, 0x00, 0x00, 0x00, 0, .none }, + .{ .add, .mr, .rm16, .r16, .none, .none, 1, 0x01, 0x00, 0x00, 0, .none }, + .{ .add, .mr, .rm32, .r32, .none, .none, 1, 0x01, 0x00, 0x00, 0, .none }, + .{ .add, .mr, .rm64, .r64, .none, .none, 1, 0x01, 0x00, 0x00, 0, .long }, + .{ .add, .rm, .r8, .rm8, .none, .none, 1, 0x02, 0x00, 0x00, 0, .none }, + .{ .add, .rm, .r16, .rm16, .none, .none, 1, 0x03, 0x00, 0x00, 0, .none }, + .{ .add, .rm, .r32, .rm32, .none, .none, 1, 0x03, 0x00, 0x00, 0, .none }, + .{ .add, .rm, .r64, .rm64, .none, .none, 1, 0x03, 0x00, 0x00, 0, .long }, + + .{ .@"and", .zi, .al, .imm8, .none, .none, 1, 0x24, 0x00, 0x00, 0, .none }, + .{ .@"and", .zi, .ax, .imm16, .none, .none, 1, 0x25, 0x00, 0x00, 0, .none }, + .{ .@"and", .zi, .eax, .imm32, .none, .none, 1, 0x25, 0x00, 0x00, 0, .none }, + .{ .@"and", .zi, .rax, .imm32, .none, .none, 1, 0x25, 0x00, 0x00, 0, .long }, + .{ .@"and", .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 4, .none }, + .{ .@"and", .mi, .rm16, .imm16, .none, .none, 1, 0x81, 0x00, 0x00, 4, .none }, + .{ .@"and", .mi, .rm32, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 4, .none }, + .{ .@"and", .mi, .rm64, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 4, .long }, + .{ .@"and", .mi, .rm16, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 4, .none }, + .{ .@"and", .mi, .rm32, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 4, .none }, + .{ .@"and", .mi, .rm64, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 4, .long }, + .{ .@"and", .mr, .rm8, .r8, .none, .none, 1, 0x20, 0x00, 0x00, 0, .none }, + .{ .@"and", .mr, .rm16, .r16, .none, .none, 1, 0x21, 0x00, 0x00, 0, .none }, + .{ .@"and", .mr, .rm32, .r32, .none, .none, 1, 0x21, 0x00, 0x00, 0, .none }, + .{ .@"and", .mr, .rm64, .r64, .none, .none, 1, 0x21, 0x00, 0x00, 0, .long }, + .{ .@"and", .rm, .r8, .rm8, .none, .none, 1, 0x22, 0x00, 0x00, 0, .none }, + .{ .@"and", .rm, .r16, .rm16, .none, .none, 1, 0x23, 0x00, 0x00, 0, .none }, + .{ .@"and", .rm, .r32, .rm32, .none, .none, 1, 0x23, 0x00, 0x00, 0, .none }, + .{ .@"and", .rm, .r64, .rm64, .none, .none, 1, 0x23, 0x00, 0x00, 0, .long }, + + // This is M encoding according to Intel, but D makes more sense here. + .{ .call, .d, .rel32, .none, .none, .none, 1, 0xe8, 0x00, 0x00, 0, .none }, + .{ .call, .m, .rm64, .none, .none, .none, 1, 0xff, 0x00, 0x00, 2, .none }, + + .{ .cbw, .np, .o16, .none, .none, .none, 1, 0x98, 0x00, 0x00, 0, .none }, + .{ .cwde, .np, .o32, .none, .none, .none, 1, 0x98, 0x00, 0x00, 0, .none }, + .{ .cdqe, .np, .o64, .none, .none, .none, 1, 0x98, 0x00, 0x00, 0, .long }, + + .{ .cwd, .np, .o16, .none, .none, .none, 1, 0x99, 0x00, 0x00, 0, .none }, + .{ .cdq, .np, .o32, .none, .none, .none, 1, 0x99, 0x00, 0x00, 0, .none }, + .{ .cqo, .np, .o64, .none, .none, .none, 1, 0x99, 0x00, 0x00, 0, .long }, + + .{ .cmova, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x47, 0x00, 0, .none }, + .{ .cmova, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x47, 0x00, 0, .none }, + .{ .cmova, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x47, 0x00, 0, .long }, + .{ .cmovae, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x43, 0x00, 0, .none }, + .{ .cmovae, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x43, 0x00, 0, .none }, + .{ .cmovae, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x43, 0x00, 0, .long }, + .{ .cmovb, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x42, 0x00, 0, .none }, + .{ .cmovb, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x42, 0x00, 0, .none }, + .{ .cmovb, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x42, 0x00, 0, .long }, + .{ .cmovbe, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x46, 0x00, 0, .none }, + .{ .cmovbe, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x46, 0x00, 0, .none }, + .{ .cmovbe, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x46, 0x00, 0, .long }, + .{ .cmovc, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x42, 0x00, 0, .none }, + .{ .cmovc, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x42, 0x00, 0, .none }, + .{ .cmovc, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x42, 0x00, 0, .long }, + .{ .cmove, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x44, 0x00, 0, .none }, + .{ .cmove, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x44, 0x00, 0, .none }, + .{ .cmove, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x44, 0x00, 0, .long }, + .{ .cmovg, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x4f, 0x00, 0, .none }, + .{ .cmovg, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x4f, 0x00, 0, .none }, + .{ .cmovg, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x4f, 0x00, 0, .long }, + .{ .cmovge, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x4d, 0x00, 0, .none }, + .{ .cmovge, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x4d, 0x00, 0, .none }, + .{ .cmovge, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x4d, 0x00, 0, .long }, + .{ .cmovl, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x4c, 0x00, 0, .none }, + .{ .cmovl, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x4c, 0x00, 0, .none }, + .{ .cmovl, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x4c, 0x00, 0, .long }, + .{ .cmovle, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x4e, 0x00, 0, .none }, + .{ .cmovle, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x4e, 0x00, 0, .none }, + .{ .cmovle, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x4e, 0x00, 0, .long }, + .{ .cmovna, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x46, 0x00, 0, .none }, + .{ .cmovna, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x46, 0x00, 0, .none }, + .{ .cmovna, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x46, 0x00, 0, .long }, + .{ .cmovnae, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x42, 0x00, 0, .none }, + .{ .cmovnae, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x42, 0x00, 0, .none }, + .{ .cmovnae, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x42, 0x00, 0, .long }, + .{ .cmovnb, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x43, 0x00, 0, .none }, + .{ .cmovnb, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x43, 0x00, 0, .none }, + .{ .cmovnb, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x43, 0x00, 0, .long }, + .{ .cmovnbe, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x47, 0x00, 0, .none }, + .{ .cmovnbe, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x47, 0x00, 0, .none }, + .{ .cmovnbe, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x47, 0x00, 0, .long }, + .{ .cmovnc, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x43, 0x00, 0, .none }, + .{ .cmovnc, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x43, 0x00, 0, .none }, + .{ .cmovnc, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x43, 0x00, 0, .long }, + .{ .cmovne, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x45, 0x00, 0, .none }, + .{ .cmovne, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x45, 0x00, 0, .none }, + .{ .cmovne, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x45, 0x00, 0, .long }, + .{ .cmovng, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x4e, 0x00, 0, .none }, + .{ .cmovng, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x4e, 0x00, 0, .none }, + .{ .cmovng, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x4e, 0x00, 0, .long }, + .{ .cmovnge, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x4c, 0x00, 0, .none }, + .{ .cmovnge, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x4c, 0x00, 0, .none }, + .{ .cmovnge, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x4c, 0x00, 0, .long }, + .{ .cmovnl, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x4d, 0x00, 0, .none }, + .{ .cmovnl, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x4d, 0x00, 0, .none }, + .{ .cmovnl, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x4d, 0x00, 0, .long }, + .{ .cmovnle, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x4f, 0x00, 0, .none }, + .{ .cmovnle, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x4f, 0x00, 0, .none }, + .{ .cmovnle, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x4f, 0x00, 0, .long }, + .{ .cmovno, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x41, 0x00, 0, .none }, + .{ .cmovno, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x41, 0x00, 0, .none }, + .{ .cmovno, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x41, 0x00, 0, .long }, + .{ .cmovnp, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x4b, 0x00, 0, .none }, + .{ .cmovnp, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x4b, 0x00, 0, .none }, + .{ .cmovnp, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x4b, 0x00, 0, .long }, + .{ .cmovns, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x49, 0x00, 0, .none }, + .{ .cmovns, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x49, 0x00, 0, .none }, + .{ .cmovns, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x49, 0x00, 0, .long }, + .{ .cmovnz, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x45, 0x00, 0, .none }, + .{ .cmovnz, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x45, 0x00, 0, .none }, + .{ .cmovnz, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x45, 0x00, 0, .long }, + .{ .cmovo, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x40, 0x00, 0, .none }, + .{ .cmovo, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x40, 0x00, 0, .none }, + .{ .cmovo, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x40, 0x00, 0, .long }, + .{ .cmovp, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x4a, 0x00, 0, .none }, + .{ .cmovp, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x4a, 0x00, 0, .none }, + .{ .cmovp, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x4a, 0x00, 0, .long }, + .{ .cmovpe, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x4a, 0x00, 0, .none }, + .{ .cmovpe, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x4a, 0x00, 0, .none }, + .{ .cmovpe, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x4a, 0x00, 0, .long }, + .{ .cmovpo, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x4b, 0x00, 0, .none }, + .{ .cmovpo, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x4b, 0x00, 0, .none }, + .{ .cmovpo, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x4b, 0x00, 0, .long }, + .{ .cmovs, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x48, 0x00, 0, .none }, + .{ .cmovs, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x48, 0x00, 0, .none }, + .{ .cmovs, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x48, 0x00, 0, .long }, + .{ .cmovz, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x44, 0x00, 0, .none }, + .{ .cmovz, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x44, 0x00, 0, .none }, + .{ .cmovz, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x44, 0x00, 0, .long }, + + .{ .cmp, .zi, .al, .imm8, .none, .none, 1, 0x3c, 0x00, 0x00, 0, .none }, + .{ .cmp, .zi, .ax, .imm16, .none, .none, 1, 0x3d, 0x00, 0x00, 0, .none }, + .{ .cmp, .zi, .eax, .imm32, .none, .none, 1, 0x3d, 0x00, 0x00, 0, .none }, + .{ .cmp, .zi, .rax, .imm32, .none, .none, 1, 0x3d, 0x00, 0x00, 0, .long }, + .{ .cmp, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 7, .none }, + .{ .cmp, .mi, .rm16, .imm16, .none, .none, 1, 0x81, 0x00, 0x00, 7, .none }, + .{ .cmp, .mi, .rm32, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 7, .none }, + .{ .cmp, .mi, .rm64, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 7, .long }, + .{ .cmp, .mi, .rm16, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 7, .none }, + .{ .cmp, .mi, .rm32, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 7, .none }, + .{ .cmp, .mi, .rm64, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 7, .long }, + .{ .cmp, .mr, .rm8, .r8, .none, .none, 1, 0x38, 0x00, 0x00, 0, .none }, + .{ .cmp, .mr, .rm16, .r16, .none, .none, 1, 0x39, 0x00, 0x00, 0, .none }, + .{ .cmp, .mr, .rm32, .r32, .none, .none, 1, 0x39, 0x00, 0x00, 0, .none }, + .{ .cmp, .mr, .rm64, .r64, .none, .none, 1, 0x39, 0x00, 0x00, 0, .long }, + .{ .cmp, .rm, .r8, .rm8, .none, .none, 1, 0x3a, 0x00, 0x00, 0, .none }, + .{ .cmp, .rm, .r16, .rm16, .none, .none, 1, 0x3b, 0x00, 0x00, 0, .none }, + .{ .cmp, .rm, .r32, .rm32, .none, .none, 1, 0x3b, 0x00, 0x00, 0, .none }, + .{ .cmp, .rm, .r64, .rm64, .none, .none, 1, 0x3b, 0x00, 0x00, 0, .long }, + + .{ .div, .m, .rm8, .none, .none, .none, 1, 0xf6, 0x00, 0x00, 6, .none }, + .{ .div, .m, .rm16, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 6, .none }, + .{ .div, .m, .rm32, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 6, .none }, + .{ .div, .m, .rm64, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 6, .long }, + + .{ .fisttp, .m, .m16, .none, .none, .none, 1, 0xdf, 0x00, 0x00, 1, .fpu }, + .{ .fisttp, .m, .m32, .none, .none, .none, 1, 0xdb, 0x00, 0x00, 1, .fpu }, + .{ .fisttp, .m, .m64, .none, .none, .none, 1, 0xdd, 0x00, 0x00, 1, .fpu }, + + .{ .fld, .m, .m32, .none, .none, .none, 1, 0xd9, 0x00, 0x00, 0, .fpu }, + .{ .fld, .m, .m64, .none, .none, .none, 1, 0xdd, 0x00, 0x00, 0, .fpu }, + .{ .fld, .m, .m80, .none, .none, .none, 1, 0xdb, 0x00, 0x00, 5, .fpu }, + + .{ .idiv, .m, .rm8, .none, .none, .none, 1, 0xf6, 0x00, 0x00, 7, .none }, + .{ .idiv, .m, .rm16, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 7, .none }, + .{ .idiv, .m, .rm32, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 7, .none }, + .{ .idiv, .m, .rm64, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 7, .long }, + + .{ .imul, .m, .rm8, .none, .none, .none, 1, 0xf6, 0x00, 0x00, 5, .none }, + .{ .imul, .m, .rm16, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 5, .none }, + .{ .imul, .m, .rm32, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 5, .none }, + .{ .imul, .m, .rm64, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 5, .long }, + .{ .imul, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0xaf, 0x00, 0, .none }, + .{ .imul, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0xaf, 0x00, 0, .none }, + .{ .imul, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0xaf, 0x00, 0, .long }, + .{ .imul, .rmi, .r16, .rm16, .imm8, .none, 1, 0x6b, 0x00, 0x00, 0, .none }, + .{ .imul, .rmi, .r32, .rm32, .imm8, .none, 1, 0x6b, 0x00, 0x00, 0, .none }, + .{ .imul, .rmi, .r64, .rm64, .imm8, .none, 1, 0x6b, 0x00, 0x00, 0, .long }, + .{ .imul, .rmi, .r16, .rm16, .imm16, .none, 1, 0x69, 0x00, 0x00, 0, .none }, + .{ .imul, .rmi, .r32, .rm32, .imm32, .none, 1, 0x69, 0x00, 0x00, 0, .none }, + .{ .imul, .rmi, .r64, .rm64, .imm32, .none, 1, 0x69, 0x00, 0x00, 0, .long }, + + .{ .int3, .np, .none, .none, .none, .none, 1, 0xcc, 0x00, 0x00, 0, .none }, + + .{ .ja, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x87, 0x00, 0, .none }, + .{ .jae, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x83, 0x00, 0, .none }, + .{ .jb, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x82, 0x00, 0, .none }, + .{ .jbe, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x86, 0x00, 0, .none }, + .{ .jc, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x82, 0x00, 0, .none }, + .{ .jrcxz, .d, .rel32, .none, .none, .none, 1, 0xe3, 0x00, 0x00, 0, .none }, + .{ .je, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x84, 0x00, 0, .none }, + .{ .jg, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x8f, 0x00, 0, .none }, + .{ .jge, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x8d, 0x00, 0, .none }, + .{ .jl, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x8c, 0x00, 0, .none }, + .{ .jle, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x8e, 0x00, 0, .none }, + .{ .jna, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x86, 0x00, 0, .none }, + .{ .jnae, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x82, 0x00, 0, .none }, + .{ .jnb, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x83, 0x00, 0, .none }, + .{ .jnbe, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x87, 0x00, 0, .none }, + .{ .jnc, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x83, 0x00, 0, .none }, + .{ .jne, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x85, 0x00, 0, .none }, + .{ .jng, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x8e, 0x00, 0, .none }, + .{ .jnge, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x8c, 0x00, 0, .none }, + .{ .jnl, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x8d, 0x00, 0, .none }, + .{ .jnle, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x8f, 0x00, 0, .none }, + .{ .jno, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x81, 0x00, 0, .none }, + .{ .jnp, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x8b, 0x00, 0, .none }, + .{ .jns, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x89, 0x00, 0, .none }, + .{ .jnz, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x85, 0x00, 0, .none }, + .{ .jo, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x80, 0x00, 0, .none }, + .{ .jp, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x8a, 0x00, 0, .none }, + .{ .jpe, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x8a, 0x00, 0, .none }, + .{ .jpo, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x8b, 0x00, 0, .none }, + .{ .js, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x88, 0x00, 0, .none }, + .{ .jz, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x84, 0x00, 0, .none }, + + .{ .jmp, .d, .rel32, .none, .none, .none, 1, 0xe9, 0x00, 0x00, 0, .none }, + .{ .jmp, .m, .rm64, .none, .none, .none, 1, 0xff, 0x00, 0x00, 4, .none }, + + .{ .lea, .rm, .r16, .m, .none, .none, 1, 0x8d, 0x00, 0x00, 0, .none }, + .{ .lea, .rm, .r32, .m, .none, .none, 1, 0x8d, 0x00, 0x00, 0, .none }, + .{ .lea, .rm, .r64, .m, .none, .none, 1, 0x8d, 0x00, 0x00, 0, .long }, + + .{ .mov, .mr, .rm8, .r8, .none, .none, 1, 0x88, 0x00, 0x00, 0, .none }, + .{ .mov, .mr, .rm16, .r16, .none, .none, 1, 0x89, 0x00, 0x00, 0, .none }, + .{ .mov, .mr, .rm32, .r32, .none, .none, 1, 0x89, 0x00, 0x00, 0, .none }, + .{ .mov, .mr, .rm64, .r64, .none, .none, 1, 0x89, 0x00, 0x00, 0, .long }, + .{ .mov, .rm, .r8, .rm8, .none, .none, 1, 0x8a, 0x00, 0x00, 0, .none }, + .{ .mov, .rm, .r16, .rm16, .none, .none, 1, 0x8b, 0x00, 0x00, 0, .none }, + .{ .mov, .rm, .r32, .rm32, .none, .none, 1, 0x8b, 0x00, 0x00, 0, .none }, + .{ .mov, .rm, .r64, .rm64, .none, .none, 1, 0x8b, 0x00, 0x00, 0, .long }, + .{ .mov, .mr, .rm16, .sreg, .none, .none, 1, 0x8c, 0x00, 0x00, 0, .none }, + .{ .mov, .mr, .rm64, .sreg, .none, .none, 1, 0x8c, 0x00, 0x00, 0, .long }, + .{ .mov, .rm, .sreg, .rm16, .none, .none, 1, 0x8e, 0x00, 0x00, 0, .none }, + .{ .mov, .rm, .sreg, .rm64, .none, .none, 1, 0x8e, 0x00, 0x00, 0, .long }, + .{ .mov, .fd, .al, .moffs, .none, .none, 1, 0xa0, 0x00, 0x00, 0, .none }, + .{ .mov, .fd, .ax, .moffs, .none, .none, 1, 0xa1, 0x00, 0x00, 0, .none }, + .{ .mov, .fd, .eax, .moffs, .none, .none, 1, 0xa1, 0x00, 0x00, 0, .none }, + .{ .mov, .fd, .rax, .moffs, .none, .none, 1, 0xa1, 0x00, 0x00, 0, .long }, + .{ .mov, .td, .moffs, .al, .none, .none, 1, 0xa2, 0x00, 0x00, 0, .none }, + .{ .mov, .td, .moffs, .ax, .none, .none, 1, 0xa3, 0x00, 0x00, 0, .none }, + .{ .mov, .td, .moffs, .eax, .none, .none, 1, 0xa3, 0x00, 0x00, 0, .none }, + .{ .mov, .td, .moffs, .rax, .none, .none, 1, 0xa3, 0x00, 0x00, 0, .long }, + .{ .mov, .oi, .r8, .imm8, .none, .none, 1, 0xb0, 0x00, 0x00, 0, .none }, + .{ .mov, .oi, .r16, .imm16, .none, .none, 1, 0xb8, 0x00, 0x00, 0, .none }, + .{ .mov, .oi, .r32, .imm32, .none, .none, 1, 0xb8, 0x00, 0x00, 0, .none }, + .{ .mov, .oi, .r64, .imm64, .none, .none, 1, 0xb8, 0x00, 0x00, 0, .long }, + .{ .mov, .mi, .rm8, .imm8, .none, .none, 1, 0xc6, 0x00, 0x00, 0, .none }, + .{ .mov, .mi, .rm16, .imm16, .none, .none, 1, 0xc7, 0x00, 0x00, 0, .none }, + .{ .mov, .mi, .rm32, .imm32, .none, .none, 1, 0xc7, 0x00, 0x00, 0, .none }, + .{ .mov, .mi, .rm64, .imm32, .none, .none, 1, 0xc7, 0x00, 0x00, 0, .long }, + + .{ .movsx, .rm, .r16, .rm8, .none, .none, 2, 0x0f, 0xbe, 0x00, 0, .none }, + .{ .movsx, .rm, .r32, .rm8, .none, .none, 2, 0x0f, 0xbe, 0x00, 0, .none }, + .{ .movsx, .rm, .r64, .rm8, .none, .none, 2, 0x0f, 0xbe, 0x00, 0, .long }, + .{ .movsx, .rm, .r32, .rm16, .none, .none, 2, 0x0f, 0xbf, 0x00, 0, .none }, + .{ .movsx, .rm, .r64, .rm16, .none, .none, 2, 0x0f, 0xbf, 0x00, 0, .long }, + + // This instruction is discouraged. + .{ .movsxd, .rm, .r32, .rm32, .none, .none, 1, 0x63, 0x00, 0x00, 0, .none }, + .{ .movsxd, .rm, .r64, .rm32, .none, .none, 1, 0x63, 0x00, 0x00, 0, .long }, + + .{ .movzx, .rm, .r16, .rm8, .none, .none, 2, 0x0f, 0xb6, 0x00, 0, .none }, + .{ .movzx, .rm, .r32, .rm8, .none, .none, 2, 0x0f, 0xb6, 0x00, 0, .none }, + .{ .movzx, .rm, .r64, .rm8, .none, .none, 2, 0x0f, 0xb6, 0x00, 0, .long }, + .{ .movzx, .rm, .r32, .rm16, .none, .none, 2, 0x0f, 0xb7, 0x00, 0, .none }, + .{ .movzx, .rm, .r64, .rm16, .none, .none, 2, 0x0f, 0xb7, 0x00, 0, .long }, + + .{ .mul, .m, .rm8, .none, .none, .none, 1, 0xf6, 0x00, 0x00, 4, .none }, + .{ .mul, .m, .rm16, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 4, .none }, + .{ .mul, .m, .rm32, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 4, .none }, + .{ .mul, .m, .rm64, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 4, .long }, + + .{ .nop, .np, .none, .none, .none, .none, 1, 0x90, 0x00, 0x00, 0, .none }, + + .{ .@"or", .zi, .al, .imm8, .none, .none, 1, 0x0c, 0x00, 0x00, 0, .none }, + .{ .@"or", .zi, .ax, .imm16, .none, .none, 1, 0x0d, 0x00, 0x00, 0, .none }, + .{ .@"or", .zi, .eax, .imm32, .none, .none, 1, 0x0d, 0x00, 0x00, 0, .none }, + .{ .@"or", .zi, .rax, .imm32, .none, .none, 1, 0x0d, 0x00, 0x00, 0, .long }, + .{ .@"or", .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 1, .none }, + .{ .@"or", .mi, .rm16, .imm16, .none, .none, 1, 0x81, 0x00, 0x00, 1, .none }, + .{ .@"or", .mi, .rm32, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 1, .none }, + .{ .@"or", .mi, .rm64, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 1, .long }, + .{ .@"or", .mi, .rm16, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 1, .none }, + .{ .@"or", .mi, .rm32, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 1, .none }, + .{ .@"or", .mi, .rm64, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 1, .long }, + .{ .@"or", .mr, .rm8, .r8, .none, .none, 1, 0x08, 0x00, 0x00, 0, .none }, + .{ .@"or", .mr, .rm16, .r16, .none, .none, 1, 0x09, 0x00, 0x00, 0, .none }, + .{ .@"or", .mr, .rm32, .r32, .none, .none, 1, 0x09, 0x00, 0x00, 0, .none }, + .{ .@"or", .mr, .rm64, .r64, .none, .none, 1, 0x09, 0x00, 0x00, 0, .long }, + .{ .@"or", .rm, .r8, .rm8, .none, .none, 1, 0x0a, 0x00, 0x00, 0, .none }, + .{ .@"or", .rm, .r16, .rm16, .none, .none, 1, 0x0b, 0x00, 0x00, 0, .none }, + .{ .@"or", .rm, .r32, .rm32, .none, .none, 1, 0x0b, 0x00, 0x00, 0, .none }, + .{ .@"or", .rm, .r64, .rm64, .none, .none, 1, 0x0b, 0x00, 0x00, 0, .long }, + + .{ .pop, .o, .r16, .none, .none, .none, 1, 0x58, 0x00, 0x00, 0, .none }, + .{ .pop, .o, .r64, .none, .none, .none, 1, 0x58, 0x00, 0x00, 0, .none }, + .{ .pop, .m, .rm16, .none, .none, .none, 1, 0x8f, 0x00, 0x00, 0, .none }, + .{ .pop, .m, .rm64, .none, .none, .none, 1, 0x8f, 0x00, 0x00, 0, .none }, + + .{ .push, .o, .r16, .none, .none, .none, 1, 0x50, 0x00, 0x00, 0, .none }, + .{ .push, .o, .r64, .none, .none, .none, 1, 0x50, 0x00, 0x00, 0, .none }, + .{ .push, .m, .rm16, .none, .none, .none, 1, 0xff, 0x00, 0x00, 6, .none }, + .{ .push, .m, .rm64, .none, .none, .none, 1, 0xff, 0x00, 0x00, 6, .none }, + .{ .push, .i, .imm8, .none, .none, .none, 1, 0x6a, 0x00, 0x00, 0, .none }, + .{ .push, .i, .imm16, .none, .none, .none, 1, 0x68, 0x00, 0x00, 0, .none }, + .{ .push, .i, .imm32, .none, .none, .none, 1, 0x68, 0x00, 0x00, 0, .none }, + + .{ .ret, .np, .none, .none, .none, .none, 1, 0xc3, 0x00, 0x00, 0, .none }, + + .{ .sal, .m1, .rm8, .unity, .none, .none, 1, 0xd0, 0x00, 0x00, 4, .none }, + .{ .sal, .m1, .rm16, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 4, .none }, + .{ .sal, .m1, .rm32, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 4, .none }, + .{ .sal, .m1, .rm64, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 4, .long }, + .{ .sal, .mc, .rm8, .cl, .none, .none, 1, 0xd2, 0x00, 0x00, 4, .none }, + .{ .sal, .mc, .rm16, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 4, .none }, + .{ .sal, .mc, .rm32, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 4, .none }, + .{ .sal, .mc, .rm64, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 4, .long }, + .{ .sal, .mi, .rm8, .imm8, .none, .none, 1, 0xc0, 0x00, 0x00, 4, .none }, + .{ .sal, .mi, .rm16, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 4, .none }, + .{ .sal, .mi, .rm32, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 4, .none }, + .{ .sal, .mi, .rm64, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 4, .long }, + + .{ .sar, .m1, .rm8, .unity, .none, .none, 1, 0xd0, 0x00, 0x00, 7, .none }, + .{ .sar, .m1, .rm16, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 7, .none }, + .{ .sar, .m1, .rm32, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 7, .none }, + .{ .sar, .m1, .rm64, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 7, .long }, + .{ .sar, .mc, .rm8, .cl, .none, .none, 1, 0xd2, 0x00, 0x00, 7, .none }, + .{ .sar, .mc, .rm16, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 7, .none }, + .{ .sar, .mc, .rm32, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 7, .none }, + .{ .sar, .mc, .rm64, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 7, .long }, + .{ .sar, .mi, .rm8, .imm8, .none, .none, 1, 0xc0, 0x00, 0x00, 7, .none }, + .{ .sar, .mi, .rm16, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 7, .none }, + .{ .sar, .mi, .rm32, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 7, .none }, + .{ .sar, .mi, .rm64, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 7, .long }, + + .{ .sbb, .zi, .al, .imm8, .none, .none, 1, 0x1c, 0x00, 0x00, 0, .none }, + .{ .sbb, .zi, .ax, .imm16, .none, .none, 1, 0x1d, 0x00, 0x00, 0, .none }, + .{ .sbb, .zi, .eax, .imm32, .none, .none, 1, 0x1d, 0x00, 0x00, 0, .none }, + .{ .sbb, .zi, .rax, .imm32, .none, .none, 1, 0x1d, 0x00, 0x00, 0, .long }, + .{ .sbb, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 3, .none }, + .{ .sbb, .mi, .rm16, .imm16, .none, .none, 1, 0x81, 0x00, 0x00, 3, .none }, + .{ .sbb, .mi, .rm32, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 3, .none }, + .{ .sbb, .mi, .rm64, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 3, .long }, + .{ .sbb, .mi, .rm16, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 3, .none }, + .{ .sbb, .mi, .rm32, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 3, .none }, + .{ .sbb, .mi, .rm64, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 3, .long }, + .{ .sbb, .mr, .rm8, .r8, .none, .none, 1, 0x18, 0x00, 0x00, 0, .none }, + .{ .sbb, .mr, .rm16, .r16, .none, .none, 1, 0x19, 0x00, 0x00, 0, .none }, + .{ .sbb, .mr, .rm32, .r32, .none, .none, 1, 0x19, 0x00, 0x00, 0, .none }, + .{ .sbb, .mr, .rm64, .r64, .none, .none, 1, 0x19, 0x00, 0x00, 0, .long }, + .{ .sbb, .rm, .r8, .rm8, .none, .none, 1, 0x1a, 0x00, 0x00, 0, .none }, + .{ .sbb, .rm, .r16, .rm16, .none, .none, 1, 0x1b, 0x00, 0x00, 0, .none }, + .{ .sbb, .rm, .r32, .rm32, .none, .none, 1, 0x1b, 0x00, 0x00, 0, .none }, + .{ .sbb, .rm, .r64, .rm64, .none, .none, 1, 0x1b, 0x00, 0x00, 0, .long }, + + .{ .seta, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x97, 0x00, 0, .none }, + .{ .setae, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x93, 0x00, 0, .none }, + .{ .setb, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x92, 0x00, 0, .none }, + .{ .setbe, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x96, 0x00, 0, .none }, + .{ .setc, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x92, 0x00, 0, .none }, + .{ .sete, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x94, 0x00, 0, .none }, + .{ .setg, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9f, 0x00, 0, .none }, + .{ .setge, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9d, 0x00, 0, .none }, + .{ .setl, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9c, 0x00, 0, .none }, + .{ .setle, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9e, 0x00, 0, .none }, + .{ .setna, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x96, 0x00, 0, .none }, + .{ .setnae, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x92, 0x00, 0, .none }, + .{ .setnb, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x93, 0x00, 0, .none }, + .{ .setnbe, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x97, 0x00, 0, .none }, + .{ .setnc, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x93, 0x00, 0, .none }, + .{ .setne, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x95, 0x00, 0, .none }, + .{ .setng, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9e, 0x00, 0, .none }, + .{ .setnge, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9c, 0x00, 0, .none }, + .{ .setnl, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9d, 0x00, 0, .none }, + .{ .setnle, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9f, 0x00, 0, .none }, + .{ .setno, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x91, 0x00, 0, .none }, + .{ .setnp, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9b, 0x00, 0, .none }, + .{ .setns, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x99, 0x00, 0, .none }, + .{ .setnz, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x95, 0x00, 0, .none }, + .{ .seto, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x90, 0x00, 0, .none }, + .{ .setp, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9a, 0x00, 0, .none }, + .{ .setpe, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9a, 0x00, 0, .none }, + .{ .setpo, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9b, 0x00, 0, .none }, + .{ .sets, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x98, 0x00, 0, .none }, + .{ .setz, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x94, 0x00, 0, .none }, + + .{ .shl, .m1, .rm8, .unity, .none, .none, 1, 0xd0, 0x00, 0x00, 4, .none }, + .{ .shl, .m1, .rm16, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 4, .none }, + .{ .shl, .m1, .rm32, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 4, .none }, + .{ .shl, .m1, .rm64, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 4, .long }, + .{ .shl, .mc, .rm8, .cl, .none, .none, 1, 0xd2, 0x00, 0x00, 4, .none }, + .{ .shl, .mc, .rm16, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 4, .none }, + .{ .shl, .mc, .rm32, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 4, .none }, + .{ .shl, .mc, .rm64, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 4, .long }, + .{ .shl, .mi, .rm8, .imm8, .none, .none, 1, 0xc0, 0x00, 0x00, 4, .none }, + .{ .shl, .mi, .rm16, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 4, .none }, + .{ .shl, .mi, .rm32, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 4, .none }, + .{ .shl, .mi, .rm64, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 4, .long }, + + .{ .shr, .m1, .rm8, .unity, .none, .none, 1, 0xd0, 0x00, 0x00, 5, .none }, + .{ .shr, .m1, .rm16, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 5, .none }, + .{ .shr, .m1, .rm32, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 5, .none }, + .{ .shr, .m1, .rm64, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 5, .long }, + .{ .shr, .mc, .rm8, .cl, .none, .none, 1, 0xd2, 0x00, 0x00, 5, .none }, + .{ .shr, .mc, .rm16, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 5, .none }, + .{ .shr, .mc, .rm32, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 5, .none }, + .{ .shr, .mc, .rm64, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 5, .long }, + .{ .shr, .mi, .rm8, .imm8, .none, .none, 1, 0xc0, 0x00, 0x00, 5, .none }, + .{ .shr, .mi, .rm16, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 5, .none }, + .{ .shr, .mi, .rm32, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 5, .none }, + .{ .shr, .mi, .rm64, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 5, .long }, + + .{ .sub, .zi, .al, .imm8, .none, .none, 1, 0x2c, 0x00, 0x00, 0, .none }, + .{ .sub, .zi, .ax, .imm16, .none, .none, 1, 0x2d, 0x00, 0x00, 0, .none }, + .{ .sub, .zi, .eax, .imm32, .none, .none, 1, 0x2d, 0x00, 0x00, 0, .none }, + .{ .sub, .zi, .rax, .imm32, .none, .none, 1, 0x2d, 0x00, 0x00, 0, .long }, + .{ .sub, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 5, .none }, + .{ .sub, .mi, .rm16, .imm16, .none, .none, 1, 0x81, 0x00, 0x00, 5, .none }, + .{ .sub, .mi, .rm32, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 5, .none }, + .{ .sub, .mi, .rm64, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 5, .long }, + .{ .sub, .mi, .rm16, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 5, .none }, + .{ .sub, .mi, .rm32, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 5, .none }, + .{ .sub, .mi, .rm64, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 5, .long }, + .{ .sub, .mr, .rm8, .r8, .none, .none, 1, 0x28, 0x00, 0x00, 0, .none }, + .{ .sub, .mr, .rm16, .r16, .none, .none, 1, 0x29, 0x00, 0x00, 0, .none }, + .{ .sub, .mr, .rm32, .r32, .none, .none, 1, 0x29, 0x00, 0x00, 0, .none }, + .{ .sub, .mr, .rm64, .r64, .none, .none, 1, 0x29, 0x00, 0x00, 0, .long }, + .{ .sub, .rm, .r8, .rm8, .none, .none, 1, 0x2a, 0x00, 0x00, 0, .none }, + .{ .sub, .rm, .r16, .rm16, .none, .none, 1, 0x2b, 0x00, 0x00, 0, .none }, + .{ .sub, .rm, .r32, .rm32, .none, .none, 1, 0x2b, 0x00, 0x00, 0, .none }, + .{ .sub, .rm, .r64, .rm64, .none, .none, 1, 0x2b, 0x00, 0x00, 0, .long }, + + .{ .syscall, .np, .none, .none, .none, .none, 2, 0x0f, 0x05, 0x00, 0, .none }, + + .{ .@"test", .zi, .al, .imm8, .none, .none, 1, 0xa8, 0x00, 0x00, 0, .none }, + .{ .@"test", .zi, .ax, .imm16, .none, .none, 1, 0xa9, 0x00, 0x00, 0, .none }, + .{ .@"test", .zi, .eax, .imm32, .none, .none, 1, 0xa9, 0x00, 0x00, 0, .none }, + .{ .@"test", .zi, .rax, .imm32, .none, .none, 1, 0xa9, 0x00, 0x00, 0, .long }, + .{ .@"test", .mi, .rm8, .imm8, .none, .none, 1, 0xf6, 0x00, 0x00, 0, .none }, + .{ .@"test", .mi, .rm16, .imm16, .none, .none, 1, 0xf7, 0x00, 0x00, 0, .none }, + .{ .@"test", .mi, .rm32, .imm32, .none, .none, 1, 0xf7, 0x00, 0x00, 0, .none }, + .{ .@"test", .mi, .rm64, .imm32, .none, .none, 1, 0xf7, 0x00, 0x00, 0, .long }, + .{ .@"test", .mr, .rm8, .r8, .none, .none, 1, 0x84, 0x00, 0x00, 0, .none }, + .{ .@"test", .mr, .rm16, .r16, .none, .none, 1, 0x85, 0x00, 0x00, 0, .none }, + .{ .@"test", .mr, .rm32, .r32, .none, .none, 1, 0x85, 0x00, 0x00, 0, .none }, + .{ .@"test", .mr, .rm64, .r64, .none, .none, 1, 0x85, 0x00, 0x00, 0, .long }, + + .{ .ud2, .np, .none, .none, .none, .none, 2, 0x0f, 0x0b, 0x00, 0, .none }, + + .{ .xor, .zi, .al, .imm8, .none, .none, 1, 0x34, 0x00, 0x00, 0, .none }, + .{ .xor, .zi, .ax, .imm16, .none, .none, 1, 0x35, 0x00, 0x00, 0, .none }, + .{ .xor, .zi, .eax, .imm32, .none, .none, 1, 0x35, 0x00, 0x00, 0, .none }, + .{ .xor, .zi, .rax, .imm32, .none, .none, 1, 0x35, 0x00, 0x00, 0, .long }, + .{ .xor, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 6, .none }, + .{ .xor, .mi, .rm16, .imm16, .none, .none, 1, 0x81, 0x00, 0x00, 6, .none }, + .{ .xor, .mi, .rm32, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 6, .none }, + .{ .xor, .mi, .rm64, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 6, .long }, + .{ .xor, .mi, .rm16, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 6, .none }, + .{ .xor, .mi, .rm32, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 6, .none }, + .{ .xor, .mi, .rm64, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 6, .long }, + .{ .xor, .mr, .rm8, .r8, .none, .none, 1, 0x30, 0x00, 0x00, 0, .none }, + .{ .xor, .mr, .rm16, .r16, .none, .none, 1, 0x31, 0x00, 0x00, 0, .none }, + .{ .xor, .mr, .rm32, .r32, .none, .none, 1, 0x31, 0x00, 0x00, 0, .none }, + .{ .xor, .mr, .rm64, .r64, .none, .none, 1, 0x31, 0x00, 0x00, 0, .long }, + .{ .xor, .rm, .r8, .rm8, .none, .none, 1, 0x32, 0x00, 0x00, 0, .none }, + .{ .xor, .rm, .r16, .rm16, .none, .none, 1, 0x33, 0x00, 0x00, 0, .none }, + .{ .xor, .rm, .r32, .rm32, .none, .none, 1, 0x33, 0x00, 0x00, 0, .none }, + .{ .xor, .rm, .r64, .rm64, .none, .none, 1, 0x33, 0x00, 0x00, 0, .long }, + + // SSE + .{ .addss, .rm, .xmm, .xmm_m32, .none, .none, 3, 0xf3, 0x0f, 0x58, 0, .sse }, + + .{ .cmpss, .rmi, .xmm, .xmm_m32, .imm8, .none, 3, 0xf3, 0x0f, 0xc2, 0, .sse }, + + .{ .movss, .rm, .xmm, .xmm_m32, .none, .none, 3, 0xf3, 0x0f, 0x10, 0, .sse }, + .{ .movss, .mr, .xmm_m32, .xmm, .none, .none, 3, 0xf3, 0x0f, 0x11, 0, .sse }, + + .{ .ucomiss, .rm, .xmm, .xmm_m32, .none, .none, 2, 0x0f, 0x2e, 0x00, 0, .sse }, + + // SSE2 + .{ .addsd, .rm, .xmm, .xmm_m64, .none, .none, 3, 0xf2, 0x0f, 0x58, 0, .sse2 }, + + .{ .cmpsd, .rmi, .xmm, .xmm_m64, .imm8, .none, 3, 0xf2, 0x0f, 0xc2, 0, .sse2 }, + + .{ .movq, .rm, .xmm, .xmm_m64, .none, .none, 3, 0xf3, 0x0f, 0x7e, 0, .sse2 }, + .{ .movq, .mr, .xmm_m64, .xmm, .none, .none, 3, 0x66, 0x0f, 0xd6, 0, .sse2 }, + + .{ .movsd, .rm, .xmm, .xmm_m64, .none, .none, 3, 0xf2, 0x0f, 0x10, 0, .sse2 }, + .{ .movsd, .mr, .xmm_m64, .xmm, .none, .none, 3, 0xf2, 0x0f, 0x11, 0, .sse2 }, + + .{ .ucomisd, .rm, .xmm, .xmm_m64, .none, .none, 3, 0x66, 0x0f, 0x2e, 0, .sse2 }, +}; +// zig fmt: on From f14831ec73d7dd87a770f902fb53d1ede486e524 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sun, 5 Mar 2023 18:47:00 +0100 Subject: [PATCH 079/294] x86_64: truncate immediates --- src/arch/x86_64/CodeGen.zig | 109 +++++++++++++++++++++++++++++------ src/arch/x86_64/Emit.zig | 16 ++--- src/arch/x86_64/Encoding.zig | 6 +- src/arch/x86_64/Mir.zig | 4 +- src/arch/x86_64/encoder.zig | 24 +++----- 5 files changed, 114 insertions(+), 45 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index c108ad6f32..ef599092de 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -385,6 +385,24 @@ pub fn addExtra(self: *Self, extra: anytype) Allocator.Error!u32 { return self.addExtraAssumeCapacity(extra); } +fn extraData(self: *Self, comptime T: type, index: u32) struct { data: T, end: u32 } { + const fields = std.meta.fields(T); + var i: u32 = index; + var result: T = undefined; + inline for (fields) |field| { + @field(result, field.name) = switch (field.type) { + u32 => self.mir_extra.items[i], + i32 => @bitCast(i32, self.mir_extra.items[i]), + else => @compileError("bad field type"), + }; + i += 1; + } + return .{ + .data = result, + .end = i, + }; +} + pub fn addExtraAssumeCapacity(self: *Self, extra: anytype) u32 { const fields = std.meta.fields(@TypeOf(extra)); const result = @intCast(u32, self.mir_extra.items.len); @@ -2759,9 +2777,15 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type 1, 2, 4 => { // TODO this is wasteful! // introduce new MIR tag specifically for mov [reg + 0], imm + const operand = switch (abi_size) { + 1 => @truncate(u8, imm), + 2 => @truncate(u16, imm), + 4 => @truncate(u32, imm), + else => unreachable, + }; const payload = try self.addExtra(Mir.ImmPair{ .dest_off = 0, - .operand = @truncate(u32, imm), + .operand = operand, }); _ = try self.addInst(.{ .tag = .mov_mem_imm, @@ -2872,10 +2896,17 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type return self.fail("TODO saving imm to memory for abi_size {}", .{abi_size}); } + const operand = switch (abi_size) { + 1 => @truncate(u8, imm), + 2 => @truncate(u16, imm), + 4 => @truncate(u32, imm), + 8 => @truncate(u32, imm), + else => unreachable, + }; const payload = try self.addExtra(Mir.ImmPair{ .dest_off = 0, // TODO check if this logic is correct - .operand = @truncate(u32, imm), + .operand = operand, }); const flags: u2 = switch (abi_size) { 1 => 0b00, @@ -3600,7 +3631,13 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu _ = try self.addInst(.{ .tag = mir_tag, .ops = Mir.Inst.Ops.encode(.{ .reg1 = registerAlias(dst_reg, abi_size) }), - .data = .{ .imm = @truncate(u32, imm) }, + .data = .{ .imm = switch (abi_size) { + 1 => @truncate(u8, imm), + 2 => @truncate(u16, imm), + 4 => @truncate(u32, imm), + 8 => @truncate(u32, imm), + else => unreachable, + } }, }); }, .memory, @@ -3671,9 +3708,16 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu 8 => 0b11, else => unreachable, }; + const operand = switch (abi_size) { + 1 => @truncate(u8, imm), + 2 => @truncate(u16, imm), + 4 => @truncate(u32, imm), + 8 => @truncate(u32, imm), + else => unreachable, + }; const payload = try self.addExtra(Mir.ImmPair{ .dest_off = -off, - .operand = @truncate(u32, imm), + .operand = operand, }); _ = try self.addInst(.{ .tag = tag, @@ -4855,7 +4899,13 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u _ = try self.addInst(.{ .tag = .xor, .ops = Mir.Inst.Ops.encode(.{ .reg1 = registerAlias(cond_reg, abi_size) }), - .data = .{ .imm = @intCast(u32, imm) }, + .data = .{ .imm = switch (abi_size) { + 1 => @truncate(u8, imm), + 2 => @truncate(u16, imm), + 4 => @truncate(u32, imm), + 8 => @truncate(u32, imm), + else => unreachable, + } }, }); }, .register => |reg| { @@ -5366,20 +5416,27 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE // We have a positive stack offset value but we want a twos complement negative // offset from rbp, which is at the top of the stack frame. // mov [rbp+offset], immediate + const operand = switch (abi_size) { + 1 => @truncate(u8, imm), + 2 => @truncate(u16, imm), + 4 => @truncate(u32, imm), + else => unreachable, + }; + const flags: u2 = switch (abi_size) { + 1 => 0b00, + 2 => 0b01, + 4 => 0b10, + else => unreachable, + }; const payload = try self.addExtra(Mir.ImmPair{ .dest_off = -stack_offset, - .operand = @truncate(u32, imm), + .operand = operand, }); _ = try self.addInst(.{ .tag = .mov_mem_imm, .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rsp, - .flags = switch (abi_size) { - 1 => 0b00, - 2 => 0b01, - 4 => 0b10, - else => unreachable, - }, + .flags = flags, }), .data = .{ .payload = payload }, }); @@ -5518,7 +5575,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl assert(ty.isError()); const payload = try self.addExtra(Mir.ImmPair{ .dest_off = -stack_offset, - .operand = @truncate(u32, x_big), + .operand = @truncate(u8, x_big), }); _ = try self.addInst(.{ .tag = .mov_mem_imm, @@ -5530,9 +5587,15 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl }); }, 1, 2, 4 => { + const operand = switch (abi_size) { + 1 => @truncate(u8, x_big), + 2 => @truncate(u16, x_big), + 4 => @truncate(u32, x_big), + else => unreachable, + }; const payload = try self.addExtra(Mir.ImmPair{ .dest_off = -stack_offset, - .operand = @truncate(u32, x_big), + .operand = operand, }); _ = try self.addInst(.{ .tag = .mov_mem_imm, @@ -5932,7 +5995,7 @@ fn genInlineMemset( const loop_start = try self.addInst(.{ .tag = .cmp, .ops = Mir.Inst.Ops.encode(.{ .reg1 = index_reg }), - .data = .{ .imm = @bitCast(u32, @as(i32, -1)) }, + .data = .{ .imm = @bitCast(u8, @as(i8, -1)) }, }); // je end @@ -6037,7 +6100,13 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void _ = try self.addInst(.{ .tag = .mov, .ops = Mir.Inst.Ops.encode(.{ .reg1 = registerAlias(reg, abi_size) }), - .data = .{ .imm = @truncate(u32, x) }, + .data = .{ .imm = switch (abi_size) { + 1 => @truncate(u8, x), + 2 => @truncate(u16, x), + 4 => @truncate(u32, x), + 8 => @truncate(u32, x), + else => unreachable, + } }, }); return; } @@ -6204,7 +6273,13 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void .reg1 = registerAlias(reg, abi_size), .flags = 0b01, }), - .data = .{ .imm = @truncate(u32, x) }, + .data = .{ .imm = switch (abi_size) { + 1 => @truncate(u8, x), + 2 => @truncate(u16, x), + 4 => @truncate(u32, x), + 8 => @truncate(u32, x), + else => unreachable, + } }, }); } else { // If this is RAX, we can use a direct load. diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index 1c540adc9d..5d52b87d87 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -236,12 +236,12 @@ fn encode(emit: *Emit, mnemonic: Instruction.Mnemonic, ops: struct { op3: Instruction.Operand = .none, op4: Instruction.Operand = .none, }) InnerError!void { - const inst = try Instruction.new(mnemonic, .{ + const inst = Instruction.new(mnemonic, .{ .op1 = ops.op1, .op2 = ops.op2, .op3 = ops.op3, .op4 = ops.op4, - }); + }) catch unreachable; return inst.encode(emit.code.writer()); } @@ -624,7 +624,7 @@ fn mirArithScaleSrc(emit: *Emit, mnemonic: Instruction.Mnemonic, inst: Mir.Inst. const payload = emit.mir.instructions.items(.data)[inst].payload; const index_reg_disp = emit.mir.extraData(Mir.IndexRegisterDisp, payload).data.decode(); const scale_index = Memory.ScaleIndex{ - .scale = scale, + .scale = @as(u4, 1) << scale, .index = index_reg_disp.index, }; return emit.encode(mnemonic, .{ @@ -643,7 +643,7 @@ fn mirArithScaleDst(emit: *Emit, mnemonic: Instruction.Mnemonic, inst: Mir.Inst. const payload = emit.mir.instructions.items(.data)[inst].payload; const index_reg_disp = emit.mir.extraData(Mir.IndexRegisterDisp, payload).data.decode(); const scale_index = Memory.ScaleIndex{ - .scale = scale, + .scale = @as(u4, 1) << scale, .index = index_reg_disp.index, }; assert(ops.reg2 != .none); @@ -663,7 +663,7 @@ fn mirArithScaleImm(emit: *Emit, mnemonic: Instruction.Mnemonic, inst: Mir.Inst. const payload = emit.mir.instructions.items(.data)[inst].payload; const index_reg_disp_imm = emit.mir.extraData(Mir.IndexRegisterDispImm, payload).data.decode(); const scale_index = Memory.ScaleIndex{ - .scale = scale, + .scale = @as(u4, 1) << scale, .index = index_reg_disp_imm.index, }; return emit.encode(mnemonic, .{ @@ -688,7 +688,7 @@ fn mirArithMemIndexImm(emit: *Emit, mnemonic: Instruction.Mnemonic, inst: Mir.In 0b11 => .qword, }; const scale_index = Memory.ScaleIndex{ - .scale = 0, + .scale = 1, .index = index_reg_disp_imm.index, }; return emit.encode(mnemonic, .{ @@ -777,7 +777,7 @@ fn mirMovabs(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { } else emit.mir.instructions.items(.data)[inst].imm; return emit.encode(.mov, .{ .op1 = .{ .reg = ops.reg1 }, - .op2 = .{ .imm = @bitCast(i64, imm) }, + .op2 = .{ .imm = imm }, }); }, 0b01 => { @@ -983,7 +983,7 @@ fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const index_reg_disp = emit.mir.extraData(Mir.IndexRegisterDisp, payload).data.decode(); const src_reg: ?Register = if (ops.reg2 != .none) ops.reg2 else null; const scale_index = Memory.ScaleIndex{ - .scale = 0, + .scale = 1, .index = index_reg_disp.index, }; return emit.encode(.lea, .{ diff --git a/src/arch/x86_64/Encoding.zig b/src/arch/x86_64/Encoding.zig index 2cccded7ec..7cf8910924 100644 --- a/src/arch/x86_64/Encoding.zig +++ b/src/arch/x86_64/Encoding.zig @@ -390,9 +390,9 @@ pub const Op = enum { .imm => |imm| { if (imm == 1) return .unity; - if (math.cast(i8, imm)) |_| return .imm8; - if (math.cast(i16, imm)) |_| return .imm16; - if (math.cast(i32, imm)) |_| return .imm32; + if (math.cast(u8, imm)) |_| return .imm8; + if (math.cast(u16, imm)) |_| return .imm16; + if (math.cast(u32, imm)) |_| return .imm32; return .imm64; }, } diff --git a/src/arch/x86_64/Mir.zig b/src/arch/x86_64/Mir.zig index b3be08e86b..4124592627 100644 --- a/src/arch/x86_64/Mir.zig +++ b/src/arch/x86_64/Mir.zig @@ -594,9 +594,9 @@ pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void { mir.* = undefined; } -pub fn extraData(mir: Mir, comptime T: type, index: usize) struct { data: T, end: usize } { +pub fn extraData(mir: Mir, comptime T: type, index: u32) struct { data: T, end: u32 } { const fields = std.meta.fields(T); - var i: usize = index; + var i: u32 = index; var result: T = undefined; inline for (fields) |field| { @field(result, field.name) = switch (field.type) { diff --git a/src/arch/x86_64/encoder.zig b/src/arch/x86_64/encoder.zig index 3daffc7ad2..eefc7fd6e2 100644 --- a/src/arch/x86_64/encoder.zig +++ b/src/arch/x86_64/encoder.zig @@ -22,7 +22,7 @@ pub const Instruction = struct { none, reg: Register, mem: Memory, - imm: i64, + imm: u64, /// Returns the bitsize of the operand. /// Asserts the operand is either register or memory. @@ -47,6 +47,7 @@ pub const Instruction = struct { } pub fn fmtPrint(op: Operand, enc_op: Encoding.Op, writer: anytype) !void { + _ = enc_op; switch (op) { .none => {}, .reg => |reg| try writer.writeAll(@tagName(reg)), @@ -92,14 +93,7 @@ pub const Instruction = struct { .moffs => |moffs| try writer.print("{s}:0x{x}", .{ @tagName(moffs.seg), moffs.offset }), }, .imm => |imm| { - if (enc_op == .imm64) { - return writer.print("0x{x}", .{@bitCast(u64, imm)}); - } - const imm_abs = try std.math.absInt(imm); - if (sign(imm) < 0) { - try writer.writeByte('-'); - } - try writer.print("0x{x}", .{imm_abs}); + try writer.print("0x{x}", .{imm}); }, } } @@ -117,7 +111,7 @@ pub const Instruction = struct { .op3 = args.op3, .op4 = args.op4, }) orelse return error.InvalidInstruction; - std.log.debug("{}", .{encoding}); + std.log.warn("{}", .{encoding}); return .{ .op1 = args.op1, .op2 = args.op2, @@ -386,12 +380,12 @@ pub const Instruction = struct { } } - fn encodeImm(imm: i64, kind: Encoding.Op, encoder: anytype) !void { + fn encodeImm(imm: u64, kind: Encoding.Op, encoder: anytype) !void { switch (kind) { - .imm8, .rel8 => try encoder.imm8(@truncate(i8, imm)), - .imm16, .rel16 => try encoder.imm16(@truncate(i16, imm)), - .imm32, .rel32 => try encoder.imm32(@truncate(i32, imm)), - .imm64 => try encoder.imm64(@bitCast(u64, imm)), + .imm8, .rel8 => try encoder.imm8(@bitCast(i8, @truncate(u8, imm))), + .imm16, .rel16 => try encoder.imm16(@bitCast(i16, @truncate(u16, imm))), + .imm32, .rel32 => try encoder.imm32(@bitCast(i32, @truncate(u32, imm))), + .imm64 => try encoder.imm64(imm), else => unreachable, } } From ea3b3e94aba405d4364fdb06231208467ac71f87 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sun, 5 Mar 2023 18:55:04 +0100 Subject: [PATCH 080/294] x86_64: clean up call semantics in codegen --- src/arch/x86_64/CodeGen.zig | 6 +++--- src/arch/x86_64/Emit.zig | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index ef599092de..9f5438a9fa 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -4091,11 +4091,11 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier if (self.bin_file.cast(link.File.Elf)) |elf_file| { const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl); const atom = elf_file.getAtom(atom_index); - const got_addr = @intCast(u32, atom.getOffsetTableAddress(elf_file)); + const got_addr = @intCast(i32, atom.getOffsetTableAddress(elf_file)); _ = try self.addInst(.{ .tag = .call, .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }), - .data = .{ .imm = got_addr }, + .data = .{ .disp = got_addr }, }); } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { const atom_index = try coff_file.getOrCreateAtomForDecl(func.owner_decl); @@ -4142,7 +4142,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier _ = try self.addInst(.{ .tag = .call, .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }), - .data = .{ .imm = @intCast(u32, fn_got_addr) }, + .data = .{ .disp = @intCast(i32, fn_got_addr) }, }); } else unreachable; } else if (func_value.castTag(.extern_fn)) |func_payload| { diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index 5d52b87d87..8bc182d773 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -340,9 +340,9 @@ fn mirJmpCall(emit: *Emit, mnemonic: Instruction.Mnemonic, inst: Mir.Inst.Index) }, 0b01 => { if (ops.reg1 == .none) { - const imm = emit.mir.instructions.items(.data)[inst].imm; + const disp = emit.mir.instructions.items(.data)[inst].disp; return emit.encode(mnemonic, .{ - .op1 = .{ .imm = imm }, + .op1 = .{ .mem = Memory.sib(.qword, .{ .disp = disp }) }, }); } return emit.encode(mnemonic, .{ From bc43cee7756d48064be0dd87f92e24c577788eec Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 7 Mar 2023 12:57:06 +0100 Subject: [PATCH 081/294] Get more things passing --- src/arch/x86_64/CodeGen.zig | 161 ++++++------- src/arch/x86_64/Emit.zig | 74 +++--- src/arch/x86_64/Encoding.zig | 89 +++++--- src/arch/x86_64/Mir.zig | 3 + src/arch/x86_64/bits.zig | 69 +++++- src/arch/x86_64/encoder.zig | 85 +++---- src/arch/x86_64/encodings.zig | 413 +++++++++++++++++----------------- 7 files changed, 476 insertions(+), 418 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 9f5438a9fa..90286a1bb2 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -852,10 +852,10 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void { branch.inst_table.putAssumeCapacity(inst, .dead); switch (prev_value) { .register => |reg| { - self.register_manager.freeReg(reg.to64()); + self.register_manager.freeReg(reg); }, .register_overflow => |ro| { - self.register_manager.freeReg(ro.reg.to64()); + self.register_manager.freeReg(ro.reg); self.eflags_inst = null; }, .eflags => { @@ -1253,7 +1253,13 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { }; defer if (dst_mcv_lock) |lock| self.register_manager.unlockReg(lock); - const mask = ~@as(u64, 0); + const mask = switch (operand_ty.abiSize(self.target.*)) { + 1 => ~@as(u8, 0), + 2 => ~@as(u16, 0), + 4 => ~@as(u32, 0), + 8 => ~@as(u64, 0), + else => unreachable, + }; try self.genBinOpMir(.xor, operand_ty, dst_mcv, .{ .immediate = mask }); break :result dst_mcv; @@ -2777,15 +2783,9 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type 1, 2, 4 => { // TODO this is wasteful! // introduce new MIR tag specifically for mov [reg + 0], imm - const operand = switch (abi_size) { - 1 => @truncate(u8, imm), - 2 => @truncate(u16, imm), - 4 => @truncate(u32, imm), - else => unreachable, - }; const payload = try self.addExtra(Mir.ImmPair{ .dest_off = 0, - .operand = operand, + .operand = @intCast(u32, imm), }); _ = try self.addInst(.{ .tag = .mov_mem_imm, @@ -2896,17 +2896,10 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type return self.fail("TODO saving imm to memory for abi_size {}", .{abi_size}); } - const operand = switch (abi_size) { - 1 => @truncate(u8, imm), - 2 => @truncate(u16, imm), - 4 => @truncate(u32, imm), - 8 => @truncate(u32, imm), - else => unreachable, - }; const payload = try self.addExtra(Mir.ImmPair{ .dest_off = 0, // TODO check if this logic is correct - .operand = operand, + .operand = @intCast(u32, imm), }); const flags: u2 = switch (abi_size) { 1 => 0b00, @@ -3102,8 +3095,8 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { const shift = @intCast(u8, struct_field_offset * @sizeOf(usize)); try self.genShiftBinOpMir(.shr, Type.usize, dst_mcv.register, .{ .immediate = shift }); - // Mask with reg.size() - struct_field_size - const max_reg_bit_width = Register.rax.size(); + // Mask with reg.bitSize() - struct_field_size + const max_reg_bit_width = Register.rax.bitSize(); const mask_shift = @intCast(u6, (max_reg_bit_width - struct_field_ty.bitSize(self.target.*))); const mask = (~@as(u64, 0)) >> mask_shift; @@ -3631,13 +3624,7 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu _ = try self.addInst(.{ .tag = mir_tag, .ops = Mir.Inst.Ops.encode(.{ .reg1 = registerAlias(dst_reg, abi_size) }), - .data = .{ .imm = switch (abi_size) { - 1 => @truncate(u8, imm), - 2 => @truncate(u16, imm), - 4 => @truncate(u32, imm), - 8 => @truncate(u32, imm), - else => unreachable, - } }, + .data = .{ .imm = @intCast(u32, imm) }, }); }, .memory, @@ -3708,16 +3695,9 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu 8 => 0b11, else => unreachable, }; - const operand = switch (abi_size) { - 1 => @truncate(u8, imm), - 2 => @truncate(u16, imm), - 4 => @truncate(u32, imm), - 8 => @truncate(u32, imm), - else => unreachable, - }; const payload = try self.addExtra(Mir.ImmPair{ .dest_off = -off, - .operand = operand, + .operand = @intCast(u32, imm), }); _ = try self.addInst(.{ .tag = tag, @@ -3791,7 +3771,7 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M .reg2 = dst_reg.to32(), .flags = 0b10, }), - .data = .{ .imm = @truncate(u32, imm) }, + .data = .{ .imm = @intCast(u32, imm) }, }); } else { // TODO verify we don't spill and assign to the same register as dst_mcv @@ -4899,13 +4879,7 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u _ = try self.addInst(.{ .tag = .xor, .ops = Mir.Inst.Ops.encode(.{ .reg1 = registerAlias(cond_reg, abi_size) }), - .data = .{ .imm = switch (abi_size) { - 1 => @truncate(u8, imm), - 2 => @truncate(u16, imm), - 4 => @truncate(u32, imm), - 8 => @truncate(u32, imm), - else => unreachable, - } }, + .data = .{ .imm = @intCast(u32, imm) }, }); }, .register => |reg| { @@ -5416,12 +5390,6 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE // We have a positive stack offset value but we want a twos complement negative // offset from rbp, which is at the top of the stack frame. // mov [rbp+offset], immediate - const operand = switch (abi_size) { - 1 => @truncate(u8, imm), - 2 => @truncate(u16, imm), - 4 => @truncate(u32, imm), - else => unreachable, - }; const flags: u2 = switch (abi_size) { 1 => 0b00, 2 => 0b01, @@ -5430,7 +5398,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE }; const payload = try self.addExtra(Mir.ImmPair{ .dest_off = -stack_offset, - .operand = operand, + .operand = @intCast(u32, imm), }); _ = try self.addInst(.{ .tag = .mov_mem_imm, @@ -5575,7 +5543,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl assert(ty.isError()); const payload = try self.addExtra(Mir.ImmPair{ .dest_off = -stack_offset, - .operand = @truncate(u8, x_big), + .operand = @intCast(u32, x_big), }); _ = try self.addInst(.{ .tag = .mov_mem_imm, @@ -5587,15 +5555,9 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl }); }, 1, 2, 4 => { - const operand = switch (abi_size) { - 1 => @truncate(u8, x_big), - 2 => @truncate(u16, x_big), - 4 => @truncate(u32, x_big), - else => unreachable, - }; const payload = try self.addExtra(Mir.ImmPair{ .dest_off = -stack_offset, - .operand = operand, + .operand = @intCast(u32, x_big), }); _ = try self.addInst(.{ .tag = .mov_mem_imm, @@ -5724,7 +5686,7 @@ fn genInlineMemcpyRegisterRegister( src_reg: Register, offset: i32, ) InnerError!void { - assert(dst_reg.size() == 64); + assert(dst_reg.bitSize() == 64); const dst_reg_lock = self.register_manager.lockReg(dst_reg); defer if (dst_reg_lock) |lock| self.register_manager.unlockReg(lock); @@ -5823,7 +5785,7 @@ fn genInlineMemcpy( _ = try self.addInst(.{ .tag = .mov, .ops = Mir.Inst.Ops.encode(.{ - .reg1 = registerAlias(dst_addr_reg, @divExact(reg.size(), 8)), + .reg1 = registerAlias(dst_addr_reg, @intCast(u32, @divExact(reg.bitSize(), 8))), .reg2 = reg, }), .data = undefined, @@ -5852,7 +5814,7 @@ fn genInlineMemcpy( _ = try self.addInst(.{ .tag = .mov, .ops = Mir.Inst.Ops.encode(.{ - .reg1 = registerAlias(src_addr_reg, @divExact(reg.size(), 8)), + .reg1 = registerAlias(src_addr_reg, @intCast(u32, @divExact(reg.bitSize(), 8))), .reg2 = reg, }), .data = undefined, @@ -5976,7 +5938,7 @@ fn genInlineMemset( _ = try self.addInst(.{ .tag = .mov, .ops = Mir.Inst.Ops.encode(.{ - .reg1 = registerAlias(addr_reg, @divExact(reg.size(), 8)), + .reg1 = registerAlias(addr_reg, @intCast(u32, @divExact(reg.bitSize(), 8))), .reg2 = reg, }), .data = undefined, @@ -5994,8 +5956,11 @@ fn genInlineMemset( // cmp index_reg, -1 const loop_start = try self.addInst(.{ .tag = .cmp, - .ops = Mir.Inst.Ops.encode(.{ .reg1 = index_reg }), - .data = .{ .imm = @bitCast(u8, @as(i8, -1)) }, + .ops = Mir.Inst.Ops.encode(.{ + .reg1 = index_reg, + .flags = 0b11, + }), + .data = .{ .imm_s = -1 }, }); // je end @@ -6016,8 +5981,14 @@ fn genInlineMemset( // mov byte ptr [rbp + index_reg + stack_offset], imm _ = try self.addInst(.{ .tag = .mov_mem_index_imm, - .ops = Mir.Inst.Ops.encode(.{ .reg1 = addr_reg }), - .data = .{ .payload = try self.addExtra(Mir.IndexRegisterDispImm.encode(index_reg, 0, @truncate(u32, x))) }, + .ops = Mir.Inst.Ops.encode(.{ + .reg1 = addr_reg, + }), + .data = .{ .payload = try self.addExtra(Mir.IndexRegisterDispImm.encode( + index_reg, + 0, + @intCast(u32, x), + )) }, }); }, else => return self.fail("TODO inline memset for value of type {}", .{value}), @@ -6064,7 +6035,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void if (!self.wantSafety()) return; // The already existing value will do just fine. // Write the debug undefined value. - switch (registerAlias(reg, abi_size).size()) { + switch (registerAlias(reg, abi_size).bitSize()) { 8 => return self.genSetReg(ty, reg, .{ .immediate = 0xaa }), 16 => return self.genSetReg(ty, reg, .{ .immediate = 0xaaaa }), 32 => return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaa }), @@ -6100,13 +6071,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void _ = try self.addInst(.{ .tag = .mov, .ops = Mir.Inst.Ops.encode(.{ .reg1 = registerAlias(reg, abi_size) }), - .data = .{ .imm = switch (abi_size) { - 1 => @truncate(u8, x), - 2 => @truncate(u16, x), - 4 => @truncate(u32, x), - 8 => @truncate(u32, x), - else => unreachable, - } }, + .data = .{ .imm = @intCast(u32, x) }, }); return; } @@ -6273,13 +6238,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void .reg1 = registerAlias(reg, abi_size), .flags = 0b01, }), - .data = .{ .imm = switch (abi_size) { - 1 => @truncate(u8, x), - 2 => @truncate(u16, x), - 4 => @truncate(u32, x), - 8 => @truncate(u32, x), - else => unreachable, - } }, + .data = .{ .disp = @intCast(i32, x) }, }); } else { // If this is RAX, we can use a direct load. @@ -6949,7 +6908,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { if (ret_ty_size == 0) { assert(ret_ty.isError()); result.return_value = .{ .immediate = 0 }; - } else if (ret_ty_size <= 8) { + } else if (ret_ty_size <= 8 and !ret_ty.isRuntimeFloat()) { const aliased_reg = registerAlias(abi.getCAbiIntReturnRegs(self.target.*)[0], ret_ty_size); result.return_value = .{ .register = aliased_reg }; } else { @@ -7024,28 +6983,34 @@ fn parseRegName(name: []const u8) ?Register { /// Returns register wide enough to hold at least `size_bytes`. fn registerAlias(reg: Register, size_bytes: u32) Register { - if (size_bytes == 0) { - unreachable; // should be comptime-known - } else if (size_bytes <= 1) { - return reg.to8(); - } else if (size_bytes <= 2) { - return reg.to16(); - } else if (size_bytes <= 4) { - return reg.to32(); - } else if (size_bytes <= 8) { - return reg.to64(); - } else if (size_bytes <= 16) { - return reg.to128(); - } else if (size_bytes <= 32) { - return reg.to256(); - } else unreachable; + return switch (reg.class()) { + .general_purpose => if (size_bytes == 0) + unreachable // should be comptime-known + else if (size_bytes <= 1) + reg.to8() + else if (size_bytes <= 2) + reg.to16() + else if (size_bytes <= 4) + reg.to32() + else if (size_bytes <= 8) + reg.to64() + else + unreachable, + .floating_point => if (size_bytes <= 16) + reg.to128() + else if (size_bytes <= 32) + reg.to256() + else + unreachable, + .segment => unreachable, + }; } /// Truncates the value in the register in place. /// Clobbers any remaining bits. fn truncateRegister(self: *Self, ty: Type, reg: Register) !void { const int_info = ty.intInfo(self.target.*); - const max_reg_bit_width = Register.rax.size(); + const max_reg_bit_width = Register.rax.bitSize(); switch (int_info.signedness) { .signed => { const shift = @intCast(u6, max_reg_bit_width - int_info.bits); diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index 8bc182d773..e23b500fb8 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -21,6 +21,7 @@ const CodeGen = @import("CodeGen.zig"); const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; const Encoder = bits.Encoder; const ErrorMsg = Module.ErrorMsg; +const Immediate = bits.Immediate; const Instruction = encoder.Instruction; const MCValue = @import("CodeGen.zig").MCValue; const Memory = bits.Memory; @@ -283,7 +284,7 @@ fn mirPushPop(emit: *Emit, mnemonic: Instruction.Mnemonic, inst: Mir.Inst.Index) 0b10 => { const imm = emit.mir.instructions.items(.data)[inst].imm; return emit.encode(.push, .{ - .op1 = .{ .imm = imm }, + .op1 = .{ .imm = Immediate.u(imm) }, }); }, 0b11 => unreachable, @@ -327,9 +328,7 @@ fn mirJmpCall(emit: *Emit, mnemonic: Instruction.Mnemonic, inst: Mir.Inst.Index) const target = emit.mir.instructions.items(.data)[inst].inst; const source = emit.code.items.len; try emit.encode(mnemonic, .{ - .op1 = .{ - .imm = 0, - }, + .op1 = .{ .imm = Immediate.s(0) }, }); try emit.relocs.append(emit.bin_file.allocator, .{ .source = source, @@ -400,7 +399,7 @@ fn mirCondJmp(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { }; const source = emit.code.items.len; try emit.encode(mnemonic, .{ - .op1 = .{ .imm = 0 }, + .op1 = .{ .imm = Immediate.s(0) }, }); try emit.relocs.append(emit.bin_file.allocator, .{ .source = source, @@ -521,7 +520,7 @@ fn mirTest(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const imm = emit.mir.instructions.items(.data)[inst].imm; return emit.encode(.@"test", .{ .op1 = .{ .reg = ops.reg1 }, - .op2 = .{ .imm = imm }, + .op2 = .{ .imm = Immediate.u(imm) }, }); } return emit.encode(.@"test", .{ @@ -543,7 +542,7 @@ fn mirRet(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { 0b10 => { const imm = emit.mir.instructions.items(.data)[inst].imm; return emit.encode(.ret, .{ - .op1 = .{ .imm = imm }, + .op1 = .{ .imm = Immediate.u(imm) }, }); }, 0b11 => { @@ -560,7 +559,7 @@ fn mirArith(emit: *Emit, mnemonic: Instruction.Mnemonic, inst: Mir.Inst.Index) I const imm = emit.mir.instructions.items(.data)[inst].imm; return emit.encode(mnemonic, .{ .op1 = .{ .reg = ops.reg1 }, - .op2 = .{ .imm = imm }, + .op2 = .{ .imm = Immediate.u(imm) }, }); } return emit.encode(mnemonic, .{ @@ -573,7 +572,7 @@ fn mirArith(emit: *Emit, mnemonic: Instruction.Mnemonic, inst: Mir.Inst.Index) I const base: ?Register = if (ops.reg2 != .none) ops.reg2 else null; return emit.encode(mnemonic, .{ .op1 = .{ .reg = ops.reg1 }, - .op2 = .{ .mem = Memory.sib(Memory.PtrSize.fromSize(ops.reg1.size()), .{ + .op2 = .{ .mem = Memory.sib(Memory.PtrSize.fromBitSize(ops.reg1.bitSize()), .{ .base = base, .disp = disp, }) }, @@ -585,7 +584,7 @@ fn mirArith(emit: *Emit, mnemonic: Instruction.Mnemonic, inst: Mir.Inst.Index) I } const disp = emit.mir.instructions.items(.data)[inst].disp; return emit.encode(mnemonic, .{ - .op1 = .{ .mem = Memory.sib(Memory.PtrSize.fromSize(ops.reg2.size()), .{ + .op1 = .{ .mem = Memory.sib(Memory.PtrSize.fromBitSize(ops.reg2.bitSize()), .{ .base = ops.reg1, .disp = disp, }) }, @@ -593,7 +592,11 @@ fn mirArith(emit: *Emit, mnemonic: Instruction.Mnemonic, inst: Mir.Inst.Index) I }); }, 0b11 => { - return emit.fail("TODO unused variant: mov reg1, reg2, 0b11", .{}); + const imm_s = emit.mir.instructions.items(.data)[inst].imm_s; + return emit.encode(mnemonic, .{ + .op1 = .{ .reg = ops.reg1 }, + .op2 = .{ .imm = Immediate.s(imm_s) }, + }); }, } } @@ -614,7 +617,7 @@ fn mirArithMemImm(emit: *Emit, mnemonic: Instruction.Mnemonic, inst: Mir.Inst.In .disp = imm_pair.dest_off, .base = ops.reg1, }) }, - .op2 = .{ .imm = imm_pair.operand }, + .op2 = .{ .imm = Immediate.u(imm_pair.operand) }, }); } @@ -629,7 +632,7 @@ fn mirArithScaleSrc(emit: *Emit, mnemonic: Instruction.Mnemonic, inst: Mir.Inst. }; return emit.encode(mnemonic, .{ .op1 = .{ .reg = ops.reg1 }, - .op2 = .{ .mem = Memory.sib(Memory.PtrSize.fromSize(ops.reg1.size()), .{ + .op2 = .{ .mem = Memory.sib(Memory.PtrSize.fromBitSize(ops.reg1.bitSize()), .{ .base = ops.reg2, .scale_index = scale_index, .disp = index_reg_disp.disp, @@ -648,7 +651,7 @@ fn mirArithScaleDst(emit: *Emit, mnemonic: Instruction.Mnemonic, inst: Mir.Inst. }; assert(ops.reg2 != .none); return emit.encode(mnemonic, .{ - .op1 = .{ .mem = Memory.sib(Memory.PtrSize.fromSize(ops.reg2.size()), .{ + .op1 = .{ .mem = Memory.sib(Memory.PtrSize.fromBitSize(ops.reg2.bitSize()), .{ .base = ops.reg1, .scale_index = scale_index, .disp = index_reg_disp.disp, @@ -672,7 +675,7 @@ fn mirArithScaleImm(emit: *Emit, mnemonic: Instruction.Mnemonic, inst: Mir.Inst. .disp = index_reg_disp_imm.disp, .scale_index = scale_index, }) }, - .op2 = .{ .imm = index_reg_disp_imm.imm }, + .op2 = .{ .imm = Immediate.u(index_reg_disp_imm.imm) }, }); } @@ -697,7 +700,7 @@ fn mirArithMemIndexImm(emit: *Emit, mnemonic: Instruction.Mnemonic, inst: Mir.In .base = ops.reg1, .scale_index = scale_index, }) }, - .op2 = .{ .imm = index_reg_disp_imm.imm }, + .op2 = .{ .imm = Immediate.u(index_reg_disp_imm.imm) }, }); } @@ -708,7 +711,7 @@ fn mirMovSignExtend(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const disp = if (ops.flags != 0b00) emit.mir.instructions.items(.data)[inst].disp else undefined; switch (ops.flags) { 0b00 => { - const mnemonic: Instruction.Mnemonic = if (ops.reg2.size() == 32) .movsxd else .movsx; + const mnemonic: Instruction.Mnemonic = if (ops.reg2.bitSize() == 32) .movsxd else .movsx; return emit.encode(mnemonic, .{ .op1 = .{ .reg = ops.reg1 }, .op2 = .{ .reg = ops.reg2 }, @@ -718,14 +721,15 @@ fn mirMovSignExtend(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const ptr_size: Memory.PtrSize = switch (ops.flags) { 0b01 => .byte, 0b10 => .word, - 0b11 => .qword, + 0b11 => .dword, else => unreachable, }; - return emit.encode(.movsx, .{ + const mnemonic: Instruction.Mnemonic = if (ops.flags == 0b11) .movsxd else .movsx; + return emit.encode(mnemonic, .{ .op1 = .{ .reg = ops.reg1 }, .op2 = .{ .mem = Memory.sib(ptr_size, .{ - .disp = disp, .base = ops.reg2, + .disp = disp, }) }, }); }, @@ -770,19 +774,19 @@ fn mirMovabs(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const ops = emit.mir.instructions.items(.ops)[inst].decode(); switch (ops.flags) { 0b00 => { - const imm: u64 = if (ops.reg1.size() == 64) blk: { + const imm: u64 = if (ops.reg1.bitSize() == 64) blk: { const payload = emit.mir.instructions.items(.data)[inst].payload; const imm = emit.mir.extraData(Mir.Imm64, payload).data; break :blk imm.decode(); } else emit.mir.instructions.items(.data)[inst].imm; return emit.encode(.mov, .{ .op1 = .{ .reg = ops.reg1 }, - .op2 = .{ .imm = imm }, + .op2 = .{ .imm = Immediate.u(imm) }, }); }, 0b01 => { if (ops.reg1 == .none) { - const imm: u64 = if (ops.reg2.size() == 64) blk: { + const imm: u64 = if (ops.reg2.bitSize() == 64) blk: { const payload = emit.mir.instructions.items(.data)[inst].payload; const imm = emit.mir.extraData(Mir.Imm64, payload).data; break :blk imm.decode(); @@ -792,7 +796,7 @@ fn mirMovabs(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { .op2 = .{ .reg = .rax }, }); } - const imm: u64 = if (ops.reg1.size() == 64) blk: { + const imm: u64 = if (ops.reg1.bitSize() == 64) blk: { const payload = emit.mir.instructions.items(.data)[inst].payload; const imm = emit.mir.extraData(Mir.Imm64, payload).data; break :blk imm.decode(); @@ -847,7 +851,7 @@ fn mirShift(emit: *Emit, mnemonic: Instruction.Mnemonic, inst: Mir.Inst.Index) I 0b00 => { return emit.encode(mnemonic, .{ .op1 = .{ .reg = ops.reg1 }, - .op2 = .{ .imm = 1 }, + .op2 = .{ .imm = Immediate.u(1) }, }); }, 0b01 => { @@ -860,7 +864,7 @@ fn mirShift(emit: *Emit, mnemonic: Instruction.Mnemonic, inst: Mir.Inst.Index) I const imm = @truncate(u8, emit.mir.instructions.items(.data)[inst].imm); return emit.encode(mnemonic, .{ .op1 = .{ .reg = ops.reg1 }, - .op2 = .{ .imm = imm }, + .op2 = .{ .imm = Immediate.u(imm) }, }); }, 0b11 => { @@ -920,7 +924,7 @@ fn mirIMulComplex(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { return emit.encode(.imul, .{ .op1 = .{ .reg = ops.reg1 }, .op2 = .{ .reg = ops.reg2 }, - .op3 = .{ .imm = imm }, + .op3 = .{ .imm = Immediate.u(imm) }, }); }, 0b11 => { @@ -932,7 +936,7 @@ fn mirIMulComplex(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { .base = ops.reg2, .disp = imm_pair.dest_off, }) }, - .op3 = .{ .imm = imm_pair.operand }, + .op3 = .{ .imm = Immediate.u(imm_pair.operand) }, }); }, } @@ -959,7 +963,7 @@ fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const src_reg: ?Register = if (ops.reg2 != .none) ops.reg2 else null; return emit.encode(.lea, .{ .op1 = .{ .reg = ops.reg1 }, - .op2 = .{ .mem = Memory.sib(Memory.PtrSize.fromSize(ops.reg1.size()), .{ + .op2 = .{ .mem = Memory.sib(Memory.PtrSize.fromBitSize(ops.reg1.bitSize()), .{ .base = src_reg, .disp = disp, }) }, @@ -969,7 +973,7 @@ fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const start_offset = emit.code.items.len; try emit.encode(.lea, .{ .op1 = .{ .reg = ops.reg1 }, - .op2 = .{ .mem = Memory.rip(Memory.PtrSize.fromSize(ops.reg1.size()), 0) }, + .op2 = .{ .mem = Memory.rip(Memory.PtrSize.fromBitSize(ops.reg1.bitSize()), 0) }, }); const end_offset = emit.code.items.len; // Backpatch the displacement @@ -988,7 +992,7 @@ fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { }; return emit.encode(.lea, .{ .op1 = .{ .reg = ops.reg1 }, - .op2 = .{ .mem = Memory.sib(Memory.PtrSize.fromSize(ops.reg1.size()), .{ + .op2 = .{ .mem = Memory.sib(Memory.PtrSize.fromBitSize(ops.reg1.bitSize()), .{ .base = src_reg, .scale_index = scale_index, .disp = index_reg_disp.disp, @@ -1012,7 +1016,7 @@ fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { try emit.encode(.lea, .{ .op1 = .{ .reg = ops.reg1 }, - .op2 = .{ .mem = Memory.rip(Memory.PtrSize.fromSize(ops.reg1.size()), 0) }, + .op2 = .{ .mem = Memory.rip(Memory.PtrSize.fromBitSize(ops.reg1.bitSize()), 0) }, }); const end_offset = emit.code.items.len; @@ -1065,7 +1069,7 @@ fn mirMovFloat(emit: *Emit, mnemonic: Instruction.Mnemonic, inst: Mir.Inst.Index const disp = emit.mir.instructions.items(.data)[inst].disp; return emit.encode(mnemonic, .{ .op1 = .{ .reg = ops.reg1 }, - .op2 = .{ .mem = Memory.sib(Memory.PtrSize.fromSize(ops.reg2.size()), .{ + .op2 = .{ .mem = Memory.sib(Memory.PtrSize.fromBitSize(ops.reg2.bitSize()), .{ .base = ops.reg2, .disp = disp, }) }, @@ -1074,7 +1078,7 @@ fn mirMovFloat(emit: *Emit, mnemonic: Instruction.Mnemonic, inst: Mir.Inst.Index 0b01 => { const disp = emit.mir.instructions.items(.data)[inst].disp; return emit.encode(mnemonic, .{ - .op1 = .{ .mem = Memory.sib(Memory.PtrSize.fromSize(ops.reg1.size()), .{ + .op1 = .{ .mem = Memory.sib(Memory.PtrSize.fromBitSize(ops.reg1.bitSize()), .{ .base = ops.reg1, .disp = disp, }) }, @@ -1127,7 +1131,7 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const offset = blk: { // callq try emit.encode(.call, .{ - .op1 = .{ .imm = 0 }, + .op1 = .{ .imm = Immediate.s(0) }, }); break :blk @intCast(u32, emit.code.items.len) - 4; }; diff --git a/src/arch/x86_64/Encoding.zig b/src/arch/x86_64/Encoding.zig index 7cf8910924..5d3944a554 100644 --- a/src/arch/x86_64/Encoding.zig +++ b/src/arch/x86_64/Encoding.zig @@ -144,20 +144,20 @@ pub fn findByOpcode(opc: []const u8, prefixes: struct { // rex.W mov dil, 0x1 // Here, rex.W is not needed. const rex_w_allowed = blk: { - const bit_size = enc.operandSize(); + const bit_size = enc.operandBitSize(); break :blk bit_size == 64 or bit_size == 8; }; if (rex_w_allowed) return enc; }, } } else if (prefixes.legacy.prefix_66) { - switch (enc.operandSize()) { + switch (enc.operandBitSize()) { 16 => return enc, else => {}, } } else { if (enc.mode == .none) { - switch (enc.operandSize()) { + switch (enc.operandBitSize()) { 16 => {}, else => return enc, } @@ -187,17 +187,17 @@ pub fn modRmExt(encoding: Encoding) u3 { }; } -pub fn operandSize(encoding: Encoding) u32 { +pub fn operandBitSize(encoding: Encoding) u64 { if (encoding.mode == .long) return 64; - const bit_size: u32 = switch (encoding.op_en) { + const bit_size: u64 = switch (encoding.op_en) { .np => switch (encoding.op1) { .o16 => 16, .o32 => 32, .o64 => 64, else => 32, }, - .td => encoding.op2.size(), - else => encoding.op1.size(), + .td => encoding.op2.bitSize(), + else => encoding.op1.bitSize(), }; return bit_size; } @@ -244,9 +244,9 @@ pub fn format( else => unreachable, }; const tag = switch (op) { - .imm8 => "ib", - .imm16 => "iw", - .imm32 => "id", + .imm8, .imm8s => "ib", + .imm16, .imm16s => "iw", + .imm32, .imm32s => "id", .imm64 => "io", .rel8 => "cb", .rel16 => "cw", @@ -330,6 +330,7 @@ pub const Op = enum { o16, o32, o64, unity, imm8, imm16, imm32, imm64, + imm8s, imm16s, imm32s, al, ax, eax, rax, cl, r8, r16, r32, r64, @@ -349,7 +350,7 @@ pub const Op = enum { .reg => |reg| { switch (reg.class()) { .segment => return .sreg, - .floating_point => return switch (reg.size()) { + .floating_point => return switch (reg.bitSize()) { 128 => .xmm, else => unreachable, }, @@ -362,7 +363,7 @@ pub const Op = enum { else => unreachable, }; if (reg == .cl) return .cl; - return switch (reg.size()) { + return switch (reg.bitSize()) { 8 => .r8, 16 => .r16, 32 => .r32, @@ -376,7 +377,7 @@ pub const Op = enum { .mem => |mem| switch (mem) { .moffs => return .moffs, .sib, .rip => { - const bit_size = mem.size(); + const bit_size = mem.bitSize(); return switch (bit_size) { 8 => .m8, 16 => .m16, @@ -389,21 +390,34 @@ pub const Op = enum { }, .imm => |imm| { - if (imm == 1) return .unity; - if (math.cast(u8, imm)) |_| return .imm8; - if (math.cast(u16, imm)) |_| return .imm16; - if (math.cast(u32, imm)) |_| return .imm32; - return .imm64; + switch (imm) { + .signed => |x| { + if (x == 1) return .unity; + if (math.cast(i8, x)) |_| return .imm8s; + if (math.cast(i16, x)) |_| return .imm16s; + return .imm32s; + }, + .unsigned => |x| { + if (x == 1) return .unity; + if (math.cast(i8, x)) |_| return .imm8s; + if (math.cast(u8, x)) |_| return .imm8; + if (math.cast(i16, x)) |_| return .imm16s; + if (math.cast(u16, x)) |_| return .imm16; + if (math.cast(i32, x)) |_| return .imm32s; + if (math.cast(u32, x)) |_| return .imm32; + return .imm64; + }, + } }, } } - pub fn size(op: Op) u32 { + pub fn bitSize(op: Op) u64 { return switch (op) { - .none, .o16, .o32, .o64, .moffs, .m, .sreg, .unity => unreachable, - .imm8, .al, .cl, .r8, .m8, .rm8, .rel8 => 8, - .imm16, .ax, .r16, .m16, .rm16, .rel16 => 16, - .imm32, .eax, .r32, .m32, .rm32, .rel32, .xmm_m32 => 32, + .none, .o16, .o32, .o64, .moffs, .m, .sreg => unreachable, + .unity, .imm8, .imm8s, .al, .cl, .r8, .m8, .rm8, .rel8 => 8, + .imm16, .imm16s, .ax, .r16, .m16, .rm16, .rel16 => 16, + .imm32, .imm32s, .eax, .r32, .m32, .rm32, .rel32, .xmm_m32 => 32, .imm64, .rax, .r64, .m64, .rm64, .xmm_m64 => 64, .m80 => 80, .xmm => 128, @@ -428,6 +442,7 @@ pub const Op = enum { // zig fmt: off return switch (op) { .imm8, .imm16, .imm32, .imm64, + .imm8s, .imm16s, .imm32s, .rel8, .rel16, .rel32, .unity, => true, @@ -479,28 +494,40 @@ pub const Op = enum { .sse, .sse2 => return op.isFloatingPointRegister() and target.isFloatingPointRegister(), else => switch (target) { .cl, .al, .ax, .eax, .rax => return op == target, - else => return op.size() == target.size(), + else => return op.bitSize() == target.bitSize(), }, } } if (op.isMemory() and target.isMemory()) { switch (target) { .m => return true, - else => return op.size() == target.size(), + else => return op.bitSize() == target.bitSize(), } } if (op.isImmediate() and target.isImmediate()) { switch (target) { - .imm32, .rel32 => switch (op) { - .unity, .imm8, .imm16, .imm32 => return true, + .imm32s, .rel32 => switch (op) { + .unity, .imm8s, .imm8, .imm16s, .imm16, .imm32s => return true, else => return op == target, }, - .imm16, .rel16 => switch (op) { - .unity, .imm8, .imm16 => return true, + .imm32 => switch (op) { + .unity, .imm8, .imm8s, .imm16, .imm16s, .imm32, .imm32s => return true, else => return op == target, }, - .imm8, .rel8 => switch (op) { - .unity, .imm8 => return true, + .imm16s, .rel16 => switch (op) { + .unity, .imm8s, .imm8, .imm16s => return true, + else => return op == target, + }, + .imm16 => switch (op) { + .unity, .imm8, .imm8s, .imm16, .imm16s => return true, + else => return op == target, + }, + .imm8s, .rel8 => switch (op) { + .unity, .imm8s => return true, + else => return op == target, + }, + .imm8 => switch (op) { + .unity, .imm8, .imm8s => return true, else => return op == target, }, else => return op == target, diff --git a/src/arch/x86_64/Mir.zig b/src/arch/x86_64/Mir.zig index 4124592627..43c24216a8 100644 --- a/src/arch/x86_64/Mir.zig +++ b/src/arch/x86_64/Mir.zig @@ -34,6 +34,7 @@ pub const Inst = struct { /// 0b01 reg1, [reg2 + imm32] /// 0b01 reg1, [ds:imm32] /// 0b10 [reg1 + imm32], reg2 + /// 0b11 reg1, imm_s /// Notes: /// * If reg2 is `none` then it means Data field `imm` is used as the immediate. /// * When two imm32 values are required, Data field `payload` points at `ImmPair`. @@ -421,6 +422,8 @@ pub const Inst = struct { inst: Index, /// A 32-bit immediate value. imm: u32, + /// A 32-bit signed immediate value. + imm_s: i32, /// A 32-bit signed displacement value. disp: i32, /// A condition code for use with EFLAGS register. diff --git a/src/arch/x86_64/bits.zig b/src/arch/x86_64/bits.zig index 9166550f16..cd7ed91849 100644 --- a/src/arch/x86_64/bits.zig +++ b/src/arch/x86_64/bits.zig @@ -207,7 +207,7 @@ pub const Register = enum(u7) { return @intCast(u6, @enumToInt(reg) - base); } - pub fn size(reg: Register) u32 { + pub fn bitSize(reg: Register) u64 { return switch (@enumToInt(reg)) { // zig fmt: off @enumToInt(Register.rax) ... @enumToInt(Register.r15) => 64, @@ -273,7 +273,7 @@ pub const Register = enum(u7) { return @truncate(u3, reg.enc()); } - pub fn toSize(reg: Register, bit_size: u32) Register { + pub fn toBitSize(reg: Register, bit_size: u64) Register { return switch (bit_size) { 8 => reg.to8(), 16 => reg.to16(), @@ -334,7 +334,17 @@ pub const Register = enum(u7) { pub fn dwarfLocOp(reg: Register) u8 { return switch (reg.class()) { - .general_purpose => @intCast(u8, @enumToInt(reg) - reg.gpBase()) + DW.OP.reg0, + .general_purpose => switch (reg.to64()) { + .rax => DW.OP.reg0, + .rdx => DW.OP.reg1, + .rcx => DW.OP.reg2, + .rbx => DW.OP.reg3, + .rsi => DW.OP.reg4, + .rdi => DW.OP.reg5, + .rbp => DW.OP.reg6, + .rsp => DW.OP.reg7, + else => @intCast(u8, @enumToInt(reg) - reg.gpBase()) + DW.OP.reg0, + }, .floating_point => @intCast(u8, @enumToInt(reg) - reg.fpBase()) + DW.OP.reg17, else => unreachable, }; @@ -345,7 +355,17 @@ pub const Register = enum(u7) { /// register to a given signed offset. pub fn dwarfLocOpDeref(reg: Register) u8 { return switch (reg.class()) { - .general_purpose => @intCast(u8, @enumToInt(reg) - reg.gpBase()) + DW.OP.breg0, + .general_purpose => switch (reg.to64()) { + .rax => DW.OP.breg0, + .rdx => DW.OP.breg1, + .rcx => DW.OP.breg2, + .rbx => DW.OP.breg3, + .rsi => DW.OP.breg4, + .rdi => DW.OP.breg5, + .rbp => DW.OP.breg6, + .rsp => DW.OP.breg7, + else => @intCast(u8, @enumToInt(reg) - reg.gpBase()) + DW.OP.breg0, + }, .floating_point => @intCast(u8, @enumToInt(reg) - reg.fpBase()) + DW.OP.breg17, else => unreachable, }; @@ -397,7 +417,7 @@ pub const Memory = union(enum) { qword, tbyte, - pub fn fromSize(bit_size: u32) PtrSize { + pub fn fromBitSize(bit_size: u64) PtrSize { return switch (bit_size) { 8 => .byte, 16 => .word, @@ -408,7 +428,7 @@ pub const Memory = union(enum) { }; } - pub fn size(s: PtrSize) u32 { + pub fn bitSize(s: PtrSize) u64 { return switch (s) { .byte => 8, .word => 16, @@ -481,11 +501,42 @@ pub const Memory = union(enum) { }; } - pub fn size(mem: Memory) u32 { + pub fn bitSize(mem: Memory) u64 { return switch (mem) { - .rip => |r| r.ptr_size.size(), - .sib => |s| s.ptr_size.size(), + .rip => |r| r.ptr_size.bitSize(), + .sib => |s| s.ptr_size.bitSize(), .moffs => unreachable, }; } }; + +pub const Immediate = union(enum) { + signed: i32, + unsigned: u64, + + pub fn u(x: u64) Immediate { + return .{ .unsigned = x }; + } + + pub fn s(x: i32) Immediate { + return .{ .signed = x }; + } + + pub fn asUnsigned(imm: Immediate, bit_size: u64) u64 { + return switch (imm) { + .signed => |x| switch (bit_size) { + 8 => @bitCast(u8, @intCast(i8, x)), + 16 => @bitCast(u16, @intCast(i16, x)), + 32 => @bitCast(u32, @intCast(i32, x)), + else => unreachable, + }, + .unsigned => |x| switch (bit_size) { + 8 => @intCast(u8, x), + 16 => @intCast(u16, x), + 32 => @intCast(u32, x), + 64 => x, + else => unreachable, + }, + }; + } +}; diff --git a/src/arch/x86_64/encoder.zig b/src/arch/x86_64/encoder.zig index eefc7fd6e2..d4ca925891 100644 --- a/src/arch/x86_64/encoder.zig +++ b/src/arch/x86_64/encoder.zig @@ -4,6 +4,7 @@ const math = std.math; const bits = @import("bits.zig"); const Encoding = @import("Encoding.zig"); +const Immediate = bits.Immediate; const Memory = bits.Memory; const Moffs = bits.Moffs; const PtrSize = bits.PtrSize; @@ -22,15 +23,14 @@ pub const Instruction = struct { none, reg: Register, mem: Memory, - imm: u64, + imm: Immediate, /// Returns the bitsize of the operand. - /// Asserts the operand is either register or memory. - pub fn size(op: Operand) u64 { + pub fn bitSize(op: Operand) u64 { return switch (op) { .none => unreachable, - .reg => |reg| reg.size(), - .mem => |mem| mem.size(), + .reg => |reg| reg.bitSize(), + .mem => |mem| mem.bitSize(), .imm => unreachable, }; } @@ -47,7 +47,6 @@ pub const Instruction = struct { } pub fn fmtPrint(op: Operand, enc_op: Encoding.Op, writer: anytype) !void { - _ = enc_op; switch (op) { .none => {}, .reg => |reg| try writer.writeAll(@tagName(reg)), @@ -92,9 +91,7 @@ pub const Instruction = struct { }, .moffs => |moffs| try writer.print("{s}:0x{x}", .{ @tagName(moffs.seg), moffs.offset }), }, - .imm => |imm| { - try writer.print("0x{x}", .{imm}); - }, + .imm => |imm| try writer.print("0x{x}", .{imm.asUnsigned(enc_op.bitSize())}), } } }; @@ -110,8 +107,17 @@ pub const Instruction = struct { .op2 = args.op2, .op3 = args.op3, .op4 = args.op4, - }) orelse return error.InvalidInstruction; - std.log.warn("{}", .{encoding}); + }) orelse { + std.log.debug("{s} {s} {s} {s} {s}", .{ + @tagName(mnemonic), + @tagName(Encoding.Op.fromOperand(args.op1)), + @tagName(Encoding.Op.fromOperand(args.op2)), + @tagName(Encoding.Op.fromOperand(args.op3)), + @tagName(Encoding.Op.fromOperand(args.op4)), + }); + return error.InvalidInstruction; + }; + std.log.debug("{}", .{encoding}); return .{ .op1 = args.op1, .op2 = args.op2, @@ -210,7 +216,7 @@ pub const Instruction = struct { var legacy = LegacyPrefixes{}; if (enc.mode == .none) { - const bit_size = enc.operandSize(); + const bit_size = enc.operandBitSize(); if (bit_size == 16) { legacy.set16BitOverride(); } @@ -380,12 +386,13 @@ pub const Instruction = struct { } } - fn encodeImm(imm: u64, kind: Encoding.Op, encoder: anytype) !void { - switch (kind) { - .imm8, .rel8 => try encoder.imm8(@bitCast(i8, @truncate(u8, imm))), - .imm16, .rel16 => try encoder.imm16(@bitCast(i16, @truncate(u16, imm))), - .imm32, .rel32 => try encoder.imm32(@bitCast(i32, @truncate(u32, imm))), - .imm64 => try encoder.imm64(imm), + fn encodeImm(imm: Immediate, kind: Encoding.Op, encoder: anytype) !void { + const raw = imm.asUnsigned(kind.bitSize()); + switch (kind.bitSize()) { + 8 => try encoder.imm8(@intCast(u8, raw)), + 16 => try encoder.imm16(@intCast(u16, raw)), + 32 => try encoder.imm32(@intCast(u32, raw)), + 64 => try encoder.imm64(raw), else => unreachable, } } @@ -732,13 +739,6 @@ fn Encoder(comptime T: type) type { // Trivial (no bit fiddling) // ------------------------- - /// Encode an 8 bit immediate - /// - /// It is sign-extended to 64 bits by the cpu. - pub fn imm8(self: Self, imm: i8) !void { - try self.writer.writeByte(@bitCast(u8, imm)); - } - /// Encode an 8 bit displacement /// /// It is sign-extended to 64 bits by the cpu. @@ -746,20 +746,6 @@ fn Encoder(comptime T: type) type { try self.writer.writeByte(@bitCast(u8, disp)); } - /// Encode an 16 bit immediate - /// - /// It is sign-extended to 64 bits by the cpu. - pub fn imm16(self: Self, imm: i16) !void { - try self.writer.writeIntLittle(i16, imm); - } - - /// Encode an 32 bit immediate - /// - /// It is sign-extended to 64 bits by the cpu. - pub fn imm32(self: Self, imm: i32) !void { - try self.writer.writeIntLittle(i32, imm); - } - /// Encode an 32 bit displacement /// /// It is sign-extended to 64 bits by the cpu. @@ -767,6 +753,27 @@ fn Encoder(comptime T: type) type { try self.writer.writeIntLittle(i32, disp); } + /// Encode an 8 bit immediate + /// + /// It is sign-extended to 64 bits by the cpu. + pub fn imm8(self: Self, imm: u8) !void { + try self.writer.writeByte(imm); + } + + /// Encode an 16 bit immediate + /// + /// It is sign-extended to 64 bits by the cpu. + pub fn imm16(self: Self, imm: u16) !void { + try self.writer.writeIntLittle(u16, imm); + } + + /// Encode an 32 bit immediate + /// + /// It is sign-extended to 64 bits by the cpu. + pub fn imm32(self: Self, imm: u32) !void { + try self.writer.writeIntLittle(u32, imm); + } + /// Encode an 64 bit immediate /// /// It is sign-extended to 64 bits by the cpu. diff --git a/src/arch/x86_64/encodings.zig b/src/arch/x86_64/encodings.zig index b7099d2ad2..c3fb347f22 100644 --- a/src/arch/x86_64/encodings.zig +++ b/src/arch/x86_64/encodings.zig @@ -13,65 +13,65 @@ const Entry = struct { Mnemonic, OpEn, Op, Op, Op, Op, opcode_len, u8, u8, u8, m // zig fmt: off pub const table = &[_]Entry{ // General-purpose - .{ .adc, .zi, .al, .imm8, .none, .none, 1, 0x14, 0x00, 0x00, 0, .none }, - .{ .adc, .zi, .ax, .imm16, .none, .none, 1, 0x15, 0x00, 0x00, 0, .none }, - .{ .adc, .zi, .eax, .imm32, .none, .none, 1, 0x15, 0x00, 0x00, 0, .none }, - .{ .adc, .zi, .rax, .imm32, .none, .none, 1, 0x15, 0x00, 0x00, 0, .long }, - .{ .adc, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 2, .none }, - .{ .adc, .mi, .rm16, .imm16, .none, .none, 1, 0x81, 0x00, 0x00, 2, .none }, - .{ .adc, .mi, .rm32, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 2, .none }, - .{ .adc, .mi, .rm64, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 2, .long }, - .{ .adc, .mi, .rm16, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 2, .none }, - .{ .adc, .mi, .rm32, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 2, .none }, - .{ .adc, .mi, .rm64, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 2, .long }, - .{ .adc, .mr, .rm8, .r8, .none, .none, 1, 0x10, 0x00, 0x00, 0, .none }, - .{ .adc, .mr, .rm16, .r16, .none, .none, 1, 0x11, 0x00, 0x00, 0, .none }, - .{ .adc, .mr, .rm32, .r32, .none, .none, 1, 0x11, 0x00, 0x00, 0, .none }, - .{ .adc, .mr, .rm64, .r64, .none, .none, 1, 0x11, 0x00, 0x00, 0, .long }, - .{ .adc, .rm, .r8, .rm8, .none, .none, 1, 0x12, 0x00, 0x00, 0, .none }, - .{ .adc, .rm, .r16, .rm16, .none, .none, 1, 0x13, 0x00, 0x00, 0, .none }, - .{ .adc, .rm, .r32, .rm32, .none, .none, 1, 0x13, 0x00, 0x00, 0, .none }, - .{ .adc, .rm, .r64, .rm64, .none, .none, 1, 0x13, 0x00, 0x00, 0, .long }, + .{ .adc, .zi, .al, .imm8, .none, .none, 1, 0x14, 0x00, 0x00, 0, .none }, + .{ .adc, .zi, .ax, .imm16, .none, .none, 1, 0x15, 0x00, 0x00, 0, .none }, + .{ .adc, .zi, .eax, .imm32, .none, .none, 1, 0x15, 0x00, 0x00, 0, .none }, + .{ .adc, .zi, .rax, .imm32s, .none, .none, 1, 0x15, 0x00, 0x00, 0, .long }, + .{ .adc, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 2, .none }, + .{ .adc, .mi, .rm16, .imm16, .none, .none, 1, 0x81, 0x00, 0x00, 2, .none }, + .{ .adc, .mi, .rm32, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 2, .none }, + .{ .adc, .mi, .rm64, .imm32s, .none, .none, 1, 0x81, 0x00, 0x00, 2, .long }, + .{ .adc, .mi, .rm16, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 2, .none }, + .{ .adc, .mi, .rm32, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 2, .none }, + .{ .adc, .mi, .rm64, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 2, .long }, + .{ .adc, .mr, .rm8, .r8, .none, .none, 1, 0x10, 0x00, 0x00, 0, .none }, + .{ .adc, .mr, .rm16, .r16, .none, .none, 1, 0x11, 0x00, 0x00, 0, .none }, + .{ .adc, .mr, .rm32, .r32, .none, .none, 1, 0x11, 0x00, 0x00, 0, .none }, + .{ .adc, .mr, .rm64, .r64, .none, .none, 1, 0x11, 0x00, 0x00, 0, .long }, + .{ .adc, .rm, .r8, .rm8, .none, .none, 1, 0x12, 0x00, 0x00, 0, .none }, + .{ .adc, .rm, .r16, .rm16, .none, .none, 1, 0x13, 0x00, 0x00, 0, .none }, + .{ .adc, .rm, .r32, .rm32, .none, .none, 1, 0x13, 0x00, 0x00, 0, .none }, + .{ .adc, .rm, .r64, .rm64, .none, .none, 1, 0x13, 0x00, 0x00, 0, .long }, - .{ .add, .zi, .al, .imm8, .none, .none, 1, 0x04, 0x00, 0x00, 0, .none }, - .{ .add, .zi, .ax, .imm16, .none, .none, 1, 0x05, 0x00, 0x00, 0, .none }, - .{ .add, .zi, .eax, .imm32, .none, .none, 1, 0x05, 0x00, 0x00, 0, .none }, - .{ .add, .zi, .rax, .imm32, .none, .none, 1, 0x05, 0x00, 0x00, 0, .long }, - .{ .add, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 0, .none }, - .{ .add, .mi, .rm16, .imm16, .none, .none, 1, 0x81, 0x00, 0x00, 0, .none }, - .{ .add, .mi, .rm32, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 0, .none }, - .{ .add, .mi, .rm64, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 0, .long }, - .{ .add, .mi, .rm16, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 0, .none }, - .{ .add, .mi, .rm32, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 0, .none }, - .{ .add, .mi, .rm64, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 0, .long }, - .{ .add, .mr, .rm8, .r8, .none, .none, 1, 0x00, 0x00, 0x00, 0, .none }, - .{ .add, .mr, .rm16, .r16, .none, .none, 1, 0x01, 0x00, 0x00, 0, .none }, - .{ .add, .mr, .rm32, .r32, .none, .none, 1, 0x01, 0x00, 0x00, 0, .none }, - .{ .add, .mr, .rm64, .r64, .none, .none, 1, 0x01, 0x00, 0x00, 0, .long }, - .{ .add, .rm, .r8, .rm8, .none, .none, 1, 0x02, 0x00, 0x00, 0, .none }, - .{ .add, .rm, .r16, .rm16, .none, .none, 1, 0x03, 0x00, 0x00, 0, .none }, - .{ .add, .rm, .r32, .rm32, .none, .none, 1, 0x03, 0x00, 0x00, 0, .none }, - .{ .add, .rm, .r64, .rm64, .none, .none, 1, 0x03, 0x00, 0x00, 0, .long }, + .{ .add, .zi, .al, .imm8, .none, .none, 1, 0x04, 0x00, 0x00, 0, .none }, + .{ .add, .zi, .ax, .imm16, .none, .none, 1, 0x05, 0x00, 0x00, 0, .none }, + .{ .add, .zi, .eax, .imm32, .none, .none, 1, 0x05, 0x00, 0x00, 0, .none }, + .{ .add, .zi, .rax, .imm32s, .none, .none, 1, 0x05, 0x00, 0x00, 0, .long }, + .{ .add, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 0, .none }, + .{ .add, .mi, .rm16, .imm16, .none, .none, 1, 0x81, 0x00, 0x00, 0, .none }, + .{ .add, .mi, .rm32, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 0, .none }, + .{ .add, .mi, .rm64, .imm32s, .none, .none, 1, 0x81, 0x00, 0x00, 0, .long }, + .{ .add, .mi, .rm16, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 0, .none }, + .{ .add, .mi, .rm32, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 0, .none }, + .{ .add, .mi, .rm64, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 0, .long }, + .{ .add, .mr, .rm8, .r8, .none, .none, 1, 0x00, 0x00, 0x00, 0, .none }, + .{ .add, .mr, .rm16, .r16, .none, .none, 1, 0x01, 0x00, 0x00, 0, .none }, + .{ .add, .mr, .rm32, .r32, .none, .none, 1, 0x01, 0x00, 0x00, 0, .none }, + .{ .add, .mr, .rm64, .r64, .none, .none, 1, 0x01, 0x00, 0x00, 0, .long }, + .{ .add, .rm, .r8, .rm8, .none, .none, 1, 0x02, 0x00, 0x00, 0, .none }, + .{ .add, .rm, .r16, .rm16, .none, .none, 1, 0x03, 0x00, 0x00, 0, .none }, + .{ .add, .rm, .r32, .rm32, .none, .none, 1, 0x03, 0x00, 0x00, 0, .none }, + .{ .add, .rm, .r64, .rm64, .none, .none, 1, 0x03, 0x00, 0x00, 0, .long }, - .{ .@"and", .zi, .al, .imm8, .none, .none, 1, 0x24, 0x00, 0x00, 0, .none }, - .{ .@"and", .zi, .ax, .imm16, .none, .none, 1, 0x25, 0x00, 0x00, 0, .none }, - .{ .@"and", .zi, .eax, .imm32, .none, .none, 1, 0x25, 0x00, 0x00, 0, .none }, - .{ .@"and", .zi, .rax, .imm32, .none, .none, 1, 0x25, 0x00, 0x00, 0, .long }, - .{ .@"and", .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 4, .none }, - .{ .@"and", .mi, .rm16, .imm16, .none, .none, 1, 0x81, 0x00, 0x00, 4, .none }, - .{ .@"and", .mi, .rm32, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 4, .none }, - .{ .@"and", .mi, .rm64, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 4, .long }, - .{ .@"and", .mi, .rm16, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 4, .none }, - .{ .@"and", .mi, .rm32, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 4, .none }, - .{ .@"and", .mi, .rm64, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 4, .long }, - .{ .@"and", .mr, .rm8, .r8, .none, .none, 1, 0x20, 0x00, 0x00, 0, .none }, - .{ .@"and", .mr, .rm16, .r16, .none, .none, 1, 0x21, 0x00, 0x00, 0, .none }, - .{ .@"and", .mr, .rm32, .r32, .none, .none, 1, 0x21, 0x00, 0x00, 0, .none }, - .{ .@"and", .mr, .rm64, .r64, .none, .none, 1, 0x21, 0x00, 0x00, 0, .long }, - .{ .@"and", .rm, .r8, .rm8, .none, .none, 1, 0x22, 0x00, 0x00, 0, .none }, - .{ .@"and", .rm, .r16, .rm16, .none, .none, 1, 0x23, 0x00, 0x00, 0, .none }, - .{ .@"and", .rm, .r32, .rm32, .none, .none, 1, 0x23, 0x00, 0x00, 0, .none }, - .{ .@"and", .rm, .r64, .rm64, .none, .none, 1, 0x23, 0x00, 0x00, 0, .long }, + .{ .@"and", .zi, .al, .imm8, .none, .none, 1, 0x24, 0x00, 0x00, 0, .none }, + .{ .@"and", .zi, .ax, .imm16, .none, .none, 1, 0x25, 0x00, 0x00, 0, .none }, + .{ .@"and", .zi, .eax, .imm32, .none, .none, 1, 0x25, 0x00, 0x00, 0, .none }, + .{ .@"and", .zi, .rax, .imm32s, .none, .none, 1, 0x25, 0x00, 0x00, 0, .long }, + .{ .@"and", .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 4, .none }, + .{ .@"and", .mi, .rm16, .imm16, .none, .none, 1, 0x81, 0x00, 0x00, 4, .none }, + .{ .@"and", .mi, .rm32, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 4, .none }, + .{ .@"and", .mi, .rm64, .imm32s, .none, .none, 1, 0x81, 0x00, 0x00, 4, .long }, + .{ .@"and", .mi, .rm16, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 4, .none }, + .{ .@"and", .mi, .rm32, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 4, .none }, + .{ .@"and", .mi, .rm64, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 4, .long }, + .{ .@"and", .mr, .rm8, .r8, .none, .none, 1, 0x20, 0x00, 0x00, 0, .none }, + .{ .@"and", .mr, .rm16, .r16, .none, .none, 1, 0x21, 0x00, 0x00, 0, .none }, + .{ .@"and", .mr, .rm32, .r32, .none, .none, 1, 0x21, 0x00, 0x00, 0, .none }, + .{ .@"and", .mr, .rm64, .r64, .none, .none, 1, 0x21, 0x00, 0x00, 0, .long }, + .{ .@"and", .rm, .r8, .rm8, .none, .none, 1, 0x22, 0x00, 0x00, 0, .none }, + .{ .@"and", .rm, .r16, .rm16, .none, .none, 1, 0x23, 0x00, 0x00, 0, .none }, + .{ .@"and", .rm, .r32, .rm32, .none, .none, 1, 0x23, 0x00, 0x00, 0, .none }, + .{ .@"and", .rm, .r64, .rm64, .none, .none, 1, 0x23, 0x00, 0x00, 0, .long }, // This is M encoding according to Intel, but D makes more sense here. .{ .call, .d, .rel32, .none, .none, .none, 1, 0xe8, 0x00, 0x00, 0, .none }, @@ -176,25 +176,25 @@ pub const table = &[_]Entry{ .{ .cmovz, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x44, 0x00, 0, .none }, .{ .cmovz, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x44, 0x00, 0, .long }, - .{ .cmp, .zi, .al, .imm8, .none, .none, 1, 0x3c, 0x00, 0x00, 0, .none }, - .{ .cmp, .zi, .ax, .imm16, .none, .none, 1, 0x3d, 0x00, 0x00, 0, .none }, - .{ .cmp, .zi, .eax, .imm32, .none, .none, 1, 0x3d, 0x00, 0x00, 0, .none }, - .{ .cmp, .zi, .rax, .imm32, .none, .none, 1, 0x3d, 0x00, 0x00, 0, .long }, - .{ .cmp, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 7, .none }, - .{ .cmp, .mi, .rm16, .imm16, .none, .none, 1, 0x81, 0x00, 0x00, 7, .none }, - .{ .cmp, .mi, .rm32, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 7, .none }, - .{ .cmp, .mi, .rm64, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 7, .long }, - .{ .cmp, .mi, .rm16, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 7, .none }, - .{ .cmp, .mi, .rm32, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 7, .none }, - .{ .cmp, .mi, .rm64, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 7, .long }, - .{ .cmp, .mr, .rm8, .r8, .none, .none, 1, 0x38, 0x00, 0x00, 0, .none }, - .{ .cmp, .mr, .rm16, .r16, .none, .none, 1, 0x39, 0x00, 0x00, 0, .none }, - .{ .cmp, .mr, .rm32, .r32, .none, .none, 1, 0x39, 0x00, 0x00, 0, .none }, - .{ .cmp, .mr, .rm64, .r64, .none, .none, 1, 0x39, 0x00, 0x00, 0, .long }, - .{ .cmp, .rm, .r8, .rm8, .none, .none, 1, 0x3a, 0x00, 0x00, 0, .none }, - .{ .cmp, .rm, .r16, .rm16, .none, .none, 1, 0x3b, 0x00, 0x00, 0, .none }, - .{ .cmp, .rm, .r32, .rm32, .none, .none, 1, 0x3b, 0x00, 0x00, 0, .none }, - .{ .cmp, .rm, .r64, .rm64, .none, .none, 1, 0x3b, 0x00, 0x00, 0, .long }, + .{ .cmp, .zi, .al, .imm8, .none, .none, 1, 0x3c, 0x00, 0x00, 0, .none }, + .{ .cmp, .zi, .ax, .imm16, .none, .none, 1, 0x3d, 0x00, 0x00, 0, .none }, + .{ .cmp, .zi, .eax, .imm32, .none, .none, 1, 0x3d, 0x00, 0x00, 0, .none }, + .{ .cmp, .zi, .rax, .imm32s, .none, .none, 1, 0x3d, 0x00, 0x00, 0, .long }, + .{ .cmp, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 7, .none }, + .{ .cmp, .mi, .rm16, .imm16, .none, .none, 1, 0x81, 0x00, 0x00, 7, .none }, + .{ .cmp, .mi, .rm32, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 7, .none }, + .{ .cmp, .mi, .rm64, .imm32s, .none, .none, 1, 0x81, 0x00, 0x00, 7, .long }, + .{ .cmp, .mi, .rm16, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 7, .none }, + .{ .cmp, .mi, .rm32, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 7, .none }, + .{ .cmp, .mi, .rm64, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 7, .long }, + .{ .cmp, .mr, .rm8, .r8, .none, .none, 1, 0x38, 0x00, 0x00, 0, .none }, + .{ .cmp, .mr, .rm16, .r16, .none, .none, 1, 0x39, 0x00, 0x00, 0, .none }, + .{ .cmp, .mr, .rm32, .r32, .none, .none, 1, 0x39, 0x00, 0x00, 0, .none }, + .{ .cmp, .mr, .rm64, .r64, .none, .none, 1, 0x39, 0x00, 0x00, 0, .long }, + .{ .cmp, .rm, .r8, .rm8, .none, .none, 1, 0x3a, 0x00, 0x00, 0, .none }, + .{ .cmp, .rm, .r16, .rm16, .none, .none, 1, 0x3b, 0x00, 0x00, 0, .none }, + .{ .cmp, .rm, .r32, .rm32, .none, .none, 1, 0x3b, 0x00, 0x00, 0, .none }, + .{ .cmp, .rm, .r64, .rm64, .none, .none, 1, 0x3b, 0x00, 0x00, 0, .long }, .{ .div, .m, .rm8, .none, .none, .none, 1, 0xf6, 0x00, 0x00, 6, .none }, .{ .div, .m, .rm16, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 6, .none }, @@ -214,19 +214,19 @@ pub const table = &[_]Entry{ .{ .idiv, .m, .rm32, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 7, .none }, .{ .idiv, .m, .rm64, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 7, .long }, - .{ .imul, .m, .rm8, .none, .none, .none, 1, 0xf6, 0x00, 0x00, 5, .none }, - .{ .imul, .m, .rm16, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 5, .none }, - .{ .imul, .m, .rm32, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 5, .none }, - .{ .imul, .m, .rm64, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 5, .long }, - .{ .imul, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0xaf, 0x00, 0, .none }, - .{ .imul, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0xaf, 0x00, 0, .none }, - .{ .imul, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0xaf, 0x00, 0, .long }, - .{ .imul, .rmi, .r16, .rm16, .imm8, .none, 1, 0x6b, 0x00, 0x00, 0, .none }, - .{ .imul, .rmi, .r32, .rm32, .imm8, .none, 1, 0x6b, 0x00, 0x00, 0, .none }, - .{ .imul, .rmi, .r64, .rm64, .imm8, .none, 1, 0x6b, 0x00, 0x00, 0, .long }, - .{ .imul, .rmi, .r16, .rm16, .imm16, .none, 1, 0x69, 0x00, 0x00, 0, .none }, - .{ .imul, .rmi, .r32, .rm32, .imm32, .none, 1, 0x69, 0x00, 0x00, 0, .none }, - .{ .imul, .rmi, .r64, .rm64, .imm32, .none, 1, 0x69, 0x00, 0x00, 0, .long }, + .{ .imul, .m, .rm8, .none, .none, .none, 1, 0xf6, 0x00, 0x00, 5, .none }, + .{ .imul, .m, .rm16, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 5, .none }, + .{ .imul, .m, .rm32, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 5, .none }, + .{ .imul, .m, .rm64, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 5, .long }, + .{ .imul, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0xaf, 0x00, 0, .none }, + .{ .imul, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0xaf, 0x00, 0, .none }, + .{ .imul, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0xaf, 0x00, 0, .long }, + .{ .imul, .rmi, .r16, .rm16, .imm8s, .none, 1, 0x6b, 0x00, 0x00, 0, .none }, + .{ .imul, .rmi, .r32, .rm32, .imm8s, .none, 1, 0x6b, 0x00, 0x00, 0, .none }, + .{ .imul, .rmi, .r64, .rm64, .imm8s, .none, 1, 0x6b, 0x00, 0x00, 0, .long }, + .{ .imul, .rmi, .r16, .rm16, .imm16, .none, 1, 0x69, 0x00, 0x00, 0, .none }, + .{ .imul, .rmi, .r32, .rm32, .imm32, .none, 1, 0x69, 0x00, 0x00, 0, .none }, + .{ .imul, .rmi, .r64, .rm64, .imm32, .none, 1, 0x69, 0x00, 0x00, 0, .long }, .{ .int3, .np, .none, .none, .none, .none, 1, 0xcc, 0x00, 0x00, 0, .none }, @@ -269,34 +269,34 @@ pub const table = &[_]Entry{ .{ .lea, .rm, .r32, .m, .none, .none, 1, 0x8d, 0x00, 0x00, 0, .none }, .{ .lea, .rm, .r64, .m, .none, .none, 1, 0x8d, 0x00, 0x00, 0, .long }, - .{ .mov, .mr, .rm8, .r8, .none, .none, 1, 0x88, 0x00, 0x00, 0, .none }, - .{ .mov, .mr, .rm16, .r16, .none, .none, 1, 0x89, 0x00, 0x00, 0, .none }, - .{ .mov, .mr, .rm32, .r32, .none, .none, 1, 0x89, 0x00, 0x00, 0, .none }, - .{ .mov, .mr, .rm64, .r64, .none, .none, 1, 0x89, 0x00, 0x00, 0, .long }, - .{ .mov, .rm, .r8, .rm8, .none, .none, 1, 0x8a, 0x00, 0x00, 0, .none }, - .{ .mov, .rm, .r16, .rm16, .none, .none, 1, 0x8b, 0x00, 0x00, 0, .none }, - .{ .mov, .rm, .r32, .rm32, .none, .none, 1, 0x8b, 0x00, 0x00, 0, .none }, - .{ .mov, .rm, .r64, .rm64, .none, .none, 1, 0x8b, 0x00, 0x00, 0, .long }, - .{ .mov, .mr, .rm16, .sreg, .none, .none, 1, 0x8c, 0x00, 0x00, 0, .none }, - .{ .mov, .mr, .rm64, .sreg, .none, .none, 1, 0x8c, 0x00, 0x00, 0, .long }, - .{ .mov, .rm, .sreg, .rm16, .none, .none, 1, 0x8e, 0x00, 0x00, 0, .none }, - .{ .mov, .rm, .sreg, .rm64, .none, .none, 1, 0x8e, 0x00, 0x00, 0, .long }, - .{ .mov, .fd, .al, .moffs, .none, .none, 1, 0xa0, 0x00, 0x00, 0, .none }, - .{ .mov, .fd, .ax, .moffs, .none, .none, 1, 0xa1, 0x00, 0x00, 0, .none }, - .{ .mov, .fd, .eax, .moffs, .none, .none, 1, 0xa1, 0x00, 0x00, 0, .none }, - .{ .mov, .fd, .rax, .moffs, .none, .none, 1, 0xa1, 0x00, 0x00, 0, .long }, - .{ .mov, .td, .moffs, .al, .none, .none, 1, 0xa2, 0x00, 0x00, 0, .none }, - .{ .mov, .td, .moffs, .ax, .none, .none, 1, 0xa3, 0x00, 0x00, 0, .none }, - .{ .mov, .td, .moffs, .eax, .none, .none, 1, 0xa3, 0x00, 0x00, 0, .none }, - .{ .mov, .td, .moffs, .rax, .none, .none, 1, 0xa3, 0x00, 0x00, 0, .long }, - .{ .mov, .oi, .r8, .imm8, .none, .none, 1, 0xb0, 0x00, 0x00, 0, .none }, - .{ .mov, .oi, .r16, .imm16, .none, .none, 1, 0xb8, 0x00, 0x00, 0, .none }, - .{ .mov, .oi, .r32, .imm32, .none, .none, 1, 0xb8, 0x00, 0x00, 0, .none }, - .{ .mov, .oi, .r64, .imm64, .none, .none, 1, 0xb8, 0x00, 0x00, 0, .long }, - .{ .mov, .mi, .rm8, .imm8, .none, .none, 1, 0xc6, 0x00, 0x00, 0, .none }, - .{ .mov, .mi, .rm16, .imm16, .none, .none, 1, 0xc7, 0x00, 0x00, 0, .none }, - .{ .mov, .mi, .rm32, .imm32, .none, .none, 1, 0xc7, 0x00, 0x00, 0, .none }, - .{ .mov, .mi, .rm64, .imm32, .none, .none, 1, 0xc7, 0x00, 0x00, 0, .long }, + .{ .mov, .mr, .rm8, .r8, .none, .none, 1, 0x88, 0x00, 0x00, 0, .none }, + .{ .mov, .mr, .rm16, .r16, .none, .none, 1, 0x89, 0x00, 0x00, 0, .none }, + .{ .mov, .mr, .rm32, .r32, .none, .none, 1, 0x89, 0x00, 0x00, 0, .none }, + .{ .mov, .mr, .rm64, .r64, .none, .none, 1, 0x89, 0x00, 0x00, 0, .long }, + .{ .mov, .rm, .r8, .rm8, .none, .none, 1, 0x8a, 0x00, 0x00, 0, .none }, + .{ .mov, .rm, .r16, .rm16, .none, .none, 1, 0x8b, 0x00, 0x00, 0, .none }, + .{ .mov, .rm, .r32, .rm32, .none, .none, 1, 0x8b, 0x00, 0x00, 0, .none }, + .{ .mov, .rm, .r64, .rm64, .none, .none, 1, 0x8b, 0x00, 0x00, 0, .long }, + .{ .mov, .mr, .rm16, .sreg, .none, .none, 1, 0x8c, 0x00, 0x00, 0, .none }, + .{ .mov, .mr, .rm64, .sreg, .none, .none, 1, 0x8c, 0x00, 0x00, 0, .long }, + .{ .mov, .rm, .sreg, .rm16, .none, .none, 1, 0x8e, 0x00, 0x00, 0, .none }, + .{ .mov, .rm, .sreg, .rm64, .none, .none, 1, 0x8e, 0x00, 0x00, 0, .long }, + .{ .mov, .fd, .al, .moffs, .none, .none, 1, 0xa0, 0x00, 0x00, 0, .none }, + .{ .mov, .fd, .ax, .moffs, .none, .none, 1, 0xa1, 0x00, 0x00, 0, .none }, + .{ .mov, .fd, .eax, .moffs, .none, .none, 1, 0xa1, 0x00, 0x00, 0, .none }, + .{ .mov, .fd, .rax, .moffs, .none, .none, 1, 0xa1, 0x00, 0x00, 0, .long }, + .{ .mov, .td, .moffs, .al, .none, .none, 1, 0xa2, 0x00, 0x00, 0, .none }, + .{ .mov, .td, .moffs, .ax, .none, .none, 1, 0xa3, 0x00, 0x00, 0, .none }, + .{ .mov, .td, .moffs, .eax, .none, .none, 1, 0xa3, 0x00, 0x00, 0, .none }, + .{ .mov, .td, .moffs, .rax, .none, .none, 1, 0xa3, 0x00, 0x00, 0, .long }, + .{ .mov, .oi, .r8, .imm8, .none, .none, 1, 0xb0, 0x00, 0x00, 0, .none }, + .{ .mov, .oi, .r16, .imm16, .none, .none, 1, 0xb8, 0x00, 0x00, 0, .none }, + .{ .mov, .oi, .r32, .imm32, .none, .none, 1, 0xb8, 0x00, 0x00, 0, .none }, + .{ .mov, .oi, .r64, .imm64, .none, .none, 1, 0xb8, 0x00, 0x00, 0, .long }, + .{ .mov, .mi, .rm8, .imm8, .none, .none, 1, 0xc6, 0x00, 0x00, 0, .none }, + .{ .mov, .mi, .rm16, .imm16, .none, .none, 1, 0xc7, 0x00, 0x00, 0, .none }, + .{ .mov, .mi, .rm32, .imm32, .none, .none, 1, 0xc7, 0x00, 0x00, 0, .none }, + .{ .mov, .mi, .rm64, .imm32s, .none, .none, 1, 0xc7, 0x00, 0x00, 0, .long }, .{ .movsx, .rm, .r16, .rm8, .none, .none, 2, 0x0f, 0xbe, 0x00, 0, .none }, .{ .movsx, .rm, .r32, .rm8, .none, .none, 2, 0x0f, 0xbe, 0x00, 0, .none }, @@ -321,25 +321,25 @@ pub const table = &[_]Entry{ .{ .nop, .np, .none, .none, .none, .none, 1, 0x90, 0x00, 0x00, 0, .none }, - .{ .@"or", .zi, .al, .imm8, .none, .none, 1, 0x0c, 0x00, 0x00, 0, .none }, - .{ .@"or", .zi, .ax, .imm16, .none, .none, 1, 0x0d, 0x00, 0x00, 0, .none }, - .{ .@"or", .zi, .eax, .imm32, .none, .none, 1, 0x0d, 0x00, 0x00, 0, .none }, - .{ .@"or", .zi, .rax, .imm32, .none, .none, 1, 0x0d, 0x00, 0x00, 0, .long }, - .{ .@"or", .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 1, .none }, - .{ .@"or", .mi, .rm16, .imm16, .none, .none, 1, 0x81, 0x00, 0x00, 1, .none }, - .{ .@"or", .mi, .rm32, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 1, .none }, - .{ .@"or", .mi, .rm64, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 1, .long }, - .{ .@"or", .mi, .rm16, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 1, .none }, - .{ .@"or", .mi, .rm32, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 1, .none }, - .{ .@"or", .mi, .rm64, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 1, .long }, - .{ .@"or", .mr, .rm8, .r8, .none, .none, 1, 0x08, 0x00, 0x00, 0, .none }, - .{ .@"or", .mr, .rm16, .r16, .none, .none, 1, 0x09, 0x00, 0x00, 0, .none }, - .{ .@"or", .mr, .rm32, .r32, .none, .none, 1, 0x09, 0x00, 0x00, 0, .none }, - .{ .@"or", .mr, .rm64, .r64, .none, .none, 1, 0x09, 0x00, 0x00, 0, .long }, - .{ .@"or", .rm, .r8, .rm8, .none, .none, 1, 0x0a, 0x00, 0x00, 0, .none }, - .{ .@"or", .rm, .r16, .rm16, .none, .none, 1, 0x0b, 0x00, 0x00, 0, .none }, - .{ .@"or", .rm, .r32, .rm32, .none, .none, 1, 0x0b, 0x00, 0x00, 0, .none }, - .{ .@"or", .rm, .r64, .rm64, .none, .none, 1, 0x0b, 0x00, 0x00, 0, .long }, + .{ .@"or", .zi, .al, .imm8, .none, .none, 1, 0x0c, 0x00, 0x00, 0, .none }, + .{ .@"or", .zi, .ax, .imm16, .none, .none, 1, 0x0d, 0x00, 0x00, 0, .none }, + .{ .@"or", .zi, .eax, .imm32, .none, .none, 1, 0x0d, 0x00, 0x00, 0, .none }, + .{ .@"or", .zi, .rax, .imm32s, .none, .none, 1, 0x0d, 0x00, 0x00, 0, .long }, + .{ .@"or", .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 1, .none }, + .{ .@"or", .mi, .rm16, .imm16, .none, .none, 1, 0x81, 0x00, 0x00, 1, .none }, + .{ .@"or", .mi, .rm32, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 1, .none }, + .{ .@"or", .mi, .rm64, .imm32s, .none, .none, 1, 0x81, 0x00, 0x00, 1, .long }, + .{ .@"or", .mi, .rm16, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 1, .none }, + .{ .@"or", .mi, .rm32, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 1, .none }, + .{ .@"or", .mi, .rm64, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 1, .long }, + .{ .@"or", .mr, .rm8, .r8, .none, .none, 1, 0x08, 0x00, 0x00, 0, .none }, + .{ .@"or", .mr, .rm16, .r16, .none, .none, 1, 0x09, 0x00, 0x00, 0, .none }, + .{ .@"or", .mr, .rm32, .r32, .none, .none, 1, 0x09, 0x00, 0x00, 0, .none }, + .{ .@"or", .mr, .rm64, .r64, .none, .none, 1, 0x09, 0x00, 0x00, 0, .long }, + .{ .@"or", .rm, .r8, .rm8, .none, .none, 1, 0x0a, 0x00, 0x00, 0, .none }, + .{ .@"or", .rm, .r16, .rm16, .none, .none, 1, 0x0b, 0x00, 0x00, 0, .none }, + .{ .@"or", .rm, .r32, .rm32, .none, .none, 1, 0x0b, 0x00, 0x00, 0, .none }, + .{ .@"or", .rm, .r64, .rm64, .none, .none, 1, 0x0b, 0x00, 0x00, 0, .long }, .{ .pop, .o, .r16, .none, .none, .none, 1, 0x58, 0x00, 0x00, 0, .none }, .{ .pop, .o, .r64, .none, .none, .none, 1, 0x58, 0x00, 0x00, 0, .none }, @@ -382,25 +382,25 @@ pub const table = &[_]Entry{ .{ .sar, .mi, .rm32, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 7, .none }, .{ .sar, .mi, .rm64, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 7, .long }, - .{ .sbb, .zi, .al, .imm8, .none, .none, 1, 0x1c, 0x00, 0x00, 0, .none }, - .{ .sbb, .zi, .ax, .imm16, .none, .none, 1, 0x1d, 0x00, 0x00, 0, .none }, - .{ .sbb, .zi, .eax, .imm32, .none, .none, 1, 0x1d, 0x00, 0x00, 0, .none }, - .{ .sbb, .zi, .rax, .imm32, .none, .none, 1, 0x1d, 0x00, 0x00, 0, .long }, - .{ .sbb, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 3, .none }, - .{ .sbb, .mi, .rm16, .imm16, .none, .none, 1, 0x81, 0x00, 0x00, 3, .none }, - .{ .sbb, .mi, .rm32, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 3, .none }, - .{ .sbb, .mi, .rm64, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 3, .long }, - .{ .sbb, .mi, .rm16, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 3, .none }, - .{ .sbb, .mi, .rm32, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 3, .none }, - .{ .sbb, .mi, .rm64, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 3, .long }, - .{ .sbb, .mr, .rm8, .r8, .none, .none, 1, 0x18, 0x00, 0x00, 0, .none }, - .{ .sbb, .mr, .rm16, .r16, .none, .none, 1, 0x19, 0x00, 0x00, 0, .none }, - .{ .sbb, .mr, .rm32, .r32, .none, .none, 1, 0x19, 0x00, 0x00, 0, .none }, - .{ .sbb, .mr, .rm64, .r64, .none, .none, 1, 0x19, 0x00, 0x00, 0, .long }, - .{ .sbb, .rm, .r8, .rm8, .none, .none, 1, 0x1a, 0x00, 0x00, 0, .none }, - .{ .sbb, .rm, .r16, .rm16, .none, .none, 1, 0x1b, 0x00, 0x00, 0, .none }, - .{ .sbb, .rm, .r32, .rm32, .none, .none, 1, 0x1b, 0x00, 0x00, 0, .none }, - .{ .sbb, .rm, .r64, .rm64, .none, .none, 1, 0x1b, 0x00, 0x00, 0, .long }, + .{ .sbb, .zi, .al, .imm8, .none, .none, 1, 0x1c, 0x00, 0x00, 0, .none }, + .{ .sbb, .zi, .ax, .imm16, .none, .none, 1, 0x1d, 0x00, 0x00, 0, .none }, + .{ .sbb, .zi, .eax, .imm32, .none, .none, 1, 0x1d, 0x00, 0x00, 0, .none }, + .{ .sbb, .zi, .rax, .imm32s, .none, .none, 1, 0x1d, 0x00, 0x00, 0, .long }, + .{ .sbb, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 3, .none }, + .{ .sbb, .mi, .rm16, .imm16, .none, .none, 1, 0x81, 0x00, 0x00, 3, .none }, + .{ .sbb, .mi, .rm32, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 3, .none }, + .{ .sbb, .mi, .rm64, .imm32s, .none, .none, 1, 0x81, 0x00, 0x00, 3, .long }, + .{ .sbb, .mi, .rm16, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 3, .none }, + .{ .sbb, .mi, .rm32, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 3, .none }, + .{ .sbb, .mi, .rm64, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 3, .long }, + .{ .sbb, .mr, .rm8, .r8, .none, .none, 1, 0x18, 0x00, 0x00, 0, .none }, + .{ .sbb, .mr, .rm16, .r16, .none, .none, 1, 0x19, 0x00, 0x00, 0, .none }, + .{ .sbb, .mr, .rm32, .r32, .none, .none, 1, 0x19, 0x00, 0x00, 0, .none }, + .{ .sbb, .mr, .rm64, .r64, .none, .none, 1, 0x19, 0x00, 0x00, 0, .long }, + .{ .sbb, .rm, .r8, .rm8, .none, .none, 1, 0x1a, 0x00, 0x00, 0, .none }, + .{ .sbb, .rm, .r16, .rm16, .none, .none, 1, 0x1b, 0x00, 0x00, 0, .none }, + .{ .sbb, .rm, .r32, .rm32, .none, .none, 1, 0x1b, 0x00, 0x00, 0, .none }, + .{ .sbb, .rm, .r64, .rm64, .none, .none, 1, 0x1b, 0x00, 0x00, 0, .long }, .{ .seta, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x97, 0x00, 0, .none }, .{ .setae, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x93, 0x00, 0, .none }, @@ -459,62 +459,62 @@ pub const table = &[_]Entry{ .{ .shr, .mi, .rm32, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 5, .none }, .{ .shr, .mi, .rm64, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 5, .long }, - .{ .sub, .zi, .al, .imm8, .none, .none, 1, 0x2c, 0x00, 0x00, 0, .none }, - .{ .sub, .zi, .ax, .imm16, .none, .none, 1, 0x2d, 0x00, 0x00, 0, .none }, - .{ .sub, .zi, .eax, .imm32, .none, .none, 1, 0x2d, 0x00, 0x00, 0, .none }, - .{ .sub, .zi, .rax, .imm32, .none, .none, 1, 0x2d, 0x00, 0x00, 0, .long }, - .{ .sub, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 5, .none }, - .{ .sub, .mi, .rm16, .imm16, .none, .none, 1, 0x81, 0x00, 0x00, 5, .none }, - .{ .sub, .mi, .rm32, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 5, .none }, - .{ .sub, .mi, .rm64, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 5, .long }, - .{ .sub, .mi, .rm16, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 5, .none }, - .{ .sub, .mi, .rm32, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 5, .none }, - .{ .sub, .mi, .rm64, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 5, .long }, - .{ .sub, .mr, .rm8, .r8, .none, .none, 1, 0x28, 0x00, 0x00, 0, .none }, - .{ .sub, .mr, .rm16, .r16, .none, .none, 1, 0x29, 0x00, 0x00, 0, .none }, - .{ .sub, .mr, .rm32, .r32, .none, .none, 1, 0x29, 0x00, 0x00, 0, .none }, - .{ .sub, .mr, .rm64, .r64, .none, .none, 1, 0x29, 0x00, 0x00, 0, .long }, - .{ .sub, .rm, .r8, .rm8, .none, .none, 1, 0x2a, 0x00, 0x00, 0, .none }, - .{ .sub, .rm, .r16, .rm16, .none, .none, 1, 0x2b, 0x00, 0x00, 0, .none }, - .{ .sub, .rm, .r32, .rm32, .none, .none, 1, 0x2b, 0x00, 0x00, 0, .none }, - .{ .sub, .rm, .r64, .rm64, .none, .none, 1, 0x2b, 0x00, 0x00, 0, .long }, + .{ .sub, .zi, .al, .imm8, .none, .none, 1, 0x2c, 0x00, 0x00, 0, .none }, + .{ .sub, .zi, .ax, .imm16, .none, .none, 1, 0x2d, 0x00, 0x00, 0, .none }, + .{ .sub, .zi, .eax, .imm32, .none, .none, 1, 0x2d, 0x00, 0x00, 0, .none }, + .{ .sub, .zi, .rax, .imm32s, .none, .none, 1, 0x2d, 0x00, 0x00, 0, .long }, + .{ .sub, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 5, .none }, + .{ .sub, .mi, .rm16, .imm16, .none, .none, 1, 0x81, 0x00, 0x00, 5, .none }, + .{ .sub, .mi, .rm32, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 5, .none }, + .{ .sub, .mi, .rm64, .imm32s, .none, .none, 1, 0x81, 0x00, 0x00, 5, .long }, + .{ .sub, .mi, .rm16, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 5, .none }, + .{ .sub, .mi, .rm32, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 5, .none }, + .{ .sub, .mi, .rm64, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 5, .long }, + .{ .sub, .mr, .rm8, .r8, .none, .none, 1, 0x28, 0x00, 0x00, 0, .none }, + .{ .sub, .mr, .rm16, .r16, .none, .none, 1, 0x29, 0x00, 0x00, 0, .none }, + .{ .sub, .mr, .rm32, .r32, .none, .none, 1, 0x29, 0x00, 0x00, 0, .none }, + .{ .sub, .mr, .rm64, .r64, .none, .none, 1, 0x29, 0x00, 0x00, 0, .long }, + .{ .sub, .rm, .r8, .rm8, .none, .none, 1, 0x2a, 0x00, 0x00, 0, .none }, + .{ .sub, .rm, .r16, .rm16, .none, .none, 1, 0x2b, 0x00, 0x00, 0, .none }, + .{ .sub, .rm, .r32, .rm32, .none, .none, 1, 0x2b, 0x00, 0x00, 0, .none }, + .{ .sub, .rm, .r64, .rm64, .none, .none, 1, 0x2b, 0x00, 0x00, 0, .long }, .{ .syscall, .np, .none, .none, .none, .none, 2, 0x0f, 0x05, 0x00, 0, .none }, - .{ .@"test", .zi, .al, .imm8, .none, .none, 1, 0xa8, 0x00, 0x00, 0, .none }, - .{ .@"test", .zi, .ax, .imm16, .none, .none, 1, 0xa9, 0x00, 0x00, 0, .none }, - .{ .@"test", .zi, .eax, .imm32, .none, .none, 1, 0xa9, 0x00, 0x00, 0, .none }, - .{ .@"test", .zi, .rax, .imm32, .none, .none, 1, 0xa9, 0x00, 0x00, 0, .long }, - .{ .@"test", .mi, .rm8, .imm8, .none, .none, 1, 0xf6, 0x00, 0x00, 0, .none }, - .{ .@"test", .mi, .rm16, .imm16, .none, .none, 1, 0xf7, 0x00, 0x00, 0, .none }, - .{ .@"test", .mi, .rm32, .imm32, .none, .none, 1, 0xf7, 0x00, 0x00, 0, .none }, - .{ .@"test", .mi, .rm64, .imm32, .none, .none, 1, 0xf7, 0x00, 0x00, 0, .long }, - .{ .@"test", .mr, .rm8, .r8, .none, .none, 1, 0x84, 0x00, 0x00, 0, .none }, - .{ .@"test", .mr, .rm16, .r16, .none, .none, 1, 0x85, 0x00, 0x00, 0, .none }, - .{ .@"test", .mr, .rm32, .r32, .none, .none, 1, 0x85, 0x00, 0x00, 0, .none }, - .{ .@"test", .mr, .rm64, .r64, .none, .none, 1, 0x85, 0x00, 0x00, 0, .long }, + .{ .@"test", .zi, .al, .imm8, .none, .none, 1, 0xa8, 0x00, 0x00, 0, .none }, + .{ .@"test", .zi, .ax, .imm16, .none, .none, 1, 0xa9, 0x00, 0x00, 0, .none }, + .{ .@"test", .zi, .eax, .imm32, .none, .none, 1, 0xa9, 0x00, 0x00, 0, .none }, + .{ .@"test", .zi, .rax, .imm32s, .none, .none, 1, 0xa9, 0x00, 0x00, 0, .long }, + .{ .@"test", .mi, .rm8, .imm8, .none, .none, 1, 0xf6, 0x00, 0x00, 0, .none }, + .{ .@"test", .mi, .rm16, .imm16, .none, .none, 1, 0xf7, 0x00, 0x00, 0, .none }, + .{ .@"test", .mi, .rm32, .imm32, .none, .none, 1, 0xf7, 0x00, 0x00, 0, .none }, + .{ .@"test", .mi, .rm64, .imm32s, .none, .none, 1, 0xf7, 0x00, 0x00, 0, .long }, + .{ .@"test", .mr, .rm8, .r8, .none, .none, 1, 0x84, 0x00, 0x00, 0, .none }, + .{ .@"test", .mr, .rm16, .r16, .none, .none, 1, 0x85, 0x00, 0x00, 0, .none }, + .{ .@"test", .mr, .rm32, .r32, .none, .none, 1, 0x85, 0x00, 0x00, 0, .none }, + .{ .@"test", .mr, .rm64, .r64, .none, .none, 1, 0x85, 0x00, 0x00, 0, .long }, - .{ .ud2, .np, .none, .none, .none, .none, 2, 0x0f, 0x0b, 0x00, 0, .none }, + .{ .ud2, .np, .none, .none, .none, .none, 2, 0x0f, 0x0b, 0x00, 0, .none }, - .{ .xor, .zi, .al, .imm8, .none, .none, 1, 0x34, 0x00, 0x00, 0, .none }, - .{ .xor, .zi, .ax, .imm16, .none, .none, 1, 0x35, 0x00, 0x00, 0, .none }, - .{ .xor, .zi, .eax, .imm32, .none, .none, 1, 0x35, 0x00, 0x00, 0, .none }, - .{ .xor, .zi, .rax, .imm32, .none, .none, 1, 0x35, 0x00, 0x00, 0, .long }, - .{ .xor, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 6, .none }, - .{ .xor, .mi, .rm16, .imm16, .none, .none, 1, 0x81, 0x00, 0x00, 6, .none }, - .{ .xor, .mi, .rm32, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 6, .none }, - .{ .xor, .mi, .rm64, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 6, .long }, - .{ .xor, .mi, .rm16, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 6, .none }, - .{ .xor, .mi, .rm32, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 6, .none }, - .{ .xor, .mi, .rm64, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 6, .long }, - .{ .xor, .mr, .rm8, .r8, .none, .none, 1, 0x30, 0x00, 0x00, 0, .none }, - .{ .xor, .mr, .rm16, .r16, .none, .none, 1, 0x31, 0x00, 0x00, 0, .none }, - .{ .xor, .mr, .rm32, .r32, .none, .none, 1, 0x31, 0x00, 0x00, 0, .none }, - .{ .xor, .mr, .rm64, .r64, .none, .none, 1, 0x31, 0x00, 0x00, 0, .long }, - .{ .xor, .rm, .r8, .rm8, .none, .none, 1, 0x32, 0x00, 0x00, 0, .none }, - .{ .xor, .rm, .r16, .rm16, .none, .none, 1, 0x33, 0x00, 0x00, 0, .none }, - .{ .xor, .rm, .r32, .rm32, .none, .none, 1, 0x33, 0x00, 0x00, 0, .none }, - .{ .xor, .rm, .r64, .rm64, .none, .none, 1, 0x33, 0x00, 0x00, 0, .long }, + .{ .xor, .zi, .al, .imm8, .none, .none, 1, 0x34, 0x00, 0x00, 0, .none }, + .{ .xor, .zi, .ax, .imm16, .none, .none, 1, 0x35, 0x00, 0x00, 0, .none }, + .{ .xor, .zi, .eax, .imm32, .none, .none, 1, 0x35, 0x00, 0x00, 0, .none }, + .{ .xor, .zi, .rax, .imm32s, .none, .none, 1, 0x35, 0x00, 0x00, 0, .long }, + .{ .xor, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 6, .none }, + .{ .xor, .mi, .rm16, .imm16, .none, .none, 1, 0x81, 0x00, 0x00, 6, .none }, + .{ .xor, .mi, .rm32, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 6, .none }, + .{ .xor, .mi, .rm64, .imm32s, .none, .none, 1, 0x81, 0x00, 0x00, 6, .long }, + .{ .xor, .mi, .rm16, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 6, .none }, + .{ .xor, .mi, .rm32, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 6, .none }, + .{ .xor, .mi, .rm64, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 6, .long }, + .{ .xor, .mr, .rm8, .r8, .none, .none, 1, 0x30, 0x00, 0x00, 0, .none }, + .{ .xor, .mr, .rm16, .r16, .none, .none, 1, 0x31, 0x00, 0x00, 0, .none }, + .{ .xor, .mr, .rm32, .r32, .none, .none, 1, 0x31, 0x00, 0x00, 0, .none }, + .{ .xor, .mr, .rm64, .r64, .none, .none, 1, 0x31, 0x00, 0x00, 0, .long }, + .{ .xor, .rm, .r8, .rm8, .none, .none, 1, 0x32, 0x00, 0x00, 0, .none }, + .{ .xor, .rm, .r16, .rm16, .none, .none, 1, 0x33, 0x00, 0x00, 0, .none }, + .{ .xor, .rm, .r32, .rm32, .none, .none, 1, 0x33, 0x00, 0x00, 0, .none }, + .{ .xor, .rm, .r64, .rm64, .none, .none, 1, 0x33, 0x00, 0x00, 0, .long }, // SSE .{ .addss, .rm, .xmm, .xmm_m32, .none, .none, 3, 0xf3, 0x0f, 0x58, 0, .sse }, @@ -540,3 +540,4 @@ pub const table = &[_]Entry{ .{ .ucomisd, .rm, .xmm, .xmm_m64, .none, .none, 3, 0x66, 0x0f, 0x2e, 0, .sse2 }, }; // zig fmt: on + From 292f91aef2d2ed360f4dbb1f8cf6e22500a682ca Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 7 Mar 2023 16:59:38 +0100 Subject: [PATCH 082/294] Handle .ah vs .spl register aliases --- src/arch/x86_64/Encoding.zig | 77 +++++++++++++++++++++++++--------- src/arch/x86_64/encoder.zig | 22 ++++------ src/arch/x86_64/encodings.zig | 78 +++++++++++++++++++++++++++++++++++ 3 files changed, 143 insertions(+), 34 deletions(-) diff --git a/src/arch/x86_64/Encoding.zig b/src/arch/x86_64/Encoding.zig index 5d3944a554..3c2e9f848c 100644 --- a/src/arch/x86_64/Encoding.zig +++ b/src/arch/x86_64/Encoding.zig @@ -29,12 +29,42 @@ pub fn findByMnemonic(mnemonic: Mnemonic, args: struct { op2: Instruction.Operand, op3: Instruction.Operand, op4: Instruction.Operand, -}) ?Encoding { +}) !?Encoding { const input_op1 = Op.fromOperand(args.op1); const input_op2 = Op.fromOperand(args.op2); const input_op3 = Op.fromOperand(args.op3); const input_op4 = Op.fromOperand(args.op4); + const ops = &[_]Instruction.Operand{ args.op1, args.op2, args.op3, args.op4 }; + const rex_required = for (ops) |op| switch (op) { + .reg => |r| switch (r) { + .spl, .bpl, .sil, .dil => break true, + else => {}, + }, + else => {}, + } else false; + const rex_invalid = for (ops) |op| switch (op) { + .reg => |r| switch (r) { + .ah, .bh, .ch, .dh => break true, + else => {}, + }, + else => {}, + } else false; + const rex_extended = for (ops) |op| switch (op) { + .reg => |r| if (r.isExtended()) break true, + .mem => |m| { + if (m.base()) |base| { + if (base.isExtended()) break true; + } + if (m.scaleIndex()) |si| { + if (si.index.isExtended()) break true; + } + }, + else => {}, + } else false; + + if ((rex_required or rex_extended) and rex_invalid) return error.CannotEncode; + // TODO work out what is the maximum number of variants we can actually find in one swoop. var candidates: [10]Encoding = undefined; var count: usize = 0; @@ -57,13 +87,24 @@ pub fn findByMnemonic(mnemonic: Mnemonic, args: struct { input_op3.isSubset(enc.op3, enc.mode) and input_op4.isSubset(enc.op4, enc.mode)) { - candidates[count] = enc; - count += 1; + if (rex_required) { + switch (enc.mode) { + .rex, .long => { + candidates[count] = enc; + count += 1; + }, + else => {}, + } + } else { + if (enc.mode != .rex) { + candidates[count] = enc; + count += 1; + } + } } } if (count == 0) return null; - if (count == 1) return candidates[0]; const EncodingLength = struct { fn estimate(encoding: Encoding, params: struct { @@ -71,7 +112,7 @@ pub fn findByMnemonic(mnemonic: Mnemonic, args: struct { op2: Instruction.Operand, op3: Instruction.Operand, op4: Instruction.Operand, - }) usize { + }) !usize { var inst = Instruction{ .op1 = params.op1, .op2 = params.op2, @@ -91,7 +132,13 @@ pub fn findByMnemonic(mnemonic: Mnemonic, args: struct { } = null; var i: usize = 0; while (i < count) : (i += 1) { - const len = EncodingLength.estimate(candidates[i], .{ + const candidate = candidates[i]; + switch (candidate.mode) { + .long, .rex => if (rex_invalid) return error.CannotEncode, + else => {}, + } + + const len = try EncodingLength.estimate(candidate, .{ .op1 = args.op1, .op2 = args.op2, .op3 = args.op3, @@ -136,20 +183,11 @@ pub fn findByOpcode(opc: []const u8, prefixes: struct { if (match) { if (prefixes.rex.w) { switch (enc.mode) { - .fpu, .sse, .sse2 => {}, - .long => return enc, - .none => { - // TODO this is a hack to allow parsing of instructions which contain - // spurious prefix bytes such as - // rex.W mov dil, 0x1 - // Here, rex.W is not needed. - const rex_w_allowed = blk: { - const bit_size = enc.operandBitSize(); - break :blk bit_size == 64 or bit_size == 8; - }; - if (rex_w_allowed) return enc; - }, + .fpu, .sse, .sse2, .none => {}, + .long, .rex => return enc, } + } else if (prefixes.rex.present and !prefixes.rex.isSet()) { + if (enc.mode == .rex) return enc; } else if (prefixes.legacy.prefix_66) { switch (enc.operandBitSize()) { 16 => return enc, @@ -542,6 +580,7 @@ pub const Op = enum { pub const Mode = enum { none, fpu, + rex, long, sse, sse2, diff --git a/src/arch/x86_64/encoder.zig b/src/arch/x86_64/encoder.zig index d4ca925891..066a733960 100644 --- a/src/arch/x86_64/encoder.zig +++ b/src/arch/x86_64/encoder.zig @@ -102,12 +102,12 @@ pub const Instruction = struct { op3: Operand = .none, op4: Operand = .none, }) !Instruction { - const encoding = Encoding.findByMnemonic(mnemonic, .{ + const encoding = (try Encoding.findByMnemonic(mnemonic, .{ .op1 = args.op1, .op2 = args.op2, .op3 = args.op3, .op4 = args.op4, - }) orelse { + })) orelse { std.log.debug("{s} {s} {s} {s} {s}", .{ @tagName(mnemonic), @tagName(Encoding.Op.fromOperand(args.op1)), @@ -251,13 +251,8 @@ pub const Instruction = struct { fn encodeRexPrefix(inst: Instruction, encoder: anytype) !void { const op_en = inst.encoding.op_en; - // Check if we need REX and can actually encode it - const is_rex_invalid = for (&[_]Operand{ inst.op1, inst.op2, inst.op3, inst.op4 }) |op| switch (op) { - .reg => |r| if (r.isRexInvalid()) break true, - else => {}, - } else false; - var rex = Rex{}; + rex.present = inst.encoding.mode == .rex; rex.w = inst.encoding.mode == .long; switch (op_en) { @@ -293,8 +288,6 @@ pub const Instruction = struct { }, } - if (rex.isSet() and is_rex_invalid) return error.CannotEncode; - try encoder.rex(rex); } @@ -507,9 +500,9 @@ fn Encoder(comptime T: type) type { /// or one of reg, index, r/m, base, or opcode-reg might be extended. /// /// See struct `Rex` for a description of each field. - /// - /// Does not add a prefix byte if none of the fields are set! pub fn rex(self: Self, byte: Rex) !void { + if (!byte.present and !byte.isSet()) return; + var value: u8 = 0b0100_0000; if (byte.w) value |= 0b1000; @@ -517,9 +510,7 @@ fn Encoder(comptime T: type) type { if (byte.x) value |= 0b0010; if (byte.b) value |= 0b0001; - if (value != 0b0100_0000) { - try self.writer.writeByte(value); - } + try self.writer.writeByte(value); } // ------ @@ -788,6 +779,7 @@ pub const Rex = struct { r: bool = false, x: bool = false, b: bool = false, + present: bool = false, pub fn isSet(rex: Rex) bool { return rex.w or rex.r or rex.x or rex.b; diff --git a/src/arch/x86_64/encodings.zig b/src/arch/x86_64/encodings.zig index c3fb347f22..b008eb9f3e 100644 --- a/src/arch/x86_64/encodings.zig +++ b/src/arch/x86_64/encodings.zig @@ -18,6 +18,7 @@ pub const table = &[_]Entry{ .{ .adc, .zi, .eax, .imm32, .none, .none, 1, 0x15, 0x00, 0x00, 0, .none }, .{ .adc, .zi, .rax, .imm32s, .none, .none, 1, 0x15, 0x00, 0x00, 0, .long }, .{ .adc, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 2, .none }, + .{ .adc, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 2, .rex }, .{ .adc, .mi, .rm16, .imm16, .none, .none, 1, 0x81, 0x00, 0x00, 2, .none }, .{ .adc, .mi, .rm32, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 2, .none }, .{ .adc, .mi, .rm64, .imm32s, .none, .none, 1, 0x81, 0x00, 0x00, 2, .long }, @@ -25,10 +26,12 @@ pub const table = &[_]Entry{ .{ .adc, .mi, .rm32, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 2, .none }, .{ .adc, .mi, .rm64, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 2, .long }, .{ .adc, .mr, .rm8, .r8, .none, .none, 1, 0x10, 0x00, 0x00, 0, .none }, + .{ .adc, .mr, .rm8, .r8, .none, .none, 1, 0x10, 0x00, 0x00, 0, .rex }, .{ .adc, .mr, .rm16, .r16, .none, .none, 1, 0x11, 0x00, 0x00, 0, .none }, .{ .adc, .mr, .rm32, .r32, .none, .none, 1, 0x11, 0x00, 0x00, 0, .none }, .{ .adc, .mr, .rm64, .r64, .none, .none, 1, 0x11, 0x00, 0x00, 0, .long }, .{ .adc, .rm, .r8, .rm8, .none, .none, 1, 0x12, 0x00, 0x00, 0, .none }, + .{ .adc, .rm, .r8, .rm8, .none, .none, 1, 0x12, 0x00, 0x00, 0, .rex }, .{ .adc, .rm, .r16, .rm16, .none, .none, 1, 0x13, 0x00, 0x00, 0, .none }, .{ .adc, .rm, .r32, .rm32, .none, .none, 1, 0x13, 0x00, 0x00, 0, .none }, .{ .adc, .rm, .r64, .rm64, .none, .none, 1, 0x13, 0x00, 0x00, 0, .long }, @@ -38,6 +41,7 @@ pub const table = &[_]Entry{ .{ .add, .zi, .eax, .imm32, .none, .none, 1, 0x05, 0x00, 0x00, 0, .none }, .{ .add, .zi, .rax, .imm32s, .none, .none, 1, 0x05, 0x00, 0x00, 0, .long }, .{ .add, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 0, .none }, + .{ .add, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 0, .rex }, .{ .add, .mi, .rm16, .imm16, .none, .none, 1, 0x81, 0x00, 0x00, 0, .none }, .{ .add, .mi, .rm32, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 0, .none }, .{ .add, .mi, .rm64, .imm32s, .none, .none, 1, 0x81, 0x00, 0x00, 0, .long }, @@ -45,10 +49,12 @@ pub const table = &[_]Entry{ .{ .add, .mi, .rm32, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 0, .none }, .{ .add, .mi, .rm64, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 0, .long }, .{ .add, .mr, .rm8, .r8, .none, .none, 1, 0x00, 0x00, 0x00, 0, .none }, + .{ .add, .mr, .rm8, .r8, .none, .none, 1, 0x00, 0x00, 0x00, 0, .rex }, .{ .add, .mr, .rm16, .r16, .none, .none, 1, 0x01, 0x00, 0x00, 0, .none }, .{ .add, .mr, .rm32, .r32, .none, .none, 1, 0x01, 0x00, 0x00, 0, .none }, .{ .add, .mr, .rm64, .r64, .none, .none, 1, 0x01, 0x00, 0x00, 0, .long }, .{ .add, .rm, .r8, .rm8, .none, .none, 1, 0x02, 0x00, 0x00, 0, .none }, + .{ .add, .rm, .r8, .rm8, .none, .none, 1, 0x02, 0x00, 0x00, 0, .rex }, .{ .add, .rm, .r16, .rm16, .none, .none, 1, 0x03, 0x00, 0x00, 0, .none }, .{ .add, .rm, .r32, .rm32, .none, .none, 1, 0x03, 0x00, 0x00, 0, .none }, .{ .add, .rm, .r64, .rm64, .none, .none, 1, 0x03, 0x00, 0x00, 0, .long }, @@ -58,6 +64,7 @@ pub const table = &[_]Entry{ .{ .@"and", .zi, .eax, .imm32, .none, .none, 1, 0x25, 0x00, 0x00, 0, .none }, .{ .@"and", .zi, .rax, .imm32s, .none, .none, 1, 0x25, 0x00, 0x00, 0, .long }, .{ .@"and", .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 4, .none }, + .{ .@"and", .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 4, .rex }, .{ .@"and", .mi, .rm16, .imm16, .none, .none, 1, 0x81, 0x00, 0x00, 4, .none }, .{ .@"and", .mi, .rm32, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 4, .none }, .{ .@"and", .mi, .rm64, .imm32s, .none, .none, 1, 0x81, 0x00, 0x00, 4, .long }, @@ -65,10 +72,12 @@ pub const table = &[_]Entry{ .{ .@"and", .mi, .rm32, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 4, .none }, .{ .@"and", .mi, .rm64, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 4, .long }, .{ .@"and", .mr, .rm8, .r8, .none, .none, 1, 0x20, 0x00, 0x00, 0, .none }, + .{ .@"and", .mr, .rm8, .r8, .none, .none, 1, 0x20, 0x00, 0x00, 0, .rex }, .{ .@"and", .mr, .rm16, .r16, .none, .none, 1, 0x21, 0x00, 0x00, 0, .none }, .{ .@"and", .mr, .rm32, .r32, .none, .none, 1, 0x21, 0x00, 0x00, 0, .none }, .{ .@"and", .mr, .rm64, .r64, .none, .none, 1, 0x21, 0x00, 0x00, 0, .long }, .{ .@"and", .rm, .r8, .rm8, .none, .none, 1, 0x22, 0x00, 0x00, 0, .none }, + .{ .@"and", .rm, .r8, .rm8, .none, .none, 1, 0x22, 0x00, 0x00, 0, .rex }, .{ .@"and", .rm, .r16, .rm16, .none, .none, 1, 0x23, 0x00, 0x00, 0, .none }, .{ .@"and", .rm, .r32, .rm32, .none, .none, 1, 0x23, 0x00, 0x00, 0, .none }, .{ .@"and", .rm, .r64, .rm64, .none, .none, 1, 0x23, 0x00, 0x00, 0, .long }, @@ -181,6 +190,7 @@ pub const table = &[_]Entry{ .{ .cmp, .zi, .eax, .imm32, .none, .none, 1, 0x3d, 0x00, 0x00, 0, .none }, .{ .cmp, .zi, .rax, .imm32s, .none, .none, 1, 0x3d, 0x00, 0x00, 0, .long }, .{ .cmp, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 7, .none }, + .{ .cmp, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 7, .rex }, .{ .cmp, .mi, .rm16, .imm16, .none, .none, 1, 0x81, 0x00, 0x00, 7, .none }, .{ .cmp, .mi, .rm32, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 7, .none }, .{ .cmp, .mi, .rm64, .imm32s, .none, .none, 1, 0x81, 0x00, 0x00, 7, .long }, @@ -188,15 +198,18 @@ pub const table = &[_]Entry{ .{ .cmp, .mi, .rm32, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 7, .none }, .{ .cmp, .mi, .rm64, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 7, .long }, .{ .cmp, .mr, .rm8, .r8, .none, .none, 1, 0x38, 0x00, 0x00, 0, .none }, + .{ .cmp, .mr, .rm8, .r8, .none, .none, 1, 0x38, 0x00, 0x00, 0, .rex }, .{ .cmp, .mr, .rm16, .r16, .none, .none, 1, 0x39, 0x00, 0x00, 0, .none }, .{ .cmp, .mr, .rm32, .r32, .none, .none, 1, 0x39, 0x00, 0x00, 0, .none }, .{ .cmp, .mr, .rm64, .r64, .none, .none, 1, 0x39, 0x00, 0x00, 0, .long }, .{ .cmp, .rm, .r8, .rm8, .none, .none, 1, 0x3a, 0x00, 0x00, 0, .none }, + .{ .cmp, .rm, .r8, .rm8, .none, .none, 1, 0x3a, 0x00, 0x00, 0, .rex }, .{ .cmp, .rm, .r16, .rm16, .none, .none, 1, 0x3b, 0x00, 0x00, 0, .none }, .{ .cmp, .rm, .r32, .rm32, .none, .none, 1, 0x3b, 0x00, 0x00, 0, .none }, .{ .cmp, .rm, .r64, .rm64, .none, .none, 1, 0x3b, 0x00, 0x00, 0, .long }, .{ .div, .m, .rm8, .none, .none, .none, 1, 0xf6, 0x00, 0x00, 6, .none }, + .{ .div, .m, .rm8, .none, .none, .none, 1, 0xf6, 0x00, 0x00, 6, .rex }, .{ .div, .m, .rm16, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 6, .none }, .{ .div, .m, .rm32, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 6, .none }, .{ .div, .m, .rm64, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 6, .long }, @@ -210,11 +223,13 @@ pub const table = &[_]Entry{ .{ .fld, .m, .m80, .none, .none, .none, 1, 0xdb, 0x00, 0x00, 5, .fpu }, .{ .idiv, .m, .rm8, .none, .none, .none, 1, 0xf6, 0x00, 0x00, 7, .none }, + .{ .idiv, .m, .rm8, .none, .none, .none, 1, 0xf6, 0x00, 0x00, 7, .rex }, .{ .idiv, .m, .rm16, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 7, .none }, .{ .idiv, .m, .rm32, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 7, .none }, .{ .idiv, .m, .rm64, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 7, .long }, .{ .imul, .m, .rm8, .none, .none, .none, 1, 0xf6, 0x00, 0x00, 5, .none }, + .{ .imul, .m, .rm8, .none, .none, .none, 1, 0xf6, 0x00, 0x00, 5, .rex }, .{ .imul, .m, .rm16, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 5, .none }, .{ .imul, .m, .rm32, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 5, .none }, .{ .imul, .m, .rm64, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 5, .long }, @@ -270,10 +285,12 @@ pub const table = &[_]Entry{ .{ .lea, .rm, .r64, .m, .none, .none, 1, 0x8d, 0x00, 0x00, 0, .long }, .{ .mov, .mr, .rm8, .r8, .none, .none, 1, 0x88, 0x00, 0x00, 0, .none }, + .{ .mov, .mr, .rm8, .r8, .none, .none, 1, 0x88, 0x00, 0x00, 0, .rex }, .{ .mov, .mr, .rm16, .r16, .none, .none, 1, 0x89, 0x00, 0x00, 0, .none }, .{ .mov, .mr, .rm32, .r32, .none, .none, 1, 0x89, 0x00, 0x00, 0, .none }, .{ .mov, .mr, .rm64, .r64, .none, .none, 1, 0x89, 0x00, 0x00, 0, .long }, .{ .mov, .rm, .r8, .rm8, .none, .none, 1, 0x8a, 0x00, 0x00, 0, .none }, + .{ .mov, .rm, .r8, .rm8, .none, .none, 1, 0x8a, 0x00, 0x00, 0, .rex }, .{ .mov, .rm, .r16, .rm16, .none, .none, 1, 0x8b, 0x00, 0x00, 0, .none }, .{ .mov, .rm, .r32, .rm32, .none, .none, 1, 0x8b, 0x00, 0x00, 0, .none }, .{ .mov, .rm, .r64, .rm64, .none, .none, 1, 0x8b, 0x00, 0x00, 0, .long }, @@ -290,16 +307,20 @@ pub const table = &[_]Entry{ .{ .mov, .td, .moffs, .eax, .none, .none, 1, 0xa3, 0x00, 0x00, 0, .none }, .{ .mov, .td, .moffs, .rax, .none, .none, 1, 0xa3, 0x00, 0x00, 0, .long }, .{ .mov, .oi, .r8, .imm8, .none, .none, 1, 0xb0, 0x00, 0x00, 0, .none }, + .{ .mov, .oi, .r8, .imm8, .none, .none, 1, 0xb0, 0x00, 0x00, 0, .rex }, .{ .mov, .oi, .r16, .imm16, .none, .none, 1, 0xb8, 0x00, 0x00, 0, .none }, .{ .mov, .oi, .r32, .imm32, .none, .none, 1, 0xb8, 0x00, 0x00, 0, .none }, .{ .mov, .oi, .r64, .imm64, .none, .none, 1, 0xb8, 0x00, 0x00, 0, .long }, .{ .mov, .mi, .rm8, .imm8, .none, .none, 1, 0xc6, 0x00, 0x00, 0, .none }, + .{ .mov, .mi, .rm8, .imm8, .none, .none, 1, 0xc6, 0x00, 0x00, 0, .rex }, .{ .mov, .mi, .rm16, .imm16, .none, .none, 1, 0xc7, 0x00, 0x00, 0, .none }, .{ .mov, .mi, .rm32, .imm32, .none, .none, 1, 0xc7, 0x00, 0x00, 0, .none }, .{ .mov, .mi, .rm64, .imm32s, .none, .none, 1, 0xc7, 0x00, 0x00, 0, .long }, .{ .movsx, .rm, .r16, .rm8, .none, .none, 2, 0x0f, 0xbe, 0x00, 0, .none }, + .{ .movsx, .rm, .r16, .rm8, .none, .none, 2, 0x0f, 0xbe, 0x00, 0, .rex }, .{ .movsx, .rm, .r32, .rm8, .none, .none, 2, 0x0f, 0xbe, 0x00, 0, .none }, + .{ .movsx, .rm, .r32, .rm8, .none, .none, 2, 0x0f, 0xbe, 0x00, 0, .rex }, .{ .movsx, .rm, .r64, .rm8, .none, .none, 2, 0x0f, 0xbe, 0x00, 0, .long }, .{ .movsx, .rm, .r32, .rm16, .none, .none, 2, 0x0f, 0xbf, 0x00, 0, .none }, .{ .movsx, .rm, .r64, .rm16, .none, .none, 2, 0x0f, 0xbf, 0x00, 0, .long }, @@ -315,6 +336,7 @@ pub const table = &[_]Entry{ .{ .movzx, .rm, .r64, .rm16, .none, .none, 2, 0x0f, 0xb7, 0x00, 0, .long }, .{ .mul, .m, .rm8, .none, .none, .none, 1, 0xf6, 0x00, 0x00, 4, .none }, + .{ .mul, .m, .rm8, .none, .none, .none, 1, 0xf6, 0x00, 0x00, 4, .rex }, .{ .mul, .m, .rm16, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 4, .none }, .{ .mul, .m, .rm32, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 4, .none }, .{ .mul, .m, .rm64, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 4, .long }, @@ -326,6 +348,7 @@ pub const table = &[_]Entry{ .{ .@"or", .zi, .eax, .imm32, .none, .none, 1, 0x0d, 0x00, 0x00, 0, .none }, .{ .@"or", .zi, .rax, .imm32s, .none, .none, 1, 0x0d, 0x00, 0x00, 0, .long }, .{ .@"or", .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 1, .none }, + .{ .@"or", .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 1, .rex }, .{ .@"or", .mi, .rm16, .imm16, .none, .none, 1, 0x81, 0x00, 0x00, 1, .none }, .{ .@"or", .mi, .rm32, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 1, .none }, .{ .@"or", .mi, .rm64, .imm32s, .none, .none, 1, 0x81, 0x00, 0x00, 1, .long }, @@ -333,10 +356,12 @@ pub const table = &[_]Entry{ .{ .@"or", .mi, .rm32, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 1, .none }, .{ .@"or", .mi, .rm64, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 1, .long }, .{ .@"or", .mr, .rm8, .r8, .none, .none, 1, 0x08, 0x00, 0x00, 0, .none }, + .{ .@"or", .mr, .rm8, .r8, .none, .none, 1, 0x08, 0x00, 0x00, 0, .rex }, .{ .@"or", .mr, .rm16, .r16, .none, .none, 1, 0x09, 0x00, 0x00, 0, .none }, .{ .@"or", .mr, .rm32, .r32, .none, .none, 1, 0x09, 0x00, 0x00, 0, .none }, .{ .@"or", .mr, .rm64, .r64, .none, .none, 1, 0x09, 0x00, 0x00, 0, .long }, .{ .@"or", .rm, .r8, .rm8, .none, .none, 1, 0x0a, 0x00, 0x00, 0, .none }, + .{ .@"or", .rm, .r8, .rm8, .none, .none, 1, 0x0a, 0x00, 0x00, 0, .rex }, .{ .@"or", .rm, .r16, .rm16, .none, .none, 1, 0x0b, 0x00, 0x00, 0, .none }, .{ .@"or", .rm, .r32, .rm32, .none, .none, 1, 0x0b, 0x00, 0x00, 0, .none }, .{ .@"or", .rm, .r64, .rm64, .none, .none, 1, 0x0b, 0x00, 0x00, 0, .long }, @@ -357,27 +382,33 @@ pub const table = &[_]Entry{ .{ .ret, .np, .none, .none, .none, .none, 1, 0xc3, 0x00, 0x00, 0, .none }, .{ .sal, .m1, .rm8, .unity, .none, .none, 1, 0xd0, 0x00, 0x00, 4, .none }, + .{ .sal, .m1, .rm8, .unity, .none, .none, 1, 0xd0, 0x00, 0x00, 4, .rex }, .{ .sal, .m1, .rm16, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 4, .none }, .{ .sal, .m1, .rm32, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 4, .none }, .{ .sal, .m1, .rm64, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 4, .long }, .{ .sal, .mc, .rm8, .cl, .none, .none, 1, 0xd2, 0x00, 0x00, 4, .none }, + .{ .sal, .mc, .rm8, .cl, .none, .none, 1, 0xd2, 0x00, 0x00, 4, .rex }, .{ .sal, .mc, .rm16, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 4, .none }, .{ .sal, .mc, .rm32, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 4, .none }, .{ .sal, .mc, .rm64, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 4, .long }, .{ .sal, .mi, .rm8, .imm8, .none, .none, 1, 0xc0, 0x00, 0x00, 4, .none }, + .{ .sal, .mi, .rm8, .imm8, .none, .none, 1, 0xc0, 0x00, 0x00, 4, .rex }, .{ .sal, .mi, .rm16, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 4, .none }, .{ .sal, .mi, .rm32, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 4, .none }, .{ .sal, .mi, .rm64, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 4, .long }, .{ .sar, .m1, .rm8, .unity, .none, .none, 1, 0xd0, 0x00, 0x00, 7, .none }, + .{ .sar, .m1, .rm8, .unity, .none, .none, 1, 0xd0, 0x00, 0x00, 7, .rex }, .{ .sar, .m1, .rm16, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 7, .none }, .{ .sar, .m1, .rm32, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 7, .none }, .{ .sar, .m1, .rm64, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 7, .long }, .{ .sar, .mc, .rm8, .cl, .none, .none, 1, 0xd2, 0x00, 0x00, 7, .none }, + .{ .sar, .mc, .rm8, .cl, .none, .none, 1, 0xd2, 0x00, 0x00, 7, .rex }, .{ .sar, .mc, .rm16, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 7, .none }, .{ .sar, .mc, .rm32, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 7, .none }, .{ .sar, .mc, .rm64, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 7, .long }, .{ .sar, .mi, .rm8, .imm8, .none, .none, 1, 0xc0, 0x00, 0x00, 7, .none }, + .{ .sar, .mi, .rm8, .imm8, .none, .none, 1, 0xc0, 0x00, 0x00, 7, .rex }, .{ .sar, .mi, .rm16, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 7, .none }, .{ .sar, .mi, .rm32, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 7, .none }, .{ .sar, .mi, .rm64, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 7, .long }, @@ -387,6 +418,7 @@ pub const table = &[_]Entry{ .{ .sbb, .zi, .eax, .imm32, .none, .none, 1, 0x1d, 0x00, 0x00, 0, .none }, .{ .sbb, .zi, .rax, .imm32s, .none, .none, 1, 0x1d, 0x00, 0x00, 0, .long }, .{ .sbb, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 3, .none }, + .{ .sbb, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 3, .rex }, .{ .sbb, .mi, .rm16, .imm16, .none, .none, 1, 0x81, 0x00, 0x00, 3, .none }, .{ .sbb, .mi, .rm32, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 3, .none }, .{ .sbb, .mi, .rm64, .imm32s, .none, .none, 1, 0x81, 0x00, 0x00, 3, .long }, @@ -394,67 +426,105 @@ pub const table = &[_]Entry{ .{ .sbb, .mi, .rm32, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 3, .none }, .{ .sbb, .mi, .rm64, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 3, .long }, .{ .sbb, .mr, .rm8, .r8, .none, .none, 1, 0x18, 0x00, 0x00, 0, .none }, + .{ .sbb, .mr, .rm8, .r8, .none, .none, 1, 0x18, 0x00, 0x00, 0, .rex }, .{ .sbb, .mr, .rm16, .r16, .none, .none, 1, 0x19, 0x00, 0x00, 0, .none }, .{ .sbb, .mr, .rm32, .r32, .none, .none, 1, 0x19, 0x00, 0x00, 0, .none }, .{ .sbb, .mr, .rm64, .r64, .none, .none, 1, 0x19, 0x00, 0x00, 0, .long }, .{ .sbb, .rm, .r8, .rm8, .none, .none, 1, 0x1a, 0x00, 0x00, 0, .none }, + .{ .sbb, .rm, .r8, .rm8, .none, .none, 1, 0x1a, 0x00, 0x00, 0, .rex }, .{ .sbb, .rm, .r16, .rm16, .none, .none, 1, 0x1b, 0x00, 0x00, 0, .none }, .{ .sbb, .rm, .r32, .rm32, .none, .none, 1, 0x1b, 0x00, 0x00, 0, .none }, .{ .sbb, .rm, .r64, .rm64, .none, .none, 1, 0x1b, 0x00, 0x00, 0, .long }, .{ .seta, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x97, 0x00, 0, .none }, + .{ .seta, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x97, 0x00, 0, .rex }, .{ .setae, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x93, 0x00, 0, .none }, + .{ .setae, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x93, 0x00, 0, .rex }, .{ .setb, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x92, 0x00, 0, .none }, + .{ .setb, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x92, 0x00, 0, .rex }, .{ .setbe, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x96, 0x00, 0, .none }, + .{ .setbe, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x96, 0x00, 0, .rex }, .{ .setc, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x92, 0x00, 0, .none }, + .{ .setc, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x92, 0x00, 0, .rex }, .{ .sete, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x94, 0x00, 0, .none }, + .{ .sete, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x94, 0x00, 0, .rex }, .{ .setg, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9f, 0x00, 0, .none }, + .{ .setg, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9f, 0x00, 0, .rex }, .{ .setge, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9d, 0x00, 0, .none }, + .{ .setge, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9d, 0x00, 0, .rex }, .{ .setl, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9c, 0x00, 0, .none }, + .{ .setl, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9c, 0x00, 0, .rex }, .{ .setle, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9e, 0x00, 0, .none }, + .{ .setle, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9e, 0x00, 0, .rex }, .{ .setna, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x96, 0x00, 0, .none }, + .{ .setna, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x96, 0x00, 0, .rex }, .{ .setnae, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x92, 0x00, 0, .none }, + .{ .setnae, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x92, 0x00, 0, .rex }, .{ .setnb, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x93, 0x00, 0, .none }, + .{ .setnb, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x93, 0x00, 0, .rex }, .{ .setnbe, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x97, 0x00, 0, .none }, + .{ .setnbe, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x97, 0x00, 0, .rex }, .{ .setnc, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x93, 0x00, 0, .none }, + .{ .setnc, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x93, 0x00, 0, .rex }, .{ .setne, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x95, 0x00, 0, .none }, + .{ .setne, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x95, 0x00, 0, .rex }, .{ .setng, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9e, 0x00, 0, .none }, + .{ .setng, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9e, 0x00, 0, .rex }, .{ .setnge, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9c, 0x00, 0, .none }, + .{ .setnge, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9c, 0x00, 0, .rex }, .{ .setnl, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9d, 0x00, 0, .none }, + .{ .setnl, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9d, 0x00, 0, .rex }, .{ .setnle, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9f, 0x00, 0, .none }, + .{ .setnle, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9f, 0x00, 0, .rex }, .{ .setno, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x91, 0x00, 0, .none }, + .{ .setno, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x91, 0x00, 0, .rex }, .{ .setnp, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9b, 0x00, 0, .none }, + .{ .setnp, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9b, 0x00, 0, .rex }, .{ .setns, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x99, 0x00, 0, .none }, + .{ .setns, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x99, 0x00, 0, .rex }, .{ .setnz, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x95, 0x00, 0, .none }, + .{ .setnz, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x95, 0x00, 0, .rex }, .{ .seto, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x90, 0x00, 0, .none }, + .{ .seto, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x90, 0x00, 0, .rex }, .{ .setp, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9a, 0x00, 0, .none }, + .{ .setp, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9a, 0x00, 0, .rex }, .{ .setpe, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9a, 0x00, 0, .none }, + .{ .setpe, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9a, 0x00, 0, .rex }, .{ .setpo, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9b, 0x00, 0, .none }, + .{ .setpo, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9b, 0x00, 0, .rex }, .{ .sets, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x98, 0x00, 0, .none }, + .{ .sets, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x98, 0x00, 0, .rex }, .{ .setz, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x94, 0x00, 0, .none }, + .{ .setz, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x94, 0x00, 0, .rex }, .{ .shl, .m1, .rm8, .unity, .none, .none, 1, 0xd0, 0x00, 0x00, 4, .none }, + .{ .shl, .m1, .rm8, .unity, .none, .none, 1, 0xd0, 0x00, 0x00, 4, .rex }, .{ .shl, .m1, .rm16, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 4, .none }, .{ .shl, .m1, .rm32, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 4, .none }, .{ .shl, .m1, .rm64, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 4, .long }, .{ .shl, .mc, .rm8, .cl, .none, .none, 1, 0xd2, 0x00, 0x00, 4, .none }, + .{ .shl, .mc, .rm8, .cl, .none, .none, 1, 0xd2, 0x00, 0x00, 4, .rex }, .{ .shl, .mc, .rm16, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 4, .none }, .{ .shl, .mc, .rm32, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 4, .none }, .{ .shl, .mc, .rm64, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 4, .long }, .{ .shl, .mi, .rm8, .imm8, .none, .none, 1, 0xc0, 0x00, 0x00, 4, .none }, + .{ .shl, .mi, .rm8, .imm8, .none, .none, 1, 0xc0, 0x00, 0x00, 4, .rex }, .{ .shl, .mi, .rm16, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 4, .none }, .{ .shl, .mi, .rm32, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 4, .none }, .{ .shl, .mi, .rm64, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 4, .long }, .{ .shr, .m1, .rm8, .unity, .none, .none, 1, 0xd0, 0x00, 0x00, 5, .none }, + .{ .shr, .m1, .rm8, .unity, .none, .none, 1, 0xd0, 0x00, 0x00, 5, .rex }, .{ .shr, .m1, .rm16, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 5, .none }, .{ .shr, .m1, .rm32, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 5, .none }, .{ .shr, .m1, .rm64, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 5, .long }, .{ .shr, .mc, .rm8, .cl, .none, .none, 1, 0xd2, 0x00, 0x00, 5, .none }, + .{ .shr, .mc, .rm8, .cl, .none, .none, 1, 0xd2, 0x00, 0x00, 5, .rex }, .{ .shr, .mc, .rm16, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 5, .none }, .{ .shr, .mc, .rm32, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 5, .none }, .{ .shr, .mc, .rm64, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 5, .long }, .{ .shr, .mi, .rm8, .imm8, .none, .none, 1, 0xc0, 0x00, 0x00, 5, .none }, + .{ .shr, .mi, .rm8, .imm8, .none, .none, 1, 0xc0, 0x00, 0x00, 5, .rex }, .{ .shr, .mi, .rm16, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 5, .none }, .{ .shr, .mi, .rm32, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 5, .none }, .{ .shr, .mi, .rm64, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 5, .long }, @@ -464,6 +534,7 @@ pub const table = &[_]Entry{ .{ .sub, .zi, .eax, .imm32, .none, .none, 1, 0x2d, 0x00, 0x00, 0, .none }, .{ .sub, .zi, .rax, .imm32s, .none, .none, 1, 0x2d, 0x00, 0x00, 0, .long }, .{ .sub, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 5, .none }, + .{ .sub, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 5, .rex }, .{ .sub, .mi, .rm16, .imm16, .none, .none, 1, 0x81, 0x00, 0x00, 5, .none }, .{ .sub, .mi, .rm32, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 5, .none }, .{ .sub, .mi, .rm64, .imm32s, .none, .none, 1, 0x81, 0x00, 0x00, 5, .long }, @@ -471,10 +542,12 @@ pub const table = &[_]Entry{ .{ .sub, .mi, .rm32, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 5, .none }, .{ .sub, .mi, .rm64, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 5, .long }, .{ .sub, .mr, .rm8, .r8, .none, .none, 1, 0x28, 0x00, 0x00, 0, .none }, + .{ .sub, .mr, .rm8, .r8, .none, .none, 1, 0x28, 0x00, 0x00, 0, .rex }, .{ .sub, .mr, .rm16, .r16, .none, .none, 1, 0x29, 0x00, 0x00, 0, .none }, .{ .sub, .mr, .rm32, .r32, .none, .none, 1, 0x29, 0x00, 0x00, 0, .none }, .{ .sub, .mr, .rm64, .r64, .none, .none, 1, 0x29, 0x00, 0x00, 0, .long }, .{ .sub, .rm, .r8, .rm8, .none, .none, 1, 0x2a, 0x00, 0x00, 0, .none }, + .{ .sub, .rm, .r8, .rm8, .none, .none, 1, 0x2a, 0x00, 0x00, 0, .rex }, .{ .sub, .rm, .r16, .rm16, .none, .none, 1, 0x2b, 0x00, 0x00, 0, .none }, .{ .sub, .rm, .r32, .rm32, .none, .none, 1, 0x2b, 0x00, 0x00, 0, .none }, .{ .sub, .rm, .r64, .rm64, .none, .none, 1, 0x2b, 0x00, 0x00, 0, .long }, @@ -486,10 +559,12 @@ pub const table = &[_]Entry{ .{ .@"test", .zi, .eax, .imm32, .none, .none, 1, 0xa9, 0x00, 0x00, 0, .none }, .{ .@"test", .zi, .rax, .imm32s, .none, .none, 1, 0xa9, 0x00, 0x00, 0, .long }, .{ .@"test", .mi, .rm8, .imm8, .none, .none, 1, 0xf6, 0x00, 0x00, 0, .none }, + .{ .@"test", .mi, .rm8, .imm8, .none, .none, 1, 0xf6, 0x00, 0x00, 0, .rex }, .{ .@"test", .mi, .rm16, .imm16, .none, .none, 1, 0xf7, 0x00, 0x00, 0, .none }, .{ .@"test", .mi, .rm32, .imm32, .none, .none, 1, 0xf7, 0x00, 0x00, 0, .none }, .{ .@"test", .mi, .rm64, .imm32s, .none, .none, 1, 0xf7, 0x00, 0x00, 0, .long }, .{ .@"test", .mr, .rm8, .r8, .none, .none, 1, 0x84, 0x00, 0x00, 0, .none }, + .{ .@"test", .mr, .rm8, .r8, .none, .none, 1, 0x84, 0x00, 0x00, 0, .rex }, .{ .@"test", .mr, .rm16, .r16, .none, .none, 1, 0x85, 0x00, 0x00, 0, .none }, .{ .@"test", .mr, .rm32, .r32, .none, .none, 1, 0x85, 0x00, 0x00, 0, .none }, .{ .@"test", .mr, .rm64, .r64, .none, .none, 1, 0x85, 0x00, 0x00, 0, .long }, @@ -501,6 +576,7 @@ pub const table = &[_]Entry{ .{ .xor, .zi, .eax, .imm32, .none, .none, 1, 0x35, 0x00, 0x00, 0, .none }, .{ .xor, .zi, .rax, .imm32s, .none, .none, 1, 0x35, 0x00, 0x00, 0, .long }, .{ .xor, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 6, .none }, + .{ .xor, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 6, .rex }, .{ .xor, .mi, .rm16, .imm16, .none, .none, 1, 0x81, 0x00, 0x00, 6, .none }, .{ .xor, .mi, .rm32, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 6, .none }, .{ .xor, .mi, .rm64, .imm32s, .none, .none, 1, 0x81, 0x00, 0x00, 6, .long }, @@ -508,10 +584,12 @@ pub const table = &[_]Entry{ .{ .xor, .mi, .rm32, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 6, .none }, .{ .xor, .mi, .rm64, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 6, .long }, .{ .xor, .mr, .rm8, .r8, .none, .none, 1, 0x30, 0x00, 0x00, 0, .none }, + .{ .xor, .mr, .rm8, .r8, .none, .none, 1, 0x30, 0x00, 0x00, 0, .rex }, .{ .xor, .mr, .rm16, .r16, .none, .none, 1, 0x31, 0x00, 0x00, 0, .none }, .{ .xor, .mr, .rm32, .r32, .none, .none, 1, 0x31, 0x00, 0x00, 0, .none }, .{ .xor, .mr, .rm64, .r64, .none, .none, 1, 0x31, 0x00, 0x00, 0, .long }, .{ .xor, .rm, .r8, .rm8, .none, .none, 1, 0x32, 0x00, 0x00, 0, .none }, + .{ .xor, .rm, .r8, .rm8, .none, .none, 1, 0x32, 0x00, 0x00, 0, .rex }, .{ .xor, .rm, .r16, .rm16, .none, .none, 1, 0x33, 0x00, 0x00, 0, .none }, .{ .xor, .rm, .r32, .rm32, .none, .none, 1, 0x33, 0x00, 0x00, 0, .none }, .{ .xor, .rm, .r64, .rm64, .none, .none, 1, 0x33, 0x00, 0x00, 0, .long }, From 219c1261a5f46636425be50fdc6e82534bb003a4 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 7 Mar 2023 20:22:45 +0100 Subject: [PATCH 083/294] x86_64: all behavior tests passing --- src/arch/x86_64/CodeGen.zig | 58 +++++++++++++++++++----------------- src/arch/x86_64/Emit.zig | 7 ++++- src/arch/x86_64/Encoding.zig | 4 +++ src/arch/x86_64/encoder.zig | 2 +- 4 files changed, 41 insertions(+), 30 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 90286a1bb2..fe44adcfa1 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -2785,7 +2785,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type // introduce new MIR tag specifically for mov [reg + 0], imm const payload = try self.addExtra(Mir.ImmPair{ .dest_off = 0, - .operand = @intCast(u32, imm), + .operand = @truncate(u32, imm), }); _ = try self.addInst(.{ .tag = .mov_mem_imm, @@ -5385,31 +5385,33 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE return self.genSetStackArg(ty, stack_offset, .{ .register = reg }); }, .immediate => |imm| { + _ = imm; switch (abi_size) { - 1, 2, 4 => { - // We have a positive stack offset value but we want a twos complement negative - // offset from rbp, which is at the top of the stack frame. - // mov [rbp+offset], immediate - const flags: u2 = switch (abi_size) { - 1 => 0b00, - 2 => 0b01, - 4 => 0b10, - else => unreachable, - }; - const payload = try self.addExtra(Mir.ImmPair{ - .dest_off = -stack_offset, - .operand = @intCast(u32, imm), - }); - _ = try self.addInst(.{ - .tag = .mov_mem_imm, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = .rsp, - .flags = flags, - }), - .data = .{ .payload = payload }, - }); - }, - 8 => { + // TODO + // 1, 2, 4 => { + // // We have a positive stack offset value but we want a twos complement negative + // // offset from rbp, which is at the top of the stack frame. + // // mov [rbp+offset], immediate + // const flags: u2 = switch (abi_size) { + // 1 => 0b00, + // 2 => 0b01, + // 4 => 0b10, + // else => unreachable, + // }; + // const payload = try self.addExtra(Mir.ImmPair{ + // .dest_off = -stack_offset, + // .operand = @intCast(u32, imm), + // }); + // _ = try self.addInst(.{ + // .tag = .mov_mem_imm, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = .rsp, + // .flags = flags, + // }), + // .data = .{ .payload = payload }, + // }); + // }, + 1, 2, 4, 8 => { const reg = try self.copyToTmpRegister(ty, mcv); return self.genSetStackArg(ty, stack_offset, MCValue{ .register = reg }); }, @@ -5543,7 +5545,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl assert(ty.isError()); const payload = try self.addExtra(Mir.ImmPair{ .dest_off = -stack_offset, - .operand = @intCast(u32, x_big), + .operand = @truncate(u32, x_big), }); _ = try self.addInst(.{ .tag = .mov_mem_imm, @@ -5557,7 +5559,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl 1, 2, 4 => { const payload = try self.addExtra(Mir.ImmPair{ .dest_off = -stack_offset, - .operand = @intCast(u32, x_big), + .operand = @truncate(u32, x_big), }); _ = try self.addInst(.{ .tag = .mov_mem_imm, @@ -7020,7 +7022,7 @@ fn truncateRegister(self: *Self, ty: Type, reg: Register) !void { .unsigned => { const shift = @intCast(u6, max_reg_bit_width - int_info.bits); const mask = (~@as(u64, 0)) >> shift; - if (int_info.bits <= 32) { + if (int_info.bits < 32) { try self.genBinOpMir(.@"and", Type.usize, .{ .register = reg }, .{ .immediate = mask }); } else { const tmp_reg = try self.copyToTmpRegister(Type.usize, .{ .immediate = mask }); diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index e23b500fb8..e69601c40b 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -612,12 +612,17 @@ fn mirArithMemImm(emit: *Emit, mnemonic: Instruction.Mnemonic, inst: Mir.Inst.In 0b10 => .dword, 0b11 => .qword, }; + const imm = switch (ops.flags) { + 0b00 => @truncate(u8, imm_pair.operand), + 0b01 => @truncate(u16, imm_pair.operand), + 0b10, 0b11 => @truncate(u32, imm_pair.operand), + }; return emit.encode(mnemonic, .{ .op1 = .{ .mem = Memory.sib(ptr_size, .{ .disp = imm_pair.dest_off, .base = ops.reg1, }) }, - .op2 = .{ .imm = Immediate.u(imm_pair.operand) }, + .op2 = .{ .imm = Immediate.u(imm) }, }); } diff --git a/src/arch/x86_64/Encoding.zig b/src/arch/x86_64/Encoding.zig index 3c2e9f848c..635a09512b 100644 --- a/src/arch/x86_64/Encoding.zig +++ b/src/arch/x86_64/Encoding.zig @@ -544,6 +544,10 @@ pub const Op = enum { } if (op.isImmediate() and target.isImmediate()) { switch (target) { + .imm64 => switch (op) { + .unity, .imm8s, .imm8, .imm16s, .imm16, .imm32s, .imm32, .imm64 => return true, + else => return op == target, + }, .imm32s, .rel32 => switch (op) { .unity, .imm8s, .imm8, .imm16s, .imm16, .imm32s => return true, else => return op == target, diff --git a/src/arch/x86_64/encoder.zig b/src/arch/x86_64/encoder.zig index 066a733960..925e3fe181 100644 --- a/src/arch/x86_64/encoder.zig +++ b/src/arch/x86_64/encoder.zig @@ -108,7 +108,7 @@ pub const Instruction = struct { .op3 = args.op3, .op4 = args.op4, })) orelse { - std.log.debug("{s} {s} {s} {s} {s}", .{ + std.log.warn("{s} {s} {s} {s} {s}", .{ @tagName(mnemonic), @tagName(Encoding.Op.fromOperand(args.op1)), @tagName(Encoding.Op.fromOperand(args.op2)), From 5b3770102845b17207f85d3a43a3b6cab551a329 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 8 Mar 2023 09:49:59 +0100 Subject: [PATCH 084/294] x86_64: refactor immediate selection logic --- src/arch/x86_64/Encoding.zig | 59 +++++++++++++++++------------------- src/arch/x86_64/bits.zig | 4 +-- 2 files changed, 29 insertions(+), 34 deletions(-) diff --git a/src/arch/x86_64/Encoding.zig b/src/arch/x86_64/Encoding.zig index 635a09512b..94f816eaa1 100644 --- a/src/arch/x86_64/Encoding.zig +++ b/src/arch/x86_64/Encoding.zig @@ -453,7 +453,8 @@ pub const Op = enum { pub fn bitSize(op: Op) u64 { return switch (op) { .none, .o16, .o32, .o64, .moffs, .m, .sreg => unreachable, - .unity, .imm8, .imm8s, .al, .cl, .r8, .m8, .rm8, .rel8 => 8, + .unity => 1, + .imm8, .imm8s, .al, .cl, .r8, .m8, .rm8, .rel8 => 8, .imm16, .imm16s, .ax, .r16, .m16, .rm16, .rel16 => 16, .imm32, .imm32s, .eax, .r32, .m32, .rm32, .rel32, .xmm_m32 => 32, .imm64, .rax, .r64, .m64, .rm64, .xmm_m64 => 64, @@ -462,6 +463,18 @@ pub const Op = enum { }; } + pub fn isSigned(op: Op) bool { + return switch (op) { + .unity, .imm8, .imm16, .imm32, .imm64 => false, + .imm8s, .imm16s, .imm32s => true, + else => unreachable, + }; + } + + pub fn isUnsigned(op: Op) bool { + return !op.isSigned(); + } + pub fn isRegister(op: Op) bool { // zig fmt: off return switch (op) { @@ -516,8 +529,7 @@ pub const Op = enum { }; } - /// Given an operand `op` checks if `target` is a subset for the purposes - /// of the encoding. + /// Given an operand `op` checks if `target` is a subset for the purposes of the encoding. pub fn isSubset(op: Op, target: Op, mode: Mode) bool { switch (op) { .m, .o16, .o32, .o64 => unreachable, @@ -544,36 +556,19 @@ pub const Op = enum { } if (op.isImmediate() and target.isImmediate()) { switch (target) { - .imm64 => switch (op) { - .unity, .imm8s, .imm8, .imm16s, .imm16, .imm32s, .imm32, .imm64 => return true, - else => return op == target, - }, - .imm32s, .rel32 => switch (op) { - .unity, .imm8s, .imm8, .imm16s, .imm16, .imm32s => return true, - else => return op == target, - }, - .imm32 => switch (op) { - .unity, .imm8, .imm8s, .imm16, .imm16s, .imm32, .imm32s => return true, - else => return op == target, - }, - .imm16s, .rel16 => switch (op) { - .unity, .imm8s, .imm8, .imm16s => return true, - else => return op == target, - }, - .imm16 => switch (op) { - .unity, .imm8, .imm8s, .imm16, .imm16s => return true, - else => return op == target, - }, - .imm8s, .rel8 => switch (op) { - .unity, .imm8s => return true, - else => return op == target, - }, - .imm8 => switch (op) { - .unity, .imm8, .imm8s => return true, - else => return op == target, - }, - else => return op == target, + .imm64 => if (op.bitSize() <= 64) return true, + .imm32s, .rel32 => if (op.bitSize() < 32 or (op.bitSize() == 32 and op.isSigned())) + return true, + .imm32 => if (op.bitSize() <= 32) return true, + .imm16s, .rel16 => if (op.bitSize() < 16 or (op.bitSize() == 16 and op.isSigned())) + return true, + .imm16 => if (op.bitSize() <= 16) return true, + .imm8s, .rel8 => if (op.bitSize() < 8 or (op.bitSize() == 8 and op.isSigned())) + return true, + .imm8 => if (op.bitSize() <= 8) return true, + else => {}, } + return op == target; } return false; }, diff --git a/src/arch/x86_64/bits.zig b/src/arch/x86_64/bits.zig index cd7ed91849..ad9a6f7f23 100644 --- a/src/arch/x86_64/bits.zig +++ b/src/arch/x86_64/bits.zig @@ -525,13 +525,13 @@ pub const Immediate = union(enum) { pub fn asUnsigned(imm: Immediate, bit_size: u64) u64 { return switch (imm) { .signed => |x| switch (bit_size) { - 8 => @bitCast(u8, @intCast(i8, x)), + 1, 8 => @bitCast(u8, @intCast(i8, x)), 16 => @bitCast(u16, @intCast(i16, x)), 32 => @bitCast(u32, @intCast(i32, x)), else => unreachable, }, .unsigned => |x| switch (bit_size) { - 8 => @intCast(u8, x), + 1, 8 => @intCast(u8, x), 16 => @intCast(u16, x), 32 => @intCast(u32, x), 64 => x, From 6e882d730b2ed1f2494e7b28f1fed2726e4a1ac0 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 8 Mar 2023 23:45:05 +0100 Subject: [PATCH 085/294] x86_64: introduce assemble() helper which encodes/decodes into MIR -> Instruction --- src/arch/x86_64/CodeGen.zig | 2154 ++++++++++++++++++----------------- src/arch/x86_64/Emit.zig | 1411 +++++++---------------- src/arch/x86_64/Mir.zig | 688 ++++------- src/arch/x86_64/encoder.zig | 11 +- 4 files changed, 1750 insertions(+), 2514 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index fe44adcfa1..be8d07a2f6 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -374,71 +374,99 @@ pub fn generate( fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index { const gpa = self.gpa; try self.mir_instructions.ensureUnusedCapacity(gpa, 1); - const result_index = @intCast(Air.Inst.Index, self.mir_instructions.len); + const result_index = @intCast(Mir.Inst.Index, self.mir_instructions.len); self.mir_instructions.appendAssumeCapacity(inst); return result_index; } -pub fn addExtra(self: *Self, extra: anytype) Allocator.Error!u32 { +fn addExtra(self: *Self, extra: anytype) Allocator.Error!u32 { const fields = std.meta.fields(@TypeOf(extra)); try self.mir_extra.ensureUnusedCapacity(self.gpa, fields.len); return self.addExtraAssumeCapacity(extra); } -fn extraData(self: *Self, comptime T: type, index: u32) struct { data: T, end: u32 } { - const fields = std.meta.fields(T); - var i: u32 = index; - var result: T = undefined; - inline for (fields) |field| { - @field(result, field.name) = switch (field.type) { - u32 => self.mir_extra.items[i], - i32 => @bitCast(i32, self.mir_extra.items[i]), - else => @compileError("bad field type"), - }; - i += 1; - } - return .{ - .data = result, - .end = i, - }; -} - -pub fn addExtraAssumeCapacity(self: *Self, extra: anytype) u32 { +fn addExtraAssumeCapacity(self: *Self, extra: anytype) u32 { const fields = std.meta.fields(@TypeOf(extra)); const result = @intCast(u32, self.mir_extra.items.len); inline for (fields) |field| { self.mir_extra.appendAssumeCapacity(switch (field.type) { u32 => @field(extra, field.name), i32 => @bitCast(u32, @field(extra, field.name)), - else => @compileError("bad field type"), + else => @compileError("bad field type: " ++ field.name ++ ": " ++ @typeName(field.type)), }); } return result; } +fn assemble(self: *Self, tag: Mir.Inst.Tag, args: struct { + op1: Mir.Operand = .none, + op2: Mir.Operand = .none, + op3: Mir.Operand = .none, + op4: Mir.Operand = .none, +}) !void { + const ops: Mir.Inst.Ops = blk: { + if (args.op1 == .none and args.op2 == .none and args.op3 == .none and args.op4 == .none) + break :blk .none; + + if (args.op1 == .reg and args.op2 == .reg) + break :blk .rr; + if (args.op1 == .reg and args.op2 == .imm) switch (args.op2.imm) { + .signed => break :blk .ri_s, + .unsigned => break :blk .ri_u, + }; + if (args.op1 == .reg) + break :blk .r; + if (args.op1 == .imm) switch (args.op1.imm) { + .signed => break :blk .imm_s, + .unsigned => break :blk .imm_u, // TODO 64bits + }; + + unreachable; + }; + const data: Mir.Inst.Data = switch (ops) { + .none => undefined, + .imm_s => .{ .imm_s = args.op1.imm.signed }, + .imm_u => .{ .imm_u = @intCast(u32, args.op1.imm.unsigned) }, + .r => .{ .r = args.op1.reg }, + .rr => .{ .rr = .{ + .r1 = args.op1.reg, + .r2 = args.op2.reg, + } }, + .ri_s => .{ .ri_s = .{ + .r1 = args.op1.reg, + .imm = args.op2.imm.signed, + } }, + .ri_u => .{ .ri_u = .{ + .r1 = args.op1.reg, + .imm = @intCast(u32, args.op2.imm.unsigned), + } }, + else => unreachable, + }; + _ = try self.addInst(.{ + .tag = tag, + .ops = ops, + .data = data, + }); +} + fn gen(self: *Self) InnerError!void { const cc = self.fn_type.fnCallingConvention(); if (cc != .Naked) { - _ = try self.addInst(.{ - .tag = .push, - .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rbp }), - .data = undefined, // unused for push reg, + try self.assemble(.push, .{ + .op1 = .{ .reg = .rbp }, }); - _ = try self.addInst(.{ - .tag = .mov, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = .rbp, - .reg2 = .rsp, - }), - .data = undefined, + try self.assemble(.mov, .{ + .op1 = .{ .reg = .rbp }, + .op2 = .{ .reg = .rsp }, }); + // We want to subtract the aligned stack frame size from rsp here, but we don't // yet know how big it will be, so we leave room for a 4-byte stack size. // TODO During semantic analysis, check if there are no function calls. If there // are none, here we can omit the part where we subtract and then add rsp. const backpatch_stack_sub = try self.addInst(.{ .tag = .nop, - .ops = undefined, + .ops = .none, .data = undefined, }); @@ -465,7 +493,7 @@ fn gen(self: *Self) InnerError!void { // Push callee-preserved regs that were used actually in use. const backpatch_push_callee_preserved_regs = try self.addInst(.{ .tag = .nop, - .ops = undefined, + .ops = .none, .data = undefined, }); @@ -496,7 +524,7 @@ fn gen(self: *Self) InnerError!void { // Pop saved callee-preserved regs. const backpatch_pop_callee_preserved_regs = try self.addInst(.{ .tag = .nop, - .ops = undefined, + .ops = .none, .data = undefined, }); @@ -509,21 +537,12 @@ fn gen(self: *Self) InnerError!void { // Maybe add rsp, x if required. This is backpatched later. const backpatch_stack_add = try self.addInst(.{ .tag = .nop, - .ops = undefined, + .ops = .none, .data = undefined, }); - _ = try self.addInst(.{ - .tag = .pop, - .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rbp }), - .data = undefined, - }); - - _ = try self.addInst(.{ - .tag = .ret, - .ops = Mir.Inst.Ops.encode(.{ .flags = 0b11 }), - .data = undefined, - }); + try self.assemble(.pop, .{ .op1 = .{ .reg = .rbp } }); + try self.assemble(.ret, .{}); // Adjust the stack if (self.max_end_stack > math.maxInt(i32)) { @@ -537,27 +556,34 @@ fn gen(self: *Self) InnerError!void { if (aligned_stack_end > 0) { self.mir_instructions.set(backpatch_stack_sub, .{ .tag = .sub, - .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rsp }), - .data = .{ .imm = aligned_stack_end }, + .ops = .ri_u, + .data = .{ .ri_u = .{ + .r1 = .rsp, + .imm = aligned_stack_end, + } }, }); self.mir_instructions.set(backpatch_stack_add, .{ .tag = .add, - .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rsp }), - .data = .{ .imm = aligned_stack_end }, + .ops = .ri_u, + .data = .{ .ri_u = .{ + .r1 = .rsp, + .imm = aligned_stack_end, + } }, }); const save_reg_list = try self.addExtra(Mir.SaveRegisterList{ + .base_reg = @enumToInt(Register.rbp), .register_list = reg_list.asInt(), .stack_end = aligned_stack_end, }); self.mir_instructions.set(backpatch_push_callee_preserved_regs, .{ .tag = .push_regs, - .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rbp }), + .ops = undefined, .data = .{ .payload = save_reg_list }, }); self.mir_instructions.set(backpatch_pop_callee_preserved_regs, .{ .tag = .pop_regs, - .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rbp }), + .ops = undefined, .data = .{ .payload = save_reg_list }, }); } @@ -1306,14 +1332,15 @@ fn airMin(self: *Self, inst: Air.Inst.Index) !void { .unsigned => .b, .signed => .l, }; - _ = try self.addInst(.{ - .tag = .cond_mov, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = dst_mcv.register, - .reg2 = lhs_reg, - }), - .data = .{ .cc = cc }, - }); + _ = cc; + // _ = try self.addInst(.{ + // .tag = .cond_mov, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = dst_mcv.register, + // .reg2 = lhs_reg, + // }), + // .data = .{ .cc = cc }, + // }); break :result dst_mcv; }; @@ -1513,13 +1540,14 @@ fn genSetStackTruncatedOverflowCompare( .signed => .o, .unsigned => .c, }; - _ = try self.addInst(.{ - .tag = .cond_set_byte, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = overflow_reg.to8(), - }), - .data = .{ .cc = cc }, - }); + _ = cc; + // _ = try self.addInst(.{ + // .tag = .cond_set_byte, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = overflow_reg.to8(), + // }), + // .data = .{ .cc = cc }, + // }); const scratch_reg = temp_regs[1]; try self.genSetReg(extended_ty, scratch_reg, .{ .register = reg }); @@ -1532,11 +1560,11 @@ fn genSetStackTruncatedOverflowCompare( ); const eq_reg = temp_regs[2]; - _ = try self.addInst(.{ - .tag = .cond_set_byte, - .ops = Mir.Inst.Ops.encode(.{ .reg1 = eq_reg.to8() }), - .data = .{ .cc = .ne }, - }); + // _ = try self.addInst(.{ + // .tag = .cond_set_byte, + // .ops = Mir.Inst.Ops.encode(.{ .reg1 = eq_reg.to8() }), + // .data = .{ .cc = .ne }, + // }); try self.genBinOpMir( .@"or", @@ -1680,25 +1708,26 @@ fn genIntMulDivOpMir( try self.genSetReg(ty, .rax, lhs); } - switch (signedness) { - .signed => { - _ = try self.addInst(.{ - .tag = .cwd, - .ops = Mir.Inst.Ops.encode(.{ .flags = 0b11 }), - .data = undefined, - }); - }, - .unsigned => { - _ = try self.addInst(.{ - .tag = .xor, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = .rdx, - .reg2 = .rdx, - }), - .data = undefined, - }); - }, - } + _ = signedness; + // switch (signedness) { + // .signed => { + // _ = try self.addInst(.{ + // .tag = .cwd, + // .ops = Mir.Inst.Ops.encode(.{ .flags = 0b11 }), + // .data = undefined, + // }); + // }, + // .unsigned => { + // _ = try self.addInst(.{ + // .tag = .xor, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = .rdx, + // .reg2 = .rdx, + // }), + // .data = undefined, + // }); + // }, + // } const factor = switch (rhs) { .register => rhs, @@ -1708,33 +1737,35 @@ fn genIntMulDivOpMir( break :blk MCValue{ .register = reg }; }, }; + _ = factor; + _ = tag; - switch (factor) { - .register => |reg| { - _ = try self.addInst(.{ - .tag = tag, - .ops = Mir.Inst.Ops.encode(.{ .reg1 = reg }), - .data = undefined, - }); - }, - .stack_offset => |off| { - _ = try self.addInst(.{ - .tag = tag, - .ops = Mir.Inst.Ops.encode(.{ - .reg2 = .rbp, - .flags = switch (abi_size) { - 1 => 0b00, - 2 => 0b01, - 4 => 0b10, - 8 => 0b11, - else => unreachable, - }, - }), - .data = .{ .disp = -off }, - }); - }, - else => unreachable, - } + // switch (factor) { + // .register => |reg| { + // _ = try self.addInst(.{ + // .tag = tag, + // .ops = Mir.Inst.Ops.encode(.{ .reg1 = reg }), + // .data = undefined, + // }); + // }, + // .stack_offset => |off| { + // _ = try self.addInst(.{ + // .tag = tag, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg2 = .rbp, + // .flags = switch (abi_size) { + // 1 => 0b00, + // 2 => 0b01, + // 4 => 0b10, + // 8 => 0b11, + // else => unreachable, + // }, + // }), + // .data = .{ .disp = -off }, + // }); + // }, + // else => unreachable, + // } } /// Always returns a register. @@ -1760,38 +1791,38 @@ fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCVa .unsigned => .div, }, Type.isize, signedness, .{ .register = dividend }, .{ .register = divisor }); - _ = try self.addInst(.{ - .tag = .xor, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = divisor.to64(), - .reg2 = dividend.to64(), - }), - .data = undefined, - }); - _ = try self.addInst(.{ - .tag = .sar, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = divisor.to64(), - .flags = 0b10, - }), - .data = .{ .imm = 63 }, - }); - _ = try self.addInst(.{ - .tag = .@"test", - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = .rdx, - .reg2 = .rdx, - }), - .data = undefined, - }); - _ = try self.addInst(.{ - .tag = .cond_mov, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = divisor.to64(), - .reg2 = .rdx, - }), - .data = .{ .cc = .e }, - }); + // _ = try self.addInst(.{ + // .tag = .xor, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = divisor.to64(), + // .reg2 = dividend.to64(), + // }), + // .data = undefined, + // }); + // _ = try self.addInst(.{ + // .tag = .sar, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = divisor.to64(), + // .flags = 0b10, + // }), + // .data = .{ .imm = 63 }, + // }); + // _ = try self.addInst(.{ + // .tag = .@"test", + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = .rdx, + // .reg2 = .rdx, + // }), + // .data = undefined, + // }); + // _ = try self.addInst(.{ + // .tag = .cond_mov, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = divisor.to64(), + // .reg2 = .rdx, + // }), + // .data = .{ .cc = .e }, + // }); try self.genBinOpMir(.add, Type.isize, .{ .register = divisor }, .{ .register = .rax }); return MCValue{ .register = divisor }; } @@ -2226,16 +2257,17 @@ fn genSliceElemPtr(self: *Self, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref) !MCValue { const addr_reg = try self.register_manager.allocReg(null, gp); switch (slice_mcv) { .stack_offset => |off| { + _ = off; // mov reg, [rbp - 8] - _ = try self.addInst(.{ - .tag = .mov, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = addr_reg.to64(), - .reg2 = .rbp, - .flags = 0b01, - }), - .data = .{ .disp = -@intCast(i32, off) }, - }); + // _ = try self.addInst(.{ + // .tag = .mov, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = addr_reg.to64(), + // .reg2 = .rbp, + // .flags = 0b01, + // }), + // .data = .{ .disp = -@intCast(i32, off) }, + // }); }, else => return self.fail("TODO implement slice_elem_ptr when slice is {}", .{slice_mcv}), } @@ -2312,25 +2344,26 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void { )); try self.genSetStack(array_ty, off, array, .{}); // lea reg, [rbp] - _ = try self.addInst(.{ - .tag = .lea, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = addr_reg.to64(), - .reg2 = .rbp, - }), - .data = .{ .disp = -off }, - }); + // _ = try self.addInst(.{ + // .tag = .lea, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = addr_reg.to64(), + // .reg2 = .rbp, + // }), + // .data = .{ .disp = -off }, + // }); }, .stack_offset => |off| { + _ = off; // lea reg, [rbp] - _ = try self.addInst(.{ - .tag = .lea, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = addr_reg.to64(), - .reg2 = .rbp, - }), - .data = .{ .disp = -off }, - }); + // _ = try self.addInst(.{ + // .tag = .lea, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = addr_reg.to64(), + // .reg2 = .rbp, + // }), + // .data = .{ .disp = -off }, + // }); }, .memory, .linker_load => { try self.loadMemPtrIntoRegister(addr_reg, Type.usize, array); @@ -2388,15 +2421,15 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void { return self.fail("TODO copy value with size {} from pointer", .{elem_abi_size}); } else { // mov dst_mcv, [dst_mcv] - _ = try self.addInst(.{ - .tag = .mov, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = registerAlias(dst_mcv.register, @intCast(u32, elem_abi_size)), - .reg2 = dst_mcv.register, - .flags = 0b01, - }), - .data = .{ .disp = 0 }, - }); + // _ = try self.addInst(.{ + // .tag = .mov, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = registerAlias(dst_mcv.register, @intCast(u32, elem_abi_size)), + // .reg2 = dst_mcv.register, + // .flags = 0b01, + // }), + // .data = .{ .disp = 0 }, + // }); break :result .{ .register = registerAlias(dst_mcv.register, @intCast(u32, elem_abi_size)) }; } }; @@ -2650,16 +2683,17 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo .undef => unreachable, .eflags => unreachable, .register => |dst_reg| { + _ = dst_reg; // mov dst_reg, [reg] - _ = try self.addInst(.{ - .tag = .mov, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = registerAlias(dst_reg, @intCast(u32, abi_size)), - .reg2 = reg, - .flags = 0b01, - }), - .data = .{ .disp = 0 }, - }); + // _ = try self.addInst(.{ + // .tag = .mov, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = registerAlias(dst_reg, @intCast(u32, abi_size)), + // .reg2 = reg, + // .flags = 0b01, + // }), + // .data = .{ .disp = 0 }, + // }); }, .stack_offset => |off| { if (abi_size <= 8) { @@ -2724,19 +2758,22 @@ fn loadMemPtrIntoRegister(self: *Self, reg: Register, ptr_ty: Type, ptr: MCValue .direct => 0b01, .import => 0b10, }; - _ = try self.addInst(.{ - .tag = .lea_pic, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = registerAlias(reg, abi_size), - .flags = flags, - }), - .data = .{ - .relocation = .{ - .atom_index = atom_index, - .sym_index = load_struct.sym_index, - }, - }, - }); + _ = abi_size; + _ = atom_index; + _ = flags; + // _ = try self.addInst(.{ + // .tag = .lea_pic, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = registerAlias(reg, abi_size), + // .flags = flags, + // }), + // .data = .{ + // .relocation = .{ + // .atom_index = atom_index, + // .sym_index = load_struct.sym_index, + // }, + // }, + // }); }, .memory => |addr| { // TODO: in case the address fits in an imm32 we can use [ds:imm32] @@ -2779,27 +2816,28 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type try self.genSetReg(value_ty, reg, value); }, .immediate => |imm| { + _ = imm; switch (abi_size) { 1, 2, 4 => { // TODO this is wasteful! // introduce new MIR tag specifically for mov [reg + 0], imm - const payload = try self.addExtra(Mir.ImmPair{ - .dest_off = 0, - .operand = @truncate(u32, imm), - }); - _ = try self.addInst(.{ - .tag = .mov_mem_imm, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = reg.to64(), - .flags = switch (abi_size) { - 1 => 0b00, - 2 => 0b01, - 4 => 0b10, - else => unreachable, - }, - }), - .data = .{ .payload = payload }, - }); + // const payload = try self.addExtra(Mir.ImmPair{ + // .dest_off = 0, + // .operand = @truncate(u32, imm), + // }); + // _ = try self.addInst(.{ + // .tag = .mov_mem_imm, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = reg.to64(), + // .flags = switch (abi_size) { + // 1 => 0b00, + // 2 => 0b01, + // 4 => 0b10, + // else => unreachable, + // }, + // }), + // .data = .{ .payload = payload }, + // }); }, 8 => { // TODO: optimization: if the imm is only using the lower @@ -2829,13 +2867,13 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type const overflow_bit_ty = value_ty.structFieldType(1); const overflow_bit_offset = value_ty.structFieldOffset(1, self.target.*); const tmp_reg = try self.register_manager.allocReg(null, gp); - _ = try self.addInst(.{ - .tag = .cond_set_byte, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = tmp_reg.to8(), - }), - .data = .{ .cc = ro.eflags }, - }); + // _ = try self.addInst(.{ + // .tag = .cond_set_byte, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = tmp_reg.to8(), + // }), + // .data = .{ .cc = ro.eflags }, + // }); try self.genInlineMemcpyRegisterRegister( overflow_bit_ty, reg, @@ -2878,15 +2916,15 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type // to get the actual address of the value we want to modify we have to go through the GOT // mov reg, [reg] - _ = try self.addInst(.{ - .tag = .mov, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = addr_reg.to64(), - .reg2 = addr_reg.to64(), - .flags = 0b01, - }), - .data = .{ .disp = 0 }, - }); + // _ = try self.addInst(.{ + // .tag = .mov, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = addr_reg.to64(), + // .reg2 = addr_reg.to64(), + // .flags = 0b01, + // }), + // .data = .{ .disp = 0 }, + // }); const new_ptr = MCValue{ .register = addr_reg.to64() }; @@ -2896,11 +2934,11 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type return self.fail("TODO saving imm to memory for abi_size {}", .{abi_size}); } - const payload = try self.addExtra(Mir.ImmPair{ - .dest_off = 0, - // TODO check if this logic is correct - .operand = @intCast(u32, imm), - }); + // const payload = try self.addExtra(Mir.ImmPair{ + // .dest_off = 0, + // // TODO check if this logic is correct + // .operand = @intCast(u32, imm), + // }); const flags: u2 = switch (abi_size) { 1 => 0b00, 2 => 0b01, @@ -2919,14 +2957,14 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type return self.fail("TODO imm64 would get incorrectly sign extended", .{}); } } - _ = try self.addInst(.{ - .tag = .mov_mem_imm, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = addr_reg.to64(), - .flags = flags, - }), - .data = .{ .payload = payload }, - }); + // _ = try self.addInst(.{ + // .tag = .mov_mem_imm, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = addr_reg.to64(), + // .flags = flags, + // }), + // .data = .{ .payload = payload }, + // }); }, .register => { return self.store(new_ptr, value, ptr_ty, value_ty); @@ -2939,15 +2977,15 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type try self.loadMemPtrIntoRegister(tmp_reg, value_ty, value); - _ = try self.addInst(.{ - .tag = .mov, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = tmp_reg, - .reg2 = tmp_reg, - .flags = 0b01, - }), - .data = .{ .disp = 0 }, - }); + // _ = try self.addInst(.{ + // .tag = .mov, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = tmp_reg, + // .reg2 = tmp_reg, + // .flags = 0b01, + // }), + // .data = .{ .disp = 0 }, + // }); return self.store(new_ptr, .{ .register = tmp_reg }, ptr_ty, value_ty); } @@ -3109,14 +3147,14 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { }; const field_size = @intCast(u32, struct_field_ty.abiSize(self.target.*)); if (signedness == .signed and field_size < 8) { - _ = try self.addInst(.{ - .tag = .mov_sign_extend, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = dst_mcv.register, - .reg2 = registerAlias(dst_mcv.register, field_size), - }), - .data = undefined, - }); + // _ = try self.addInst(.{ + // .tag = .mov_sign_extend, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = dst_mcv.register, + // .reg2 = registerAlias(dst_mcv.register, field_size), + // }), + // .data = undefined, + // }); } break :result dst_mcv; @@ -3133,13 +3171,13 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { defer self.register_manager.unlockReg(reg_lock); const dst_reg = try self.register_manager.allocReg(inst, gp); - _ = try self.addInst(.{ - .tag = .cond_set_byte, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = dst_reg.to8(), - }), - .data = .{ .cc = ro.eflags }, - }); + // _ = try self.addInst(.{ + // .tag = .cond_set_byte, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = dst_reg.to8(), + // }), + // .data = .{ .cc = ro.eflags }, + // }); break :result MCValue{ .register = dst_reg.to8() }; }, else => unreachable, @@ -3176,22 +3214,22 @@ fn genShiftBinOpMir(self: *Self, tag: Mir.Inst.Tag, ty: Type, reg: Register, shi .immediate => |imm| switch (imm) { 0 => return, 1 => { - _ = try self.addInst(.{ - .tag = tag, - .ops = Mir.Inst.Ops.encode(.{ .reg1 = registerAlias(reg, abi_size) }), - .data = undefined, - }); + // _ = try self.addInst(.{ + // .tag = tag, + // .ops = Mir.Inst.Ops.encode(.{ .reg1 = registerAlias(reg, abi_size) }), + // .data = undefined, + // }); return; }, else => { - _ = try self.addInst(.{ - .tag = tag, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = registerAlias(reg, abi_size), - .flags = 0b10, - }), - .data = .{ .imm = @intCast(u8, imm) }, - }); + // _ = try self.addInst(.{ + // .tag = tag, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = registerAlias(reg, abi_size), + // .flags = 0b10, + // }), + // .data = .{ .imm = @intCast(u8, imm) }, + // }); return; }, }, @@ -3204,15 +3242,16 @@ fn genShiftBinOpMir(self: *Self, tag: Mir.Inst.Tag, ty: Type, reg: Register, shi try self.register_manager.getReg(.rcx, null); try self.genSetReg(Type.u8, .rcx, shift); } + _ = abi_size; - _ = try self.addInst(.{ - .tag = tag, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = registerAlias(reg, abi_size), - .flags = 0b01, - }), - .data = undefined, - }); + // _ = try self.addInst(.{ + // .tag = tag, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = registerAlias(reg, abi_size), + // .flags = 0b01, + // }), + // .data = undefined, + // }); } /// Result is always a register. @@ -3583,49 +3622,51 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu .register => |src_reg| switch (dst_ty.zigTypeTag()) { .Float => { if (intrinsicsAllowed(self.target.*, dst_ty)) { - const actual_tag: Mir.Inst.Tag = switch (dst_ty.tag()) { - .f32 => switch (mir_tag) { - .add => Mir.Inst.Tag.add_f32, - .cmp => Mir.Inst.Tag.cmp_f32, - else => return self.fail("TODO genBinOpMir for f32 register-register with MIR tag {}", .{mir_tag}), - }, - .f64 => switch (mir_tag) { - .add => Mir.Inst.Tag.add_f64, - .cmp => Mir.Inst.Tag.cmp_f64, - else => return self.fail("TODO genBinOpMir for f64 register-register with MIR tag {}", .{mir_tag}), - }, - else => return self.fail("TODO genBinOpMir for float register-register and type {}", .{dst_ty.fmtDebug()}), - }; - _ = try self.addInst(.{ - .tag = actual_tag, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = dst_reg.to128(), - .reg2 = src_reg.to128(), - }), - .data = undefined, - }); + // const actual_tag: Mir.Inst.Tag = switch (dst_ty.tag()) { + // .f32 => switch (mir_tag) { + // .add => Mir.Inst.Tag.add_f32, + // .cmp => Mir.Inst.Tag.cmp_f32, + // else => return self.fail("TODO genBinOpMir for f32 register-register with MIR tag {}", .{mir_tag}), + // }, + // .f64 => switch (mir_tag) { + // .add => Mir.Inst.Tag.add_f64, + // .cmp => Mir.Inst.Tag.cmp_f64, + // else => return self.fail("TODO genBinOpMir for f64 register-register with MIR tag {}", .{mir_tag}), + // }, + // else => return self.fail("TODO genBinOpMir for float register-register and type {}", .{dst_ty.fmtDebug()}), + // }; + // _ = try self.addInst(.{ + // .tag = actual_tag, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = dst_reg.to128(), + // .reg2 = src_reg.to128(), + // }), + // .data = undefined, + // }); return; } return self.fail("TODO genBinOpMir for float register-register and no intrinsics", .{}); }, else => { - _ = try self.addInst(.{ - .tag = mir_tag, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = registerAlias(dst_reg, abi_size), - .reg2 = registerAlias(src_reg, abi_size), - }), - .data = undefined, - }); + _ = src_reg; + // _ = try self.addInst(.{ + // .tag = mir_tag, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = registerAlias(dst_reg, abi_size), + // .reg2 = registerAlias(src_reg, abi_size), + // }), + // .data = undefined, + // }); }, }, .immediate => |imm| { - _ = try self.addInst(.{ - .tag = mir_tag, - .ops = Mir.Inst.Ops.encode(.{ .reg1 = registerAlias(dst_reg, abi_size) }), - .data = .{ .imm = @intCast(u32, imm) }, - }); + _ = imm; + // _ = try self.addInst(.{ + // .tag = mir_tag, + // .ops = Mir.Inst.Ops.encode(.{ .reg1 = registerAlias(dst_reg, abi_size) }), + // .data = .{ .imm = @intCast(u32, imm) }, + // }); }, .memory, .linker_load, @@ -3642,15 +3683,15 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu if (off > math.maxInt(i32)) { return self.fail("stack offset too large", .{}); } - _ = try self.addInst(.{ - .tag = mir_tag, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = registerAlias(dst_reg, abi_size), - .reg2 = .rbp, - .flags = 0b01, - }), - .data = .{ .disp = -off }, - }); + // _ = try self.addInst(.{ + // .tag = mir_tag, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = registerAlias(dst_reg, abi_size), + // .reg2 = .rbp, + // .flags = 0b01, + // }), + // .data = .{ .disp = -off }, + // }); }, } }, @@ -3668,26 +3709,28 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu .dead, .unreach => unreachable, .register_overflow => unreachable, .register => |src_reg| { - _ = try self.addInst(.{ - .tag = mir_tag, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = .rbp, - .reg2 = registerAlias(src_reg, abi_size), - .flags = 0b10, - }), - .data = .{ .disp = -off }, - }); + _ = src_reg; + // _ = try self.addInst(.{ + // .tag = mir_tag, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = .rbp, + // .reg2 = registerAlias(src_reg, abi_size), + // .flags = 0b10, + // }), + // .data = .{ .disp = -off }, + // }); }, .immediate => |imm| { - const tag: Mir.Inst.Tag = switch (mir_tag) { - .add => .add_mem_imm, - .@"or" => .or_mem_imm, - .@"and" => .and_mem_imm, - .sub => .sub_mem_imm, - .xor => .xor_mem_imm, - .cmp => .cmp_mem_imm, - else => unreachable, - }; + _ = imm; + // const tag: Mir.Inst.Tag = switch (mir_tag) { + // .add => .add_mem_imm, + // .@"or" => .or_mem_imm, + // .@"and" => .and_mem_imm, + // .sub => .sub_mem_imm, + // .xor => .xor_mem_imm, + // .cmp => .cmp_mem_imm, + // else => unreachable, + // }; const flags: u2 = switch (abi_size) { 1 => 0b00, 2 => 0b01, @@ -3695,18 +3738,19 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu 8 => 0b11, else => unreachable, }; - const payload = try self.addExtra(Mir.ImmPair{ - .dest_off = -off, - .operand = @intCast(u32, imm), - }); - _ = try self.addInst(.{ - .tag = tag, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = .rbp, - .flags = flags, - }), - .data = .{ .payload = payload }, - }); + // const payload = try self.addExtra(Mir.ImmPair{ + // .dest_off = -off, + // .operand = @intCast(u32, imm), + // }); + _ = flags; + // _ = try self.addInst(.{ + // .tag = tag, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = .rbp, + // .flags = flags, + // }), + // .data = .{ .payload = payload }, + // }); }, .memory, .stack_offset, @@ -3735,6 +3779,7 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu /// Does not support byte-size operands. fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) InnerError!void { const abi_size = @intCast(u32, dst_ty.abiSize(self.target.*)); + _ = abi_size; switch (dst_mcv) { .none => unreachable, .undef => unreachable, @@ -3750,29 +3795,30 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M .ptr_stack_offset => unreachable, .register_overflow => unreachable, .register => |src_reg| { + _ = src_reg; // register, register - _ = try self.addInst(.{ - .tag = .imul_complex, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = registerAlias(dst_reg, abi_size), - .reg2 = registerAlias(src_reg, abi_size), - }), - .data = undefined, - }); + // _ = try self.addInst(.{ + // .tag = .imul_complex, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = registerAlias(dst_reg, abi_size), + // .reg2 = registerAlias(src_reg, abi_size), + // }), + // .data = undefined, + // }); }, .immediate => |imm| { // TODO take into account the type's ABI size when selecting the register alias // register, immediate if (math.minInt(i32) <= imm and imm <= math.maxInt(i32)) { - _ = try self.addInst(.{ - .tag = .imul_complex, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = dst_reg.to32(), - .reg2 = dst_reg.to32(), - .flags = 0b10, - }), - .data = .{ .imm = @intCast(u32, imm) }, - }); + // _ = try self.addInst(.{ + // .tag = .imul_complex, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = dst_reg.to32(), + // .reg2 = dst_reg.to32(), + // .flags = 0b10, + // }), + // .data = .{ .imm = @intCast(u32, imm) }, + // }); } else { // TODO verify we don't spill and assign to the same register as dst_mcv const src_reg = try self.copyToTmpRegister(dst_ty, src_mcv); @@ -3780,15 +3826,16 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M } }, .stack_offset => |off| { - _ = try self.addInst(.{ - .tag = .imul_complex, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = registerAlias(dst_reg, abi_size), - .reg2 = .rbp, - .flags = 0b01, - }), - .data = .{ .disp = -off }, - }); + _ = off; + // _ = try self.addInst(.{ + // .tag = .imul_complex, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = registerAlias(dst_reg, abi_size), + // .reg2 = .rbp, + // .flags = 0b01, + // }), + // .data = .{ .disp = -off }, + // }); }, .memory => { return self.fail("TODO implement x86 multiply source memory", .{}); @@ -3811,16 +3858,17 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M .register => |src_reg| { // copy dst to a register const dst_reg = try self.copyToTmpRegister(dst_ty, dst_mcv); + _ = src_reg; // multiply into dst_reg // register, register - _ = try self.addInst(.{ - .tag = .imul_complex, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = registerAlias(dst_reg, abi_size), - .reg2 = registerAlias(src_reg, abi_size), - }), - .data = undefined, - }); + // _ = try self.addInst(.{ + // .tag = .imul_complex, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = registerAlias(dst_reg, abi_size), + // .reg2 = registerAlias(src_reg, abi_size), + // }), + // .data = undefined, + // }); // copy dst_reg back out return self.genSetStack(dst_ty, off, .{ .register = dst_reg }, .{}); }, @@ -3946,20 +3994,20 @@ fn genVarDbgInfo( } fn airTrap(self: *Self) !void { - _ = try self.addInst(.{ - .tag = .ud, - .ops = Mir.Inst.Ops.encode(.{}), - .data = undefined, - }); + // _ = try self.addInst(.{ + // .tag = .ud, + // .ops = Mir.Inst.Ops.encode(.{}), + // .data = undefined, + // }); return self.finishAirBookkeeping(); } fn airBreakpoint(self: *Self) !void { - _ = try self.addInst(.{ - .tag = .interrupt, - .ops = Mir.Inst.Ops.encode(.{}), - .data = undefined, - }); + // _ = try self.addInst(.{ + // .tag = .interrupt, + // .ops = Mir.Inst.Ops.encode(.{}), + // .data = undefined, + // }); return self.finishAirBookkeeping(); } @@ -4054,11 +4102,11 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier if (info.stack_byte_count > 0) { // Adjust the stack - _ = try self.addInst(.{ - .tag = .sub, - .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rsp }), - .data = .{ .imm = info.stack_byte_count }, - }); + // _ = try self.addInst(.{ + // .tag = .sub, + // .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rsp }), + // .data = .{ .imm = info.stack_byte_count }, + // }); } // Due to incremental compilation, how function calls are generated depends @@ -4072,11 +4120,12 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl); const atom = elf_file.getAtom(atom_index); const got_addr = @intCast(i32, atom.getOffsetTableAddress(elf_file)); - _ = try self.addInst(.{ - .tag = .call, - .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }), - .data = .{ .disp = got_addr }, - }); + _ = got_addr; + // _ = try self.addInst(.{ + // .tag = .call, + // .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }), + // .data = .{ .disp = got_addr }, + // }); } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { const atom_index = try coff_file.getOrCreateAtomForDecl(func.owner_decl); const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?; @@ -4086,14 +4135,14 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier .sym_index = sym_index, }, }); - _ = try self.addInst(.{ - .tag = .call, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = .rax, - .flags = 0b01, - }), - .data = undefined, - }); + // _ = try self.addInst(.{ + // .tag = .call, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = .rax, + // .flags = 0b01, + // }), + // .data = undefined, + // }); } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { const atom_index = try macho_file.getOrCreateAtomForDecl(func.owner_decl); const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?; @@ -4103,14 +4152,14 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier .sym_index = sym_index, }, }); - _ = try self.addInst(.{ - .tag = .call, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = .rax, - .flags = 0b01, - }), - .data = undefined, - }); + // _ = try self.addInst(.{ + // .tag = .call, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = .rax, + // .flags = 0b01, + // }), + // .data = undefined, + // }); } else if (self.bin_file.cast(link.File.Plan9)) |p9| { const decl_block_index = try p9.seeDecl(func.owner_decl); const decl_block = p9.getDeclBlock(decl_block_index); @@ -4119,11 +4168,12 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier const got_addr = p9.bases.data; const got_index = decl_block.got_index.?; const fn_got_addr = got_addr + got_index * ptr_bytes; - _ = try self.addInst(.{ - .tag = .call, - .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }), - .data = .{ .disp = @intCast(i32, fn_got_addr) }, - }); + _ = fn_got_addr; + // _ = try self.addInst(.{ + // .tag = .call, + // .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }), + // .data = .{ .disp = @intCast(i32, fn_got_addr) }, + // }); } else unreachable; } else if (func_value.castTag(.extern_fn)) |func_payload| { const extern_fn = func_payload.data; @@ -4143,26 +4193,28 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier .sym_index = sym_index, }, }); - _ = try self.addInst(.{ - .tag = .call, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = .rax, - .flags = 0b01, - }), - .data = undefined, - }); + // _ = try self.addInst(.{ + // .tag = .call, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = .rax, + // .flags = 0b01, + // }), + // .data = undefined, + // }); } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { const sym_index = try macho_file.getGlobalSymbol(mem.sliceTo(decl_name, 0)); const atom = try macho_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl); const atom_index = macho_file.getAtom(atom).getSymbolIndex().?; - _ = try self.addInst(.{ - .tag = .call_extern, - .ops = undefined, - .data = .{ .relocation = .{ - .atom_index = atom_index, - .sym_index = sym_index, - } }, - }); + _ = sym_index; + _ = atom_index; + // _ = try self.addInst(.{ + // .tag = .call_extern, + // .ops = undefined, + // .data = .{ .relocation = .{ + // .atom_index = atom_index, + // .sym_index = sym_index, + // } }, + // }); } else { return self.fail("TODO implement calling extern functions", .{}); } @@ -4173,23 +4225,23 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier assert(ty.zigTypeTag() == .Pointer); const mcv = try self.resolveInst(callee); try self.genSetReg(Type.initTag(.usize), .rax, mcv); - _ = try self.addInst(.{ - .tag = .call, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = .rax, - .flags = 0b01, - }), - .data = undefined, - }); + // _ = try self.addInst(.{ + // .tag = .call, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = .rax, + // .flags = 0b01, + // }), + // .data = undefined, + // }); } if (info.stack_byte_count > 0) { // Readjust the stack - _ = try self.addInst(.{ - .tag = .add, - .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rsp }), - .data = .{ .imm = info.stack_byte_count }, - }); + // _ = try self.addInst(.{ + // .tag = .add, + // .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rsp }), + // .data = .{ .imm = info.stack_byte_count }, + // }); } const result: MCValue = result: { @@ -4246,12 +4298,12 @@ fn airRet(self: *Self, inst: Air.Inst.Index) !void { // TODO when implementing defer, this will need to jump to the appropriate defer expression. // TODO optimization opportunity: figure out when we can emit this as a 2 byte instruction // which is available if the jump is 127 bytes or less forward. - const jmp_reloc = try self.addInst(.{ - .tag = .jmp, - .ops = Mir.Inst.Ops.encode(.{}), - .data = .{ .inst = undefined }, - }); - try self.exitlude_jump_relocs.append(self.gpa, jmp_reloc); + // const jmp_reloc = try self.addInst(.{ + // .tag = .jmp, + // .ops = Mir.Inst.Ops.encode(.{}), + // .data = .{ .inst = undefined }, + // }); + // try self.exitlude_jump_relocs.append(self.gpa, jmp_reloc); return self.finishAir(inst, .dead, .{ un_op, .none, .none }); } @@ -4282,12 +4334,12 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { // TODO when implementing defer, this will need to jump to the appropriate defer expression. // TODO optimization opportunity: figure out when we can emit this as a 2 byte instruction // which is available if the jump is 127 bytes or less forward. - const jmp_reloc = try self.addInst(.{ - .tag = .jmp, - .ops = Mir.Inst.Ops.encode(.{}), - .data = .{ .inst = undefined }, - }); - try self.exitlude_jump_relocs.append(self.gpa, jmp_reloc); + // const jmp_reloc = try self.addInst(.{ + // .tag = .jmp, + // .ops = Mir.Inst.Ops.encode(.{}), + // .data = .{ .inst = undefined }, + // }); + // try self.exitlude_jump_relocs.append(self.gpa, jmp_reloc); return self.finishAir(inst, .dead, .{ un_op, .none, .none }); } @@ -4461,33 +4513,35 @@ fn genCondBrMir(self: *Self, ty: Type, mcv: MCValue) !u32 { const abi_size = ty.abiSize(self.target.*); switch (mcv) { .eflags => |cc| { - return self.addInst(.{ - .tag = .cond_jmp, - .ops = Mir.Inst.Ops.encode(.{}), - .data = .{ - .inst_cc = .{ - .inst = undefined, - // Here we map the opposites since the jump is to the false branch. - .cc = cc.negate(), - }, - }, - }); + _ = cc; + // return self.addInst(.{ + // .tag = .cond_jmp, + // .ops = Mir.Inst.Ops.encode(.{}), + // .data = .{ + // .inst_cc = .{ + // .inst = undefined, + // // Here we map the opposites since the jump is to the false branch. + // .cc = cc.negate(), + // }, + // }, + // }); }, .register => |reg| { + _ = reg; try self.spillEflagsIfOccupied(); - _ = try self.addInst(.{ - .tag = .@"test", - .ops = Mir.Inst.Ops.encode(.{ .reg1 = reg }), - .data = .{ .imm = 1 }, - }); - return self.addInst(.{ - .tag = .cond_jmp, - .ops = Mir.Inst.Ops.encode(.{}), - .data = .{ .inst_cc = .{ - .inst = undefined, - .cc = .e, - } }, - }); + // _ = try self.addInst(.{ + // .tag = .@"test", + // .ops = Mir.Inst.Ops.encode(.{ .reg1 = reg }), + // .data = .{ .imm = 1 }, + // }); + // return self.addInst(.{ + // .tag = .cond_jmp, + // .ops = Mir.Inst.Ops.encode(.{}), + // .data = .{ .inst_cc = .{ + // .inst = undefined, + // .cc = .e, + // } }, + // }); }, .immediate, .stack_offset, @@ -4501,6 +4555,7 @@ fn genCondBrMir(self: *Self, ty: Type, mcv: MCValue) !u32 { }, else => return self.fail("TODO implement condbr when condition is {s}", .{@tagName(mcv)}), } + return 0; // TODO } fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { @@ -4825,12 +4880,13 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void { const loop = self.air.extraData(Air.Block, ty_pl.payload); const body = self.air.extra[loop.end..][0..loop.data.body_len]; const jmp_target = @intCast(u32, self.mir_instructions.len); + _ = jmp_target; try self.genBody(body); - _ = try self.addInst(.{ - .tag = .jmp, - .ops = Mir.Inst.Ops.encode(.{}), - .data = .{ .inst = jmp_target }, - }); + // _ = try self.addInst(.{ + // .tag = .jmp, + // .ops = Mir.Inst.Ops.encode(.{}), + // .data = .{ .inst = jmp_target }, + // }); return self.finishAirBookkeeping(); } @@ -4876,21 +4932,23 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u .undef => unreachable, .dead, .unreach => unreachable, .immediate => |imm| { - _ = try self.addInst(.{ - .tag = .xor, - .ops = Mir.Inst.Ops.encode(.{ .reg1 = registerAlias(cond_reg, abi_size) }), - .data = .{ .imm = @intCast(u32, imm) }, - }); + _ = imm; + // _ = try self.addInst(.{ + // .tag = .xor, + // .ops = Mir.Inst.Ops.encode(.{ .reg1 = registerAlias(cond_reg, abi_size) }), + // .data = .{ .imm = @intCast(u32, imm) }, + // }); }, .register => |reg| { - _ = try self.addInst(.{ - .tag = .xor, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = registerAlias(cond_reg, abi_size), - .reg2 = registerAlias(reg, abi_size), - }), - .data = undefined, - }); + _ = reg; + // _ = try self.addInst(.{ + // .tag = .xor, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = registerAlias(cond_reg, abi_size), + // .reg2 = registerAlias(reg, abi_size), + // }), + // .data = undefined, + // }); }, .stack_offset => { if (abi_size <= 8) { @@ -4905,22 +4963,22 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u }, } - _ = try self.addInst(.{ - .tag = .@"test", - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = registerAlias(cond_reg, abi_size), - .reg2 = registerAlias(cond_reg, abi_size), - }), - .data = undefined, - }); - return self.addInst(.{ - .tag = .cond_jmp, - .ops = Mir.Inst.Ops.encode(.{}), - .data = .{ .inst_cc = .{ - .inst = undefined, - .cc = .ne, - } }, - }); + // _ = try self.addInst(.{ + // .tag = .@"test", + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = registerAlias(cond_reg, abi_size), + // .reg2 = registerAlias(cond_reg, abi_size), + // }), + // .data = undefined, + // }); + // return self.addInst(.{ + // .tag = .cond_jmp, + // .ops = Mir.Inst.Ops.encode(.{}), + // .data = .{ .inst_cc = .{ + // .inst = undefined, + // .cc = .ne, + // } }, + // }); }, .stack_offset => { try self.spillEflagsIfOccupied(); @@ -4938,6 +4996,7 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u return self.fail("TODO implemenent switch mir when condition is {}", .{condition}); }, } + return 0; // TODO } fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { @@ -5132,9 +5191,9 @@ fn canonicaliseBranches(self: *Self, parent_branch: *Branch, canon_branch: *Bran fn performReloc(self: *Self, reloc: Mir.Inst.Index) !void { const next_inst = @intCast(u32, self.mir_instructions.len); switch (self.mir_instructions.items(.tag)[reloc]) { - .cond_jmp => { - self.mir_instructions.items(.data)[reloc].inst_cc.inst = next_inst; - }, + // .cond_jmp => { + // self.mir_instructions.items(.data)[reloc].inst_cc.inst = next_inst; + // }, .jmp => { self.mir_instructions.items(.data)[reloc].inst = next_inst; }, @@ -5177,12 +5236,12 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void { // Emit a jump with a relocation. It will be patched up after the block ends. try block_data.relocs.ensureUnusedCapacity(self.gpa, 1); // Leave the jump offset undefined - const jmp_reloc = try self.addInst(.{ - .tag = .jmp, - .ops = Mir.Inst.Ops.encode(.{}), - .data = .{ .inst = undefined }, - }); - block_data.relocs.appendAssumeCapacity(jmp_reloc); + // const jmp_reloc = try self.addInst(.{ + // .tag = .jmp, + // .ops = Mir.Inst.Ops.encode(.{}), + // .data = .{ .inst = undefined }, + // }); + // block_data.relocs.appendAssumeCapacity(jmp_reloc); } fn airAsm(self: *Self, inst: Air.Inst.Index) !void { @@ -5254,30 +5313,22 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { var iter = std.mem.tokenize(u8, asm_source, "\n\r"); while (iter.next()) |ins| { if (mem.eql(u8, ins, "syscall")) { - _ = try self.addInst(.{ - .tag = .syscall, - .ops = undefined, - .data = undefined, - }); + try self.assemble(.syscall, .{}); } else if (mem.indexOf(u8, ins, "push")) |_| { const arg = ins[4..]; if (mem.indexOf(u8, arg, "$")) |l| { const n = std.fmt.parseInt(u8, ins[4 + l + 1 ..], 10) catch { return self.fail("TODO implement more inline asm int parsing", .{}); }; - _ = try self.addInst(.{ - .tag = .push, - .ops = Mir.Inst.Ops.encode(.{ .flags = 0b10 }), - .data = .{ .imm = n }, + try self.assemble(.push, .{ + .op1 = .{ .imm = Mir.Operand.Immediate.u(n) }, }); } else if (mem.indexOf(u8, arg, "%%")) |l| { const reg_name = ins[4 + l + 2 ..]; const reg = parseRegName(reg_name) orelse return self.fail("unrecognized register: '{s}'", .{reg_name}); - _ = try self.addInst(.{ - .tag = .push, - .ops = Mir.Inst.Ops.encode(.{ .reg1 = reg }), - .data = undefined, + try self.assemble(.push, .{ + .op1 = .{ .reg = reg }, }); } else return self.fail("TODO more push operands", .{}); } else if (mem.indexOf(u8, ins, "pop")) |_| { @@ -5286,10 +5337,8 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { const reg_name = ins[3 + l + 2 ..]; const reg = parseRegName(reg_name) orelse return self.fail("unrecognized register: '{s}'", .{reg_name}); - _ = try self.addInst(.{ - .tag = .pop, - .ops = Mir.Inst.Ops.encode(.{ .reg1 = reg }), - .data = undefined, + try self.assemble(.pop, .{ + .op1 = .{ .reg = reg }, }); } else return self.fail("TODO more pop operands", .{}); } else { @@ -5433,39 +5482,40 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE switch (ty.zigTypeTag()) { .Float => { if (intrinsicsAllowed(self.target.*, ty)) { - const tag: Mir.Inst.Tag = switch (ty.tag()) { - .f32 => Mir.Inst.Tag.mov_f32, - .f64 => Mir.Inst.Tag.mov_f64, - else => return self.fail("TODO genSetStackArg for register for type {}", .{ty.fmtDebug()}), - }; - _ = try self.addInst(.{ - .tag = tag, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = switch (ty.tag()) { - .f32 => .esp, - .f64 => .rsp, - else => unreachable, - }, - .reg2 = reg.to128(), - .flags = 0b01, - }), - .data = .{ .disp = -stack_offset }, - }); + // const tag: Mir.Inst.Tag = switch (ty.tag()) { + // .f32 => Mir.Inst.Tag.mov_f32, + // .f64 => Mir.Inst.Tag.mov_f64, + // else => return self.fail("TODO genSetStackArg for register for type {}", .{ty.fmtDebug()}), + // }; + _ = reg; + // _ = try self.addInst(.{ + // .tag = tag, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = switch (ty.tag()) { + // .f32 => .esp, + // .f64 => .rsp, + // else => unreachable, + // }, + // .reg2 = reg.to128(), + // .flags = 0b01, + // }), + // .data = .{ .disp = -stack_offset }, + // }); return; } return self.fail("TODO genSetStackArg for register with no intrinsics", .{}); }, else => { - _ = try self.addInst(.{ - .tag = .mov, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = .rsp, - .reg2 = registerAlias(reg, @intCast(u32, abi_size)), - .flags = 0b10, - }), - .data = .{ .disp = -stack_offset }, - }); + // _ = try self.addInst(.{ + // .tag = .mov, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = .rsp, + // .reg2 = registerAlias(reg, @intCast(u32, abi_size)), + // .flags = 0b10, + // }), + // .data = .{ .disp = -stack_offset }, + // }); }, } }, @@ -5519,13 +5569,13 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl const overflow_bit_ty = ty.structFieldType(1); const overflow_bit_offset = ty.structFieldOffset(1, self.target.*); const tmp_reg = try self.register_manager.allocReg(null, gp); - _ = try self.addInst(.{ - .tag = .cond_set_byte, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = tmp_reg.to8(), - }), - .data = .{ .cc = ro.eflags }, - }); + // _ = try self.addInst(.{ + // .tag = .cond_set_byte, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = tmp_reg.to8(), + // }), + // .data = .{ .cc = ro.eflags }, + // }); return self.genSetStack( overflow_bit_ty, @@ -5539,72 +5589,74 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl return self.genSetStack(ty, stack_offset, .{ .register = reg }, opts); }, .immediate => |x_big| { + _ = x_big; const base_reg = opts.dest_stack_base orelse .rbp; + _ = base_reg; switch (abi_size) { 0 => { assert(ty.isError()); - const payload = try self.addExtra(Mir.ImmPair{ - .dest_off = -stack_offset, - .operand = @truncate(u32, x_big), - }); - _ = try self.addInst(.{ - .tag = .mov_mem_imm, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = base_reg, - .flags = 0b00, - }), - .data = .{ .payload = payload }, - }); + // const payload = try self.addExtra(Mir.ImmPair{ + // .dest_off = -stack_offset, + // .operand = @truncate(u32, x_big), + // }); + // _ = try self.addInst(.{ + // .tag = .mov_mem_imm, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = base_reg, + // .flags = 0b00, + // }), + // .data = .{ .payload = payload }, + // }); }, 1, 2, 4 => { - const payload = try self.addExtra(Mir.ImmPair{ - .dest_off = -stack_offset, - .operand = @truncate(u32, x_big), - }); - _ = try self.addInst(.{ - .tag = .mov_mem_imm, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = base_reg, - .flags = switch (abi_size) { - 1 => 0b00, - 2 => 0b01, - 4 => 0b10, - else => unreachable, - }, - }), - .data = .{ .payload = payload }, - }); + // const payload = try self.addExtra(Mir.ImmPair{ + // .dest_off = -stack_offset, + // .operand = @truncate(u32, x_big), + // }); + // _ = try self.addInst(.{ + // .tag = .mov_mem_imm, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = base_reg, + // .flags = switch (abi_size) { + // 1 => 0b00, + // 2 => 0b01, + // 4 => 0b10, + // else => unreachable, + // }, + // }), + // .data = .{ .payload = payload }, + // }); }, 8 => { // 64 bit write to memory would take two mov's anyways so we // insted just use two 32 bit writes to avoid register allocation { - const payload = try self.addExtra(Mir.ImmPair{ - .dest_off = -stack_offset + 4, - .operand = @truncate(u32, x_big >> 32), - }); - _ = try self.addInst(.{ - .tag = .mov_mem_imm, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = base_reg, - .flags = 0b10, - }), - .data = .{ .payload = payload }, - }); + // const payload = try self.addExtra(Mir.ImmPair{ + // .dest_off = -stack_offset + 4, + // .operand = @truncate(u32, x_big >> 32), + // }); + // _ = try self.addInst(.{ + // .tag = .mov_mem_imm, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = base_reg, + // .flags = 0b10, + // }), + // .data = .{ .payload = payload }, + // }); } { - const payload = try self.addExtra(Mir.ImmPair{ - .dest_off = -stack_offset, - .operand = @truncate(u32, x_big), - }); - _ = try self.addInst(.{ - .tag = .mov_mem_imm, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = base_reg, - .flags = 0b10, - }), - .data = .{ .payload = payload }, - }); + // const payload = try self.addExtra(Mir.ImmPair{ + // .dest_off = -stack_offset, + // .operand = @truncate(u32, x_big), + // }); + // _ = try self.addInst(.{ + // .tag = .mov_mem_imm, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = base_reg, + // .flags = 0b10, + // }), + // .data = .{ .payload = payload }, + // }); } }, else => { @@ -5622,24 +5674,24 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl switch (ty.zigTypeTag()) { .Float => { if (intrinsicsAllowed(self.target.*, ty)) { - const tag: Mir.Inst.Tag = switch (ty.tag()) { - .f32 => Mir.Inst.Tag.mov_f32, - .f64 => Mir.Inst.Tag.mov_f64, - else => return self.fail("TODO genSetStack for register for type {}", .{ty.fmtDebug()}), - }; - _ = try self.addInst(.{ - .tag = tag, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = switch (ty.tag()) { - .f32 => base_reg.to32(), - .f64 => base_reg.to64(), - else => unreachable, - }, - .reg2 = reg.to128(), - .flags = 0b01, - }), - .data = .{ .disp = -stack_offset }, - }); + // const tag: Mir.Inst.Tag = switch (ty.tag()) { + // .f32 => Mir.Inst.Tag.mov_f32, + // .f64 => Mir.Inst.Tag.mov_f64, + // else => return self.fail("TODO genSetStack for register for type {}", .{ty.fmtDebug()}), + // }; + // _ = try self.addInst(.{ + // .tag = tag, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = switch (ty.tag()) { + // .f32 => base_reg.to32(), + // .f64 => base_reg.to64(), + // else => unreachable, + // }, + // .reg2 = reg.to128(), + // .flags = 0b01, + // }), + // .data = .{ .disp = -stack_offset }, + // }); return; } @@ -5706,15 +5758,15 @@ fn genInlineMemcpyRegisterRegister( while (remainder > 0) { const nearest_power_of_two = @as(u6, 1) << math.log2_int(u3, @intCast(u3, remainder)); - _ = try self.addInst(.{ - .tag = .mov, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = dst_reg, - .reg2 = registerAlias(tmp_reg, nearest_power_of_two), - .flags = 0b10, - }), - .data = .{ .disp = -next_offset }, - }); + // _ = try self.addInst(.{ + // .tag = .mov, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = dst_reg, + // .reg2 = registerAlias(tmp_reg, nearest_power_of_two), + // .flags = 0b10, + // }), + // .data = .{ .disp = -next_offset }, + // }); if (nearest_power_of_two > 1) { try self.genShiftBinOpMir(.shr, ty, tmp_reg, .{ @@ -5726,15 +5778,15 @@ fn genInlineMemcpyRegisterRegister( next_offset -= nearest_power_of_two; } } else { - _ = try self.addInst(.{ - .tag = .mov, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = dst_reg, - .reg2 = registerAlias(src_reg, @intCast(u32, abi_size)), - .flags = 0b10, - }), - .data = .{ .disp = -offset }, - }); + // _ = try self.addInst(.{ + // .tag = .mov, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = dst_reg, + // .reg2 = registerAlias(src_reg, @intCast(u32, abi_size)), + // .flags = 0b10, + // }), + // .data = .{ .disp = -offset }, + // }); } } @@ -5768,30 +5820,34 @@ fn genInlineMemcpy( const index_reg = regs[2].to64(); const count_reg = regs[3].to64(); const tmp_reg = regs[4].to8(); + _ = index_reg; + _ = tmp_reg; switch (dst_ptr) { .memory, .linker_load => { try self.loadMemPtrIntoRegister(dst_addr_reg, Type.usize, dst_ptr); }, .ptr_stack_offset, .stack_offset => |off| { - _ = try self.addInst(.{ - .tag = .lea, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = dst_addr_reg.to64(), - .reg2 = opts.dest_stack_base orelse .rbp, - }), - .data = .{ .disp = -off }, - }); + _ = off; + // _ = try self.addInst(.{ + // .tag = .lea, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = dst_addr_reg.to64(), + // .reg2 = opts.dest_stack_base orelse .rbp, + // }), + // .data = .{ .disp = -off }, + // }); }, .register => |reg| { - _ = try self.addInst(.{ - .tag = .mov, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = registerAlias(dst_addr_reg, @intCast(u32, @divExact(reg.bitSize(), 8))), - .reg2 = reg, - }), - .data = undefined, - }); + _ = reg; + // _ = try self.addInst(.{ + // .tag = .mov, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = registerAlias(dst_addr_reg, @intCast(u32, @divExact(reg.bitSize(), 8))), + // .reg2 = reg, + // }), + // .data = undefined, + // }); }, else => { return self.fail("TODO implement memcpy for setting stack when dest is {}", .{dst_ptr}); @@ -5803,24 +5859,26 @@ fn genInlineMemcpy( try self.loadMemPtrIntoRegister(src_addr_reg, Type.usize, src_ptr); }, .ptr_stack_offset, .stack_offset => |off| { - _ = try self.addInst(.{ - .tag = .lea, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = src_addr_reg.to64(), - .reg2 = opts.source_stack_base orelse .rbp, - }), - .data = .{ .disp = -off }, - }); + _ = off; + // _ = try self.addInst(.{ + // .tag = .lea, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = src_addr_reg.to64(), + // .reg2 = opts.source_stack_base orelse .rbp, + // }), + // .data = .{ .disp = -off }, + // }); }, .register => |reg| { - _ = try self.addInst(.{ - .tag = .mov, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = registerAlias(src_addr_reg, @intCast(u32, @divExact(reg.bitSize(), 8))), - .reg2 = reg, - }), - .data = undefined, - }); + _ = reg; + // _ = try self.addInst(.{ + // .tag = .mov, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = registerAlias(src_addr_reg, @intCast(u32, @divExact(reg.bitSize(), 8))), + // .reg2 = reg, + // }), + // .data = undefined, + // }); }, else => { return self.fail("TODO implement memcpy for setting stack when src is {}", .{src_ptr}); @@ -5830,73 +5888,73 @@ fn genInlineMemcpy( try self.genSetReg(Type.usize, count_reg, len); // mov index_reg, 0 - _ = try self.addInst(.{ - .tag = .mov, - .ops = Mir.Inst.Ops.encode(.{ .reg1 = index_reg }), - .data = .{ .imm = 0 }, - }); + // _ = try self.addInst(.{ + // .tag = .mov, + // .ops = Mir.Inst.Ops.encode(.{ .reg1 = index_reg }), + // .data = .{ .imm = 0 }, + // }); // loop: // cmp count, 0 - const loop_start = try self.addInst(.{ - .tag = .cmp, - .ops = Mir.Inst.Ops.encode(.{ .reg1 = count_reg }), - .data = .{ .imm = 0 }, - }); + // const loop_start = try self.addInst(.{ + // .tag = .cmp, + // .ops = Mir.Inst.Ops.encode(.{ .reg1 = count_reg }), + // .data = .{ .imm = 0 }, + // }); // je end - const loop_reloc = try self.addInst(.{ - .tag = .cond_jmp, - .ops = Mir.Inst.Ops.encode(.{}), - .data = .{ .inst_cc = .{ - .inst = undefined, - .cc = .e, - } }, - }); + // const loop_reloc = try self.addInst(.{ + // .tag = .cond_jmp, + // .ops = Mir.Inst.Ops.encode(.{}), + // .data = .{ .inst_cc = .{ + // .inst = undefined, + // .cc = .e, + // } }, + // }); // mov tmp, [addr + index_reg] - _ = try self.addInst(.{ - .tag = .mov_scale_src, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = tmp_reg.to8(), - .reg2 = src_addr_reg, - }), - .data = .{ .payload = try self.addExtra(Mir.IndexRegisterDisp.encode(index_reg, 0)) }, - }); + // _ = try self.addInst(.{ + // .tag = .mov_scale_src, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = tmp_reg.to8(), + // .reg2 = src_addr_reg, + // }), + // .data = .{ .payload = try self.addExtra(Mir.IndexRegisterDisp.encode(index_reg, 0)) }, + // }); // mov [stack_offset + index_reg], tmp - _ = try self.addInst(.{ - .tag = .mov_scale_dst, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = dst_addr_reg, - .reg2 = tmp_reg.to8(), - }), - .data = .{ .payload = try self.addExtra(Mir.IndexRegisterDisp.encode(index_reg, 0)) }, - }); + // _ = try self.addInst(.{ + // .tag = .mov_scale_dst, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = dst_addr_reg, + // .reg2 = tmp_reg.to8(), + // }), + // .data = .{ .payload = try self.addExtra(Mir.IndexRegisterDisp.encode(index_reg, 0)) }, + // }); // add index_reg, 1 - _ = try self.addInst(.{ - .tag = .add, - .ops = Mir.Inst.Ops.encode(.{ .reg1 = index_reg }), - .data = .{ .imm = 1 }, - }); + // _ = try self.addInst(.{ + // .tag = .add, + // .ops = Mir.Inst.Ops.encode(.{ .reg1 = index_reg }), + // .data = .{ .imm = 1 }, + // }); // sub count, 1 - _ = try self.addInst(.{ - .tag = .sub, - .ops = Mir.Inst.Ops.encode(.{ .reg1 = count_reg }), - .data = .{ .imm = 1 }, - }); + // _ = try self.addInst(.{ + // .tag = .sub, + // .ops = Mir.Inst.Ops.encode(.{ .reg1 = count_reg }), + // .data = .{ .imm = 1 }, + // }); // jmp loop - _ = try self.addInst(.{ - .tag = .jmp, - .ops = Mir.Inst.Ops.encode(.{}), - .data = .{ .inst = loop_start }, - }); + // _ = try self.addInst(.{ + // .tag = .jmp, + // .ops = Mir.Inst.Ops.encode(.{}), + // .data = .{ .inst = loop_start }, + // }); // end: - try self.performReloc(loop_reloc); + // try self.performReloc(loop_reloc); } fn genInlineMemset( @@ -5927,24 +5985,26 @@ fn genInlineMemset( try self.loadMemPtrIntoRegister(addr_reg, Type.usize, dst_ptr); }, .ptr_stack_offset, .stack_offset => |off| { - _ = try self.addInst(.{ - .tag = .lea, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = addr_reg.to64(), - .reg2 = opts.dest_stack_base orelse .rbp, - }), - .data = .{ .disp = -off }, - }); + _ = off; + // _ = try self.addInst(.{ + // .tag = .lea, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = addr_reg.to64(), + // .reg2 = opts.dest_stack_base orelse .rbp, + // }), + // .data = .{ .disp = -off }, + // }); }, .register => |reg| { - _ = try self.addInst(.{ - .tag = .mov, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = registerAlias(addr_reg, @intCast(u32, @divExact(reg.bitSize(), 8))), - .reg2 = reg, - }), - .data = undefined, - }); + _ = reg; + // _ = try self.addInst(.{ + // .tag = .mov, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = registerAlias(addr_reg, @intCast(u32, @divExact(reg.bitSize(), 8))), + // .reg2 = reg, + // }), + // .data = undefined, + // }); }, else => { return self.fail("TODO implement memcpy for setting stack when dest is {}", .{dst_ptr}); @@ -5956,24 +6016,24 @@ fn genInlineMemset( // loop: // cmp index_reg, -1 - const loop_start = try self.addInst(.{ - .tag = .cmp, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = index_reg, - .flags = 0b11, - }), - .data = .{ .imm_s = -1 }, - }); + // const loop_start = try self.addInst(.{ + // .tag = .cmp, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = index_reg, + // .flags = 0b11, + // }), + // .data = .{ .imm_s = -1 }, + // }); // je end - const loop_reloc = try self.addInst(.{ - .tag = .cond_jmp, - .ops = Mir.Inst.Ops.encode(.{}), - .data = .{ .inst_cc = .{ - .inst = undefined, - .cc = .e, - } }, - }); + // const loop_reloc = try self.addInst(.{ + // .tag = .cond_jmp, + // .ops = Mir.Inst.Ops.encode(.{}), + // .data = .{ .inst_cc = .{ + // .inst = undefined, + // .cc = .e, + // } }, + // }); switch (value) { .immediate => |x| { @@ -5981,37 +6041,37 @@ fn genInlineMemset( return self.fail("TODO inline memset for value immediate larger than 32bits", .{}); } // mov byte ptr [rbp + index_reg + stack_offset], imm - _ = try self.addInst(.{ - .tag = .mov_mem_index_imm, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = addr_reg, - }), - .data = .{ .payload = try self.addExtra(Mir.IndexRegisterDispImm.encode( - index_reg, - 0, - @intCast(u32, x), - )) }, - }); + // _ = try self.addInst(.{ + // .tag = .mov_mem_index_imm, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = addr_reg, + // }), + // .data = .{ .payload = try self.addExtra(Mir.IndexRegisterDispImm.encode( + // index_reg, + // 0, + // @intCast(u32, x), + // )) }, + // }); }, else => return self.fail("TODO inline memset for value of type {}", .{value}), } // sub index_reg, 1 - _ = try self.addInst(.{ - .tag = .sub, - .ops = Mir.Inst.Ops.encode(.{ .reg1 = index_reg }), - .data = .{ .imm = 1 }, - }); + // _ = try self.addInst(.{ + // .tag = .sub, + // .ops = Mir.Inst.Ops.encode(.{ .reg1 = index_reg }), + // .data = .{ .imm = 1 }, + // }); // jmp loop - _ = try self.addInst(.{ - .tag = .jmp, - .ops = Mir.Inst.Ops.encode(.{}), - .data = .{ .inst = loop_start }, - }); + // _ = try self.addInst(.{ + // .tag = .jmp, + // .ops = Mir.Inst.Ops.encode(.{}), + // .data = .{ .inst = loop_start }, + // }); // end: - try self.performReloc(loop_reloc); + // try self.performReloc(loop_reloc); } fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void { @@ -6023,14 +6083,14 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void if (off < std.math.minInt(i32) or off > std.math.maxInt(i32)) { return self.fail("stack offset too large", .{}); } - _ = try self.addInst(.{ - .tag = .lea, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = registerAlias(reg, abi_size), - .reg2 = .rbp, - }), - .data = .{ .disp = -off }, - }); + // _ = try self.addInst(.{ + // .tag = .lea, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = registerAlias(reg, abi_size), + // .reg2 = .rbp, + // }), + // .data = .{ .disp = -off }, + // }); }, .unreach, .none => return, // Nothing to do. .undef => { @@ -6046,34 +6106,30 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void } }, .eflags => |cc| { - _ = try self.addInst(.{ - .tag = .cond_set_byte, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = reg.to8(), - }), - .data = .{ .cc = cc }, - }); + _ = cc; + // _ = try self.addInst(.{ + // .tag = .cond_set_byte, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = reg.to8(), + // }), + // .data = .{ .cc = cc }, + // }); }, .immediate => |x| { // 32-bit moves zero-extend to 64-bit, so xoring the 32-bit // register is the fastest way to zero a register. if (x == 0) { - _ = try self.addInst(.{ - .tag = .xor, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = reg.to32(), - .reg2 = reg.to32(), - }), - .data = undefined, + try self.assemble(.xor, .{ + .op1 = .{ .reg = reg.to32() }, + .op2 = .{ .reg = reg.to32() }, }); return; } if (x <= math.maxInt(i32)) { // Next best case: if we set the lower four bytes, the upper four will be zeroed. - _ = try self.addInst(.{ - .tag = .mov, - .ops = Mir.Inst.Ops.encode(.{ .reg1 = registerAlias(reg, abi_size) }), - .data = .{ .imm = @intCast(u32, x) }, + try self.assemble(.mov, .{ + .op1 = .{ .reg = registerAlias(reg, abi_size) }, + .op2 = .{ .imm = Mir.Operand.Immediate.u(@intCast(u32, x)) }, }); return; } @@ -6084,12 +6140,12 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void // This encoding is, in fact, the *same* as the one used for 32-bit loads. The only // difference is that we set REX.W before the instruction, which extends the load to // 64-bit and uses the full bit-width of the register. - const payload = try self.addExtra(Mir.Imm64.encode(x)); - _ = try self.addInst(.{ - .tag = .movabs, - .ops = Mir.Inst.Ops.encode(.{ .reg1 = reg.to64() }), - .data = .{ .payload = payload }, - }); + // const payload = try self.addExtra(Mir.Imm64.encode(x)); + // _ = try self.addInst(.{ + // .tag = .movabs, + // .ops = Mir.Inst.Ops.encode(.{ .reg1 = reg.to64() }), + // .data = .{ .payload = payload }, + // }); }, .register => |src_reg| { // If the registers are the same, nothing to do. @@ -6100,47 +6156,47 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void .Int => switch (ty.intInfo(self.target.*).signedness) { .signed => { if (abi_size <= 4) { - _ = try self.addInst(.{ - .tag = .mov_sign_extend, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = reg.to64(), - .reg2 = registerAlias(src_reg, abi_size), - }), - .data = undefined, - }); + // _ = try self.addInst(.{ + // .tag = .mov_sign_extend, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = reg.to64(), + // .reg2 = registerAlias(src_reg, abi_size), + // }), + // .data = undefined, + // }); return; } }, .unsigned => { if (abi_size <= 2) { - _ = try self.addInst(.{ - .tag = .mov_zero_extend, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = reg.to64(), - .reg2 = registerAlias(src_reg, abi_size), - }), - .data = undefined, - }); + // _ = try self.addInst(.{ + // .tag = .mov_zero_extend, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = reg.to64(), + // .reg2 = registerAlias(src_reg, abi_size), + // }), + // .data = undefined, + // }); return; } }, }, .Float => { if (intrinsicsAllowed(self.target.*, ty)) { - const tag: Mir.Inst.Tag = switch (ty.tag()) { - .f32 => Mir.Inst.Tag.mov_f32, - .f64 => Mir.Inst.Tag.mov_f64, - else => return self.fail("TODO genSetReg from register for {}", .{ty.fmtDebug()}), - }; - _ = try self.addInst(.{ - .tag = tag, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = reg.to128(), - .reg2 = src_reg.to128(), - .flags = 0b10, - }), - .data = undefined, - }); + // const tag: Mir.Inst.Tag = switch (ty.tag()) { + // .f32 => Mir.Inst.Tag.mov_f32, + // .f64 => Mir.Inst.Tag.mov_f64, + // else => return self.fail("TODO genSetReg from register for {}", .{ty.fmtDebug()}), + // }; + // _ = try self.addInst(.{ + // .tag = tag, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = reg.to128(), + // .reg2 = src_reg.to128(), + // .flags = 0b10, + // }), + // .data = undefined, + // }); return; } @@ -6149,14 +6205,14 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void else => {}, } - _ = try self.addInst(.{ - .tag = .mov, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = registerAlias(reg, abi_size), - .reg2 = registerAlias(src_reg, abi_size), - }), - .data = undefined, - }); + // _ = try self.addInst(.{ + // .tag = .mov, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = registerAlias(reg, abi_size), + // .reg2 = registerAlias(src_reg, abi_size), + // }), + // .data = undefined, + // }); }, .linker_load => { switch (ty.zigTypeTag()) { @@ -6165,24 +6221,24 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void try self.loadMemPtrIntoRegister(base_reg, Type.usize, mcv); if (intrinsicsAllowed(self.target.*, ty)) { - const tag: Mir.Inst.Tag = switch (ty.tag()) { - .f32 => Mir.Inst.Tag.mov_f32, - .f64 => Mir.Inst.Tag.mov_f64, - else => return self.fail("TODO genSetReg from memory for {}", .{ty.fmtDebug()}), - }; + // const tag: Mir.Inst.Tag = switch (ty.tag()) { + // .f32 => Mir.Inst.Tag.mov_f32, + // .f64 => Mir.Inst.Tag.mov_f64, + // else => return self.fail("TODO genSetReg from memory for {}", .{ty.fmtDebug()}), + // }; - _ = try self.addInst(.{ - .tag = tag, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = reg.to128(), - .reg2 = switch (ty.tag()) { - .f32 => base_reg.to32(), - .f64 => base_reg.to64(), - else => unreachable, - }, - }), - .data = .{ .disp = 0 }, - }); + // _ = try self.addInst(.{ + // .tag = tag, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = reg.to128(), + // .reg2 = switch (ty.tag()) { + // .f32 => base_reg.to32(), + // .f64 => base_reg.to64(), + // else => unreachable, + // }, + // }), + // .data = .{ .disp = 0 }, + // }); return; } @@ -6190,15 +6246,15 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void }, else => { try self.loadMemPtrIntoRegister(reg, Type.usize, mcv); - _ = try self.addInst(.{ - .tag = .mov, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = registerAlias(reg, abi_size), - .reg2 = reg.to64(), - .flags = 0b01, - }), - .data = .{ .disp = 0 }, - }); + // _ = try self.addInst(.{ + // .tag = .mov, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = registerAlias(reg, abi_size), + // .reg2 = reg.to64(), + // .flags = 0b01, + // }), + // .data = .{ .disp = 0 }, + // }); }, } }, @@ -6208,24 +6264,24 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void try self.loadMemPtrIntoRegister(base_reg, Type.usize, mcv); if (intrinsicsAllowed(self.target.*, ty)) { - const tag: Mir.Inst.Tag = switch (ty.tag()) { - .f32 => Mir.Inst.Tag.mov_f32, - .f64 => Mir.Inst.Tag.mov_f64, - else => return self.fail("TODO genSetReg from memory for {}", .{ty.fmtDebug()}), - }; + // const tag: Mir.Inst.Tag = switch (ty.tag()) { + // .f32 => Mir.Inst.Tag.mov_f32, + // .f64 => Mir.Inst.Tag.mov_f64, + // else => return self.fail("TODO genSetReg from memory for {}", .{ty.fmtDebug()}), + // }; - _ = try self.addInst(.{ - .tag = tag, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = reg.to128(), - .reg2 = switch (ty.tag()) { - .f32 => base_reg.to32(), - .f64 => base_reg.to64(), - else => unreachable, - }, - }), - .data = .{ .disp = 0 }, - }); + // _ = try self.addInst(.{ + // .tag = tag, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = reg.to128(), + // .reg2 = switch (ty.tag()) { + // .f32 => base_reg.to32(), + // .f64 => base_reg.to64(), + // else => unreachable, + // }, + // }), + // .data = .{ .disp = 0 }, + // }); return; } @@ -6234,42 +6290,42 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void else => { if (x <= math.maxInt(i32)) { // mov reg, [ds:imm32] - _ = try self.addInst(.{ - .tag = .mov, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = registerAlias(reg, abi_size), - .flags = 0b01, - }), - .data = .{ .disp = @intCast(i32, x) }, - }); + // _ = try self.addInst(.{ + // .tag = .mov, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = registerAlias(reg, abi_size), + // .flags = 0b01, + // }), + // .data = .{ .disp = @intCast(i32, x) }, + // }); } else { // If this is RAX, we can use a direct load. // Otherwise, we need to load the address, then indirectly load the value. if (reg.id() == 0) { // movabs rax, ds:moffs64 - const payload = try self.addExtra(Mir.Imm64.encode(x)); - _ = try self.addInst(.{ - .tag = .movabs, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = .rax, - .flags = 0b01, // imm64 will become moffs64 - }), - .data = .{ .payload = payload }, - }); + // const payload = try self.addExtra(Mir.Imm64.encode(x)); + // _ = try self.addInst(.{ + // .tag = .movabs, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = .rax, + // .flags = 0b01, // imm64 will become moffs64 + // }), + // .data = .{ .payload = payload }, + // }); } else { // Rather than duplicate the logic used for the move, we just use a self-call with a new MCValue. try self.genSetReg(ty, reg, MCValue{ .immediate = x }); // mov reg, [reg + 0x0] - _ = try self.addInst(.{ - .tag = .mov, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = registerAlias(reg, abi_size), - .reg2 = reg.to64(), - .flags = 0b01, - }), - .data = .{ .disp = 0 }, - }); + // _ = try self.addInst(.{ + // .tag = .mov, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = registerAlias(reg, abi_size), + // .reg2 = reg.to64(), + // .flags = 0b01, + // }), + // .data = .{ .disp = 0 }, + // }); } } }, @@ -6289,15 +6345,16 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void 4 => 0b11, else => unreachable, }; - _ = try self.addInst(.{ - .tag = .mov_sign_extend, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = reg.to64(), - .reg2 = .rbp, - .flags = flags, - }), - .data = .{ .disp = -off }, - }); + _ = flags; + // _ = try self.addInst(.{ + // .tag = .mov_sign_extend, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = reg.to64(), + // .reg2 = .rbp, + // .flags = flags, + // }), + // .data = .{ .disp = -off }, + // }); return; } }, @@ -6308,38 +6365,39 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void 2 => 0b10, else => unreachable, }; - _ = try self.addInst(.{ - .tag = .mov_zero_extend, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = reg.to64(), - .reg2 = .rbp, - .flags = flags, - }), - .data = .{ .disp = -off }, - }); + _ = flags; + // _ = try self.addInst(.{ + // .tag = .mov_zero_extend, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = reg.to64(), + // .reg2 = .rbp, + // .flags = flags, + // }), + // .data = .{ .disp = -off }, + // }); return; } }, }, .Float => { if (intrinsicsAllowed(self.target.*, ty)) { - const tag: Mir.Inst.Tag = switch (ty.tag()) { - .f32 => Mir.Inst.Tag.mov_f32, - .f64 => Mir.Inst.Tag.mov_f64, - else => return self.fail("TODO genSetReg from stack offset for {}", .{ty.fmtDebug()}), - }; - _ = try self.addInst(.{ - .tag = tag, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = reg.to128(), - .reg2 = switch (ty.tag()) { - .f32 => .ebp, - .f64 => .rbp, - else => unreachable, - }, - }), - .data = .{ .disp = -off }, - }); + // const tag: Mir.Inst.Tag = switch (ty.tag()) { + // .f32 => Mir.Inst.Tag.mov_f32, + // .f64 => Mir.Inst.Tag.mov_f64, + // else => return self.fail("TODO genSetReg from stack offset for {}", .{ty.fmtDebug()}), + // }; + // _ = try self.addInst(.{ + // .tag = tag, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = reg.to128(), + // .reg2 = switch (ty.tag()) { + // .f32 => .ebp, + // .f64 => .rbp, + // else => unreachable, + // }, + // }), + // .data = .{ .disp = -off }, + // }); return; } return self.fail("TODO genSetReg from stack offset for float with no intrinsics", .{}); @@ -6347,15 +6405,15 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void else => {}, } - _ = try self.addInst(.{ - .tag = .mov, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = registerAlias(reg, abi_size), - .reg2 = .rbp, - .flags = 0b01, - }), - .data = .{ .disp = -off }, - }); + // _ = try self.addInst(.{ + // .tag = .mov, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = registerAlias(reg, abi_size), + // .reg2 = .rbp, + // .flags = 0b01, + // }), + // .data = .{ .disp = -off }, + // }); }, } } @@ -6419,6 +6477,7 @@ fn airFloatToInt(self: *Self, inst: Air.Inst.Index) !void { const src_ty = self.air.typeOf(ty_op.operand); const dst_ty = self.air.typeOfIndex(inst); const operand = try self.resolveInst(ty_op.operand); + _ = dst_ty; // move float src to ST(0) const stack_offset = switch (operand) { @@ -6433,34 +6492,35 @@ fn airFloatToInt(self: *Self, inst: Air.Inst.Index) !void { break :blk offset; }, }; - _ = try self.addInst(.{ - .tag = .fld, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = .rbp, - .flags = switch (src_ty.abiSize(self.target.*)) { - 4 => 0b01, - 8 => 0b10, - else => |size| return self.fail("TODO load ST(0) with abiSize={}", .{size}), - }, - }), - .data = .{ .disp = -stack_offset }, - }); + _ = stack_offset; + // _ = try self.addInst(.{ + // .tag = .fld, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = .rbp, + // .flags = switch (src_ty.abiSize(self.target.*)) { + // 4 => 0b01, + // 8 => 0b10, + // else => |size| return self.fail("TODO load ST(0) with abiSize={}", .{size}), + // }, + // }), + // .data = .{ .disp = -stack_offset }, + // }); // convert const stack_dst = try self.allocRegOrMem(inst, false); - _ = try self.addInst(.{ - .tag = .fisttp, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = .rbp, - .flags = switch (dst_ty.abiSize(self.target.*)) { - 1...2 => 0b00, - 3...4 => 0b01, - 5...8 => 0b10, - else => |size| return self.fail("TODO convert float with abiSize={}", .{size}), - }, - }), - .data = .{ .disp = -stack_dst.stack_offset }, - }); + // _ = try self.addInst(.{ + // .tag = .fisttp, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = .rbp, + // .flags = switch (dst_ty.abiSize(self.target.*)) { + // 1...2 => 0b00, + // 3...4 => 0b01, + // 5...8 => 0b10, + // else => |size| return self.fail("TODO convert float with abiSize={}", .{size}), + // }, + // }), + // .data = .{ .disp = -stack_dst.stack_offset }, + // }); return self.finishAir(inst, stack_dst, .{ ty_op.operand, .none, .none }); } @@ -6551,15 +6611,15 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void { .linker_load, .memory => { const reg = try self.register_manager.allocReg(null, gp); try self.loadMemPtrIntoRegister(reg, src_ty, src_ptr); - _ = try self.addInst(.{ - .tag = .mov, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = reg, - .reg2 = reg, - .flags = 0b01, - }), - .data = .{ .disp = 0 }, - }); + // _ = try self.addInst(.{ + // .tag = .mov, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg1 = reg, + // .reg2 = reg, + // .flags = 0b01, + // }), + // .data = .{ .disp = 0 }, + // }); break :blk MCValue{ .register = reg }; }, else => break :blk src_ptr, diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index e69601c40b..15f41a943c 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -71,124 +71,53 @@ pub fn lowerMir(emit: *Emit) InnerError!void { const inst = @intCast(u32, index); try emit.code_offset_mapping.putNoClobber(emit.bin_file.allocator, inst, emit.code.items.len); switch (tag) { - // GPR instructions - .adc => try emit.mirArith(.adc, inst), - .add => try emit.mirArith(.add, inst), - .sub => try emit.mirArith(.sub, inst), - .xor => try emit.mirArith(.xor, inst), - .@"and" => try emit.mirArith(.@"and", inst), - .@"or" => try emit.mirArith(.@"or", inst), - .sbb => try emit.mirArith(.sbb, inst), - .cmp => try emit.mirArith(.cmp, inst), - .mov => try emit.mirArith(.mov, inst), + .adc, + .add, + .@"and", + .cbw, + .cwde, + .cdqe, + .cwd, + .cdq, + .cqo, + .cmp, + .div, + .fisttp, + .fld, + .idiv, + .imul, + .int3, + .mov, + .movsx, + .movzx, + .mul, + .nop, + .@"or", + .pop, + .push, + .ret, + .sal, + .sar, + .sbb, + .shl, + .shr, + .sub, + .syscall, + .@"test", + .ud2, + .xor, - .adc_mem_imm => try emit.mirArithMemImm(.adc, inst), - .add_mem_imm => try emit.mirArithMemImm(.add, inst), - .sub_mem_imm => try emit.mirArithMemImm(.sub, inst), - .xor_mem_imm => try emit.mirArithMemImm(.xor, inst), - .and_mem_imm => try emit.mirArithMemImm(.@"and", inst), - .or_mem_imm => try emit.mirArithMemImm(.@"or", inst), - .sbb_mem_imm => try emit.mirArithMemImm(.sbb, inst), - .cmp_mem_imm => try emit.mirArithMemImm(.cmp, inst), - .mov_mem_imm => try emit.mirArithMemImm(.mov, inst), - - .adc_scale_src => try emit.mirArithScaleSrc(.adc, inst), - .add_scale_src => try emit.mirArithScaleSrc(.add, inst), - .sub_scale_src => try emit.mirArithScaleSrc(.sub, inst), - .xor_scale_src => try emit.mirArithScaleSrc(.xor, inst), - .and_scale_src => try emit.mirArithScaleSrc(.@"and", inst), - .or_scale_src => try emit.mirArithScaleSrc(.@"or", inst), - .sbb_scale_src => try emit.mirArithScaleSrc(.sbb, inst), - .cmp_scale_src => try emit.mirArithScaleSrc(.cmp, inst), - .mov_scale_src => try emit.mirArithScaleSrc(.mov, inst), - - .adc_scale_dst => try emit.mirArithScaleDst(.adc, inst), - .add_scale_dst => try emit.mirArithScaleDst(.add, inst), - .sub_scale_dst => try emit.mirArithScaleDst(.sub, inst), - .xor_scale_dst => try emit.mirArithScaleDst(.xor, inst), - .and_scale_dst => try emit.mirArithScaleDst(.@"and", inst), - .or_scale_dst => try emit.mirArithScaleDst(.@"or", inst), - .sbb_scale_dst => try emit.mirArithScaleDst(.sbb, inst), - .cmp_scale_dst => try emit.mirArithScaleDst(.cmp, inst), - .mov_scale_dst => try emit.mirArithScaleDst(.mov, inst), - - .adc_scale_imm => try emit.mirArithScaleImm(.adc, inst), - .add_scale_imm => try emit.mirArithScaleImm(.add, inst), - .sub_scale_imm => try emit.mirArithScaleImm(.sub, inst), - .xor_scale_imm => try emit.mirArithScaleImm(.xor, inst), - .and_scale_imm => try emit.mirArithScaleImm(.@"and", inst), - .or_scale_imm => try emit.mirArithScaleImm(.@"or", inst), - .sbb_scale_imm => try emit.mirArithScaleImm(.sbb, inst), - .cmp_scale_imm => try emit.mirArithScaleImm(.cmp, inst), - .mov_scale_imm => try emit.mirArithScaleImm(.mov, inst), - - .adc_mem_index_imm => try emit.mirArithMemIndexImm(.adc, inst), - .add_mem_index_imm => try emit.mirArithMemIndexImm(.add, inst), - .sub_mem_index_imm => try emit.mirArithMemIndexImm(.sub, inst), - .xor_mem_index_imm => try emit.mirArithMemIndexImm(.xor, inst), - .and_mem_index_imm => try emit.mirArithMemIndexImm(.@"and", inst), - .or_mem_index_imm => try emit.mirArithMemIndexImm(.@"or", inst), - .sbb_mem_index_imm => try emit.mirArithMemIndexImm(.sbb, inst), - .cmp_mem_index_imm => try emit.mirArithMemIndexImm(.cmp, inst), - .mov_mem_index_imm => try emit.mirArithMemIndexImm(.mov, inst), - - .mov_sign_extend => try emit.mirMovSignExtend(inst), - .mov_zero_extend => try emit.mirMovZeroExtend(inst), - - .movabs => try emit.mirMovabs(inst), - - .fisttp => try emit.mirFisttp(inst), - .fld => try emit.mirFld(inst), - - .lea => try emit.mirLea(inst), - .lea_pic => try emit.mirLeaPic(inst), - - .shl => try emit.mirShift(.shl, inst), - .sal => try emit.mirShift(.sal, inst), - .shr => try emit.mirShift(.shr, inst), - .sar => try emit.mirShift(.sar, inst), - - .imul => try emit.mirMulDiv(.imul, inst), - .mul => try emit.mirMulDiv(.mul, inst), - .idiv => try emit.mirMulDiv(.idiv, inst), - .div => try emit.mirMulDiv(.div, inst), - .imul_complex => try emit.mirIMulComplex(inst), - - .cwd => try emit.mirCwd(inst), - - .push => try emit.mirPushPop(.push, inst), - .pop => try emit.mirPushPop(.pop, inst), - - .jmp => try emit.mirJmpCall(.jmp, inst), - .call => try emit.mirJmpCall(.call, inst), - - .cond_jmp => try emit.mirCondJmp(inst), - .cond_set_byte => try emit.mirCondSetByte(inst), - .cond_mov => try emit.mirCondMov(inst), - - .ret => try emit.mirRet(inst), - - .syscall => try emit.mirSyscall(), - - .@"test" => try emit.mirTest(inst), - - .ud => try emit.mirUndefinedInstruction(), - .interrupt => try emit.mirInterrupt(inst), - .nop => {}, // just skip it - - // SSE/AVX instructions - .mov_f64 => try emit.mirMovFloat(.movsd, inst), - .mov_f32 => try emit.mirMovFloat(.movss, inst), - - .add_f64 => try emit.mirAddFloat(.addsd, inst), - .add_f32 => try emit.mirAddFloat(.addss, inst), - - .cmp_f64 => try emit.mirCmpFloat(.ucomisd, inst), - .cmp_f32 => try emit.mirCmpFloat(.ucomiss, inst), + .addss, + .cmpss, + .movss, + .ucomiss, + .addsd, + .cmpsd, + .movsd, + .ucomisd, + => try emit.mirEncodeGeneric(tag, inst), // Pseudo-instructions - .call_extern => try emit.mirCallExtern(inst), - .dbg_line => try emit.mirDbgLine(inst), .dbg_prologue_end => try emit.mirDbgPrologueEnd(inst), .dbg_epilogue_begin => try emit.mirDbgEpilogueBegin(inst), @@ -196,9 +125,7 @@ pub fn lowerMir(emit: *Emit) InnerError!void { .push_regs => try emit.mirPushPopRegisterList(.push, inst), .pop_regs => try emit.mirPushPopRegisterList(.pop, inst), - else => { - return emit.fail("Implement MIR->Emit lowering for x86_64 for pseudo-inst: {}", .{tag}); - }, + else => return emit.fail("Implement MIR->Emit lowering for x86_64 for pseudo-inst: {}", .{tag}), } } @@ -246,66 +173,57 @@ fn encode(emit: *Emit, mnemonic: Instruction.Mnemonic, ops: struct { return inst.encode(emit.code.writer()); } -fn mirUndefinedInstruction(emit: *Emit) InnerError!void { - return emit.encode(.ud2, .{}); -} +fn mirEncodeGeneric(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void { + const mnemonic = inline for (@typeInfo(Instruction.Mnemonic).Enum.fields) |field| { + if (mem.eql(u8, field.name, @tagName(tag))) break @field(Instruction.Mnemonic, field.name); + } else unreachable; -fn mirInterrupt(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { - const tag = emit.mir.instructions.items(.tag)[inst]; - assert(tag == .interrupt); - const ops = emit.mir.instructions.items(.ops)[inst].decode(); - switch (ops.flags) { - 0b00 => return emit.encode(.int3, .{}), - else => return emit.fail("TODO handle variant 0b{b} of interrupt instruction", .{ops.flags}), + var operands = [4]Instruction.Operand{ .none, .none, .none, .none }; + const ops = emit.mir.instructions.items(.ops)[inst]; + const data = emit.mir.instructions.items(.data)[inst]; + switch (ops) { + .none => {}, + .imm_s => operands[0] = .{ .imm = Immediate.s(data.imm_s) }, + .imm_u => operands[0] = .{ .imm = Immediate.u(data.imm_u) }, + .r => operands[0] = .{ .reg = data.r }, + .rr => operands[0..2].* = .{ + .{ .reg = data.rr.r1 }, + .{ .reg = data.rr.r2 }, + }, + .ri_s => operands[0..2].* = .{ + .{ .reg = data.ri_s.r1 }, + .{ .imm = Immediate.s(data.ri_s.imm) }, + }, + .ri_u => operands[0..2].* = .{ + .{ .reg = data.ri_u.r1 }, + .{ .imm = Immediate.u(data.ri_u.imm) }, + }, + else => unreachable, } + + return emit.encode(mnemonic, .{ + .op1 = operands[0], + .op2 = operands[1], + .op3 = operands[2], + .op4 = operands[3], + }); } -fn mirSyscall(emit: *Emit) InnerError!void { - return emit.encode(.syscall, .{}); -} - -fn mirPushPop(emit: *Emit, mnemonic: Instruction.Mnemonic, inst: Mir.Inst.Index) InnerError!void { - const ops = emit.mir.instructions.items(.ops)[inst].decode(); - switch (ops.flags) { - 0b00 => { - return emit.encode(mnemonic, .{ - .op1 = .{ .reg = ops.reg1 }, - }); - }, - 0b01 => { - const disp = emit.mir.instructions.items(.data)[inst].disp; - return emit.encode(mnemonic, .{ - .op1 = .{ .mem = Memory.sib(.qword, .{ - .base = ops.reg1, - .disp = disp, - }) }, - }); - }, - 0b10 => { - const imm = emit.mir.instructions.items(.data)[inst].imm; - return emit.encode(.push, .{ - .op1 = .{ .imm = Immediate.u(imm) }, - }); - }, - 0b11 => unreachable, - } -} - -fn mirPushPopRegisterList(emit: *Emit, mnemonic: Instruction.Mnemonic, inst: Mir.Inst.Index) InnerError!void { - const ops = emit.mir.instructions.items(.ops)[inst].decode(); +fn mirPushPopRegisterList(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void { const payload = emit.mir.instructions.items(.data)[inst].payload; const save_reg_list = emit.mir.extraData(Mir.SaveRegisterList, payload).data; + const base = @intToEnum(Register, save_reg_list.base_reg); var disp: i32 = -@intCast(i32, save_reg_list.stack_end); const reg_list = Mir.RegisterList.fromInt(save_reg_list.register_list); const callee_preserved_regs = abi.getCalleePreservedRegs(emit.target.*); for (callee_preserved_regs) |reg| { if (reg_list.isSet(callee_preserved_regs, reg)) { const op1: Instruction.Operand = .{ .mem = Memory.sib(.qword, .{ - .base = ops.reg1, + .base = base, .disp = disp, }) }; const op2: Instruction.Operand = .{ .reg = reg }; - switch (mnemonic) { + switch (tag) { .push => try emit.encode(.mov, .{ .op1 = op1, .op2 = op2, @@ -321,858 +239,345 @@ fn mirPushPopRegisterList(emit: *Emit, mnemonic: Instruction.Mnemonic, inst: Mir } } -fn mirJmpCall(emit: *Emit, mnemonic: Instruction.Mnemonic, inst: Mir.Inst.Index) InnerError!void { - const ops = emit.mir.instructions.items(.ops)[inst].decode(); - switch (ops.flags) { - 0b00 => { - const target = emit.mir.instructions.items(.data)[inst].inst; - const source = emit.code.items.len; - try emit.encode(mnemonic, .{ - .op1 = .{ .imm = Immediate.s(0) }, - }); - try emit.relocs.append(emit.bin_file.allocator, .{ - .source = source, - .target = target, - .offset = emit.code.items.len - 4, - .length = 5, - }); - }, - 0b01 => { - if (ops.reg1 == .none) { - const disp = emit.mir.instructions.items(.data)[inst].disp; - return emit.encode(mnemonic, .{ - .op1 = .{ .mem = Memory.sib(.qword, .{ .disp = disp }) }, - }); - } - return emit.encode(mnemonic, .{ - .op1 = .{ .reg = ops.reg1 }, - }); - }, - 0b10 => { - const disp = emit.mir.instructions.items(.data)[inst].disp; - return emit.encode(mnemonic, .{ - .op1 = .{ .mem = Memory.sib(.qword, .{ - .base = ops.reg1, - .disp = disp, - }) }, - }); - }, - 0b11 => return emit.fail("TODO unused variant jmp/call 0b11", .{}), - } -} +// fn mirJmpCall(emit: *Emit, mnemonic: Instruction.Mnemonic, inst: Mir.Inst.Index) InnerError!void { +// const ops = emit.mir.instructions.items(.ops)[inst].decode(); +// switch (ops.flags) { +// 0b00 => { +// const target = emit.mir.instructions.items(.data)[inst].inst; +// const source = emit.code.items.len; +// try emit.encode(mnemonic, .{ +// .op1 = .{ .imm = Immediate.s(0) }, +// }); +// try emit.relocs.append(emit.bin_file.allocator, .{ +// .source = source, +// .target = target, +// .offset = emit.code.items.len - 4, +// .length = 5, +// }); +// }, +// 0b01 => { +// if (ops.reg1 == .none) { +// const disp = emit.mir.instructions.items(.data)[inst].disp; +// return emit.encode(mnemonic, .{ +// .op1 = .{ .mem = Memory.sib(.qword, .{ .disp = disp }) }, +// }); +// } +// return emit.encode(mnemonic, .{ +// .op1 = .{ .reg = ops.reg1 }, +// }); +// }, +// 0b10 => { +// const disp = emit.mir.instructions.items(.data)[inst].disp; +// return emit.encode(mnemonic, .{ +// .op1 = .{ .mem = Memory.sib(.qword, .{ +// .base = ops.reg1, +// .disp = disp, +// }) }, +// }); +// }, +// 0b11 => return emit.fail("TODO unused variant jmp/call 0b11", .{}), +// } +// } -fn mirCondJmp(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { - const tag = emit.mir.instructions.items(.tag)[inst]; - assert(tag == .cond_jmp); - const inst_cc = emit.mir.instructions.items(.data)[inst].inst_cc; - const mnemonic: Instruction.Mnemonic = switch (inst_cc.cc) { - .a => .ja, - .ae => .jae, - .b => .jb, - .be => .jbe, - .c => .jc, - .e => .je, - .g => .jg, - .ge => .jge, - .l => .jl, - .le => .jle, - .na => .jna, - .nae => .jnae, - .nb => .jnb, - .nbe => .jnbe, - .nc => .jnc, - .ne => .jne, - .ng => .jng, - .nge => .jnge, - .nl => .jnl, - .nle => .jnle, - .no => .jno, - .np => .jnp, - .ns => .jns, - .nz => .jnz, - .o => .jo, - .p => .jp, - .pe => .jpe, - .po => .jpo, - .s => .js, - .z => .jz, - }; - const source = emit.code.items.len; - try emit.encode(mnemonic, .{ - .op1 = .{ .imm = Immediate.s(0) }, - }); - try emit.relocs.append(emit.bin_file.allocator, .{ - .source = source, - .target = inst_cc.inst, - .offset = emit.code.items.len - 4, - .length = 6, - }); -} +// fn mirCondJmp(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { +// const tag = emit.mir.instructions.items(.tag)[inst]; +// assert(tag == .cond_jmp); +// const inst_cc = emit.mir.instructions.items(.data)[inst].inst_cc; +// const mnemonic: Instruction.Mnemonic = switch (inst_cc.cc) { +// .a => .ja, +// .ae => .jae, +// .b => .jb, +// .be => .jbe, +// .c => .jc, +// .e => .je, +// .g => .jg, +// .ge => .jge, +// .l => .jl, +// .le => .jle, +// .na => .jna, +// .nae => .jnae, +// .nb => .jnb, +// .nbe => .jnbe, +// .nc => .jnc, +// .ne => .jne, +// .ng => .jng, +// .nge => .jnge, +// .nl => .jnl, +// .nle => .jnle, +// .no => .jno, +// .np => .jnp, +// .ns => .jns, +// .nz => .jnz, +// .o => .jo, +// .p => .jp, +// .pe => .jpe, +// .po => .jpo, +// .s => .js, +// .z => .jz, +// }; +// const source = emit.code.items.len; +// try emit.encode(mnemonic, .{ +// .op1 = .{ .imm = Immediate.s(0) }, +// }); +// try emit.relocs.append(emit.bin_file.allocator, .{ +// .source = source, +// .target = inst_cc.inst, +// .offset = emit.code.items.len - 4, +// .length = 6, +// }); +// } -fn mirCondSetByte(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { - const tag = emit.mir.instructions.items(.tag)[inst]; - assert(tag == .cond_set_byte); - const ops = emit.mir.instructions.items(.ops)[inst].decode(); - const cc = emit.mir.instructions.items(.data)[inst].cc; - const mnemonic: Instruction.Mnemonic = switch (cc) { - .a => .seta, - .ae => .setae, - .b => .setb, - .be => .setbe, - .c => .setc, - .e => .sete, - .g => .setg, - .ge => .setge, - .l => .setl, - .le => .setle, - .na => .setna, - .nae => .setnae, - .nb => .setnb, - .nbe => .setnbe, - .nc => .setnc, - .ne => .setne, - .ng => .setng, - .nge => .setnge, - .nl => .setnl, - .nle => .setnle, - .no => .setno, - .np => .setnp, - .ns => .setns, - .nz => .setnz, - .o => .seto, - .p => .setp, - .pe => .setpe, - .po => .setpo, - .s => .sets, - .z => .setz, - }; - return emit.encode(mnemonic, .{ .op1 = .{ .reg = ops.reg1 } }); -} +// fn mirCondSetByte(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { +// const tag = emit.mir.instructions.items(.tag)[inst]; +// assert(tag == .cond_set_byte); +// const ops = emit.mir.instructions.items(.ops)[inst].decode(); +// const cc = emit.mir.instructions.items(.data)[inst].cc; +// const mnemonic: Instruction.Mnemonic = switch (cc) { +// .a => .seta, +// .ae => .setae, +// .b => .setb, +// .be => .setbe, +// .c => .setc, +// .e => .sete, +// .g => .setg, +// .ge => .setge, +// .l => .setl, +// .le => .setle, +// .na => .setna, +// .nae => .setnae, +// .nb => .setnb, +// .nbe => .setnbe, +// .nc => .setnc, +// .ne => .setne, +// .ng => .setng, +// .nge => .setnge, +// .nl => .setnl, +// .nle => .setnle, +// .no => .setno, +// .np => .setnp, +// .ns => .setns, +// .nz => .setnz, +// .o => .seto, +// .p => .setp, +// .pe => .setpe, +// .po => .setpo, +// .s => .sets, +// .z => .setz, +// }; +// return emit.encode(mnemonic, .{ .op1 = .{ .reg = ops.reg1 } }); +// } -fn mirCondMov(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { - const tag = emit.mir.instructions.items(.tag)[inst]; - assert(tag == .cond_mov); - const ops = emit.mir.instructions.items(.ops)[inst].decode(); - const cc = emit.mir.instructions.items(.data)[inst].cc; - const mnemonic: Instruction.Mnemonic = switch (cc) { - .a => .cmova, - .ae => .cmovae, - .b => .cmovb, - .be => .cmovbe, - .c => .cmovc, - .e => .cmove, - .g => .cmovg, - .ge => .cmovge, - .l => .cmovl, - .le => .cmovle, - .na => .cmovna, - .nae => .cmovnae, - .nb => .cmovnb, - .nbe => .cmovnbe, - .nc => .cmovnc, - .ne => .cmovne, - .ng => .cmovng, - .nge => .cmovnge, - .nl => .cmovnl, - .nle => .cmovnle, - .no => .cmovno, - .np => .cmovnp, - .ns => .cmovns, - .nz => .cmovnz, - .o => .cmovo, - .p => .cmovp, - .pe => .cmovpe, - .po => .cmovpo, - .s => .cmovs, - .z => .cmovz, - }; - const op1: Instruction.Operand = .{ .reg = ops.reg1 }; +// fn mirCondMov(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { +// const tag = emit.mir.instructions.items(.tag)[inst]; +// assert(tag == .cond_mov); +// const ops = emit.mir.instructions.items(.ops)[inst].decode(); +// const cc = emit.mir.instructions.items(.data)[inst].cc; +// const mnemonic: Instruction.Mnemonic = switch (cc) { +// .a => .cmova, +// .ae => .cmovae, +// .b => .cmovb, +// .be => .cmovbe, +// .c => .cmovc, +// .e => .cmove, +// .g => .cmovg, +// .ge => .cmovge, +// .l => .cmovl, +// .le => .cmovle, +// .na => .cmovna, +// .nae => .cmovnae, +// .nb => .cmovnb, +// .nbe => .cmovnbe, +// .nc => .cmovnc, +// .ne => .cmovne, +// .ng => .cmovng, +// .nge => .cmovnge, +// .nl => .cmovnl, +// .nle => .cmovnle, +// .no => .cmovno, +// .np => .cmovnp, +// .ns => .cmovns, +// .nz => .cmovnz, +// .o => .cmovo, +// .p => .cmovp, +// .pe => .cmovpe, +// .po => .cmovpo, +// .s => .cmovs, +// .z => .cmovz, +// }; +// const op1: Instruction.Operand = .{ .reg = ops.reg1 }; - if (ops.flags == 0b00) { - return emit.encode(mnemonic, .{ - .op1 = op1, - .op2 = .{ .reg = ops.reg2 }, - }); - } - const disp = emit.mir.instructions.items(.data)[inst].disp; - const ptr_size: Memory.PtrSize = switch (ops.flags) { - 0b00 => unreachable, - 0b01 => .word, - 0b10 => .dword, - 0b11 => .qword, - }; - return emit.encode(mnemonic, .{ - .op1 = op1, - .op2 = .{ .mem = Memory.sib(ptr_size, .{ - .base = ops.reg2, - .disp = disp, - }) }, - }); -} +// if (ops.flags == 0b00) { +// return emit.encode(mnemonic, .{ +// .op1 = op1, +// .op2 = .{ .reg = ops.reg2 }, +// }); +// } +// const disp = emit.mir.instructions.items(.data)[inst].disp; +// const ptr_size: Memory.PtrSize = switch (ops.flags) { +// 0b00 => unreachable, +// 0b01 => .word, +// 0b10 => .dword, +// 0b11 => .qword, +// }; +// return emit.encode(mnemonic, .{ +// .op1 = op1, +// .op2 = .{ .mem = Memory.sib(ptr_size, .{ +// .base = ops.reg2, +// .disp = disp, +// }) }, +// }); +// } -fn mirTest(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { - const tag = emit.mir.instructions.items(.tag)[inst]; - assert(tag == .@"test"); - const ops = emit.mir.instructions.items(.ops)[inst].decode(); - switch (ops.flags) { - 0b00 => { - if (ops.reg2 == .none) { - const imm = emit.mir.instructions.items(.data)[inst].imm; - return emit.encode(.@"test", .{ - .op1 = .{ .reg = ops.reg1 }, - .op2 = .{ .imm = Immediate.u(imm) }, - }); - } - return emit.encode(.@"test", .{ - .op1 = .{ .reg = ops.reg1 }, - .op2 = .{ .reg = ops.reg2 }, - }); - }, - else => return emit.fail("TODO more TEST alternatives", .{}), - } -} +// fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { +// const tag = emit.mir.instructions.items(.tag)[inst]; +// assert(tag == .lea); +// const ops = emit.mir.instructions.items(.ops)[inst].decode(); +// switch (ops.flags) { +// 0b00 => { +// const disp = emit.mir.instructions.items(.data)[inst].disp; +// const src_reg: ?Register = if (ops.reg2 != .none) ops.reg2 else null; +// return emit.encode(.lea, .{ +// .op1 = .{ .reg = ops.reg1 }, +// .op2 = .{ .mem = Memory.sib(Memory.PtrSize.fromBitSize(ops.reg1.bitSize()), .{ +// .base = src_reg, +// .disp = disp, +// }) }, +// }); +// }, +// 0b01 => { +// const start_offset = emit.code.items.len; +// try emit.encode(.lea, .{ +// .op1 = .{ .reg = ops.reg1 }, +// .op2 = .{ .mem = Memory.rip(Memory.PtrSize.fromBitSize(ops.reg1.bitSize()), 0) }, +// }); +// const end_offset = emit.code.items.len; +// // Backpatch the displacement +// const payload = emit.mir.instructions.items(.data)[inst].payload; +// const imm = emit.mir.extraData(Mir.Imm64, payload).data.decode(); +// const disp = @intCast(i32, @intCast(i64, imm) - @intCast(i64, end_offset - start_offset)); +// mem.writeIntLittle(i32, emit.code.items[end_offset - 4 ..][0..4], disp); +// }, +// 0b10 => { +// const payload = emit.mir.instructions.items(.data)[inst].payload; +// const index_reg_disp = emit.mir.extraData(Mir.IndexRegisterDisp, payload).data.decode(); +// const src_reg: ?Register = if (ops.reg2 != .none) ops.reg2 else null; +// const scale_index = Memory.ScaleIndex{ +// .scale = 1, +// .index = index_reg_disp.index, +// }; +// return emit.encode(.lea, .{ +// .op1 = .{ .reg = ops.reg1 }, +// .op2 = .{ .mem = Memory.sib(Memory.PtrSize.fromBitSize(ops.reg1.bitSize()), .{ +// .base = src_reg, +// .scale_index = scale_index, +// .disp = index_reg_disp.disp, +// }) }, +// }); +// }, +// 0b11 => return emit.fail("TODO unused LEA variant 0b11", .{}), +// } +// } -fn mirRet(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { - const tag = emit.mir.instructions.items(.tag)[inst]; - assert(tag == .ret); - const ops = emit.mir.instructions.items(.ops)[inst].decode(); - switch (ops.flags) { - 0b00 => unreachable, - 0b01 => unreachable, - 0b10 => { - const imm = emit.mir.instructions.items(.data)[inst].imm; - return emit.encode(.ret, .{ - .op1 = .{ .imm = Immediate.u(imm) }, - }); - }, - 0b11 => { - return emit.encode(.ret, .{}); - }, - } -} +// fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { +// const tag = emit.mir.instructions.items(.tag)[inst]; +// assert(tag == .lea_pic); +// const ops = emit.mir.instructions.items(.ops)[inst].decode(); +// const relocation = emit.mir.instructions.items(.data)[inst].relocation; -fn mirArith(emit: *Emit, mnemonic: Instruction.Mnemonic, inst: Mir.Inst.Index) InnerError!void { - const ops = emit.mir.instructions.items(.ops)[inst].decode(); - switch (ops.flags) { - 0b00 => { - if (ops.reg2 == .none) { - const imm = emit.mir.instructions.items(.data)[inst].imm; - return emit.encode(mnemonic, .{ - .op1 = .{ .reg = ops.reg1 }, - .op2 = .{ .imm = Immediate.u(imm) }, - }); - } - return emit.encode(mnemonic, .{ - .op1 = .{ .reg = ops.reg1 }, - .op2 = .{ .reg = ops.reg2 }, - }); - }, - 0b01 => { - const disp = emit.mir.instructions.items(.data)[inst].disp; - const base: ?Register = if (ops.reg2 != .none) ops.reg2 else null; - return emit.encode(mnemonic, .{ - .op1 = .{ .reg = ops.reg1 }, - .op2 = .{ .mem = Memory.sib(Memory.PtrSize.fromBitSize(ops.reg1.bitSize()), .{ - .base = base, - .disp = disp, - }) }, - }); - }, - 0b10 => { - if (ops.reg2 == .none) { - return emit.fail("TODO unused variant: mov reg1, none, 0b10", .{}); - } - const disp = emit.mir.instructions.items(.data)[inst].disp; - return emit.encode(mnemonic, .{ - .op1 = .{ .mem = Memory.sib(Memory.PtrSize.fromBitSize(ops.reg2.bitSize()), .{ - .base = ops.reg1, - .disp = disp, - }) }, - .op2 = .{ .reg = ops.reg2 }, - }); - }, - 0b11 => { - const imm_s = emit.mir.instructions.items(.data)[inst].imm_s; - return emit.encode(mnemonic, .{ - .op1 = .{ .reg = ops.reg1 }, - .op2 = .{ .imm = Immediate.s(imm_s) }, - }); - }, - } -} +// switch (ops.flags) { +// 0b00, 0b01, 0b10 => {}, +// else => return emit.fail("TODO unused LEA PIC variant 0b11", .{}), +// } -fn mirArithMemImm(emit: *Emit, mnemonic: Instruction.Mnemonic, inst: Mir.Inst.Index) InnerError!void { - const ops = emit.mir.instructions.items(.ops)[inst].decode(); - assert(ops.reg2 == .none); - const payload = emit.mir.instructions.items(.data)[inst].payload; - const imm_pair = emit.mir.extraData(Mir.ImmPair, payload).data; - const ptr_size: Memory.PtrSize = switch (ops.flags) { - 0b00 => .byte, - 0b01 => .word, - 0b10 => .dword, - 0b11 => .qword, - }; - const imm = switch (ops.flags) { - 0b00 => @truncate(u8, imm_pair.operand), - 0b01 => @truncate(u16, imm_pair.operand), - 0b10, 0b11 => @truncate(u32, imm_pair.operand), - }; - return emit.encode(mnemonic, .{ - .op1 = .{ .mem = Memory.sib(ptr_size, .{ - .disp = imm_pair.dest_off, - .base = ops.reg1, - }) }, - .op2 = .{ .imm = Immediate.u(imm) }, - }); -} +// try emit.encode(.lea, .{ +// .op1 = .{ .reg = ops.reg1 }, +// .op2 = .{ .mem = Memory.rip(Memory.PtrSize.fromBitSize(ops.reg1.bitSize()), 0) }, +// }); -fn mirArithScaleSrc(emit: *Emit, mnemonic: Instruction.Mnemonic, inst: Mir.Inst.Index) InnerError!void { - const ops = emit.mir.instructions.items(.ops)[inst].decode(); - const scale = ops.flags; - const payload = emit.mir.instructions.items(.data)[inst].payload; - const index_reg_disp = emit.mir.extraData(Mir.IndexRegisterDisp, payload).data.decode(); - const scale_index = Memory.ScaleIndex{ - .scale = @as(u4, 1) << scale, - .index = index_reg_disp.index, - }; - return emit.encode(mnemonic, .{ - .op1 = .{ .reg = ops.reg1 }, - .op2 = .{ .mem = Memory.sib(Memory.PtrSize.fromBitSize(ops.reg1.bitSize()), .{ - .base = ops.reg2, - .scale_index = scale_index, - .disp = index_reg_disp.disp, - }) }, - }); -} +// const end_offset = emit.code.items.len; -fn mirArithScaleDst(emit: *Emit, mnemonic: Instruction.Mnemonic, inst: Mir.Inst.Index) InnerError!void { - const ops = emit.mir.instructions.items(.ops)[inst].decode(); - const scale = ops.flags; - const payload = emit.mir.instructions.items(.data)[inst].payload; - const index_reg_disp = emit.mir.extraData(Mir.IndexRegisterDisp, payload).data.decode(); - const scale_index = Memory.ScaleIndex{ - .scale = @as(u4, 1) << scale, - .index = index_reg_disp.index, - }; - assert(ops.reg2 != .none); - return emit.encode(mnemonic, .{ - .op1 = .{ .mem = Memory.sib(Memory.PtrSize.fromBitSize(ops.reg2.bitSize()), .{ - .base = ops.reg1, - .scale_index = scale_index, - .disp = index_reg_disp.disp, - }) }, - .op2 = .{ .reg = ops.reg2 }, - }); -} +// if (emit.bin_file.cast(link.File.MachO)) |macho_file| { +// const reloc_type = switch (ops.flags) { +// 0b00 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_GOT), +// 0b01 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_SIGNED), +// else => unreachable, +// }; +// const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?; +// try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{ +// .type = reloc_type, +// .target = .{ .sym_index = relocation.sym_index, .file = null }, +// .offset = @intCast(u32, end_offset - 4), +// .addend = 0, +// .pcrel = true, +// .length = 2, +// }); +// } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| { +// const atom_index = coff_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?; +// try link.File.Coff.Atom.addRelocation(coff_file, atom_index, .{ +// .type = switch (ops.flags) { +// 0b00 => .got, +// 0b01 => .direct, +// 0b10 => .import, +// else => unreachable, +// }, +// .target = switch (ops.flags) { +// 0b00, 0b01 => .{ .sym_index = relocation.sym_index, .file = null }, +// 0b10 => coff_file.getGlobalByIndex(relocation.sym_index), +// else => unreachable, +// }, +// .offset = @intCast(u32, end_offset - 4), +// .addend = 0, +// .pcrel = true, +// .length = 2, +// }); +// } else { +// return emit.fail("TODO implement lea reg, [rip + reloc] for linking backends different than MachO", .{}); +// } +// } -fn mirArithScaleImm(emit: *Emit, mnemonic: Instruction.Mnemonic, inst: Mir.Inst.Index) InnerError!void { - const ops = emit.mir.instructions.items(.ops)[inst].decode(); - const scale = ops.flags; - const payload = emit.mir.instructions.items(.data)[inst].payload; - const index_reg_disp_imm = emit.mir.extraData(Mir.IndexRegisterDispImm, payload).data.decode(); - const scale_index = Memory.ScaleIndex{ - .scale = @as(u4, 1) << scale, - .index = index_reg_disp_imm.index, - }; - return emit.encode(mnemonic, .{ - .op1 = .{ .mem = Memory.sib(.qword, .{ - .base = ops.reg1, - .disp = index_reg_disp_imm.disp, - .scale_index = scale_index, - }) }, - .op2 = .{ .imm = Immediate.u(index_reg_disp_imm.imm) }, - }); -} +// fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { +// const tag = emit.mir.instructions.items(.tag)[inst]; +// assert(tag == .call_extern); +// const relocation = emit.mir.instructions.items(.data)[inst].relocation; -fn mirArithMemIndexImm(emit: *Emit, mnemonic: Instruction.Mnemonic, inst: Mir.Inst.Index) InnerError!void { - const ops = emit.mir.instructions.items(.ops)[inst].decode(); - assert(ops.reg2 == .none); - const payload = emit.mir.instructions.items(.data)[inst].payload; - const index_reg_disp_imm = emit.mir.extraData(Mir.IndexRegisterDispImm, payload).data.decode(); - const ptr_size: Memory.PtrSize = switch (ops.flags) { - 0b00 => .byte, - 0b01 => .word, - 0b10 => .dword, - 0b11 => .qword, - }; - const scale_index = Memory.ScaleIndex{ - .scale = 1, - .index = index_reg_disp_imm.index, - }; - return emit.encode(mnemonic, .{ - .op1 = .{ .mem = Memory.sib(ptr_size, .{ - .disp = index_reg_disp_imm.disp, - .base = ops.reg1, - .scale_index = scale_index, - }) }, - .op2 = .{ .imm = Immediate.u(index_reg_disp_imm.imm) }, - }); -} +// const offset = blk: { +// // callq +// try emit.encode(.call, .{ +// .op1 = .{ .imm = Immediate.s(0) }, +// }); +// break :blk @intCast(u32, emit.code.items.len) - 4; +// }; -fn mirMovSignExtend(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { - const tag = emit.mir.instructions.items(.tag)[inst]; - assert(tag == .mov_sign_extend); - const ops = emit.mir.instructions.items(.ops)[inst].decode(); - const disp = if (ops.flags != 0b00) emit.mir.instructions.items(.data)[inst].disp else undefined; - switch (ops.flags) { - 0b00 => { - const mnemonic: Instruction.Mnemonic = if (ops.reg2.bitSize() == 32) .movsxd else .movsx; - return emit.encode(mnemonic, .{ - .op1 = .{ .reg = ops.reg1 }, - .op2 = .{ .reg = ops.reg2 }, - }); - }, - else => { - const ptr_size: Memory.PtrSize = switch (ops.flags) { - 0b01 => .byte, - 0b10 => .word, - 0b11 => .dword, - else => unreachable, - }; - const mnemonic: Instruction.Mnemonic = if (ops.flags == 0b11) .movsxd else .movsx; - return emit.encode(mnemonic, .{ - .op1 = .{ .reg = ops.reg1 }, - .op2 = .{ .mem = Memory.sib(ptr_size, .{ - .base = ops.reg2, - .disp = disp, - }) }, - }); - }, - } -} - -fn mirMovZeroExtend(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { - const tag = emit.mir.instructions.items(.tag)[inst]; - assert(tag == .mov_zero_extend); - const ops = emit.mir.instructions.items(.ops)[inst].decode(); - const disp = if (ops.flags != 0b00) emit.mir.instructions.items(.data)[inst].disp else undefined; - switch (ops.flags) { - 0b00 => { - return emit.encode(.movzx, .{ - .op1 = .{ .reg = ops.reg1 }, - .op2 = .{ .reg = ops.reg2 }, - }); - }, - 0b01, 0b10 => { - const ptr_size: Memory.PtrSize = switch (ops.flags) { - 0b01 => .byte, - 0b10 => .word, - else => unreachable, - }; - return emit.encode(.movzx, .{ - .op1 = .{ .reg = ops.reg1 }, - .op2 = .{ .mem = Memory.sib(ptr_size, .{ - .disp = disp, - .base = ops.reg2, - }) }, - }); - }, - 0b11 => { - return emit.fail("TODO unused variant: movzx 0b11", .{}); - }, - } -} - -fn mirMovabs(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { - const tag = emit.mir.instructions.items(.tag)[inst]; - assert(tag == .movabs); - const ops = emit.mir.instructions.items(.ops)[inst].decode(); - switch (ops.flags) { - 0b00 => { - const imm: u64 = if (ops.reg1.bitSize() == 64) blk: { - const payload = emit.mir.instructions.items(.data)[inst].payload; - const imm = emit.mir.extraData(Mir.Imm64, payload).data; - break :blk imm.decode(); - } else emit.mir.instructions.items(.data)[inst].imm; - return emit.encode(.mov, .{ - .op1 = .{ .reg = ops.reg1 }, - .op2 = .{ .imm = Immediate.u(imm) }, - }); - }, - 0b01 => { - if (ops.reg1 == .none) { - const imm: u64 = if (ops.reg2.bitSize() == 64) blk: { - const payload = emit.mir.instructions.items(.data)[inst].payload; - const imm = emit.mir.extraData(Mir.Imm64, payload).data; - break :blk imm.decode(); - } else emit.mir.instructions.items(.data)[inst].imm; - return emit.encode(.mov, .{ - .op1 = .{ .mem = Memory.moffs(ops.reg2, imm) }, - .op2 = .{ .reg = .rax }, - }); - } - const imm: u64 = if (ops.reg1.bitSize() == 64) blk: { - const payload = emit.mir.instructions.items(.data)[inst].payload; - const imm = emit.mir.extraData(Mir.Imm64, payload).data; - break :blk imm.decode(); - } else emit.mir.instructions.items(.data)[inst].imm; - return emit.encode(.mov, .{ - .op1 = .{ .reg = .rax }, - .op2 = .{ .mem = Memory.moffs(ops.reg1, imm) }, - }); - }, - else => return emit.fail("TODO unused movabs variant", .{}), - } -} - -fn mirFisttp(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { - const tag = emit.mir.instructions.items(.tag)[inst]; - assert(tag == .fisttp); - const ops = emit.mir.instructions.items(.ops)[inst].decode(); - const ptr_size: Memory.PtrSize = switch (ops.flags) { - 0b00 => .word, - 0b01 => .dword, - 0b10 => .qword, - else => unreachable, - }; - return emit.encode(.fisttp, .{ - .op1 = .{ .mem = Memory.sib(ptr_size, .{ - .base = ops.reg1, - .disp = emit.mir.instructions.items(.data)[inst].disp, - }) }, - }); -} - -fn mirFld(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { - const tag = emit.mir.instructions.items(.tag)[inst]; - assert(tag == .fld); - const ops = emit.mir.instructions.items(.ops)[inst].decode(); - const ptr_size: Memory.PtrSize = switch (ops.flags) { - 0b01 => .dword, - 0b10 => .qword, - else => unreachable, - }; - return emit.encode(.fld, .{ - .op1 = .{ .mem = Memory.sib(ptr_size, .{ - .base = ops.reg1, - .disp = emit.mir.instructions.items(.data)[inst].disp, - }) }, - }); -} - -fn mirShift(emit: *Emit, mnemonic: Instruction.Mnemonic, inst: Mir.Inst.Index) InnerError!void { - const ops = emit.mir.instructions.items(.ops)[inst].decode(); - switch (ops.flags) { - 0b00 => { - return emit.encode(mnemonic, .{ - .op1 = .{ .reg = ops.reg1 }, - .op2 = .{ .imm = Immediate.u(1) }, - }); - }, - 0b01 => { - return emit.encode(mnemonic, .{ - .op1 = .{ .reg = ops.reg1 }, - .op2 = .{ .reg = .cl }, - }); - }, - 0b10 => { - const imm = @truncate(u8, emit.mir.instructions.items(.data)[inst].imm); - return emit.encode(mnemonic, .{ - .op1 = .{ .reg = ops.reg1 }, - .op2 = .{ .imm = Immediate.u(imm) }, - }); - }, - 0b11 => { - return emit.fail("TODO unused variant: SHIFT reg1, 0b11", .{}); - }, - } -} - -fn mirMulDiv(emit: *Emit, mnemonic: Instruction.Mnemonic, inst: Mir.Inst.Index) InnerError!void { - const ops = emit.mir.instructions.items(.ops)[inst].decode(); - if (ops.reg1 != .none) { - assert(ops.reg2 == .none); - return emit.encode(mnemonic, .{ - .op1 = .{ .reg = ops.reg1 }, - }); - } - assert(ops.reg2 != .none); - const disp = emit.mir.instructions.items(.data)[inst].disp; - const ptr_size: Memory.PtrSize = switch (ops.flags) { - 0b00 => .byte, - 0b01 => .word, - 0b10 => .dword, - 0b11 => .qword, - }; - return emit.encode(mnemonic, .{ - .op1 = .{ .mem = Memory.sib(ptr_size, .{ - .base = ops.reg2, - .disp = disp, - }) }, - }); -} - -fn mirIMulComplex(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { - const tag = emit.mir.instructions.items(.tag)[inst]; - assert(tag == .imul_complex); - const ops = emit.mir.instructions.items(.ops)[inst].decode(); - switch (ops.flags) { - 0b00 => { - return emit.encode(.imul, .{ - .op1 = .{ .reg = ops.reg1 }, - .op2 = .{ .reg = ops.reg2 }, - }); - }, - 0b01 => { - const disp = emit.mir.instructions.items(.data)[inst].disp; - const src_reg: ?Register = if (ops.reg2 != .none) ops.reg2 else null; - return emit.encode(.imul, .{ - .op1 = .{ .reg = ops.reg1 }, - .op2 = .{ .mem = Memory.sib(.qword, .{ - .base = src_reg, - .disp = disp, - }) }, - }); - }, - 0b10 => { - const imm = emit.mir.instructions.items(.data)[inst].imm; - return emit.encode(.imul, .{ - .op1 = .{ .reg = ops.reg1 }, - .op2 = .{ .reg = ops.reg2 }, - .op3 = .{ .imm = Immediate.u(imm) }, - }); - }, - 0b11 => { - const payload = emit.mir.instructions.items(.data)[inst].payload; - const imm_pair = emit.mir.extraData(Mir.ImmPair, payload).data; - return emit.encode(.imul, .{ - .op1 = .{ .reg = ops.reg1 }, - .op2 = .{ .mem = Memory.sib(.qword, .{ - .base = ops.reg2, - .disp = imm_pair.dest_off, - }) }, - .op3 = .{ .imm = Immediate.u(imm_pair.operand) }, - }); - }, - } -} - -fn mirCwd(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { - const ops = emit.mir.instructions.items(.ops)[inst].decode(); - const mnemonic: Instruction.Mnemonic = switch (ops.flags) { - 0b00 => .cbw, - 0b01 => .cwd, - 0b10 => .cdq, - 0b11 => .cqo, - }; - return emit.encode(mnemonic, .{}); -} - -fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { - const tag = emit.mir.instructions.items(.tag)[inst]; - assert(tag == .lea); - const ops = emit.mir.instructions.items(.ops)[inst].decode(); - switch (ops.flags) { - 0b00 => { - const disp = emit.mir.instructions.items(.data)[inst].disp; - const src_reg: ?Register = if (ops.reg2 != .none) ops.reg2 else null; - return emit.encode(.lea, .{ - .op1 = .{ .reg = ops.reg1 }, - .op2 = .{ .mem = Memory.sib(Memory.PtrSize.fromBitSize(ops.reg1.bitSize()), .{ - .base = src_reg, - .disp = disp, - }) }, - }); - }, - 0b01 => { - const start_offset = emit.code.items.len; - try emit.encode(.lea, .{ - .op1 = .{ .reg = ops.reg1 }, - .op2 = .{ .mem = Memory.rip(Memory.PtrSize.fromBitSize(ops.reg1.bitSize()), 0) }, - }); - const end_offset = emit.code.items.len; - // Backpatch the displacement - const payload = emit.mir.instructions.items(.data)[inst].payload; - const imm = emit.mir.extraData(Mir.Imm64, payload).data.decode(); - const disp = @intCast(i32, @intCast(i64, imm) - @intCast(i64, end_offset - start_offset)); - mem.writeIntLittle(i32, emit.code.items[end_offset - 4 ..][0..4], disp); - }, - 0b10 => { - const payload = emit.mir.instructions.items(.data)[inst].payload; - const index_reg_disp = emit.mir.extraData(Mir.IndexRegisterDisp, payload).data.decode(); - const src_reg: ?Register = if (ops.reg2 != .none) ops.reg2 else null; - const scale_index = Memory.ScaleIndex{ - .scale = 1, - .index = index_reg_disp.index, - }; - return emit.encode(.lea, .{ - .op1 = .{ .reg = ops.reg1 }, - .op2 = .{ .mem = Memory.sib(Memory.PtrSize.fromBitSize(ops.reg1.bitSize()), .{ - .base = src_reg, - .scale_index = scale_index, - .disp = index_reg_disp.disp, - }) }, - }); - }, - 0b11 => return emit.fail("TODO unused LEA variant 0b11", .{}), - } -} - -fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { - const tag = emit.mir.instructions.items(.tag)[inst]; - assert(tag == .lea_pic); - const ops = emit.mir.instructions.items(.ops)[inst].decode(); - const relocation = emit.mir.instructions.items(.data)[inst].relocation; - - switch (ops.flags) { - 0b00, 0b01, 0b10 => {}, - else => return emit.fail("TODO unused LEA PIC variant 0b11", .{}), - } - - try emit.encode(.lea, .{ - .op1 = .{ .reg = ops.reg1 }, - .op2 = .{ .mem = Memory.rip(Memory.PtrSize.fromBitSize(ops.reg1.bitSize()), 0) }, - }); - - const end_offset = emit.code.items.len; - - if (emit.bin_file.cast(link.File.MachO)) |macho_file| { - const reloc_type = switch (ops.flags) { - 0b00 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_GOT), - 0b01 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_SIGNED), - else => unreachable, - }; - const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?; - try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{ - .type = reloc_type, - .target = .{ .sym_index = relocation.sym_index, .file = null }, - .offset = @intCast(u32, end_offset - 4), - .addend = 0, - .pcrel = true, - .length = 2, - }); - } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| { - const atom_index = coff_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?; - try link.File.Coff.Atom.addRelocation(coff_file, atom_index, .{ - .type = switch (ops.flags) { - 0b00 => .got, - 0b01 => .direct, - 0b10 => .import, - else => unreachable, - }, - .target = switch (ops.flags) { - 0b00, 0b01 => .{ .sym_index = relocation.sym_index, .file = null }, - 0b10 => coff_file.getGlobalByIndex(relocation.sym_index), - else => unreachable, - }, - .offset = @intCast(u32, end_offset - 4), - .addend = 0, - .pcrel = true, - .length = 2, - }); - } else { - return emit.fail("TODO implement lea reg, [rip + reloc] for linking backends different than MachO", .{}); - } -} - -// SSE/AVX instructions - -fn mirMovFloat(emit: *Emit, mnemonic: Instruction.Mnemonic, inst: Mir.Inst.Index) InnerError!void { - const ops = emit.mir.instructions.items(.ops)[inst].decode(); - switch (ops.flags) { - 0b00 => { - const disp = emit.mir.instructions.items(.data)[inst].disp; - return emit.encode(mnemonic, .{ - .op1 = .{ .reg = ops.reg1 }, - .op2 = .{ .mem = Memory.sib(Memory.PtrSize.fromBitSize(ops.reg2.bitSize()), .{ - .base = ops.reg2, - .disp = disp, - }) }, - }); - }, - 0b01 => { - const disp = emit.mir.instructions.items(.data)[inst].disp; - return emit.encode(mnemonic, .{ - .op1 = .{ .mem = Memory.sib(Memory.PtrSize.fromBitSize(ops.reg1.bitSize()), .{ - .base = ops.reg1, - .disp = disp, - }) }, - .op2 = .{ .reg = ops.reg2 }, - }); - }, - 0b10 => { - return emit.encode(mnemonic, .{ - .op1 = .{ .reg = ops.reg1 }, - .op2 = .{ .reg = ops.reg2 }, - }); - }, - else => return emit.fail("TODO unused variant 0b{b} for {}", .{ ops.flags, mnemonic }), - } -} - -fn mirAddFloat(emit: *Emit, mnemonic: Instruction.Mnemonic, inst: Mir.Inst.Index) InnerError!void { - const ops = emit.mir.instructions.items(.ops)[inst].decode(); - switch (ops.flags) { - 0b00 => { - return emit.encode(mnemonic, .{ - .op1 = .{ .reg = ops.reg1 }, - .op2 = .{ .reg = ops.reg2 }, - }); - }, - else => return emit.fail("TODO unused variant 0b{b} for {}", .{ ops.flags, mnemonic }), - } -} - -fn mirCmpFloat(emit: *Emit, mnemonic: Instruction.Mnemonic, inst: Mir.Inst.Index) InnerError!void { - const ops = emit.mir.instructions.items(.ops)[inst].decode(); - switch (ops.flags) { - 0b00 => { - return emit.encode(mnemonic, .{ - .op1 = .{ .reg = ops.reg1 }, - .op2 = .{ .reg = ops.reg2 }, - }); - }, - else => return emit.fail("TODO unused variant 0b{b} for {}", .{ ops.flags, mnemonic }), - } -} - -// Pseudo-instructions - -fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { - const tag = emit.mir.instructions.items(.tag)[inst]; - assert(tag == .call_extern); - const relocation = emit.mir.instructions.items(.data)[inst].relocation; - - const offset = blk: { - // callq - try emit.encode(.call, .{ - .op1 = .{ .imm = Immediate.s(0) }, - }); - break :blk @intCast(u32, emit.code.items.len) - 4; - }; - - if (emit.bin_file.cast(link.File.MachO)) |macho_file| { - // Add relocation to the decl. - const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?; - const target = macho_file.getGlobalByIndex(relocation.sym_index); - try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{ - .type = @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_BRANCH), - .target = target, - .offset = offset, - .addend = 0, - .pcrel = true, - .length = 2, - }); - } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| { - // Add relocation to the decl. - const atom_index = coff_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?; - const target = coff_file.getGlobalByIndex(relocation.sym_index); - try link.File.Coff.Atom.addRelocation(coff_file, atom_index, .{ - .type = .direct, - .target = target, - .offset = offset, - .addend = 0, - .pcrel = true, - .length = 2, - }); - } else { - return emit.fail("TODO implement call_extern for linking backends different than MachO and COFF", .{}); - } -} +// if (emit.bin_file.cast(link.File.MachO)) |macho_file| { +// // Add relocation to the decl. +// const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?; +// const target = macho_file.getGlobalByIndex(relocation.sym_index); +// try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{ +// .type = @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_BRANCH), +// .target = target, +// .offset = offset, +// .addend = 0, +// .pcrel = true, +// .length = 2, +// }); +// } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| { +// // Add relocation to the decl. +// const atom_index = coff_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?; +// const target = coff_file.getGlobalByIndex(relocation.sym_index); +// try link.File.Coff.Atom.addRelocation(coff_file, atom_index, .{ +// .type = .direct, +// .target = target, +// .offset = offset, +// .addend = 0, +// .pcrel = true, +// .length = 2, +// }); +// } else { +// return emit.fail("TODO implement call_extern for linking backends different than MachO and COFF", .{}); +// } +// } fn mirDbgLine(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { - const tag = emit.mir.instructions.items(.tag)[inst]; - assert(tag == .dbg_line); const payload = emit.mir.instructions.items(.data)[inst].payload; const dbg_line_column = emit.mir.extraData(Mir.DbgLineColumn, payload).data; log.debug("mirDbgLine", .{}); @@ -1230,8 +635,7 @@ fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) InnerError!void { } fn mirDbgPrologueEnd(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { - const tag = emit.mir.instructions.items(.tag)[inst]; - assert(tag == .dbg_prologue_end); + _ = inst; switch (emit.debug_output) { .dwarf => |dw| { try dw.setPrologueEnd(); @@ -1247,8 +651,7 @@ fn mirDbgPrologueEnd(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { } fn mirDbgEpilogueBegin(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { - const tag = emit.mir.instructions.items(.tag)[inst]; - assert(tag == .dbg_epilogue_begin); + _ = inst; switch (emit.debug_output) { .dwarf => |dw| { try dw.setEpilogueBegin(); diff --git a/src/arch/x86_64/Mir.zig b/src/arch/x86_64/Mir.zig index 43c24216a8..5f4ed05deb 100644 --- a/src/arch/x86_64/Mir.zig +++ b/src/arch/x86_64/Mir.zig @@ -12,6 +12,8 @@ const builtin = @import("builtin"); const assert = std.debug.assert; const bits = @import("bits.zig"); +const encoder = @import("encoder.zig"); + const Air = @import("../../Air.zig"); const CodeGen = @import("CodeGen.zig"); const IntegerBitSet = std.bit_set.IntegerBitSet; @@ -21,421 +23,239 @@ instructions: std.MultiArrayList(Inst).Slice, /// The meaning of this data is determined by `Inst.Tag` value. extra: []const u32, +pub const Mnemonic = encoder.Instruction.Mnemonic; +pub const Operand = encoder.Instruction.Operand; + pub const Inst = struct { tag: Tag, ops: Ops, - /// The meaning of this depends on `tag` and `ops`. data: Data, - pub const Tag = enum(u16) { - /// ops flags: form: - /// 0b00 reg1, reg2 - /// 0b00 reg1, imm32 - /// 0b01 reg1, [reg2 + imm32] - /// 0b01 reg1, [ds:imm32] - /// 0b10 [reg1 + imm32], reg2 - /// 0b11 reg1, imm_s - /// Notes: - /// * If reg2 is `none` then it means Data field `imm` is used as the immediate. - /// * When two imm32 values are required, Data field `payload` points at `ImmPair`. - adc, - - /// ops flags: form: - /// 0b00 byte ptr [reg1 + imm32], imm8 - /// 0b01 word ptr [reg1 + imm32], imm16 - /// 0b10 dword ptr [reg1 + imm32], imm32 - /// 0b11 qword ptr [reg1 + imm32], imm32 (sign-extended to imm64) - /// Notes: - /// * Uses `ImmPair` as payload - adc_mem_imm, - - /// form: reg1, [reg2 + scale*index + imm32] - /// ops flags scale - /// 0b00 1 - /// 0b01 2 - /// 0b10 4 - /// 0b11 8 - /// Notes: - /// * Uses `IndexRegisterDisp` as payload - adc_scale_src, - - /// form: [reg1 + scale*index + imm32], reg2 - /// ops flags scale - /// 0b00 1 - /// 0b01 2 - /// 0b10 4 - /// 0b11 8 - /// Notes: - /// * Uses `IndexRegisterDisp` payload. - adc_scale_dst, - - /// form: [reg1 + scale*rax + imm32], imm32 - /// ops flags scale - /// 0b00 1 - /// 0b01 2 - /// 0b10 4 - /// 0b11 8 - /// Notes: - /// * Uses `IndexRegisterDispImm` payload. - adc_scale_imm, - - /// ops flags: form: - /// 0b00 byte ptr [reg1 + index + imm32], imm8 - /// 0b01 word ptr [reg1 + index + imm32], imm16 - /// 0b10 dword ptr [reg1 + index + imm32], imm32 - /// 0b11 qword ptr [reg1 + index + imm32], imm32 (sign-extended to imm64) - /// Notes: - /// * Uses `IndexRegisterDispImm` payload. - adc_mem_index_imm, - - // The following instructions all have the same encoding as `adc`. - - add, - add_mem_imm, - add_scale_src, - add_scale_dst, - add_scale_imm, - add_mem_index_imm, - sub, - sub_mem_imm, - sub_scale_src, - sub_scale_dst, - sub_scale_imm, - sub_mem_index_imm, - xor, - xor_mem_imm, - xor_scale_src, - xor_scale_dst, - xor_scale_imm, - xor_mem_index_imm, - @"and", - and_mem_imm, - and_scale_src, - and_scale_dst, - and_scale_imm, - and_mem_index_imm, - @"or", - or_mem_imm, - or_scale_src, - or_scale_dst, - or_scale_imm, - or_mem_index_imm, - rol, - rol_mem_imm, - rol_scale_src, - rol_scale_dst, - rol_scale_imm, - rol_mem_index_imm, - ror, - ror_mem_imm, - ror_scale_src, - ror_scale_dst, - ror_scale_imm, - ror_mem_index_imm, - rcl, - rcl_mem_imm, - rcl_scale_src, - rcl_scale_dst, - rcl_scale_imm, - rcl_mem_index_imm, - rcr, - rcr_mem_imm, - rcr_scale_src, - rcr_scale_dst, - rcr_scale_imm, - rcr_mem_index_imm, - sbb, - sbb_mem_imm, - sbb_scale_src, - sbb_scale_dst, - sbb_scale_imm, - sbb_mem_index_imm, - cmp, - cmp_mem_imm, - cmp_scale_src, - cmp_scale_dst, - cmp_scale_imm, - cmp_mem_index_imm, - mov, - mov_mem_imm, - mov_scale_src, - mov_scale_dst, - mov_scale_imm, - mov_mem_index_imm, - - /// ops flags: form: - /// 0b00 reg1, reg2, - /// 0b01 reg1, byte ptr [reg2 + imm32] - /// 0b10 reg1, word ptr [reg2 + imm32] - /// 0b11 reg1, dword ptr [reg2 + imm32] - mov_sign_extend, - - /// ops flags: form: - /// 0b00 reg1, reg2 - /// 0b01 reg1, byte ptr [reg2 + imm32] - /// 0b10 reg1, word ptr [reg2 + imm32] - mov_zero_extend, - - /// ops flags: form: - /// 0b00 reg1, [reg2 + imm32] - /// 0b00 reg1, [ds:imm32] - /// 0b01 reg1, [rip + imm32] - /// 0b10 reg1, [reg2 + index + imm32] - /// Notes: - /// * 0b10 uses `IndexRegisterDisp` payload - lea, - - /// ops flags: form: - /// 0b00 reg1, [rip + reloc] // via GOT PIC - /// 0b01 reg1, [rip + reloc] // direct load PIC - /// 0b10 reg1, [rip + reloc] // via imports table PIC - /// Notes: - /// * `Data` contains `relocation` - lea_pic, - - /// ops flags: form: - /// 0b00 reg1, 1 - /// 0b01 reg1, .cl - /// 0b10 reg1, imm8 - /// Notes: - /// * If flags == 0b10, uses `imm`. - shl, - shl_mem_imm, - shl_scale_src, - shl_scale_dst, - shl_scale_imm, - shl_mem_index_imm, - sal, - sal_mem_imm, - sal_scale_src, - sal_scale_dst, - sal_scale_imm, - sal_mem_index_imm, - shr, - shr_mem_imm, - shr_scale_src, - shr_scale_dst, - shr_scale_imm, - shr_mem_index_imm, - sar, - sar_mem_imm, - sar_scale_src, - sar_scale_dst, - sar_scale_imm, - sar_mem_index_imm, - - /// ops flags: form: - /// 0b00 reg1 - /// 0b00 byte ptr [reg2 + imm32] - /// 0b01 word ptr [reg2 + imm32] - /// 0b10 dword ptr [reg2 + imm32] - /// 0b11 qword ptr [reg2 + imm32] - imul, - idiv, - mul, - div, - - /// ops flags: form: - /// 0b00 AX <- AL - /// 0b01 DX:AX <- AX - /// 0b10 EDX:EAX <- EAX - /// 0b11 RDX:RAX <- RAX - cwd, - - /// ops flags: form: - /// 0b00 reg1, reg2 - /// 0b01 reg1, [reg2 + imm32] - /// 0b01 reg1, [imm32] if reg2 is none - /// 0b10 reg1, reg2, imm32 - /// 0b11 reg1, [reg2 + imm32], imm32 - imul_complex, - - /// ops flags: form: - /// 0b00 reg1, imm64 - /// 0b01 rax, moffs64 - /// Notes: - /// * If reg1 is 64-bit, the immediate is 64-bit and stored - /// within extra data `Imm64`. - /// * For 0b01, reg1 (or reg2) need to be - /// a version of rax. If reg1 == .none, then reg2 == .rax, - /// or vice versa. - movabs, - - /// ops flags: form: - /// 0b00 word ptr [reg1 + imm32] - /// 0b01 dword ptr [reg1 + imm32] - /// 0b10 qword ptr [reg1 + imm32] - /// Notes: - /// * source is always ST(0) - /// * only supports memory operands as destination - fisttp, - - /// ops flags: form: - /// 0b01 dword ptr [reg1 + imm32] - /// 0b10 qword ptr [reg1 + imm32] - fld, - - /// ops flags: form: - /// 0b00 inst - /// 0b01 reg1 - /// 0b01 [imm32] if reg1 is none - /// 0b10 [reg1 + imm32] - jmp, - call, - - /// ops flags: - /// unused - /// Notes: - /// * uses `inst_cc` in Data. - cond_jmp, - - /// ops flags: - /// 0b00 reg1 - /// Notes: - /// * uses condition code (CC) stored as part of data - cond_set_byte, - - /// ops flags: - /// 0b00 reg1, reg2, - /// 0b01 reg1, word ptr [reg2 + imm] - /// 0b10 reg1, dword ptr [reg2 + imm] - /// 0b11 reg1, qword ptr [reg2 + imm] - /// Notes: - /// * uses condition code (CC) stored as part of data - cond_mov, - - /// ops flags: form: - /// 0b00 reg1 - /// 0b01 [reg1 + imm32] - /// 0b10 imm32 - /// Notes: - /// * If 0b10 is specified and the tag is push, pushes immediate onto the stack - /// using the mnemonic PUSH imm32. - push, - pop, - - /// ops flags: form: - /// 0b00 retf imm16 - /// 0b01 retf - /// 0b10 retn imm16 - /// 0b11 retn - ret, - - /// Fast system call - syscall, - - /// ops flags: form: - /// 0b00 reg1, imm32 if reg2 == .none - /// 0b00 reg1, reg2 - /// TODO handle more cases - @"test", - - /// Undefined Instruction - ud, - - /// Breakpoint form: - /// 0b00 int3 - interrupt, - - /// Nop - nop, - - /// SSE/AVX instructions - /// ops flags: form: - /// 0b00 reg1, qword ptr [reg2 + imm32] - /// 0b01 qword ptr [reg1 + imm32], reg2 - /// 0b10 reg1, reg2 - mov_f64, - mov_f32, - - /// ops flags: form: - /// 0b00 reg1, reg2 - add_f64, - add_f32, - - /// ops flags: form: - /// 0b00 reg1, reg2 - cmp_f64, - cmp_f32, - - /// Pseudo-instructions - /// call extern function - /// Notes: - /// * target of the call is stored as `relocation` in `Data` union. - call_extern, - - /// end of prologue - dbg_prologue_end, - - /// start of epilogue - dbg_epilogue_begin, - - /// update debug line - dbg_line, - - /// push registers - /// Uses `payload` field with `SaveRegisterList` as payload. - push_regs, - - /// pop registers - /// Uses `payload` field with `SaveRegisterList` as payload. - pop_regs, - }; - /// The position of an MIR instruction within the `Mir` instructions array. pub const Index = u32; - pub const Ops = packed struct { - reg1: u7, - reg2: u7, - flags: u2, + pub const Tag = enum(u8) { + /// Add with carry + adc, + /// Add + add, + /// Logical and + @"and", + /// Call + call, + /// Convert byte to word + cbw, + /// Convert word to doubleword + cwde, + /// Convert doubleword to quadword + cdqe, + /// Convert word to doubleword + cwd, + /// Convert doubleword to quadword + cdq, + /// Convert doubleword to quadword + cqo, + /// Logical compare + cmp, + /// Conditional move + cmovcc, + /// Unsigned division + div, + /// Store integer with truncation + fisttp, + /// Load floating-point value + fld, + /// Signed division + idiv, + /// Signed multiplication + imul, + /// + int3, + /// Conditional jump + jcc, + /// Jump + jmp, + /// Load effective address + lea, + /// Move + mov, + /// Move with sign extension + movsx, + /// Move with zero extension + movzx, + /// Multiply + mul, + /// No-op + nop, + /// Logical or + @"or", + /// Pop + pop, + /// Push + push, + /// Return + ret, + /// Arithmetic shift left + sal, + /// Arithmetic shift right + sar, + /// Integer subtraction with borrow + sbb, + /// Set byte on condition + setcc, + /// Logical shift left + shl, + /// Logical shift right + shr, + /// Subtract + sub, + /// Syscall + syscall, + /// Test condition + @"test", + /// Undefined instruction + ud2, + /// Logical exclusive-or + xor, - pub fn encode(vals: struct { - reg1: Register = .none, - reg2: Register = .none, - flags: u2 = 0b00, - }) Ops { - return .{ - .reg1 = @enumToInt(vals.reg1), - .reg2 = @enumToInt(vals.reg2), - .flags = vals.flags, - }; - } + /// Add single precision floating point + addss, + /// Compare scalar single-precision floating-point values + cmpss, + /// Move scalar single-precision floating-point value + movss, + /// Unordered compare scalar single-precision floating-point values + ucomiss, + /// Add double precision floating point + addsd, + /// Compare scalar double-precision floating-point values + cmpsd, + /// Move scalar double-precision floating-point value + movsd, + /// Unordered compare scalar double-precision floating-point values + ucomisd, - pub fn decode(ops: Ops) struct { - reg1: Register, - reg2: Register, - flags: u2, - } { - return .{ - .reg1 = @intToEnum(Register, ops.reg1), - .reg2 = @intToEnum(Register, ops.reg2), - .flags = ops.flags, - }; - } + /// End of prologue + dbg_prologue_end, + /// Start of epilogue + dbg_epilogue_begin, + /// Update debug line + /// Uses `payload` payload with data of type `DbgLineColumn`. + dbg_line, + /// Push registers + /// Uses `payload` payload with data of type `SaveRegisterList`. + push_regs, + /// Pop registers + /// Uses `payload` payload with data of type `SaveRegisterList`. + pop_regs, + }; + + pub const Ops = enum(u8) { + /// No data associated with this instruction (only mnemonic is used). + none, + /// Single register operand. + /// Uses `r` payload. + r, + /// Register, register operands. + /// Uses `rr` payload. + rr, + /// Register, register, register operands. + /// Uses `rrr` payload. + rrr, + /// Register, immediate (sign-extended) operands. + /// Uses `ri_s` payload. + ri_s, + /// Register, immediate (unsigned) operands. + /// Uses `ri_u` payload. + ri_u, + /// Register, 64-bit unsigned immediate operands. + /// Uses `rx` payload with payload type `Imm64`. + ri64, + /// Immediate (sign-extended) operand. + /// Uses `imm_s` payload. + imm_s, + /// Immediate (unsigned) operand. + /// Uses `imm_u` payload. + imm_u, + /// Relative displacement operand. + /// Uses `rel` payload. + rel, + /// Register, memory operands. + /// Uses `rx` payload. + rm, + /// Register, memory, immediate (unsigned) operands + /// Uses `rx` payload. + rmi_u, + /// Register, memory, immediate (sign-extended) operands + /// Uses `rx` payload. + rmi_s, + /// Memory, immediate (unsigned) operands. + /// Uses `payload` payload. + mi_u, + /// Memory, immediate (sign-extend) operands. + /// Uses `payload` payload. + mi_s, + /// Memory, register operands. + /// Uses `payload` payload. + mr, + /// Lea into register with linker relocation. + /// Uses `payload` payload with data of type `LeaRegisterReloc`. + lea_r_reloc, + /// References another Mir instruction directly. + /// Uses `inst` payload. + inst, + /// References another Mir instruction directly with condition code (CC). + /// Uses `inst_cc` payload. + inst_cc, + /// Uses `payload` payload with data of type `MemoryConditionCode`. + m_cc, + /// Uses `rx` payload with extra data of type `MemoryConditionCode`. + rm_cc, + /// Uses `reloc` payload. + reloc, }; - /// All instructions have a 4-byte payload, which is contained within - /// this union. `Tag` determines which union field is active, as well as - /// how to interpret the data within. pub const Data = union { - /// Another instruction. + /// References another Mir instruction. inst: Index, - /// A 32-bit immediate value. - imm: u32, - /// A 32-bit signed immediate value. - imm_s: i32, - /// A 32-bit signed displacement value. - disp: i32, - /// A condition code for use with EFLAGS register. - cc: bits.Condition, - /// Another instruction with condition code. - /// Used by `cond_jmp`. + /// Another instruction with condition code (CC). + /// Used by `jcc`. inst_cc: struct { /// Another instruction. inst: Index, /// A condition code for use with EFLAGS register. cc: bits.Condition, }, + /// A 32-bit signed immediate value. + imm_s: i32, + /// A 32-bit unsigned immediate value. + imm_u: u32, + /// A 32-bit signed relative offset value. + rel: i32, + r: Register, + rr: struct { + r1: Register, + r2: Register, + }, + rrr: struct { + r1: Register, + r2: Register, + r3: Register, + }, + /// Register, signed immediate. + ri_s: struct { + r1: Register, + imm: i32, + }, + /// Register, unsigned immediate. + ri_u: struct { + r1: Register, + imm: u32, + }, + /// Register, followed by custom payload found in extra. + rx: struct { + r1: Register, + payload: u32, + }, /// Relocation for the linker where: /// * `atom_index` is the index of the source /// * `sym_index` is the index of the target @@ -458,62 +278,19 @@ pub const Inst = struct { } }; -pub const IndexRegisterDisp = struct { - /// Index register to use with SIB-based encoding - index: u32, - - /// Displacement value - disp: i32, - - pub fn encode(index: Register, disp: i32) IndexRegisterDisp { - return .{ - .index = @enumToInt(index), - .disp = disp, - }; - } - - pub fn decode(this: IndexRegisterDisp) struct { - index: Register, - disp: i32, - } { - return .{ - .index = @intToEnum(Register, this.index), - .disp = this.disp, - }; - } -}; - -/// TODO: would it be worth making `IndexRegisterDisp` and `IndexRegisterDispImm` a variable length list -/// instead of having two structs, one a superset of the other one? -pub const IndexRegisterDispImm = struct { - /// Index register to use with SIB-based encoding - index: u32, - - /// Displacement value - disp: i32, - - /// Immediate - imm: u32, - - pub fn encode(index: Register, disp: i32, imm: u32) IndexRegisterDispImm { - return .{ - .index = @enumToInt(index), - .disp = disp, - .imm = imm, - }; - } - - pub fn decode(this: IndexRegisterDispImm) struct { - index: Register, - disp: i32, - imm: u32, - } { - return .{ - .index = @intToEnum(Register, this.index), - .disp = this.disp, - .imm = this.imm, - }; - } +pub const LeaRegisterReloc = struct { + /// Destination register. + reg: Register, + /// Type of the load. + load_type: enum(u2) { + got, + direct, + import, + }, + /// Index of the containing atom. + atom_index: u32, + /// Index into the linker's symbol table. + sym_index: u32, }; /// Used in conjunction with `SaveRegisterList` payload to transfer a list of used registers @@ -557,16 +334,13 @@ pub const RegisterList = struct { }; pub const SaveRegisterList = struct { + /// Base register + base_reg: u32, /// Use `RegisterList` to populate. register_list: u32, stack_end: u32, }; -pub const ImmPair = struct { - dest_off: i32, - operand: u32, -}; - pub const Imm64 = struct { msb: u32, lsb: u32, diff --git a/src/arch/x86_64/encoder.zig b/src/arch/x86_64/encoder.zig index 925e3fe181..690f777c28 100644 --- a/src/arch/x86_64/encoder.zig +++ b/src/arch/x86_64/encoder.zig @@ -4,10 +4,6 @@ const math = std.math; const bits = @import("bits.zig"); const Encoding = @import("Encoding.zig"); -const Immediate = bits.Immediate; -const Memory = bits.Memory; -const Moffs = bits.Moffs; -const PtrSize = bits.PtrSize; const Register = bits.Register; pub const Instruction = struct { @@ -25,6 +21,9 @@ pub const Instruction = struct { mem: Memory, imm: Immediate, + pub const Memory = bits.Memory; + pub const Immediate = bits.Immediate; + /// Returns the bitsize of the operand. pub fn bitSize(op: Operand) u64 { return switch (op) { @@ -296,7 +295,7 @@ pub const Instruction = struct { try encoder.opcode_1byte(prefix); } - fn encodeMemory(encoding: Encoding, mem: Memory, operand: Operand, encoder: anytype) !void { + fn encodeMemory(encoding: Encoding, mem: Operand.Memory, operand: Operand, encoder: anytype) !void { const operand_enc = switch (operand) { .reg => |reg| reg.lowEnc(), .none => encoding.modRmExt(), @@ -379,7 +378,7 @@ pub const Instruction = struct { } } - fn encodeImm(imm: Immediate, kind: Encoding.Op, encoder: anytype) !void { + fn encodeImm(imm: Operand.Immediate, kind: Encoding.Op, encoder: anytype) !void { const raw = imm.asUnsigned(kind.bitSize()); switch (kind.bitSize()) { 8 => try encoder.imm8(@intCast(u8, raw)), From aa8fda799e64c02d44fe80d1297b5ec8ae6b7677 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 9 Mar 2023 09:02:06 +0100 Subject: [PATCH 086/294] x86_64: split up assemble() into more declarative single-purpose helpers --- src/arch/x86_64/CodeGen.zig | 126 ++++++++++++++++++------------------ src/arch/x86_64/encoder.zig | 9 ++- 2 files changed, 66 insertions(+), 69 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index be8d07a2f6..6dacbadd8f 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -33,9 +33,11 @@ const errUnionPayloadOffset = codegen.errUnionPayloadOffset; const errUnionErrorOffset = codegen.errUnionErrorOffset; const Condition = bits.Condition; +const Immediate = bits.Immediate; +const Memory = bits.Memory; +const Register = bits.Register; const RegisterManager = abi.RegisterManager; const RegisterLock = RegisterManager.RegisterLock; -const Register = bits.Register; const gp = abi.RegisterClass.gp; const sse = abi.RegisterClass.sse; @@ -398,47 +400,58 @@ fn addExtraAssumeCapacity(self: *Self, extra: anytype) u32 { return result; } -fn assemble(self: *Self, tag: Mir.Inst.Tag, args: struct { - op1: Mir.Operand = .none, - op2: Mir.Operand = .none, - op3: Mir.Operand = .none, - op4: Mir.Operand = .none, -}) !void { - const ops: Mir.Inst.Ops = blk: { - if (args.op1 == .none and args.op2 == .none and args.op3 == .none and args.op4 == .none) - break :blk .none; +fn asmNone(self: *Self, tag: Mir.Inst.Tag) !void { + _ = try self.addInst(.{ + .tag = tag, + .ops = .none, + .data = undefined, + }); +} - if (args.op1 == .reg and args.op2 == .reg) - break :blk .rr; - if (args.op1 == .reg and args.op2 == .imm) switch (args.op2.imm) { - .signed => break :blk .ri_s, - .unsigned => break :blk .ri_u, - }; - if (args.op1 == .reg) - break :blk .r; - if (args.op1 == .imm) switch (args.op1.imm) { - .signed => break :blk .imm_s, - .unsigned => break :blk .imm_u, // TODO 64bits - }; +fn asmRegister(self: *Self, tag: Mir.Inst.Tag, reg: Register) !void { + _ = try self.addInst(.{ + .tag = tag, + .ops = .r, + .data = .{ .r = reg }, + }); +} - unreachable; - }; +fn asmImmediate(self: *Self, tag: Mir.Inst.Tag, imm: Immediate) !void { + // TODO imm64 + const ops: Mir.Inst.Ops = if (imm == .signed) .imm_s else .imm_u; const data: Mir.Inst.Data = switch (ops) { - .none => undefined, - .imm_s => .{ .imm_s = args.op1.imm.signed }, - .imm_u => .{ .imm_u = @intCast(u32, args.op1.imm.unsigned) }, - .r => .{ .r = args.op1.reg }, - .rr => .{ .rr = .{ - .r1 = args.op1.reg, - .r2 = args.op2.reg, + .imm_s => .{ .imm_s = imm.signed }, + .imm_u => .{ .imm_u = @intCast(u32, imm.unsigned) }, + else => unreachable, + }; + _ = try self.addInst(.{ + .tag = tag, + .ops = ops, + .data = data, + }); +} + +fn asmRegisterRegister(self: *Self, tag: Mir.Inst.Tag, reg1: Register, reg2: Register) !void { + _ = try self.addInst(.{ + .tag = tag, + .ops = .rr, + .data = .{ .rr = .{ + .r1 = reg1, + .r2 = reg2, } }, + }); +} + +fn asmRegisterImmediate(self: *Self, tag: Mir.Inst.Tag, reg: Register, imm: Immediate) !void { + const ops: Mir.Inst.Ops = if (imm == .signed) .ri_s else .ri_u; + const data: Mir.Inst.Data = switch (ops) { .ri_s => .{ .ri_s = .{ - .r1 = args.op1.reg, - .imm = args.op2.imm.signed, + .r1 = reg, + .imm = imm.signed, } }, .ri_u => .{ .ri_u = .{ - .r1 = args.op1.reg, - .imm = @intCast(u32, args.op2.imm.unsigned), + .r1 = reg, + .imm = @intCast(u32, imm.unsigned), } }, else => unreachable, }; @@ -452,13 +465,8 @@ fn assemble(self: *Self, tag: Mir.Inst.Tag, args: struct { fn gen(self: *Self) InnerError!void { const cc = self.fn_type.fnCallingConvention(); if (cc != .Naked) { - try self.assemble(.push, .{ - .op1 = .{ .reg = .rbp }, - }); - try self.assemble(.mov, .{ - .op1 = .{ .reg = .rbp }, - .op2 = .{ .reg = .rsp }, - }); + try self.asmRegister(.push, .rbp); + try self.asmRegisterRegister(.mov, .rbp, .rsp); // We want to subtract the aligned stack frame size from rsp here, but we don't // yet know how big it will be, so we leave room for a 4-byte stack size. @@ -541,8 +549,8 @@ fn gen(self: *Self) InnerError!void { .data = undefined, }); - try self.assemble(.pop, .{ .op1 = .{ .reg = .rbp } }); - try self.assemble(.ret, .{}); + try self.asmRegister(.pop, .rbp); + try self.asmNone(.ret); // Adjust the stack if (self.max_end_stack > math.maxInt(i32)) { @@ -5313,23 +5321,19 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { var iter = std.mem.tokenize(u8, asm_source, "\n\r"); while (iter.next()) |ins| { if (mem.eql(u8, ins, "syscall")) { - try self.assemble(.syscall, .{}); + try self.asmNone(.syscall); } else if (mem.indexOf(u8, ins, "push")) |_| { const arg = ins[4..]; if (mem.indexOf(u8, arg, "$")) |l| { const n = std.fmt.parseInt(u8, ins[4 + l + 1 ..], 10) catch { return self.fail("TODO implement more inline asm int parsing", .{}); }; - try self.assemble(.push, .{ - .op1 = .{ .imm = Mir.Operand.Immediate.u(n) }, - }); + try self.asmImmediate(.push, Immediate.u(n)); } else if (mem.indexOf(u8, arg, "%%")) |l| { const reg_name = ins[4 + l + 2 ..]; const reg = parseRegName(reg_name) orelse return self.fail("unrecognized register: '{s}'", .{reg_name}); - try self.assemble(.push, .{ - .op1 = .{ .reg = reg }, - }); + try self.asmRegister(.push, reg); } else return self.fail("TODO more push operands", .{}); } else if (mem.indexOf(u8, ins, "pop")) |_| { const arg = ins[3..]; @@ -5337,9 +5341,7 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { const reg_name = ins[3 + l + 2 ..]; const reg = parseRegName(reg_name) orelse return self.fail("unrecognized register: '{s}'", .{reg_name}); - try self.assemble(.pop, .{ - .op1 = .{ .reg = reg }, - }); + try self.asmRegister(.pop, reg); } else return self.fail("TODO more pop operands", .{}); } else { return self.fail("TODO implement support for more x86 assembly instructions", .{}); @@ -6119,19 +6121,15 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void // 32-bit moves zero-extend to 64-bit, so xoring the 32-bit // register is the fastest way to zero a register. if (x == 0) { - try self.assemble(.xor, .{ - .op1 = .{ .reg = reg.to32() }, - .op2 = .{ .reg = reg.to32() }, - }); - return; + return self.asmRegisterRegister(.xor, reg.to32(), reg.to32()); } if (x <= math.maxInt(i32)) { // Next best case: if we set the lower four bytes, the upper four will be zeroed. - try self.assemble(.mov, .{ - .op1 = .{ .reg = registerAlias(reg, abi_size) }, - .op2 = .{ .imm = Mir.Operand.Immediate.u(@intCast(u32, x)) }, - }); - return; + return self.asmRegisterImmediate( + .mov, + registerAlias(reg, abi_size), + Immediate.u(@intCast(u32, x)), + ); } // Worst case: we need to load the 64-bit register with the IMM. GNU's assemblers calls // this `movabs`, though this is officially just a different variant of the plain `mov` diff --git a/src/arch/x86_64/encoder.zig b/src/arch/x86_64/encoder.zig index 690f777c28..292b61ee21 100644 --- a/src/arch/x86_64/encoder.zig +++ b/src/arch/x86_64/encoder.zig @@ -4,6 +4,8 @@ const math = std.math; const bits = @import("bits.zig"); const Encoding = @import("Encoding.zig"); +const Immediate = bits.Immediate; +const Memory = bits.Memory; const Register = bits.Register; pub const Instruction = struct { @@ -21,9 +23,6 @@ pub const Instruction = struct { mem: Memory, imm: Immediate, - pub const Memory = bits.Memory; - pub const Immediate = bits.Immediate; - /// Returns the bitsize of the operand. pub fn bitSize(op: Operand) u64 { return switch (op) { @@ -295,7 +294,7 @@ pub const Instruction = struct { try encoder.opcode_1byte(prefix); } - fn encodeMemory(encoding: Encoding, mem: Operand.Memory, operand: Operand, encoder: anytype) !void { + fn encodeMemory(encoding: Encoding, mem: Memory, operand: Operand, encoder: anytype) !void { const operand_enc = switch (operand) { .reg => |reg| reg.lowEnc(), .none => encoding.modRmExt(), @@ -378,7 +377,7 @@ pub const Instruction = struct { } } - fn encodeImm(imm: Operand.Immediate, kind: Encoding.Op, encoder: anytype) !void { + fn encodeImm(imm: Immediate, kind: Encoding.Op, encoder: anytype) !void { const raw = imm.asUnsigned(kind.bitSize()); switch (kind.bitSize()) { 8 => try encoder.imm8(@intCast(u8, raw)), From f61a70e812b0301f4e54e38ff4ce2b041f395e8d Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 9 Mar 2023 09:38:59 +0100 Subject: [PATCH 087/294] x86_64: handle encoding and decoding Imm64 unsigned --- src/arch/x86_64/CodeGen.zig | 37 +++++++++++++++++-------------------- src/arch/x86_64/Emit.zig | 8 +++++++- 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 6dacbadd8f..d6dd63ee57 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -417,7 +417,6 @@ fn asmRegister(self: *Self, tag: Mir.Inst.Tag, reg: Register) !void { } fn asmImmediate(self: *Self, tag: Mir.Inst.Tag, imm: Immediate) !void { - // TODO imm64 const ops: Mir.Inst.Ops = if (imm == .signed) .imm_s else .imm_u; const data: Mir.Inst.Data = switch (ops) { .imm_s => .{ .imm_s = imm.signed }, @@ -443,7 +442,10 @@ fn asmRegisterRegister(self: *Self, tag: Mir.Inst.Tag, reg1: Register, reg2: Reg } fn asmRegisterImmediate(self: *Self, tag: Mir.Inst.Tag, reg: Register, imm: Immediate) !void { - const ops: Mir.Inst.Ops = if (imm == .signed) .ri_s else .ri_u; + const ops: Mir.Inst.Ops = switch (imm) { + .signed => .ri_s, + .unsigned => |x| if (x <= math.maxInt(u32)) .ri_u else .ri64, + }; const data: Mir.Inst.Data = switch (ops) { .ri_s => .{ .ri_s = .{ .r1 = reg, @@ -453,6 +455,10 @@ fn asmRegisterImmediate(self: *Self, tag: Mir.Inst.Tag, reg: Register, imm: Imme .r1 = reg, .imm = @intCast(u32, imm.unsigned), } }, + .ri64 => .{ .rx = .{ + .r1 = reg, + .payload = try self.addExtra(Mir.Imm64.encode(imm.unsigned)), + } }, else => unreachable, }; _ = try self.addInst(.{ @@ -6118,32 +6124,23 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void // }); }, .immediate => |x| { - // 32-bit moves zero-extend to 64-bit, so xoring the 32-bit - // register is the fastest way to zero a register. if (x == 0) { + // 32-bit moves zero-extend to 64-bit, so xoring the 32-bit + // register is the fastest way to zero a register. return self.asmRegisterRegister(.xor, reg.to32(), reg.to32()); } - if (x <= math.maxInt(i32)) { - // Next best case: if we set the lower four bytes, the upper four will be zeroed. + if (ty.isSignedInt() and x <= math.maxInt(i32)) { return self.asmRegisterImmediate( .mov, registerAlias(reg, abi_size), - Immediate.u(@intCast(u32, x)), + Immediate.s(@intCast(i32, @bitCast(i64, x))), ); } - // Worst case: we need to load the 64-bit register with the IMM. GNU's assemblers calls - // this `movabs`, though this is officially just a different variant of the plain `mov` - // instruction. - // - // This encoding is, in fact, the *same* as the one used for 32-bit loads. The only - // difference is that we set REX.W before the instruction, which extends the load to - // 64-bit and uses the full bit-width of the register. - // const payload = try self.addExtra(Mir.Imm64.encode(x)); - // _ = try self.addInst(.{ - // .tag = .movabs, - // .ops = Mir.Inst.Ops.encode(.{ .reg1 = reg.to64() }), - // .data = .{ .payload = payload }, - // }); + return self.asmRegisterImmediate( + .mov, + registerAlias(reg, abi_size), + Immediate.u(x), + ); }, .register => |src_reg| { // If the registers are the same, nothing to do. diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index 15f41a943c..35b8b59846 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -178,9 +178,10 @@ fn mirEncodeGeneric(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerE if (mem.eql(u8, field.name, @tagName(tag))) break @field(Instruction.Mnemonic, field.name); } else unreachable; - var operands = [4]Instruction.Operand{ .none, .none, .none, .none }; const ops = emit.mir.instructions.items(.ops)[inst]; const data = emit.mir.instructions.items(.data)[inst]; + + var operands = [4]Instruction.Operand{ .none, .none, .none, .none }; switch (ops) { .none => {}, .imm_s => operands[0] = .{ .imm = Immediate.s(data.imm_s) }, @@ -198,6 +199,11 @@ fn mirEncodeGeneric(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerE .{ .reg = data.ri_u.r1 }, .{ .imm = Immediate.u(data.ri_u.imm) }, }, + .ri64 => { + operands[0] = .{ .reg = data.rx.r1 }; + const imm64 = emit.mir.extraData(Mir.Imm64, data.rx.payload).data; + operands[1] = .{ .imm = Immediate.u(Mir.Imm64.decode(imm64)) }; + }, else => unreachable, } From 7221cd8ec90b5f206cf0b2979ba165719e5a2f23 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 9 Mar 2023 11:48:23 +0100 Subject: [PATCH 088/294] x86_64: add helpers for CMOVcc and SETcc at the MIR level --- src/arch/x86_64/CodeGen.zig | 91 ++++++++-------------- src/arch/x86_64/Emit.zig | 146 +++++++++++------------------------- src/arch/x86_64/Mir.zig | 22 +++++- 3 files changed, 94 insertions(+), 165 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index d6dd63ee57..829d6b83fd 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -400,6 +400,29 @@ fn addExtraAssumeCapacity(self: *Self, extra: anytype) u32 { return result; } +fn asmSetCCRegister(self: *Self, reg: Register, cc: bits.Condition) !void { + _ = try self.addInst(.{ + .tag = .setcc, + .ops = .r_c, + .data = .{ .r_c = .{ + .r1 = reg, + .cc = cc, + } }, + }); +} + +fn asmCmovCCRegisterRegister(self: *Self, reg1: Register, reg2: Register, cc: bits.Condition) !void { + _ = try self.addInst(.{ + .tag = .cmovcc, + .ops = .rr_c, + .data = .{ .rr_c = .{ + .r1 = reg1, + .r2 = reg2, + .cc = cc, + } }, + }); +} + fn asmNone(self: *Self, tag: Mir.Inst.Tag) !void { _ = try self.addInst(.{ .tag = tag, @@ -1346,15 +1369,7 @@ fn airMin(self: *Self, inst: Air.Inst.Index) !void { .unsigned => .b, .signed => .l, }; - _ = cc; - // _ = try self.addInst(.{ - // .tag = .cond_mov, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = dst_mcv.register, - // .reg2 = lhs_reg, - // }), - // .data = .{ .cc = cc }, - // }); + try self.asmCmovCCRegisterRegister(dst_mcv.register, lhs_reg, cc); break :result dst_mcv; }; @@ -1554,14 +1569,7 @@ fn genSetStackTruncatedOverflowCompare( .signed => .o, .unsigned => .c, }; - _ = cc; - // _ = try self.addInst(.{ - // .tag = .cond_set_byte, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = overflow_reg.to8(), - // }), - // .data = .{ .cc = cc }, - // }); + try self.asmSetCCRegister(overflow_reg.to8(), cc); const scratch_reg = temp_regs[1]; try self.genSetReg(extended_ty, scratch_reg, .{ .register = reg }); @@ -1574,12 +1582,7 @@ fn genSetStackTruncatedOverflowCompare( ); const eq_reg = temp_regs[2]; - // _ = try self.addInst(.{ - // .tag = .cond_set_byte, - // .ops = Mir.Inst.Ops.encode(.{ .reg1 = eq_reg.to8() }), - // .data = .{ .cc = .ne }, - // }); - + try self.asmSetCCRegister(eq_reg.to8(), .ne); try self.genBinOpMir( .@"or", Type.u8, @@ -1829,14 +1832,7 @@ fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCVa // }), // .data = undefined, // }); - // _ = try self.addInst(.{ - // .tag = .cond_mov, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = divisor.to64(), - // .reg2 = .rdx, - // }), - // .data = .{ .cc = .e }, - // }); + try self.asmCmovCCRegisterRegister(divisor.to64(), .rdx, .e); try self.genBinOpMir(.add, Type.isize, .{ .register = divisor }, .{ .register = .rax }); return MCValue{ .register = divisor }; } @@ -2881,13 +2877,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type const overflow_bit_ty = value_ty.structFieldType(1); const overflow_bit_offset = value_ty.structFieldOffset(1, self.target.*); const tmp_reg = try self.register_manager.allocReg(null, gp); - // _ = try self.addInst(.{ - // .tag = .cond_set_byte, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = tmp_reg.to8(), - // }), - // .data = .{ .cc = ro.eflags }, - // }); + try self.asmSetCCRegister(tmp_reg.to8(), ro.eflags); try self.genInlineMemcpyRegisterRegister( overflow_bit_ty, reg, @@ -3185,13 +3175,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { defer self.register_manager.unlockReg(reg_lock); const dst_reg = try self.register_manager.allocReg(inst, gp); - // _ = try self.addInst(.{ - // .tag = .cond_set_byte, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = dst_reg.to8(), - // }), - // .data = .{ .cc = ro.eflags }, - // }); + try self.asmSetCCRegister(dst_reg.to8(), ro.eflags); break :result MCValue{ .register = dst_reg.to8() }; }, else => unreachable, @@ -5577,13 +5561,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl const overflow_bit_ty = ty.structFieldType(1); const overflow_bit_offset = ty.structFieldOffset(1, self.target.*); const tmp_reg = try self.register_manager.allocReg(null, gp); - // _ = try self.addInst(.{ - // .tag = .cond_set_byte, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = tmp_reg.to8(), - // }), - // .data = .{ .cc = ro.eflags }, - // }); + try self.asmSetCCRegister(tmp_reg.to8(), ro.eflags); return self.genSetStack( overflow_bit_ty, @@ -6114,14 +6092,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void } }, .eflags => |cc| { - _ = cc; - // _ = try self.addInst(.{ - // .tag = .cond_set_byte, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = reg.to8(), - // }), - // .data = .{ .cc = cc }, - // }); + return self.asmSetCCRegister(reg.to8(), cc); }, .immediate => |x| { if (x == 0) { diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index 35b8b59846..2b49a6051c 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -118,6 +118,9 @@ pub fn lowerMir(emit: *Emit) InnerError!void { => try emit.mirEncodeGeneric(tag, inst), // Pseudo-instructions + .cmovcc => try emit.mirCmovCC(inst), + .setcc => try emit.mirSetCC(inst), + .dbg_line => try emit.mirDbgLine(inst), .dbg_prologue_end => try emit.mirDbgPrologueEnd(inst), .dbg_epilogue_begin => try emit.mirDbgEpilogueBegin(inst), @@ -200,9 +203,11 @@ fn mirEncodeGeneric(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerE .{ .imm = Immediate.u(data.ri_u.imm) }, }, .ri64 => { - operands[0] = .{ .reg = data.rx.r1 }; const imm64 = emit.mir.extraData(Mir.Imm64, data.rx.payload).data; - operands[1] = .{ .imm = Immediate.u(Mir.Imm64.decode(imm64)) }; + operands[0..2].* = .{ + .{ .reg = data.rx.r1 }, + .{ .imm = Immediate.u(Mir.Imm64.decode(imm64)) }, + }; }, else => unreachable, } @@ -215,6 +220,42 @@ fn mirEncodeGeneric(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerE }); } +fn mnemonicFromCC(comptime basename: []const u8, cc: bits.Condition) Instruction.Mnemonic { + inline for (@typeInfo(bits.Condition).Enum.fields) |field| { + if (mem.eql(u8, field.name, @tagName(cc))) + return @field(Instruction.Mnemonic, basename ++ field.name); + } else unreachable; +} + +fn mirCmovCC(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { + const ops = emit.mir.instructions.items(.ops)[inst]; + switch (ops) { + .rr_c => { + const data = emit.mir.instructions.items(.data)[inst].rr_c; + const mnemonic = mnemonicFromCC("cmov", data.cc); + return emit.encode(mnemonic, .{ + .op1 = .{ .reg = data.r1 }, + .op2 = .{ .reg = data.r2 }, + }); + }, + else => unreachable, // TODO + } +} + +fn mirSetCC(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { + const ops = emit.mir.instructions.items(.ops)[inst]; + switch (ops) { + .r_c => { + const data = emit.mir.instructions.items(.data)[inst].r_c; + const mnemonic = mnemonicFromCC("set", data.cc); + return emit.encode(mnemonic, .{ + .op1 = .{ .reg = data.r1 }, + }); + }, + else => unreachable, // TODO + } +} + fn mirPushPopRegisterList(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void { const payload = emit.mir.instructions.items(.data)[inst].payload; const save_reg_list = emit.mir.extraData(Mir.SaveRegisterList, payload).data; @@ -333,107 +374,6 @@ fn mirPushPopRegisterList(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) // }); // } -// fn mirCondSetByte(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { -// const tag = emit.mir.instructions.items(.tag)[inst]; -// assert(tag == .cond_set_byte); -// const ops = emit.mir.instructions.items(.ops)[inst].decode(); -// const cc = emit.mir.instructions.items(.data)[inst].cc; -// const mnemonic: Instruction.Mnemonic = switch (cc) { -// .a => .seta, -// .ae => .setae, -// .b => .setb, -// .be => .setbe, -// .c => .setc, -// .e => .sete, -// .g => .setg, -// .ge => .setge, -// .l => .setl, -// .le => .setle, -// .na => .setna, -// .nae => .setnae, -// .nb => .setnb, -// .nbe => .setnbe, -// .nc => .setnc, -// .ne => .setne, -// .ng => .setng, -// .nge => .setnge, -// .nl => .setnl, -// .nle => .setnle, -// .no => .setno, -// .np => .setnp, -// .ns => .setns, -// .nz => .setnz, -// .o => .seto, -// .p => .setp, -// .pe => .setpe, -// .po => .setpo, -// .s => .sets, -// .z => .setz, -// }; -// return emit.encode(mnemonic, .{ .op1 = .{ .reg = ops.reg1 } }); -// } - -// fn mirCondMov(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { -// const tag = emit.mir.instructions.items(.tag)[inst]; -// assert(tag == .cond_mov); -// const ops = emit.mir.instructions.items(.ops)[inst].decode(); -// const cc = emit.mir.instructions.items(.data)[inst].cc; -// const mnemonic: Instruction.Mnemonic = switch (cc) { -// .a => .cmova, -// .ae => .cmovae, -// .b => .cmovb, -// .be => .cmovbe, -// .c => .cmovc, -// .e => .cmove, -// .g => .cmovg, -// .ge => .cmovge, -// .l => .cmovl, -// .le => .cmovle, -// .na => .cmovna, -// .nae => .cmovnae, -// .nb => .cmovnb, -// .nbe => .cmovnbe, -// .nc => .cmovnc, -// .ne => .cmovne, -// .ng => .cmovng, -// .nge => .cmovnge, -// .nl => .cmovnl, -// .nle => .cmovnle, -// .no => .cmovno, -// .np => .cmovnp, -// .ns => .cmovns, -// .nz => .cmovnz, -// .o => .cmovo, -// .p => .cmovp, -// .pe => .cmovpe, -// .po => .cmovpo, -// .s => .cmovs, -// .z => .cmovz, -// }; -// const op1: Instruction.Operand = .{ .reg = ops.reg1 }; - -// if (ops.flags == 0b00) { -// return emit.encode(mnemonic, .{ -// .op1 = op1, -// .op2 = .{ .reg = ops.reg2 }, -// }); -// } -// const disp = emit.mir.instructions.items(.data)[inst].disp; -// const ptr_size: Memory.PtrSize = switch (ops.flags) { -// 0b00 => unreachable, -// 0b01 => .word, -// 0b10 => .dword, -// 0b11 => .qword, -// }; -// return emit.encode(mnemonic, .{ -// .op1 = op1, -// .op2 = .{ .mem = Memory.sib(ptr_size, .{ -// .base = ops.reg2, -// .disp = disp, -// }) }, -// }); -// } - // fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { // const tag = emit.mir.instructions.items(.tag)[inst]; // assert(tag == .lea); diff --git a/src/arch/x86_64/Mir.zig b/src/arch/x86_64/Mir.zig index 5f4ed05deb..02bc70614d 100644 --- a/src/arch/x86_64/Mir.zig +++ b/src/arch/x86_64/Mir.zig @@ -56,8 +56,6 @@ pub const Inst = struct { cqo, /// Logical compare cmp, - /// Conditional move - cmovcc, /// Unsigned division div, /// Store integer with truncation @@ -134,6 +132,9 @@ pub const Inst = struct { /// Unordered compare scalar double-precision floating-point values ucomisd, + /// Conditional move + cmovcc, + /// End of prologue dbg_prologue_end, /// Start of epilogue @@ -161,6 +162,12 @@ pub const Inst = struct { /// Register, register, register operands. /// Uses `rrr` payload. rrr, + /// Register with condition code (CC). + /// Uses `r_c` payload. + r_c, + /// Register, register with condition code (CC). + /// Uses `rr_c` payload. + rr_c, /// Register, immediate (sign-extended) operands. /// Uses `ri_s` payload. ri_s, @@ -241,6 +248,17 @@ pub const Inst = struct { r2: Register, r3: Register, }, + /// Register with condition code (CC). + r_c: struct { + r1: Register, + cc: bits.Condition, + }, + /// Register, register with condition code (CC). + rr_c: struct { + r1: Register, + r2: Register, + cc: bits.Condition, + }, /// Register, signed immediate. ri_s: struct { r1: Register, From 1bde522c2c6cae52c581458774ad1dfa479d8426 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 9 Mar 2023 15:03:41 +0100 Subject: [PATCH 089/294] x86_64: add helper for Jcc instruction --- src/arch/x86_64/CodeGen.zig | 247 ++++++++++++++---------------------- src/arch/x86_64/Emit.zig | 85 +++++-------- 2 files changed, 126 insertions(+), 206 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 829d6b83fd..711dc29724 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -4511,35 +4511,29 @@ fn genCondBrMir(self: *Self, ty: Type, mcv: MCValue) !u32 { const abi_size = ty.abiSize(self.target.*); switch (mcv) { .eflags => |cc| { - _ = cc; - // return self.addInst(.{ - // .tag = .cond_jmp, - // .ops = Mir.Inst.Ops.encode(.{}), - // .data = .{ - // .inst_cc = .{ - // .inst = undefined, - // // Here we map the opposites since the jump is to the false branch. - // .cc = cc.negate(), - // }, - // }, - // }); + return self.addInst(.{ + .tag = .jcc, + .ops = .inst_cc, + .data = .{ + .inst_cc = .{ + .inst = undefined, + // Here we map the opposites since the jump is to the false branch. + .cc = cc.negate(), + }, + }, + }); }, .register => |reg| { - _ = reg; try self.spillEflagsIfOccupied(); - // _ = try self.addInst(.{ - // .tag = .@"test", - // .ops = Mir.Inst.Ops.encode(.{ .reg1 = reg }), - // .data = .{ .imm = 1 }, - // }); - // return self.addInst(.{ - // .tag = .cond_jmp, - // .ops = Mir.Inst.Ops.encode(.{}), - // .data = .{ .inst_cc = .{ - // .inst = undefined, - // .cc = .e, - // } }, - // }); + try self.asmRegisterImmediate(.@"test", reg, Immediate.u(1)); + return self.addInst(.{ + .tag = .jcc, + .ops = .inst_cc, + .data = .{ .inst_cc = .{ + .inst = undefined, + .cc = .e, + } }, + }); }, .immediate, .stack_offset, @@ -4961,22 +4955,17 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u }, } - // _ = try self.addInst(.{ - // .tag = .@"test", - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = registerAlias(cond_reg, abi_size), - // .reg2 = registerAlias(cond_reg, abi_size), - // }), - // .data = undefined, - // }); - // return self.addInst(.{ - // .tag = .cond_jmp, - // .ops = Mir.Inst.Ops.encode(.{}), - // .data = .{ .inst_cc = .{ - // .inst = undefined, - // .cc = .ne, - // } }, - // }); + const aliased_reg = registerAlias(cond_reg, abi_size); + try self.asmRegisterRegister(.@"test", aliased_reg, aliased_reg); + + return self.addInst(.{ + .tag = .jcc, + .ops = .inst_cc, + .data = .{ .inst_cc = .{ + .inst = undefined, + .cc = .ne, + } }, + }); }, .stack_offset => { try self.spillEflagsIfOccupied(); @@ -5189,9 +5178,9 @@ fn canonicaliseBranches(self: *Self, parent_branch: *Branch, canon_branch: *Bran fn performReloc(self: *Self, reloc: Mir.Inst.Index) !void { const next_inst = @intCast(u32, self.mir_instructions.len); switch (self.mir_instructions.items(.tag)[reloc]) { - // .cond_jmp => { - // self.mir_instructions.items(.data)[reloc].inst_cc.inst = next_inst; - // }, + .jcc => { + self.mir_instructions.items(.data)[reloc].inst_cc.inst = next_inst; + }, .jmp => { self.mir_instructions.items(.data)[reloc].inst = next_inst; }, @@ -5806,7 +5795,6 @@ fn genInlineMemcpy( const index_reg = regs[2].to64(); const count_reg = regs[3].to64(); const tmp_reg = regs[4].to8(); - _ = index_reg; _ = tmp_reg; switch (dst_ptr) { @@ -5825,15 +5813,11 @@ fn genInlineMemcpy( // }); }, .register => |reg| { - _ = reg; - // _ = try self.addInst(.{ - // .tag = .mov, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = registerAlias(dst_addr_reg, @intCast(u32, @divExact(reg.bitSize(), 8))), - // .reg2 = reg, - // }), - // .data = undefined, - // }); + try self.asmRegisterRegister( + .mov, + registerAlias(dst_addr_reg, @intCast(u32, @divExact(reg.bitSize(), 8))), + reg, + ); }, else => { return self.fail("TODO implement memcpy for setting stack when dest is {}", .{dst_ptr}); @@ -5856,15 +5840,11 @@ fn genInlineMemcpy( // }); }, .register => |reg| { - _ = reg; - // _ = try self.addInst(.{ - // .tag = .mov, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = registerAlias(src_addr_reg, @intCast(u32, @divExact(reg.bitSize(), 8))), - // .reg2 = reg, - // }), - // .data = undefined, - // }); + try self.asmRegisterRegister( + .mov, + registerAlias(src_addr_reg, @intCast(u32, @divExact(reg.bitSize(), 8))), + reg, + ); }, else => { return self.fail("TODO implement memcpy for setting stack when src is {}", .{src_ptr}); @@ -5873,30 +5853,24 @@ fn genInlineMemcpy( try self.genSetReg(Type.usize, count_reg, len); - // mov index_reg, 0 - // _ = try self.addInst(.{ - // .tag = .mov, - // .ops = Mir.Inst.Ops.encode(.{ .reg1 = index_reg }), - // .data = .{ .imm = 0 }, - // }); + try self.asmRegisterImmediate(.mov, index_reg, Immediate.u(0)); - // loop: - // cmp count, 0 - // const loop_start = try self.addInst(.{ - // .tag = .cmp, - // .ops = Mir.Inst.Ops.encode(.{ .reg1 = count_reg }), - // .data = .{ .imm = 0 }, - // }); - - // je end - // const loop_reloc = try self.addInst(.{ - // .tag = .cond_jmp, - // .ops = Mir.Inst.Ops.encode(.{}), - // .data = .{ .inst_cc = .{ - // .inst = undefined, - // .cc = .e, - // } }, - // }); + const loop_start = try self.addInst(.{ + .tag = .cmp, + .ops = .ri_u, + .data = .{ .ri_u = .{ + .r1 = count_reg, + .imm = 0, + } }, + }); + const loop_reloc = try self.addInst(.{ + .tag = .jcc, + .ops = .inst_cc, + .data = .{ .inst_cc = .{ + .inst = undefined, + .cc = .e, + } }, + }); // mov tmp, [addr + index_reg] // _ = try self.addInst(.{ @@ -5918,29 +5892,16 @@ fn genInlineMemcpy( // .data = .{ .payload = try self.addExtra(Mir.IndexRegisterDisp.encode(index_reg, 0)) }, // }); - // add index_reg, 1 - // _ = try self.addInst(.{ - // .tag = .add, - // .ops = Mir.Inst.Ops.encode(.{ .reg1 = index_reg }), - // .data = .{ .imm = 1 }, - // }); + try self.asmRegisterImmediate(.add, index_reg, Immediate.u(1)); + try self.asmRegisterImmediate(.sub, count_reg, Immediate.u(1)); - // sub count, 1 - // _ = try self.addInst(.{ - // .tag = .sub, - // .ops = Mir.Inst.Ops.encode(.{ .reg1 = count_reg }), - // .data = .{ .imm = 1 }, - // }); + _ = try self.addInst(.{ + .tag = .jmp, + .ops = .inst, + .data = .{ .inst = loop_start }, + }); - // jmp loop - // _ = try self.addInst(.{ - // .tag = .jmp, - // .ops = Mir.Inst.Ops.encode(.{}), - // .data = .{ .inst = loop_start }, - // }); - - // end: - // try self.performReloc(loop_reloc); + try self.performReloc(loop_reloc); } fn genInlineMemset( @@ -5982,15 +5943,11 @@ fn genInlineMemset( // }); }, .register => |reg| { - _ = reg; - // _ = try self.addInst(.{ - // .tag = .mov, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = registerAlias(addr_reg, @intCast(u32, @divExact(reg.bitSize(), 8))), - // .reg2 = reg, - // }), - // .data = undefined, - // }); + try self.asmRegisterRegister( + .mov, + registerAlias(addr_reg, @intCast(u32, @divExact(reg.bitSize(), 8))), + reg, + ); }, else => { return self.fail("TODO implement memcpy for setting stack when dest is {}", .{dst_ptr}); @@ -6000,26 +5957,23 @@ fn genInlineMemset( try self.genSetReg(Type.usize, index_reg, len); try self.genBinOpMir(.sub, Type.usize, .{ .register = index_reg }, .{ .immediate = 1 }); - // loop: - // cmp index_reg, -1 - // const loop_start = try self.addInst(.{ - // .tag = .cmp, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = index_reg, - // .flags = 0b11, - // }), - // .data = .{ .imm_s = -1 }, - // }); + const loop_start = try self.addInst(.{ + .tag = .cmp, + .ops = .ri_s, + .data = .{ .ri_s = .{ + .r1 = index_reg, + .imm = -1, + } }, + }); - // je end - // const loop_reloc = try self.addInst(.{ - // .tag = .cond_jmp, - // .ops = Mir.Inst.Ops.encode(.{}), - // .data = .{ .inst_cc = .{ - // .inst = undefined, - // .cc = .e, - // } }, - // }); + const loop_reloc = try self.addInst(.{ + .tag = .jcc, + .ops = .inst_cc, + .data = .{ .inst_cc = .{ + .inst = undefined, + .cc = .e, + } }, + }); switch (value) { .immediate => |x| { @@ -6042,22 +5996,15 @@ fn genInlineMemset( else => return self.fail("TODO inline memset for value of type {}", .{value}), } - // sub index_reg, 1 - // _ = try self.addInst(.{ - // .tag = .sub, - // .ops = Mir.Inst.Ops.encode(.{ .reg1 = index_reg }), - // .data = .{ .imm = 1 }, - // }); + try self.asmRegisterImmediate(.sub, index_reg, Immediate.u(1)); - // jmp loop - // _ = try self.addInst(.{ - // .tag = .jmp, - // .ops = Mir.Inst.Ops.encode(.{}), - // .data = .{ .inst = loop_start }, - // }); + _ = try self.addInst(.{ + .tag = .jmp, + .ops = .inst, + .data = .{ .inst = loop_start }, + }); - // end: - // try self.performReloc(loop_reloc); + try self.performReloc(loop_reloc); } fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void { diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index 2b49a6051c..607fc00e41 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -1,4 +1,3 @@ -//! //! This file contains the functionality for lowering x86_64 MIR into //! machine code @@ -118,8 +117,9 @@ pub fn lowerMir(emit: *Emit) InnerError!void { => try emit.mirEncodeGeneric(tag, inst), // Pseudo-instructions - .cmovcc => try emit.mirCmovCC(inst), - .setcc => try emit.mirSetCC(inst), + .cmovcc => try emit.mirCmovcc(inst), + .setcc => try emit.mirSetcc(inst), + .jcc => try emit.mirJcc(inst), .dbg_line => try emit.mirDbgLine(inst), .dbg_prologue_end => try emit.mirDbgPrologueEnd(inst), @@ -220,19 +220,19 @@ fn mirEncodeGeneric(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerE }); } -fn mnemonicFromCC(comptime basename: []const u8, cc: bits.Condition) Instruction.Mnemonic { +fn mnemonicFromConditionCode(comptime basename: []const u8, cc: bits.Condition) Instruction.Mnemonic { inline for (@typeInfo(bits.Condition).Enum.fields) |field| { if (mem.eql(u8, field.name, @tagName(cc))) return @field(Instruction.Mnemonic, basename ++ field.name); } else unreachable; } -fn mirCmovCC(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { +fn mirCmovcc(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const ops = emit.mir.instructions.items(.ops)[inst]; switch (ops) { .rr_c => { const data = emit.mir.instructions.items(.data)[inst].rr_c; - const mnemonic = mnemonicFromCC("cmov", data.cc); + const mnemonic = mnemonicFromConditionCode("cmov", data.cc); return emit.encode(mnemonic, .{ .op1 = .{ .reg = data.r1 }, .op2 = .{ .reg = data.r2 }, @@ -242,12 +242,12 @@ fn mirCmovCC(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { } } -fn mirSetCC(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { +fn mirSetcc(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const ops = emit.mir.instructions.items(.ops)[inst]; switch (ops) { .r_c => { const data = emit.mir.instructions.items(.data)[inst].r_c; - const mnemonic = mnemonicFromCC("set", data.cc); + const mnemonic = mnemonicFromConditionCode("set", data.cc); return emit.encode(mnemonic, .{ .op1 = .{ .reg = data.r1 }, }); @@ -256,6 +256,27 @@ fn mirSetCC(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { } } +fn mirJcc(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { + const ops = emit.mir.instructions.items(.ops)[inst]; + switch (ops) { + .inst_cc => { + const data = emit.mir.instructions.items(.data)[inst].inst_cc; + const mnemonic = mnemonicFromConditionCode("j", data.cc); + const source = emit.code.items.len; + try emit.encode(mnemonic, .{ + .op1 = .{ .imm = Immediate.s(0) }, + }); + try emit.relocs.append(emit.bin_file.allocator, .{ + .source = source, + .target = data.inst, + .offset = emit.code.items.len - 4, + .length = 6, + }); + }, + else => unreachable, // TODO + } +} + fn mirPushPopRegisterList(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void { const payload = emit.mir.instructions.items(.data)[inst].payload; const save_reg_list = emit.mir.extraData(Mir.SaveRegisterList, payload).data; @@ -326,54 +347,6 @@ fn mirPushPopRegisterList(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) // } // } -// fn mirCondJmp(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { -// const tag = emit.mir.instructions.items(.tag)[inst]; -// assert(tag == .cond_jmp); -// const inst_cc = emit.mir.instructions.items(.data)[inst].inst_cc; -// const mnemonic: Instruction.Mnemonic = switch (inst_cc.cc) { -// .a => .ja, -// .ae => .jae, -// .b => .jb, -// .be => .jbe, -// .c => .jc, -// .e => .je, -// .g => .jg, -// .ge => .jge, -// .l => .jl, -// .le => .jle, -// .na => .jna, -// .nae => .jnae, -// .nb => .jnb, -// .nbe => .jnbe, -// .nc => .jnc, -// .ne => .jne, -// .ng => .jng, -// .nge => .jnge, -// .nl => .jnl, -// .nle => .jnle, -// .no => .jno, -// .np => .jnp, -// .ns => .jns, -// .nz => .jnz, -// .o => .jo, -// .p => .jp, -// .pe => .jpe, -// .po => .jpo, -// .s => .js, -// .z => .jz, -// }; -// const source = emit.code.items.len; -// try emit.encode(mnemonic, .{ -// .op1 = .{ .imm = Immediate.s(0) }, -// }); -// try emit.relocs.append(emit.bin_file.allocator, .{ -// .source = source, -// .target = inst_cc.inst, -// .offset = emit.code.items.len - 4, -// .length = 6, -// }); -// } - // fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { // const tag = emit.mir.instructions.items(.tag)[inst]; // assert(tag == .lea); From 9658ab676643ef5c2457ac4908c180e05dc7f729 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 9 Mar 2023 18:38:54 +0100 Subject: [PATCH 090/294] x86_64: handle all instructions without introducing Memory operand --- src/arch/x86_64/CodeGen.zig | 463 ++++++++++++------------------------ src/arch/x86_64/Emit.zig | 111 ++++++++- 2 files changed, 256 insertions(+), 318 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 711dc29724..f5ee4d99eb 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -400,7 +400,7 @@ fn addExtraAssumeCapacity(self: *Self, extra: anytype) u32 { return result; } -fn asmSetCCRegister(self: *Self, reg: Register, cc: bits.Condition) !void { +fn asmSetccRegister(self: *Self, reg: Register, cc: bits.Condition) !void { _ = try self.addInst(.{ .tag = .setcc, .ops = .r_c, @@ -411,7 +411,7 @@ fn asmSetCCRegister(self: *Self, reg: Register, cc: bits.Condition) !void { }); } -fn asmCmovCCRegisterRegister(self: *Self, reg1: Register, reg2: Register, cc: bits.Condition) !void { +fn asmCmovccRegisterRegister(self: *Self, reg1: Register, reg2: Register, cc: bits.Condition) !void { _ = try self.addInst(.{ .tag = .cmovcc, .ops = .rr_c, @@ -1369,7 +1369,7 @@ fn airMin(self: *Self, inst: Air.Inst.Index) !void { .unsigned => .b, .signed => .l, }; - try self.asmCmovCCRegisterRegister(dst_mcv.register, lhs_reg, cc); + try self.asmCmovccRegisterRegister(dst_mcv.register, lhs_reg, cc); break :result dst_mcv; }; @@ -1569,7 +1569,7 @@ fn genSetStackTruncatedOverflowCompare( .signed => .o, .unsigned => .c, }; - try self.asmSetCCRegister(overflow_reg.to8(), cc); + try self.asmSetccRegister(overflow_reg.to8(), cc); const scratch_reg = temp_regs[1]; try self.genSetReg(extended_ty, scratch_reg, .{ .register = reg }); @@ -1582,7 +1582,7 @@ fn genSetStackTruncatedOverflowCompare( ); const eq_reg = temp_regs[2]; - try self.asmSetCCRegister(eq_reg.to8(), .ne); + try self.asmSetccRegister(eq_reg.to8(), .ne); try self.genBinOpMir( .@"or", Type.u8, @@ -1725,26 +1725,10 @@ fn genIntMulDivOpMir( try self.genSetReg(ty, .rax, lhs); } - _ = signedness; - // switch (signedness) { - // .signed => { - // _ = try self.addInst(.{ - // .tag = .cwd, - // .ops = Mir.Inst.Ops.encode(.{ .flags = 0b11 }), - // .data = undefined, - // }); - // }, - // .unsigned => { - // _ = try self.addInst(.{ - // .tag = .xor, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = .rdx, - // .reg2 = .rdx, - // }), - // .data = undefined, - // }); - // }, - // } + switch (signedness) { + .signed => try self.asmNone(.cqo), + .unsigned => try self.asmRegisterRegister(.xor, .rdx, .rdx), + } const factor = switch (rhs) { .register => rhs, @@ -1754,35 +1738,28 @@ fn genIntMulDivOpMir( break :blk MCValue{ .register = reg }; }, }; - _ = factor; - _ = tag; - // switch (factor) { - // .register => |reg| { - // _ = try self.addInst(.{ - // .tag = tag, - // .ops = Mir.Inst.Ops.encode(.{ .reg1 = reg }), - // .data = undefined, - // }); - // }, - // .stack_offset => |off| { - // _ = try self.addInst(.{ - // .tag = tag, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg2 = .rbp, - // .flags = switch (abi_size) { - // 1 => 0b00, - // 2 => 0b01, - // 4 => 0b10, - // 8 => 0b11, - // else => unreachable, - // }, - // }), - // .data = .{ .disp = -off }, - // }); - // }, - // else => unreachable, - // } + switch (factor) { + .register => |reg| try self.asmRegister(tag, reg), + .stack_offset => |off| { + _ = off; + // _ = try self.addInst(.{ + // .tag = tag, + // .ops = Mir.Inst.Ops.encode(.{ + // .reg2 = .rbp, + // .flags = switch (abi_size) { + // 1 => 0b00, + // 2 => 0b01, + // 4 => 0b10, + // 8 => 0b11, + // else => unreachable, + // }, + // }), + // .data = .{ .disp = -off }, + // }); + }, + else => unreachable, + } } /// Always returns a register. @@ -1808,31 +1785,10 @@ fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCVa .unsigned => .div, }, Type.isize, signedness, .{ .register = dividend }, .{ .register = divisor }); - // _ = try self.addInst(.{ - // .tag = .xor, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = divisor.to64(), - // .reg2 = dividend.to64(), - // }), - // .data = undefined, - // }); - // _ = try self.addInst(.{ - // .tag = .sar, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = divisor.to64(), - // .flags = 0b10, - // }), - // .data = .{ .imm = 63 }, - // }); - // _ = try self.addInst(.{ - // .tag = .@"test", - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = .rdx, - // .reg2 = .rdx, - // }), - // .data = undefined, - // }); - try self.asmCmovCCRegisterRegister(divisor.to64(), .rdx, .e); + try self.asmRegisterRegister(.xor, divisor.to64(), dividend.to64()); + try self.asmRegisterImmediate(.sar, divisor.to64(), Immediate.u(63)); + try self.asmRegisterRegister(.@"test", .rdx, .rdx); + try self.asmCmovccRegisterRegister(divisor.to64(), .rdx, .e); try self.genBinOpMir(.add, Type.isize, .{ .register = divisor }, .{ .register = .rax }); return MCValue{ .register = divisor }; } @@ -2877,7 +2833,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type const overflow_bit_ty = value_ty.structFieldType(1); const overflow_bit_offset = value_ty.structFieldOffset(1, self.target.*); const tmp_reg = try self.register_manager.allocReg(null, gp); - try self.asmSetCCRegister(tmp_reg.to8(), ro.eflags); + try self.asmSetccRegister(tmp_reg.to8(), ro.eflags); try self.genInlineMemcpyRegisterRegister( overflow_bit_ty, reg, @@ -3151,14 +3107,11 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { }; const field_size = @intCast(u32, struct_field_ty.abiSize(self.target.*)); if (signedness == .signed and field_size < 8) { - // _ = try self.addInst(.{ - // .tag = .mov_sign_extend, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = dst_mcv.register, - // .reg2 = registerAlias(dst_mcv.register, field_size), - // }), - // .data = undefined, - // }); + try self.asmRegisterRegister( + .movsx, + dst_mcv.register, + registerAlias(dst_mcv.register, field_size), + ); } break :result dst_mcv; @@ -3175,7 +3128,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { defer self.register_manager.unlockReg(reg_lock); const dst_reg = try self.register_manager.allocReg(inst, gp); - try self.asmSetCCRegister(dst_reg.to8(), ro.eflags); + try self.asmSetccRegister(dst_reg.to8(), ro.eflags); break :result MCValue{ .register = dst_reg.to8() }; }, else => unreachable, @@ -3211,25 +3164,7 @@ fn genShiftBinOpMir(self: *Self, tag: Mir.Inst.Tag, ty: Type, reg: Register, shi switch (shift) { .immediate => |imm| switch (imm) { 0 => return, - 1 => { - // _ = try self.addInst(.{ - // .tag = tag, - // .ops = Mir.Inst.Ops.encode(.{ .reg1 = registerAlias(reg, abi_size) }), - // .data = undefined, - // }); - return; - }, - else => { - // _ = try self.addInst(.{ - // .tag = tag, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = registerAlias(reg, abi_size), - // .flags = 0b10, - // }), - // .data = .{ .imm = @intCast(u8, imm) }, - // }); - return; - }, + else => return self.asmRegisterImmediate(tag, registerAlias(reg, abi_size), Immediate.u(imm)), }, .register => |shift_reg| { if (shift_reg == .rcx) break :blk; @@ -3240,16 +3175,8 @@ fn genShiftBinOpMir(self: *Self, tag: Mir.Inst.Tag, ty: Type, reg: Register, shi try self.register_manager.getReg(.rcx, null); try self.genSetReg(Type.u8, .rcx, shift); } - _ = abi_size; - // _ = try self.addInst(.{ - // .tag = tag, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = registerAlias(reg, abi_size), - // .flags = 0b01, - // }), - // .data = undefined, - // }); + try self.asmRegisterRegister(tag, registerAlias(reg, abi_size), .cl); } /// Result is always a register. @@ -3620,43 +3547,38 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu .register => |src_reg| switch (dst_ty.zigTypeTag()) { .Float => { if (intrinsicsAllowed(self.target.*, dst_ty)) { - // const actual_tag: Mir.Inst.Tag = switch (dst_ty.tag()) { - // .f32 => switch (mir_tag) { - // .add => Mir.Inst.Tag.add_f32, - // .cmp => Mir.Inst.Tag.cmp_f32, - // else => return self.fail("TODO genBinOpMir for f32 register-register with MIR tag {}", .{mir_tag}), - // }, - // .f64 => switch (mir_tag) { - // .add => Mir.Inst.Tag.add_f64, - // .cmp => Mir.Inst.Tag.cmp_f64, - // else => return self.fail("TODO genBinOpMir for f64 register-register with MIR tag {}", .{mir_tag}), - // }, - // else => return self.fail("TODO genBinOpMir for float register-register and type {}", .{dst_ty.fmtDebug()}), - // }; - // _ = try self.addInst(.{ - // .tag = actual_tag, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = dst_reg.to128(), - // .reg2 = src_reg.to128(), - // }), - // .data = undefined, - // }); - return; + const actual_tag: Mir.Inst.Tag = switch (dst_ty.tag()) { + .f32 => switch (mir_tag) { + .add => .addss, + .cmp => .cmpss, + else => return self.fail( + "TODO genBinOpMir for f32 register-register with MIR tag {}", + .{mir_tag}, + ), + }, + .f64 => switch (mir_tag) { + .add => .addsd, + .cmp => .cmpsd, + else => return self.fail( + "TODO genBinOpMir for f64 register-register with MIR tag {}", + .{mir_tag}, + ), + }, + else => return self.fail( + "TODO genBinOpMir for float register-register and type {}", + .{dst_ty.fmtDebug()}, + ), + }; + try self.asmRegisterRegister(actual_tag, dst_reg.to128(), src_reg.to128()); } return self.fail("TODO genBinOpMir for float register-register and no intrinsics", .{}); }, - else => { - _ = src_reg; - // _ = try self.addInst(.{ - // .tag = mir_tag, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = registerAlias(dst_reg, abi_size), - // .reg2 = registerAlias(src_reg, abi_size), - // }), - // .data = undefined, - // }); - }, + else => try self.asmRegisterRegister( + mir_tag, + registerAlias(dst_reg, abi_size), + registerAlias(src_reg, abi_size), + ), }, .immediate => |imm| { _ = imm; @@ -3777,7 +3699,6 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu /// Does not support byte-size operands. fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) InnerError!void { const abi_size = @intCast(u32, dst_ty.abiSize(self.target.*)); - _ = abi_size; switch (dst_mcv) { .none => unreachable, .undef => unreachable, @@ -3792,18 +3713,11 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M .dead, .unreach => unreachable, .ptr_stack_offset => unreachable, .register_overflow => unreachable, - .register => |src_reg| { - _ = src_reg; - // register, register - // _ = try self.addInst(.{ - // .tag = .imul_complex, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = registerAlias(dst_reg, abi_size), - // .reg2 = registerAlias(src_reg, abi_size), - // }), - // .data = undefined, - // }); - }, + .register => |src_reg| try self.asmRegisterRegister( + .imul, + registerAlias(dst_reg, abi_size), + registerAlias(src_reg, abi_size), + ), .immediate => |imm| { // TODO take into account the type's ABI size when selecting the register alias // register, immediate @@ -3992,20 +3906,12 @@ fn genVarDbgInfo( } fn airTrap(self: *Self) !void { - // _ = try self.addInst(.{ - // .tag = .ud, - // .ops = Mir.Inst.Ops.encode(.{}), - // .data = undefined, - // }); + try self.asmNone(.ud2); return self.finishAirBookkeeping(); } fn airBreakpoint(self: *Self) !void { - // _ = try self.addInst(.{ - // .tag = .interrupt, - // .ops = Mir.Inst.Ops.encode(.{}), - // .data = undefined, - // }); + try self.asmNone(.int3); return self.finishAirBookkeeping(); } @@ -4117,13 +4023,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier if (self.bin_file.cast(link.File.Elf)) |elf_file| { const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl); const atom = elf_file.getAtom(atom_index); - const got_addr = @intCast(i32, atom.getOffsetTableAddress(elf_file)); - _ = got_addr; - // _ = try self.addInst(.{ - // .tag = .call, - // .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }), - // .data = .{ .disp = got_addr }, - // }); + const got_addr = atom.getOffsetTableAddress(elf_file); + try self.asmImmediate(.call, Immediate.s(@intCast(i32, got_addr))); } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { const atom_index = try coff_file.getOrCreateAtomForDecl(func.owner_decl); const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?; @@ -4133,14 +4034,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier .sym_index = sym_index, }, }); - // _ = try self.addInst(.{ - // .tag = .call, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = .rax, - // .flags = 0b01, - // }), - // .data = undefined, - // }); + try self.asmRegister(.call, .rax); } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { const atom_index = try macho_file.getOrCreateAtomForDecl(func.owner_decl); const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?; @@ -4150,14 +4044,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier .sym_index = sym_index, }, }); - // _ = try self.addInst(.{ - // .tag = .call, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = .rax, - // .flags = 0b01, - // }), - // .data = undefined, - // }); + try self.asmRegister(.call, .rax); } else if (self.bin_file.cast(link.File.Plan9)) |p9| { const decl_block_index = try p9.seeDecl(func.owner_decl); const decl_block = p9.getDeclBlock(decl_block_index); @@ -4166,12 +4053,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier const got_addr = p9.bases.data; const got_index = decl_block.got_index.?; const fn_got_addr = got_addr + got_index * ptr_bytes; - _ = fn_got_addr; - // _ = try self.addInst(.{ - // .tag = .call, - // .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }), - // .data = .{ .disp = @intCast(i32, fn_got_addr) }, - // }); + try self.asmImmediate(.call, Immediate.s(@intCast(i32, fn_got_addr))); } else unreachable; } else if (func_value.castTag(.extern_fn)) |func_payload| { const extern_fn = func_payload.data; @@ -4191,14 +4073,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier .sym_index = sym_index, }, }); - // _ = try self.addInst(.{ - // .tag = .call, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = .rax, - // .flags = 0b01, - // }), - // .data = undefined, - // }); + try self.asmRegister(.call, .rax); } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { const sym_index = try macho_file.getGlobalSymbol(mem.sliceTo(decl_name, 0)); const atom = try macho_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl); @@ -4223,23 +4098,12 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier assert(ty.zigTypeTag() == .Pointer); const mcv = try self.resolveInst(callee); try self.genSetReg(Type.initTag(.usize), .rax, mcv); - // _ = try self.addInst(.{ - // .tag = .call, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = .rax, - // .flags = 0b01, - // }), - // .data = undefined, - // }); + try self.asmRegister(.call, .rax); } if (info.stack_byte_count > 0) { // Readjust the stack - // _ = try self.addInst(.{ - // .tag = .add, - // .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rsp }), - // .data = .{ .imm = info.stack_byte_count }, - // }); + try self.asmRegisterImmediate(.add, .rsp, Immediate.u(info.stack_byte_count)); } const result: MCValue = result: { @@ -4296,12 +4160,12 @@ fn airRet(self: *Self, inst: Air.Inst.Index) !void { // TODO when implementing defer, this will need to jump to the appropriate defer expression. // TODO optimization opportunity: figure out when we can emit this as a 2 byte instruction // which is available if the jump is 127 bytes or less forward. - // const jmp_reloc = try self.addInst(.{ - // .tag = .jmp, - // .ops = Mir.Inst.Ops.encode(.{}), - // .data = .{ .inst = undefined }, - // }); - // try self.exitlude_jump_relocs.append(self.gpa, jmp_reloc); + const jmp_reloc = try self.addInst(.{ + .tag = .jmp, + .ops = .inst, + .data = .{ .inst = undefined }, + }); + try self.exitlude_jump_relocs.append(self.gpa, jmp_reloc); return self.finishAir(inst, .dead, .{ un_op, .none, .none }); } @@ -4332,12 +4196,12 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { // TODO when implementing defer, this will need to jump to the appropriate defer expression. // TODO optimization opportunity: figure out when we can emit this as a 2 byte instruction // which is available if the jump is 127 bytes or less forward. - // const jmp_reloc = try self.addInst(.{ - // .tag = .jmp, - // .ops = Mir.Inst.Ops.encode(.{}), - // .data = .{ .inst = undefined }, - // }); - // try self.exitlude_jump_relocs.append(self.gpa, jmp_reloc); + const jmp_reloc = try self.addInst(.{ + .tag = .jmp, + .ops = .inst, + .data = .{ .inst = undefined }, + }); + try self.exitlude_jump_relocs.append(self.gpa, jmp_reloc); return self.finishAir(inst, .dead, .{ un_op, .none, .none }); } @@ -4872,13 +4736,12 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void { const loop = self.air.extraData(Air.Block, ty_pl.payload); const body = self.air.extra[loop.end..][0..loop.data.body_len]; const jmp_target = @intCast(u32, self.mir_instructions.len); - _ = jmp_target; try self.genBody(body); - // _ = try self.addInst(.{ - // .tag = .jmp, - // .ops = Mir.Inst.Ops.encode(.{}), - // .data = .{ .inst = jmp_target }, - // }); + _ = try self.addInst(.{ + .tag = .jmp, + .ops = .inst, + .data = .{ .inst = jmp_target }, + }); return self.finishAirBookkeeping(); } @@ -4923,25 +4786,16 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u .none => unreachable, .undef => unreachable, .dead, .unreach => unreachable, - .immediate => |imm| { - _ = imm; - // _ = try self.addInst(.{ - // .tag = .xor, - // .ops = Mir.Inst.Ops.encode(.{ .reg1 = registerAlias(cond_reg, abi_size) }), - // .data = .{ .imm = @intCast(u32, imm) }, - // }); - }, - .register => |reg| { - _ = reg; - // _ = try self.addInst(.{ - // .tag = .xor, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = registerAlias(cond_reg, abi_size), - // .reg2 = registerAlias(reg, abi_size), - // }), - // .data = undefined, - // }); - }, + .immediate => |imm| try self.asmRegisterImmediate( + .xor, + registerAlias(cond_reg, abi_size), + Immediate.u(imm), + ), + .register => |reg| try self.asmRegisterRegister( + .xor, + registerAlias(cond_reg, abi_size), + registerAlias(reg, abi_size), + ), .stack_offset => { if (abi_size <= 8) { const reg = try self.copyToTmpRegister(ty, case); @@ -5223,12 +5077,12 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void { // Emit a jump with a relocation. It will be patched up after the block ends. try block_data.relocs.ensureUnusedCapacity(self.gpa, 1); // Leave the jump offset undefined - // const jmp_reloc = try self.addInst(.{ - // .tag = .jmp, - // .ops = Mir.Inst.Ops.encode(.{}), - // .data = .{ .inst = undefined }, - // }); - // block_data.relocs.appendAssumeCapacity(jmp_reloc); + const jmp_reloc = try self.addInst(.{ + .tag = .jmp, + .ops = .inst, + .data = .{ .inst = undefined }, + }); + block_data.relocs.appendAssumeCapacity(jmp_reloc); } fn airAsm(self: *Self, inst: Air.Inst.Index) !void { @@ -5463,11 +5317,15 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE switch (ty.zigTypeTag()) { .Float => { if (intrinsicsAllowed(self.target.*, ty)) { - // const tag: Mir.Inst.Tag = switch (ty.tag()) { - // .f32 => Mir.Inst.Tag.mov_f32, - // .f64 => Mir.Inst.Tag.mov_f64, - // else => return self.fail("TODO genSetStackArg for register for type {}", .{ty.fmtDebug()}), - // }; + const tag: Mir.Inst.Tag = switch (ty.tag()) { + .f32 => .movss, + .f64 => .movsd, + else => return self.fail( + "TODO genSetStackArg for register for type {}", + .{ty.fmtDebug()}, + ), + }; + _ = tag; _ = reg; // _ = try self.addInst(.{ // .tag = tag, @@ -5550,7 +5408,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl const overflow_bit_ty = ty.structFieldType(1); const overflow_bit_offset = ty.structFieldOffset(1, self.target.*); const tmp_reg = try self.register_manager.allocReg(null, gp); - try self.asmSetCCRegister(tmp_reg.to8(), ro.eflags); + try self.asmSetccRegister(tmp_reg.to8(), ro.eflags); return self.genSetStack( overflow_bit_ty, @@ -6039,7 +5897,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void } }, .eflags => |cc| { - return self.asmSetCCRegister(reg.to8(), cc); + return self.asmSetccRegister(reg.to8(), cc); }, .immediate => |x| { if (x == 0) { @@ -6069,63 +5927,38 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void .Int => switch (ty.intInfo(self.target.*).signedness) { .signed => { if (abi_size <= 4) { - // _ = try self.addInst(.{ - // .tag = .mov_sign_extend, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = reg.to64(), - // .reg2 = registerAlias(src_reg, abi_size), - // }), - // .data = undefined, - // }); - return; + return self.asmRegisterRegister( + .movsx, + reg.to64(), + registerAlias(src_reg, abi_size), + ); } }, .unsigned => { if (abi_size <= 2) { - // _ = try self.addInst(.{ - // .tag = .mov_zero_extend, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = reg.to64(), - // .reg2 = registerAlias(src_reg, abi_size), - // }), - // .data = undefined, - // }); - return; + return self.asmRegisterRegister( + .movzx, + reg.to64(), + registerAlias(src_reg, abi_size), + ); } }, }, .Float => { if (intrinsicsAllowed(self.target.*, ty)) { - // const tag: Mir.Inst.Tag = switch (ty.tag()) { - // .f32 => Mir.Inst.Tag.mov_f32, - // .f64 => Mir.Inst.Tag.mov_f64, - // else => return self.fail("TODO genSetReg from register for {}", .{ty.fmtDebug()}), - // }; - // _ = try self.addInst(.{ - // .tag = tag, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = reg.to128(), - // .reg2 = src_reg.to128(), - // .flags = 0b10, - // }), - // .data = undefined, - // }); - return; + const tag: Mir.Inst.Tag = switch (ty.tag()) { + .f32 => .movss, + .f64 => .movsd, + else => return self.fail("TODO genSetReg from register for {}", .{ty.fmtDebug()}), + }; + return self.asmRegisterRegister(tag, reg.to128(), src_reg.to128()); } - return self.fail("TODO genSetReg from register for float with no intrinsics", .{}); }, else => {}, } - // _ = try self.addInst(.{ - // .tag = .mov, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = registerAlias(reg, abi_size), - // .reg2 = registerAlias(src_reg, abi_size), - // }), - // .data = undefined, - // }); + try self.asmRegisterRegister(.mov, registerAlias(reg, abi_size), registerAlias(src_reg, abi_size)); }, .linker_load => { switch (ty.zigTypeTag()) { @@ -6214,7 +6047,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void } else { // If this is RAX, we can use a direct load. // Otherwise, we need to load the address, then indirectly load the value. - if (reg.id() == 0) { + if (reg.to64() == .rax) { // movabs rax, ds:moffs64 // const payload = try self.addExtra(Mir.Imm64.encode(x)); // _ = try self.addInst(.{ diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index 607fc00e41..4eff2d11a0 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -87,7 +87,6 @@ pub fn lowerMir(emit: *Emit) InnerError!void { .imul, .int3, .mov, - .movsx, .movzx, .mul, .nop, @@ -116,7 +115,11 @@ pub fn lowerMir(emit: *Emit) InnerError!void { .ucomisd, => try emit.mirEncodeGeneric(tag, inst), - // Pseudo-instructions + .call, + .jmp, + => try emit.mirCallJmp(inst), + + .movsx => try emit.mirMovsx(inst), .cmovcc => try emit.mirCmovcc(inst), .setcc => try emit.mirSetcc(inst), .jcc => try emit.mirJcc(inst), @@ -209,7 +212,7 @@ fn mirEncodeGeneric(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerE .{ .imm = Immediate.u(Mir.Imm64.decode(imm64)) }, }; }, - else => unreachable, + else => unreachable, // TODO } return emit.encode(mnemonic, .{ @@ -220,6 +223,28 @@ fn mirEncodeGeneric(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerE }); } +fn mirMovsx(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { + const ops = emit.mir.instructions.items(.ops)[inst]; + const data = emit.mir.instructions.items(.data)[inst]; + + var op1: Instruction.Operand = .none; + var op2: Instruction.Operand = .none; + switch (ops) { + .rr => { + op1 = .{ .reg = data.rr.r1 }; + op2 = .{ .reg = data.rr.r2 }; + }, + else => unreachable, // TODO + } + + const mnemonic: Instruction.Mnemonic = if (op1.bitSize() == 64 and op2.bitSize() == 32) .movsxd else .movsx; + + return emit.encode(mnemonic, .{ + .op1 = op1, + .op2 = op2, + }); +} + fn mnemonicFromConditionCode(comptime basename: []const u8, cc: bits.Condition) Instruction.Mnemonic { inline for (@typeInfo(bits.Condition).Enum.fields) |field| { if (mem.eql(u8, field.name, @tagName(cc))) @@ -277,6 +302,86 @@ fn mirJcc(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { } } +fn mirCallJmp(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { + const tag = emit.mir.instructions.items(.tag)[inst]; + const mnemonic: Instruction.Mnemonic = switch (tag) { + .call => .call, + .jmp => .jmp, + else => unreachable, + }; + const ops = emit.mir.instructions.items(.ops)[inst]; + switch (ops) { + .inst => { + const target = emit.mir.instructions.items(.data)[inst].inst; + const source = emit.code.items.len; + try emit.encode(mnemonic, .{ + .op1 = .{ .imm = Immediate.s(0) }, + }); + try emit.relocs.append(emit.bin_file.allocator, .{ + .source = source, + .target = target, + .offset = emit.code.items.len - 4, + .length = 5, + }); + }, + .r => { + const reg = emit.mir.instructions.items(.data)[inst].r; + try emit.encode(mnemonic, .{ + .op1 = .{ .reg = reg }, + }); + }, + .imm_s => { + const imm = emit.mir.instructions.items(.data)[inst].imm_s; + try emit.encode(mnemonic, .{ + .op1 = .{ .imm = Immediate.s(imm) }, + }); + }, + else => unreachable, // TODO + } +} + +// fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { +// const tag = emit.mir.instructions.items(.tag)[inst]; +// assert(tag == .call_extern); +// const relocation = emit.mir.instructions.items(.data)[inst].relocation; + +// const offset = blk: { +// // callq +// try emit.encode(.call, .{ +// .op1 = .{ .imm = Immediate.s(0) }, +// }); +// break :blk @intCast(u32, emit.code.items.len) - 4; +// }; + +// if (emit.bin_file.cast(link.File.MachO)) |macho_file| { +// // Add relocation to the decl. +// const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?; +// const target = macho_file.getGlobalByIndex(relocation.sym_index); +// try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{ +// .type = @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_BRANCH), +// .target = target, +// .offset = offset, +// .addend = 0, +// .pcrel = true, +// .length = 2, +// }); +// } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| { +// // Add relocation to the decl. +// const atom_index = coff_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?; +// const target = coff_file.getGlobalByIndex(relocation.sym_index); +// try link.File.Coff.Atom.addRelocation(coff_file, atom_index, .{ +// .type = .direct, +// .target = target, +// .offset = offset, +// .addend = 0, +// .pcrel = true, +// .length = 2, +// }); +// } else { +// return emit.fail("TODO implement call_extern for linking backends different than MachO and COFF", .{}); +// } +// } + fn mirPushPopRegisterList(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void { const payload = emit.mir.instructions.items(.data)[inst].payload; const save_reg_list = emit.mir.extraData(Mir.SaveRegisterList, payload).data; From 32708dd6e2f16b4b688b2deed22253ea36233c91 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 9 Mar 2023 20:32:07 +0100 Subject: [PATCH 091/294] x86_64: add RM and MR helpers to codegen --- src/arch/x86_64/CodeGen.zig | 419 ++++++++++++++++-------------------- src/arch/x86_64/Emit.zig | 86 +++++--- src/arch/x86_64/Mir.zig | 147 ++++++++++++- src/arch/x86_64/bits.zig | 11 + 4 files changed, 397 insertions(+), 266 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index f5ee4d99eb..4441e63aba 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -491,6 +491,72 @@ fn asmRegisterImmediate(self: *Self, tag: Mir.Inst.Tag, reg: Register, imm: Imme }); } +fn asmMemory(self: *Self, tag: Mir.Inst.Tag, m: Memory) !void { + const ops: Mir.Inst.Ops = switch (m) { + .sib => .m_sib, + .rip => .m_rip, + else => unreachable, + }; + const data: Mir.Inst.Data = switch (ops) { + .m_sib => .{ .payload = try self.addExtra(Mir.MemorySib.encode(m)) }, + .m_rip => .{ .payload = try self.addExtra(Mir.MemoryRip.encode(m)) }, + else => unreachable, + }; + _ = try self.addInst(.{ + .tag = tag, + .ops = ops, + .data = data, + }); +} + +fn asmRegisterMemory(self: *Self, tag: Mir.Inst.Tag, reg: Register, m: Memory) !void { + const ops: Mir.Inst.Ops = switch (m) { + .sib => .rm_sib, + .rip => .rm_rip, + else => unreachable, + }; + const data: Mir.Inst.Data = switch (ops) { + .rm_sib => .{ .rx = .{ + .r1 = reg, + .payload = try self.addExtra(Mir.MemorySib.encode(m)), + } }, + .rm_rip => .{ .rx = .{ + .r1 = reg, + .payload = try self.addExtra(Mir.MemoryRip.encode(m)), + } }, + else => unreachable, + }; + _ = try self.addInst(.{ + .tag = tag, + .ops = ops, + .data = data, + }); +} + +fn asmMemoryRegister(self: *Self, tag: Mir.Inst.Tag, m: Memory, reg: Register) !void { + const ops: Mir.Inst.Ops = switch (m) { + .sib => .mr_sib, + .rip => .mr_rip, + else => unreachable, + }; + const data: Mir.Inst.Data = switch (ops) { + .mr_sib => .{ .rx = .{ + .r1 = reg, + .payload = try self.addExtra(Mir.MemorySib.encode(m)), + } }, + .mr_rip => .{ .rx = .{ + .r1 = reg, + .payload = try self.addExtra(Mir.MemoryRip.encode(m)), + } }, + else => unreachable, + }; + _ = try self.addInst(.{ + .tag = tag, + .ops = ops, + .data = data, + }); +} + fn gen(self: *Self) InnerError!void { const cc = self.fn_type.fnCallingConvention(); if (cc != .Naked) { @@ -1741,23 +1807,10 @@ fn genIntMulDivOpMir( switch (factor) { .register => |reg| try self.asmRegister(tag, reg), - .stack_offset => |off| { - _ = off; - // _ = try self.addInst(.{ - // .tag = tag, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg2 = .rbp, - // .flags = switch (abi_size) { - // 1 => 0b00, - // 2 => 0b01, - // 4 => 0b10, - // 8 => 0b11, - // else => unreachable, - // }, - // }), - // .data = .{ .disp = -off }, - // }); - }, + .stack_offset => |off| try self.asmMemory(tag, Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ + .base = .rbp, + .disp = -off, + })), else => unreachable, } } @@ -2222,19 +2275,10 @@ fn genSliceElemPtr(self: *Self, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref) !MCValue { const addr_reg = try self.register_manager.allocReg(null, gp); switch (slice_mcv) { - .stack_offset => |off| { - _ = off; - // mov reg, [rbp - 8] - // _ = try self.addInst(.{ - // .tag = .mov, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = addr_reg.to64(), - // .reg2 = .rbp, - // .flags = 0b01, - // }), - // .data = .{ .disp = -@intCast(i32, off) }, - // }); - }, + .stack_offset => |off| try self.asmRegisterMemory(.mov, addr_reg.to64(), Memory.sib(.qword, .{ + .base = .rbp, + .disp = -off, + })), else => return self.fail("TODO implement slice_elem_ptr when slice is {}", .{slice_mcv}), } // TODO we could allocate register here, but need to expect addr register and potentially @@ -2309,27 +2353,16 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void { array_ty.abiAlignment(self.target.*), )); try self.genSetStack(array_ty, off, array, .{}); - // lea reg, [rbp] - // _ = try self.addInst(.{ - // .tag = .lea, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = addr_reg.to64(), - // .reg2 = .rbp, - // }), - // .data = .{ .disp = -off }, - // }); + try self.asmRegisterMemory(.lea, addr_reg.to64(), Memory.sib(.qword, .{ + .base = .rbp, + .disp = -off, + })); }, .stack_offset => |off| { - _ = off; - // lea reg, [rbp] - // _ = try self.addInst(.{ - // .tag = .lea, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = addr_reg.to64(), - // .reg2 = .rbp, - // }), - // .data = .{ .disp = -off }, - // }); + try self.asmRegisterMemory(.lea, addr_reg.to64(), Memory.sib(.qword, .{ + .base = .rbp, + .disp = -off, + })); }, .memory, .linker_load => { try self.loadMemPtrIntoRegister(addr_reg, Type.usize, array); @@ -2366,7 +2399,7 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void { defer if (ptr_lock) |lock| self.register_manager.unlockReg(lock); const elem_ty = ptr_ty.elemType2(); - const elem_abi_size = elem_ty.abiSize(self.target.*); + const elem_abi_size = @intCast(u32, elem_ty.abiSize(self.target.*)); const index_ty = self.air.typeOf(bin_op.rhs); const index = try self.resolveInst(bin_op.rhs); const index_lock: ?RegisterLock = switch (index) { @@ -2386,16 +2419,14 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void { if (elem_abi_size > 8) { return self.fail("TODO copy value with size {} from pointer", .{elem_abi_size}); } else { - // mov dst_mcv, [dst_mcv] - // _ = try self.addInst(.{ - // .tag = .mov, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = registerAlias(dst_mcv.register, @intCast(u32, elem_abi_size)), - // .reg2 = dst_mcv.register, - // .flags = 0b01, - // }), - // .data = .{ .disp = 0 }, - // }); + try self.asmRegisterMemory( + .mov, + registerAlias(dst_mcv.register, elem_abi_size), + Memory.sib(Memory.PtrSize.fromSize(elem_abi_size), .{ + .base = dst_mcv.register, + .disp = 0, + }), + ); break :result .{ .register = registerAlias(dst_mcv.register, @intCast(u32, elem_abi_size)) }; } }; @@ -2622,7 +2653,7 @@ fn reuseOperand( fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!void { const elem_ty = ptr_ty.elemType(); - const abi_size = elem_ty.abiSize(self.target.*); + const abi_size = @intCast(u32, elem_ty.abiSize(self.target.*)); switch (ptr) { .none => unreachable, .undef => unreachable, @@ -2649,17 +2680,11 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo .undef => unreachable, .eflags => unreachable, .register => |dst_reg| { - _ = dst_reg; - // mov dst_reg, [reg] - // _ = try self.addInst(.{ - // .tag = .mov, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = registerAlias(dst_reg, @intCast(u32, abi_size)), - // .reg2 = reg, - // .flags = 0b01, - // }), - // .data = .{ .disp = 0 }, - // }); + try self.asmRegisterMemory( + .mov, + registerAlias(dst_reg, abi_size), + Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = reg, .disp = 0 }), + ); }, .stack_offset => |off| { if (abi_size <= 8) { @@ -2874,17 +2899,11 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type try self.loadMemPtrIntoRegister(addr_reg, ptr_ty, ptr); - // to get the actual address of the value we want to modify we have to go through the GOT - // mov reg, [reg] - // _ = try self.addInst(.{ - // .tag = .mov, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = addr_reg.to64(), - // .reg2 = addr_reg.to64(), - // .flags = 0b01, - // }), - // .data = .{ .disp = 0 }, - // }); + // To get the actual address of the value we want to modify we have to go through the GOT + try self.asmRegisterMemory(.mov, addr_reg.to64(), Memory.sib(.qword, .{ + .base = addr_reg.to64(), + .disp = 0, + })); const new_ptr = MCValue{ .register = addr_reg.to64() }; @@ -2936,16 +2955,11 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type defer self.register_manager.unlockReg(tmp_reg_lock); try self.loadMemPtrIntoRegister(tmp_reg, value_ty, value); + try self.asmRegisterMemory(.mov, tmp_reg, Memory.sib(.qword, .{ + .base = tmp_reg, + .disp = 0, + })); - // _ = try self.addInst(.{ - // .tag = .mov, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = tmp_reg, - // .reg2 = tmp_reg, - // .flags = 0b01, - // }), - // .data = .{ .disp = 0 }, - // }); return self.store(new_ptr, .{ .register = tmp_reg }, ptr_ty, value_ty); } @@ -3603,15 +3617,11 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu if (off > math.maxInt(i32)) { return self.fail("stack offset too large", .{}); } - // _ = try self.addInst(.{ - // .tag = mir_tag, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = registerAlias(dst_reg, abi_size), - // .reg2 = .rbp, - // .flags = 0b01, - // }), - // .data = .{ .disp = -off }, - // }); + try self.asmRegisterMemory( + mir_tag, + registerAlias(dst_reg, abi_size), + Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = .rbp, .disp = -off }), + ); }, } }, @@ -3629,16 +3639,10 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu .dead, .unreach => unreachable, .register_overflow => unreachable, .register => |src_reg| { - _ = src_reg; - // _ = try self.addInst(.{ - // .tag = mir_tag, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = .rbp, - // .reg2 = registerAlias(src_reg, abi_size), - // .flags = 0b10, - // }), - // .data = .{ .disp = -off }, - // }); + try self.asmMemoryRegister(mir_tag, Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ + .base = .rbp, + .disp = -off, + }), registerAlias(src_reg, abi_size)); }, .immediate => |imm| { _ = imm; @@ -3738,16 +3742,11 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M } }, .stack_offset => |off| { - _ = off; - // _ = try self.addInst(.{ - // .tag = .imul_complex, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = registerAlias(dst_reg, abi_size), - // .reg2 = .rbp, - // .flags = 0b01, - // }), - // .data = .{ .disp = -off }, - // }); + try self.asmRegisterMemory( + .imul, + registerAlias(dst_reg, abi_size), + Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = .rbp, .disp = -off }), + ); }, .memory => { return self.fail("TODO implement x86 multiply source memory", .{}); @@ -3770,17 +3769,11 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M .register => |src_reg| { // copy dst to a register const dst_reg = try self.copyToTmpRegister(dst_ty, dst_mcv); - _ = src_reg; - // multiply into dst_reg - // register, register - // _ = try self.addInst(.{ - // .tag = .imul_complex, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = registerAlias(dst_reg, abi_size), - // .reg2 = registerAlias(src_reg, abi_size), - // }), - // .data = undefined, - // }); + try self.asmRegisterRegister( + .imul, + registerAlias(dst_reg, abi_size), + registerAlias(src_reg, abi_size), + ); // copy dst_reg back out return self.genSetStack(dst_ty, off, .{ .register = dst_reg }, .{}); }, @@ -4006,11 +3999,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier if (info.stack_byte_count > 0) { // Adjust the stack - // _ = try self.addInst(.{ - // .tag = .sub, - // .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rsp }), - // .data = .{ .imm = info.stack_byte_count }, - // }); + try self.asmRegisterImmediate(.sub, .rsp, Immediate.u(info.stack_byte_count)); } // Due to incremental compilation, how function calls are generated depends @@ -4161,7 +4150,7 @@ fn airRet(self: *Self, inst: Air.Inst.Index) !void { // TODO optimization opportunity: figure out when we can emit this as a 2 byte instruction // which is available if the jump is 127 bytes or less forward. const jmp_reloc = try self.addInst(.{ - .tag = .jmp, + .tag = .jmp_reloc, .ops = .inst, .data = .{ .inst = undefined }, }); @@ -4197,7 +4186,7 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { // TODO optimization opportunity: figure out when we can emit this as a 2 byte instruction // which is available if the jump is 127 bytes or less forward. const jmp_reloc = try self.addInst(.{ - .tag = .jmp, + .tag = .jmp_reloc, .ops = .inst, .data = .{ .inst = undefined }, }); @@ -4738,7 +4727,7 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void { const jmp_target = @intCast(u32, self.mir_instructions.len); try self.genBody(body); _ = try self.addInst(.{ - .tag = .jmp, + .tag = .jmp_reloc, .ops = .inst, .data = .{ .inst = jmp_target }, }); @@ -5035,7 +5024,7 @@ fn performReloc(self: *Self, reloc: Mir.Inst.Index) !void { .jcc => { self.mir_instructions.items(.data)[reloc].inst_cc.inst = next_inst; }, - .jmp => { + .jmp_reloc => { self.mir_instructions.items(.data)[reloc].inst = next_inst; }, else => unreachable, @@ -5078,7 +5067,7 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void { try block_data.relocs.ensureUnusedCapacity(self.gpa, 1); // Leave the jump offset undefined const jmp_reloc = try self.addInst(.{ - .tag = .jmp, + .tag = .jmp_reloc, .ops = .inst, .data = .{ .inst = undefined }, }); @@ -5247,7 +5236,7 @@ fn setRegOrMem(self: *Self, ty: Type, loc: MCValue, val: MCValue) !void { } fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerError!void { - const abi_size = ty.abiSize(self.target.*); + const abi_size = @intCast(u32, ty.abiSize(self.target.*)); switch (mcv) { .dead => unreachable, .unreach, .none => return, @@ -5325,36 +5314,25 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE .{ty.fmtDebug()}, ), }; - _ = tag; - _ = reg; - // _ = try self.addInst(.{ - // .tag = tag, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = switch (ty.tag()) { - // .f32 => .esp, - // .f64 => .rsp, - // else => unreachable, - // }, - // .reg2 = reg.to128(), - // .flags = 0b01, - // }), - // .data = .{ .disp = -stack_offset }, - // }); - return; + // TODO verify this + const ptr_size: Memory.PtrSize = switch (ty.tag()) { + .f32 => .dword, + .f64 => .qword, + else => unreachable, + }; + return self.asmMemoryRegister(tag, Memory.sib(ptr_size, .{ + .base = .rsp, + .disp = -stack_offset, + }), reg.to128()); } return self.fail("TODO genSetStackArg for register with no intrinsics", .{}); }, else => { - // _ = try self.addInst(.{ - // .tag = .mov, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = .rsp, - // .reg2 = registerAlias(reg, @intCast(u32, abi_size)), - // .flags = 0b10, - // }), - // .data = .{ .disp = -stack_offset }, - // }); + try self.asmMemoryRegister(.mov, Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ + .base = .rsp, + .disp = -stack_offset, + }), registerAlias(reg, abi_size)); }, } }, @@ -5507,25 +5485,23 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl switch (ty.zigTypeTag()) { .Float => { if (intrinsicsAllowed(self.target.*, ty)) { - // const tag: Mir.Inst.Tag = switch (ty.tag()) { - // .f32 => Mir.Inst.Tag.mov_f32, - // .f64 => Mir.Inst.Tag.mov_f64, - // else => return self.fail("TODO genSetStack for register for type {}", .{ty.fmtDebug()}), - // }; - // _ = try self.addInst(.{ - // .tag = tag, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = switch (ty.tag()) { - // .f32 => base_reg.to32(), - // .f64 => base_reg.to64(), - // else => unreachable, - // }, - // .reg2 = reg.to128(), - // .flags = 0b01, - // }), - // .data = .{ .disp = -stack_offset }, - // }); - return; + const tag: Mir.Inst.Tag = switch (ty.tag()) { + .f32 => .movss, + .f64 => .movsd, + else => return self.fail( + "TODO genSetStack for register for type {}", + .{ty.fmtDebug()}, + ), + }; + const ptr_size: Memory.PtrSize = switch (ty.tag()) { + .f32 => .dword, + .f64 => .qword, + else => unreachable, + }; + return self.asmMemoryRegister(tag, Memory.sib(ptr_size, .{ + .base = base_reg.to64(), + .disp = -stack_offset, + }), reg.to128()); } return self.fail("TODO genSetStack for register for type float with no intrinsics", .{}); @@ -5590,16 +5566,10 @@ fn genInlineMemcpyRegisterRegister( var remainder = abi_size; while (remainder > 0) { const nearest_power_of_two = @as(u6, 1) << math.log2_int(u3, @intCast(u3, remainder)); - - // _ = try self.addInst(.{ - // .tag = .mov, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = dst_reg, - // .reg2 = registerAlias(tmp_reg, nearest_power_of_two), - // .flags = 0b10, - // }), - // .data = .{ .disp = -next_offset }, - // }); + try self.asmMemoryRegister(.mov, Memory.sib(Memory.PtrSize.fromSize(nearest_power_of_two), .{ + .base = dst_reg, + .disp = -next_offset, + }), registerAlias(tmp_reg, nearest_power_of_two)); if (nearest_power_of_two > 1) { try self.genShiftBinOpMir(.shr, ty, tmp_reg, .{ @@ -5611,15 +5581,10 @@ fn genInlineMemcpyRegisterRegister( next_offset -= nearest_power_of_two; } } else { - // _ = try self.addInst(.{ - // .tag = .mov, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = dst_reg, - // .reg2 = registerAlias(src_reg, @intCast(u32, abi_size)), - // .flags = 0b10, - // }), - // .data = .{ .disp = -offset }, - // }); + try self.asmMemoryRegister(.mov, Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ + .base = dst_reg, + .disp = -offset, + }), registerAlias(src_reg, abi_size)); } } @@ -5660,15 +5625,10 @@ fn genInlineMemcpy( try self.loadMemPtrIntoRegister(dst_addr_reg, Type.usize, dst_ptr); }, .ptr_stack_offset, .stack_offset => |off| { - _ = off; - // _ = try self.addInst(.{ - // .tag = .lea, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = dst_addr_reg.to64(), - // .reg2 = opts.dest_stack_base orelse .rbp, - // }), - // .data = .{ .disp = -off }, - // }); + try self.asmRegisterMemory(.lea, dst_addr_reg.to64(), Memory.sib(.qword, .{ + .base = opts.dest_stack_base orelse .rbp, + .disp = -off, + })); }, .register => |reg| { try self.asmRegisterRegister( @@ -5754,7 +5714,7 @@ fn genInlineMemcpy( try self.asmRegisterImmediate(.sub, count_reg, Immediate.u(1)); _ = try self.addInst(.{ - .tag = .jmp, + .tag = .jmp_reloc, .ops = .inst, .data = .{ .inst = loop_start }, }); @@ -5857,7 +5817,7 @@ fn genInlineMemset( try self.asmRegisterImmediate(.sub, index_reg, Immediate.u(1)); _ = try self.addInst(.{ - .tag = .jmp, + .tag = .jmp_reloc, .ops = .inst, .data = .{ .inst = loop_start }, }); @@ -6045,19 +6005,20 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void // .data = .{ .disp = @intCast(i32, x) }, // }); } else { - // If this is RAX, we can use a direct load. - // Otherwise, we need to load the address, then indirectly load the value. if (reg.to64() == .rax) { - // movabs rax, ds:moffs64 - // const payload = try self.addExtra(Mir.Imm64.encode(x)); - // _ = try self.addInst(.{ - // .tag = .movabs, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = .rax, - // .flags = 0b01, // imm64 will become moffs64 - // }), - // .data = .{ .payload = payload }, - // }); + // If this is RAX, we can use a direct load. + // Otherwise, we need to load the address, then indirectly load the value. + var moffs: Mir.MemoryMoffs = .{ + .seg = @enumToInt(Register.ds), + .msb = undefined, + .lsb = undefined, + }; + moffs.encodeOffset(x); + _ = try self.addInst(.{ + .tag = .mov_moffs, + .ops = .rax_moffs, + .data = .{ .payload = try self.addExtra(moffs) }, + }); } else { // Rather than duplicate the logic used for the move, we just use a self-call with a new MCValue. try self.genSetReg(ty, reg, MCValue{ .immediate = x }); diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index 4eff2d11a0..fc1e345a5a 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -73,6 +73,7 @@ pub fn lowerMir(emit: *Emit) InnerError!void { .adc, .add, .@"and", + .call, .cbw, .cwde, .cdqe, @@ -86,6 +87,8 @@ pub fn lowerMir(emit: *Emit) InnerError!void { .idiv, .imul, .int3, + .jmp, + .lea, .mov, .movzx, .mul, @@ -115,9 +118,9 @@ pub fn lowerMir(emit: *Emit) InnerError!void { .ucomisd, => try emit.mirEncodeGeneric(tag, inst), - .call, - .jmp, - => try emit.mirCallJmp(inst), + .jmp_reloc => try emit.mirJmpReloc(inst), + + .mov_moffs => try emit.mirMovMoffs(inst), .movsx => try emit.mirMovsx(inst), .cmovcc => try emit.mirCmovcc(inst), @@ -130,8 +133,6 @@ pub fn lowerMir(emit: *Emit) InnerError!void { .push_regs => try emit.mirPushPopRegisterList(.push, inst), .pop_regs => try emit.mirPushPopRegisterList(.pop, inst), - - else => return emit.fail("Implement MIR->Emit lowering for x86_64 for pseudo-inst: {}", .{tag}), } } @@ -212,6 +213,34 @@ fn mirEncodeGeneric(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerE .{ .imm = Immediate.u(Mir.Imm64.decode(imm64)) }, }; }, + .m_sib => { + const msib = emit.mir.extraData(Mir.MemorySib, data.payload).data; + operands[0] = .{ .mem = Mir.MemorySib.decode(msib) }; + }, + .m_rip => { + const mrip = emit.mir.extraData(Mir.MemoryRip, data.payload).data; + operands[0] = .{ .mem = Mir.MemoryRip.decode(mrip) }; + }, + .rm_sib, .mr_sib => { + const msib = emit.mir.extraData(Mir.MemorySib, data.rx.payload).data; + const op1 = .{ .reg = data.rx.r1 }; + const op2 = .{ .mem = Mir.MemorySib.decode(msib) }; + switch (ops) { + .rm_sib => operands[0..2].* = .{ op1, op2 }, + .mr_sib => operands[0..2].* = .{ op2, op1 }, + else => unreachable, + } + }, + .rm_rip, .mr_rip => { + const mrip = emit.mir.extraData(Mir.MemoryRip, data.rx.payload).data; + const op1 = .{ .reg = data.rx.r1 }; + const op2 = .{ .mem = Mir.MemoryRip.decode(mrip) }; + switch (ops) { + .rm_rip => operands[0..2].* = .{ op1, op2 }, + .mr_rip => operands[0..2].* = .{ op2, op1 }, + else => unreachable, + } + }, else => unreachable, // TODO } @@ -223,6 +252,29 @@ fn mirEncodeGeneric(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerE }); } +fn mirMovMoffs(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { + const ops = emit.mir.instructions.items(.ops)[inst]; + const payload = emit.mir.instructions.items(.data)[inst].payload; + const moffs = emit.mir.extraData(Mir.MemoryMoffs, payload).data; + const seg = @intToEnum(Register, moffs.seg); + const offset = moffs.decodeOffset(); + switch (ops) { + .rax_moffs => { + try emit.encode(.mov, .{ + .op1 = .{ .reg = .rax }, + .op2 = .{ .mem = Memory.moffs(seg, offset) }, + }); + }, + .moffs_rax => { + try emit.encode(.mov, .{ + .op1 = .{ .mem = Memory.moffs(seg, offset) }, + .op2 = .{ .reg = .rax }, + }); + }, + else => unreachable, + } +} + fn mirMovsx(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const ops = emit.mir.instructions.items(.ops)[inst]; const data = emit.mir.instructions.items(.data)[inst]; @@ -302,19 +354,13 @@ fn mirJcc(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { } } -fn mirCallJmp(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { - const tag = emit.mir.instructions.items(.tag)[inst]; - const mnemonic: Instruction.Mnemonic = switch (tag) { - .call => .call, - .jmp => .jmp, - else => unreachable, - }; +fn mirJmpReloc(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const ops = emit.mir.instructions.items(.ops)[inst]; switch (ops) { .inst => { const target = emit.mir.instructions.items(.data)[inst].inst; const source = emit.code.items.len; - try emit.encode(mnemonic, .{ + try emit.encode(.jmp, .{ .op1 = .{ .imm = Immediate.s(0) }, }); try emit.relocs.append(emit.bin_file.allocator, .{ @@ -324,19 +370,7 @@ fn mirCallJmp(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { .length = 5, }); }, - .r => { - const reg = emit.mir.instructions.items(.data)[inst].r; - try emit.encode(mnemonic, .{ - .op1 = .{ .reg = reg }, - }); - }, - .imm_s => { - const imm = emit.mir.instructions.items(.data)[inst].imm_s; - try emit.encode(mnemonic, .{ - .op1 = .{ .imm = Immediate.s(imm) }, - }); - }, - else => unreachable, // TODO + else => unreachable, } } diff --git a/src/arch/x86_64/Mir.zig b/src/arch/x86_64/Mir.zig index 02bc70614d..40fd1953de 100644 --- a/src/arch/x86_64/Mir.zig +++ b/src/arch/x86_64/Mir.zig @@ -17,6 +17,7 @@ const encoder = @import("encoder.zig"); const Air = @import("../../Air.zig"); const CodeGen = @import("CodeGen.zig"); const IntegerBitSet = std.bit_set.IntegerBitSet; +const Memory = bits.Memory; const Register = bits.Register; instructions: std.MultiArrayList(Inst).Slice, @@ -135,6 +136,12 @@ pub const Inst = struct { /// Conditional move cmovcc, + /// Mov absolute to/from memory wrt segment register to/from rax + mov_moffs, + + /// Jump with relocation to another local MIR instruction + jmp_reloc, + /// End of prologue dbg_prologue_end, /// Start of epilogue @@ -186,24 +193,48 @@ pub const Inst = struct { /// Relative displacement operand. /// Uses `rel` payload. rel, - /// Register, memory operands. + /// Register, memory (SIB) operands. /// Uses `rx` payload. - rm, + rm_sib, + /// Register, memory (RIP) operands. + /// Uses `rx` payload. + rm_rip, /// Register, memory, immediate (unsigned) operands /// Uses `rx` payload. rmi_u, /// Register, memory, immediate (sign-extended) operands /// Uses `rx` payload. rmi_s, - /// Memory, immediate (unsigned) operands. - /// Uses `payload` payload. - mi_u, - /// Memory, immediate (sign-extend) operands. - /// Uses `payload` payload. - mi_s, - /// Memory, register operands. - /// Uses `payload` payload. - mr, + /// Single memory (SIB) operand. + /// Uses `payload` with extra data of type `MemorySib`. + m_sib, + /// Single memory (RIP) operand. + /// Uses `payload` with extra data of type `MemoryRip`. + m_rip, + /// Memory (SIB), immediate (unsigned) operands. + /// Uses `xi_u` payload with extra data of type `MemorySib`. + mi_u_sib, + /// Memory (RIP), immediate (unsigned) operands. + /// Uses `xi_u` payload with extra data of type `MemoryRip`. + mi_u_rip, + /// Memory (SIB), immediate (sign-extend) operands. + /// Uses `xi_s` payload with extra data of type `MemorySib`. + mi_s_sib, + /// Memory (RIP), immediate (sign-extend) operands. + /// Uses `xi_s` payload with extra data of type `MemoryRip`. + mi_s_rip, + /// Memory (SIB), register operands. + /// Uses `rx` payload with extra data of type `MemorySib`. + mr_sib, + /// Memory (RIP), register operands. + /// Uses `rx` payload with extra data of type `MemoryRip`. + mr_rip, + /// Rax, Memory moffs. + /// Uses `payload` with extra data of type `MemoryMoffs`. + rax_moffs, + /// Memory moffs, rax. + /// Uses `payload` with extra data of type `MemoryMoffs`. + moffs_rax, /// Lea into register with linker relocation. /// Uses `payload` payload with data of type `LeaRegisterReloc`. lea_r_reloc, @@ -274,6 +305,16 @@ pub const Inst = struct { r1: Register, payload: u32, }, + /// Custom payload followed by an unsigned immediate. + xi_u: struct { + payload: u32, + imm: u32, + }, + /// Custom payload followed by a signed immediate. + xi_s: struct { + payload: u32, + imm: i32, + }, /// Relocation for the linker where: /// * `atom_index` is the index of the source /// * `sym_index` is the index of the target @@ -378,6 +419,90 @@ pub const Imm64 = struct { } }; +// TODO this can be further compacted using packed struct +pub const MemorySib = struct { + /// Size of the pointer. + ptr_size: u32, + /// Base register. -1 means null, or no base register. + base: i32, + /// Scale for index register. -1 means null, or no scale. + /// This has to be in sync with `index` field. + scale: i32, + /// Index register. -1 means null, or no index register. + /// This has to be in sync with `scale` field. + index: i32, + /// Displacement value. + disp: i32, + + pub fn encode(mem: Memory) MemorySib { + const sib = mem.sib; + return .{ + .ptr_size = @enumToInt(sib.ptr_size), + .base = if (sib.base) |r| @enumToInt(r) else -1, + .scale = if (sib.scale_index) |si| si.scale else -1, + .index = if (sib.scale_index) |si| @enumToInt(si.index) else -1, + .disp = sib.disp, + }; + } + + pub fn decode(msib: MemorySib) Memory { + const base: ?Register = if (msib.base == -1) null else @intToEnum(Register, msib.base); + const scale_index: ?Memory.ScaleIndex = if (msib.index == -1) null else .{ + .scale = @intCast(u4, msib.scale), + .index = @intToEnum(Register, msib.index), + }; + const mem: Memory = .{ .sib = .{ + .ptr_size = @intToEnum(Memory.PtrSize, msib.ptr_size), + .base = base, + .scale_index = scale_index, + .disp = msib.disp, + } }; + return mem; + } +}; + +pub const MemoryRip = struct { + /// Size of the pointer. + ptr_size: u32, + /// Displacement value. + disp: i32, + + pub fn encode(mem: Memory) MemoryRip { + return .{ + .ptr_size = @enumToInt(mem.rip.ptr_size), + .disp = mem.rip.disp, + }; + } + + pub fn decode(mrip: MemoryRip) Memory { + return .{ .rip = .{ + .ptr_size = @intToEnum(Memory.PtrSize, mrip.ptr_size), + .disp = mrip.disp, + } }; + } +}; + +pub const MemoryMoffs = struct { + /// Segment register. + seg: u32, + /// Absolute offset wrt to the segment register split between MSB and LSB parts much like + /// `Imm64` payload. + msb: u32, + lsb: u32, + + pub fn encodeOffset(moffs: *MemoryMoffs, v: u64) void { + moffs.msb = @truncate(u32, v >> 32); + moffs.lsb = @truncate(u32, v); + } + + pub fn decodeOffset(moffs: *const MemoryMoffs) u64 { + var res: u64 = 0; + res |= (@intCast(u64, moffs.msb) << 32); + res |= @intCast(u64, moffs.lsb); + return res; + } +}; + pub const DbgLineColumn = struct { line: u32, column: u32, diff --git a/src/arch/x86_64/bits.zig b/src/arch/x86_64/bits.zig index ad9a6f7f23..b6ac9ec5a8 100644 --- a/src/arch/x86_64/bits.zig +++ b/src/arch/x86_64/bits.zig @@ -417,6 +417,17 @@ pub const Memory = union(enum) { qword, tbyte, + pub fn fromSize(size: u32) PtrSize { + return switch (size) { + 1 => .byte, + 2 => .word, + 4 => .dword, + 8 => .qword, + 10 => .tbyte, + else => unreachable, + }; + } + pub fn fromBitSize(bit_size: u64) PtrSize { return switch (bit_size) { 8 => .byte, From 4af8313f362e393f51af1bcefd0b91c3b1ce5611 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 9 Mar 2023 21:47:11 +0100 Subject: [PATCH 092/294] x86_64: plug up all RM/MR references --- src/arch/x86_64/CodeGen.zig | 284 ++++++++++++++---------------------- 1 file changed, 112 insertions(+), 172 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 4441e63aba..f3f425d549 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -5834,14 +5834,11 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void if (off < std.math.minInt(i32) or off > std.math.maxInt(i32)) { return self.fail("stack offset too large", .{}); } - // _ = try self.addInst(.{ - // .tag = .lea, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = registerAlias(reg, abi_size), - // .reg2 = .rbp, - // }), - // .data = .{ .disp = -off }, - // }); + try self.asmRegisterMemory( + .lea, + registerAlias(reg, abi_size), + Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = .rbp, .disp = -off }), + ); }, .unreach, .none => return, // Nothing to do. .undef => { @@ -5927,40 +5924,31 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void try self.loadMemPtrIntoRegister(base_reg, Type.usize, mcv); if (intrinsicsAllowed(self.target.*, ty)) { - // const tag: Mir.Inst.Tag = switch (ty.tag()) { - // .f32 => Mir.Inst.Tag.mov_f32, - // .f64 => Mir.Inst.Tag.mov_f64, - // else => return self.fail("TODO genSetReg from memory for {}", .{ty.fmtDebug()}), - // }; - - // _ = try self.addInst(.{ - // .tag = tag, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = reg.to128(), - // .reg2 = switch (ty.tag()) { - // .f32 => base_reg.to32(), - // .f64 => base_reg.to64(), - // else => unreachable, - // }, - // }), - // .data = .{ .disp = 0 }, - // }); - return; + const tag: Mir.Inst.Tag = switch (ty.tag()) { + .f32 => .movss, + .f64 => .movsx, + else => return self.fail("TODO genSetReg from memory for {}", .{ty.fmtDebug()}), + }; + const ptr_size: Memory.PtrSize = switch (ty.tag()) { + .f32 => .dword, + .f64 => .qword, + else => unreachable, + }; + return self.asmRegisterMemory(tag, reg.to128(), Memory.sib(ptr_size, .{ + .base = base_reg.to64(), + .disp = 0, + })); } return self.fail("TODO genSetReg from memory for float with no intrinsics", .{}); }, else => { try self.loadMemPtrIntoRegister(reg, Type.usize, mcv); - // _ = try self.addInst(.{ - // .tag = .mov, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = registerAlias(reg, abi_size), - // .reg2 = reg.to64(), - // .flags = 0b01, - // }), - // .data = .{ .disp = 0 }, - // }); + try self.asmRegisterMemory( + .mov, + registerAlias(reg, abi_size), + Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = reg.to64(), .disp = 0 }), + ); }, } }, @@ -5970,40 +5958,34 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void try self.loadMemPtrIntoRegister(base_reg, Type.usize, mcv); if (intrinsicsAllowed(self.target.*, ty)) { - // const tag: Mir.Inst.Tag = switch (ty.tag()) { - // .f32 => Mir.Inst.Tag.mov_f32, - // .f64 => Mir.Inst.Tag.mov_f64, - // else => return self.fail("TODO genSetReg from memory for {}", .{ty.fmtDebug()}), - // }; - - // _ = try self.addInst(.{ - // .tag = tag, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = reg.to128(), - // .reg2 = switch (ty.tag()) { - // .f32 => base_reg.to32(), - // .f64 => base_reg.to64(), - // else => unreachable, - // }, - // }), - // .data = .{ .disp = 0 }, - // }); - return; + const tag: Mir.Inst.Tag = switch (ty.tag()) { + .f32 => .movss, + .f64 => .movsd, + else => return self.fail("TODO genSetReg from memory for {}", .{ty.fmtDebug()}), + }; + const ptr_size: Memory.PtrSize = switch (ty.tag()) { + .f32 => .dword, + .f64 => .qword, + else => unreachable, + }; + return self.asmRegisterMemory(tag, reg.to128(), Memory.sib(ptr_size, .{ + .base = base_reg.to64(), + .disp = 0, + })); } return self.fail("TODO genSetReg from memory for float with no intrinsics", .{}); }, else => { if (x <= math.maxInt(i32)) { - // mov reg, [ds:imm32] - // _ = try self.addInst(.{ - // .tag = .mov, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = registerAlias(reg, abi_size), - // .flags = 0b01, - // }), - // .data = .{ .disp = @intCast(i32, x) }, - // }); + try self.asmRegisterMemory( + .mov, + registerAlias(reg, abi_size), + Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ + .base = .ds, + .disp = @intCast(i32, x), + }), + ); } else { if (reg.to64() == .rax) { // If this is RAX, we can use a direct load. @@ -6022,17 +6004,11 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void } else { // Rather than duplicate the logic used for the move, we just use a self-call with a new MCValue. try self.genSetReg(ty, reg, MCValue{ .immediate = x }); - - // mov reg, [reg + 0x0] - // _ = try self.addInst(.{ - // .tag = .mov, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = registerAlias(reg, abi_size), - // .reg2 = reg.to64(), - // .flags = 0b01, - // }), - // .data = .{ .disp = 0 }, - // }); + try self.asmRegisterMemory( + .mov, + registerAlias(reg, abi_size), + Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = reg.to64(), .disp = 0 }), + ); } } }, @@ -6046,81 +6022,59 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void .Int => switch (ty.intInfo(self.target.*).signedness) { .signed => { if (abi_size <= 4) { - const flags: u2 = switch (abi_size) { - 1 => 0b01, - 2 => 0b10, - 4 => 0b11, - else => unreachable, - }; - _ = flags; - // _ = try self.addInst(.{ - // .tag = .mov_sign_extend, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = reg.to64(), - // .reg2 = .rbp, - // .flags = flags, - // }), - // .data = .{ .disp = -off }, - // }); - return; + return self.asmRegisterMemory( + .movsx, + reg.to64(), + Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ + .base = .rbp, + .disp = -off, + }), + ); } }, .unsigned => { if (abi_size <= 2) { - const flags: u2 = switch (abi_size) { - 1 => 0b01, - 2 => 0b10, - else => unreachable, - }; - _ = flags; - // _ = try self.addInst(.{ - // .tag = .mov_zero_extend, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = reg.to64(), - // .reg2 = .rbp, - // .flags = flags, - // }), - // .data = .{ .disp = -off }, - // }); - return; + return self.asmRegisterMemory( + .movzx, + reg.to64(), + Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ + .base = .rbp, + .disp = -off, + }), + ); } }, }, .Float => { if (intrinsicsAllowed(self.target.*, ty)) { - // const tag: Mir.Inst.Tag = switch (ty.tag()) { - // .f32 => Mir.Inst.Tag.mov_f32, - // .f64 => Mir.Inst.Tag.mov_f64, - // else => return self.fail("TODO genSetReg from stack offset for {}", .{ty.fmtDebug()}), - // }; - // _ = try self.addInst(.{ - // .tag = tag, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = reg.to128(), - // .reg2 = switch (ty.tag()) { - // .f32 => .ebp, - // .f64 => .rbp, - // else => unreachable, - // }, - // }), - // .data = .{ .disp = -off }, - // }); - return; + const tag: Mir.Inst.Tag = switch (ty.tag()) { + .f32 => .movss, + .f64 => .movsd, + else => return self.fail( + "TODO genSetReg from stack offset for {}", + .{ty.fmtDebug()}, + ), + }; + const ptr_size: Memory.PtrSize = switch (ty.tag()) { + .f32 => .dword, + .f64 => .qword, + else => unreachable, + }; + return self.asmRegisterMemory(tag, reg.to128(), Memory.sib(ptr_size, .{ + .base = .rbp, + .disp = -off, + })); } return self.fail("TODO genSetReg from stack offset for float with no intrinsics", .{}); }, else => {}, } - // _ = try self.addInst(.{ - // .tag = .mov, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = registerAlias(reg, abi_size), - // .reg2 = .rbp, - // .flags = 0b01, - // }), - // .data = .{ .disp = -off }, - // }); + try self.asmRegisterMemory( + .mov, + registerAlias(reg, abi_size), + Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = .rbp, .disp = -off }), + ); }, } } @@ -6184,7 +6138,16 @@ fn airFloatToInt(self: *Self, inst: Air.Inst.Index) !void { const src_ty = self.air.typeOf(ty_op.operand); const dst_ty = self.air.typeOfIndex(inst); const operand = try self.resolveInst(ty_op.operand); - _ = dst_ty; + const src_abi_size = @intCast(u32, src_ty.abiSize(self.target.*)); + const dst_abi_size = @intCast(u32, dst_ty.abiSize(self.target.*)); + + switch (src_abi_size) { + 4, 8 => {}, + else => |size| return self.fail("TODO load ST(0) with abiSize={}", .{size}), + } + if (dst_abi_size > 8) { + return self.fail("TODO convert float with abiSize={}", .{dst_abi_size}); + } // move float src to ST(0) const stack_offset = switch (operand) { @@ -6192,42 +6155,24 @@ fn airFloatToInt(self: *Self, inst: Air.Inst.Index) !void { else => blk: { const offset = @intCast(i32, try self.allocMem( inst, - @intCast(u32, src_ty.abiSize(self.target.*)), + src_abi_size, src_ty.abiAlignment(self.target.*), )); try self.genSetStack(src_ty, offset, operand, .{}); break :blk offset; }, }; - _ = stack_offset; - // _ = try self.addInst(.{ - // .tag = .fld, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = .rbp, - // .flags = switch (src_ty.abiSize(self.target.*)) { - // 4 => 0b01, - // 8 => 0b10, - // else => |size| return self.fail("TODO load ST(0) with abiSize={}", .{size}), - // }, - // }), - // .data = .{ .disp = -stack_offset }, - // }); + try self.asmMemory(.fld, Memory.sib(Memory.PtrSize.fromSize(src_abi_size), .{ + .base = .rbp, + .disp = -stack_offset, + })); // convert const stack_dst = try self.allocRegOrMem(inst, false); - // _ = try self.addInst(.{ - // .tag = .fisttp, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = .rbp, - // .flags = switch (dst_ty.abiSize(self.target.*)) { - // 1...2 => 0b00, - // 3...4 => 0b01, - // 5...8 => 0b10, - // else => |size| return self.fail("TODO convert float with abiSize={}", .{size}), - // }, - // }), - // .data = .{ .disp = -stack_dst.stack_offset }, - // }); + try self.asmMemory(.fisttp, Memory.sib(Memory.PtrSize.fromSize(dst_abi_size), .{ + .base = .rbp, + .disp = -stack_dst.stack_offset, + })); return self.finishAir(inst, stack_dst, .{ ty_op.operand, .none, .none }); } @@ -6318,15 +6263,10 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void { .linker_load, .memory => { const reg = try self.register_manager.allocReg(null, gp); try self.loadMemPtrIntoRegister(reg, src_ty, src_ptr); - // _ = try self.addInst(.{ - // .tag = .mov, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = reg, - // .reg2 = reg, - // .flags = 0b01, - // }), - // .data = .{ .disp = 0 }, - // }); + try self.asmRegisterMemory(.mov, reg, Memory.sib(.qword, .{ + .base = reg, + .disp = 0, + })); break :blk MCValue{ .register = reg }; }, else => break :blk src_ptr, From 022b308d6a0a3d3cf178ddc6e887f24369f69deb Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 9 Mar 2023 22:15:58 +0100 Subject: [PATCH 093/294] x86_64: start converting MI references --- src/arch/x86_64/CodeGen.zig | 251 +++++++++++------------------------- src/arch/x86_64/Emit.zig | 28 ++++ 2 files changed, 106 insertions(+), 173 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index f3f425d549..e9e7e1875b 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -509,6 +509,29 @@ fn asmMemory(self: *Self, tag: Mir.Inst.Tag, m: Memory) !void { }); } +fn asmMemoryImmediate(self: *Self, tag: Mir.Inst.Tag, m: Memory, imm: Immediate) !void { + const ops: Mir.Inst.Ops = switch (m) { + .sib => if (imm == .signed) .mi_s_sib else .mi_u_sib, + .rip => if (imm == .signed) .mi_s_rip else .mi_u_rip, + else => unreachable, + }; + const payload: u32 = switch (ops) { + .mi_s_sib, .mi_u_sib => try self.addExtra(Mir.MemorySib.encode(m)), + .mi_s_rip, .mi_u_rip => try self.addExtra(Mir.MemoryRip.encode(m)), + else => unreachable, + }; + const data: Mir.Inst.Data = switch (ops) { + .mi_s_sib, .mi_s_rip => .{ .xi_s = .{ .imm = imm.signed, .payload = payload } }, + .mi_u_sib, .mi_u_rip => .{ .xi_u = .{ .imm = @intCast(u32, imm.unsigned), .payload = payload } }, + else => unreachable, + }; + _ = try self.addInst(.{ + .tag = tag, + .ops = ops, + .data = data, + }); +} + fn asmRegisterMemory(self: *Self, tag: Mir.Inst.Tag, reg: Register, m: Memory) !void { const ops: Mir.Inst.Ops = switch (m) { .sib => .rm_sib, @@ -2776,7 +2799,7 @@ fn loadMemPtrIntoRegister(self: *Self, reg: Register, ptr_ty: Type, ptr: MCValue } fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type) InnerError!void { - const abi_size = value_ty.abiSize(self.target.*); + const abi_size = @intCast(u32, value_ty.abiSize(self.target.*)); switch (ptr) { .none => unreachable, .undef => unreachable, @@ -2807,28 +2830,12 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type try self.genSetReg(value_ty, reg, value); }, .immediate => |imm| { - _ = imm; switch (abi_size) { 1, 2, 4 => { - // TODO this is wasteful! - // introduce new MIR tag specifically for mov [reg + 0], imm - // const payload = try self.addExtra(Mir.ImmPair{ - // .dest_off = 0, - // .operand = @truncate(u32, imm), - // }); - // _ = try self.addInst(.{ - // .tag = .mov_mem_imm, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = reg.to64(), - // .flags = switch (abi_size) { - // 1 => 0b00, - // 2 => 0b01, - // 4 => 0b10, - // else => unreachable, - // }, - // }), - // .data = .{ .payload = payload }, - // }); + try self.asmMemoryImmediate(.mov, Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ + .base = reg.to64(), + .disp = 0, + }), Immediate.u(@truncate(u32, imm))); }, 8 => { // TODO: optimization: if the imm is only using the lower @@ -2913,19 +2920,8 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type return self.fail("TODO saving imm to memory for abi_size {}", .{abi_size}); } - // const payload = try self.addExtra(Mir.ImmPair{ - // .dest_off = 0, - // // TODO check if this logic is correct - // .operand = @intCast(u32, imm), - // }); - const flags: u2 = switch (abi_size) { - 1 => 0b00, - 2 => 0b01, - 4 => 0b10, - 8 => 0b11, - else => unreachable, - }; - if (flags == 0b11) { + if (abi_size == 8) { + // TODO const top_bits: u32 = @intCast(u32, imm >> 32); const can_extend = if (value_ty.isUnsignedInt()) (top_bits == 0) and (imm & 0x8000_0000) == 0 @@ -2936,14 +2932,10 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type return self.fail("TODO imm64 would get incorrectly sign extended", .{}); } } - // _ = try self.addInst(.{ - // .tag = .mov_mem_imm, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = addr_reg.to64(), - // .flags = flags, - // }), - // .data = .{ .payload = payload }, - // }); + try self.asmMemoryImmediate(.mov, Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ + .base = addr_reg.to64(), + .disp = 0, + }), Immediate.u(@intCast(u32, imm))); }, .register => { return self.store(new_ptr, value, ptr_ty, value_ty); @@ -3595,12 +3587,12 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu ), }, .immediate => |imm| { - _ = imm; - // _ = try self.addInst(.{ - // .tag = mir_tag, - // .ops = Mir.Inst.Ops.encode(.{ .reg1 = registerAlias(dst_reg, abi_size) }), - // .data = .{ .imm = @intCast(u32, imm) }, - // }); + // TODO + try self.asmRegisterImmediate( + mir_tag, + registerAlias(dst_reg, abi_size), + Immediate.u(@intCast(u32, imm)), + ); }, .memory, .linker_load, @@ -3645,36 +3637,11 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu }), registerAlias(src_reg, abi_size)); }, .immediate => |imm| { - _ = imm; - // const tag: Mir.Inst.Tag = switch (mir_tag) { - // .add => .add_mem_imm, - // .@"or" => .or_mem_imm, - // .@"and" => .and_mem_imm, - // .sub => .sub_mem_imm, - // .xor => .xor_mem_imm, - // .cmp => .cmp_mem_imm, - // else => unreachable, - // }; - const flags: u2 = switch (abi_size) { - 1 => 0b00, - 2 => 0b01, - 4 => 0b10, - 8 => 0b11, - else => unreachable, - }; - // const payload = try self.addExtra(Mir.ImmPair{ - // .dest_off = -off, - // .operand = @intCast(u32, imm), - // }); - _ = flags; - // _ = try self.addInst(.{ - // .tag = tag, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = .rbp, - // .flags = flags, - // }), - // .data = .{ .payload = payload }, - // }); + // TODO + try self.asmMemoryImmediate(mir_tag, Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ + .base = .rbp, + .disp = -off, + }), Immediate.u(@intCast(u32, imm))); }, .memory, .stack_offset, @@ -5258,33 +5225,18 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE return self.genSetStackArg(ty, stack_offset, .{ .register = reg }); }, .immediate => |imm| { - _ = imm; switch (abi_size) { - // TODO - // 1, 2, 4 => { - // // We have a positive stack offset value but we want a twos complement negative - // // offset from rbp, which is at the top of the stack frame. - // // mov [rbp+offset], immediate - // const flags: u2 = switch (abi_size) { - // 1 => 0b00, - // 2 => 0b01, - // 4 => 0b10, - // else => unreachable, - // }; - // const payload = try self.addExtra(Mir.ImmPair{ - // .dest_off = -stack_offset, - // .operand = @intCast(u32, imm), - // }); - // _ = try self.addInst(.{ - // .tag = .mov_mem_imm, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = .rsp, - // .flags = flags, - // }), - // .data = .{ .payload = payload }, - // }); - // }, - 1, 2, 4, 8 => { + 1, 2, 4 => { + // TODO + // We have a positive stack offset value but we want a twos complement negative + // offset from rbp, which is at the top of the stack frame. + // mov [rbp+offset], immediate + try self.asmMemoryImmediate(.mov, Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ + .base = .rsp, + .disp = -stack_offset, + }), Immediate.u(@intCast(u32, imm))); + }, + 8 => { const reg = try self.copyToTmpRegister(ty, mcv); return self.genSetStackArg(ty, stack_offset, MCValue{ .register = reg }); }, @@ -5355,7 +5307,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE } fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: InlineMemcpyOpts) InnerError!void { - const abi_size = ty.abiSize(self.target.*); + const abi_size = @intCast(u32, ty.abiSize(self.target.*)); switch (mcv) { .dead => unreachable, .unreach, .none => return, // Nothing to do. @@ -5400,75 +5352,33 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl return self.genSetStack(ty, stack_offset, .{ .register = reg }, opts); }, .immediate => |x_big| { - _ = x_big; const base_reg = opts.dest_stack_base orelse .rbp; - _ = base_reg; + // TODO switch (abi_size) { 0 => { assert(ty.isError()); - // const payload = try self.addExtra(Mir.ImmPair{ - // .dest_off = -stack_offset, - // .operand = @truncate(u32, x_big), - // }); - // _ = try self.addInst(.{ - // .tag = .mov_mem_imm, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = base_reg, - // .flags = 0b00, - // }), - // .data = .{ .payload = payload }, - // }); + try self.asmMemoryImmediate(.mov, Memory.sib(.byte, .{ + .base = base_reg, + .disp = -stack_offset, + }), Immediate.u(@truncate(u32, x_big))); }, 1, 2, 4 => { - // const payload = try self.addExtra(Mir.ImmPair{ - // .dest_off = -stack_offset, - // .operand = @truncate(u32, x_big), - // }); - // _ = try self.addInst(.{ - // .tag = .mov_mem_imm, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = base_reg, - // .flags = switch (abi_size) { - // 1 => 0b00, - // 2 => 0b01, - // 4 => 0b10, - // else => unreachable, - // }, - // }), - // .data = .{ .payload = payload }, - // }); + try self.asmMemoryImmediate(.mov, Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ + .base = base_reg, + .disp = -stack_offset, + }), Immediate.u(@truncate(u32, x_big))); }, 8 => { // 64 bit write to memory would take two mov's anyways so we // insted just use two 32 bit writes to avoid register allocation - { - // const payload = try self.addExtra(Mir.ImmPair{ - // .dest_off = -stack_offset + 4, - // .operand = @truncate(u32, x_big >> 32), - // }); - // _ = try self.addInst(.{ - // .tag = .mov_mem_imm, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = base_reg, - // .flags = 0b10, - // }), - // .data = .{ .payload = payload }, - // }); - } - { - // const payload = try self.addExtra(Mir.ImmPair{ - // .dest_off = -stack_offset, - // .operand = @truncate(u32, x_big), - // }); - // _ = try self.addInst(.{ - // .tag = .mov_mem_imm, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = base_reg, - // .flags = 0b10, - // }), - // .data = .{ .payload = payload }, - // }); - } + try self.asmMemoryImmediate(.mov, Memory.sib(.dword, .{ + .base = base_reg, + .disp = -stack_offset + 4, + }), Immediate.u(@truncate(u32, x_big >> 32))); + try self.asmMemoryImmediate(.mov, Memory.sib(.dword, .{ + .base = base_reg, + .disp = -stack_offset, + }), Immediate.u(@truncate(u32, x_big))); }, else => { return self.fail("TODO implement set abi_size=large stack variable with immediate", .{}); @@ -5647,15 +5557,10 @@ fn genInlineMemcpy( try self.loadMemPtrIntoRegister(src_addr_reg, Type.usize, src_ptr); }, .ptr_stack_offset, .stack_offset => |off| { - _ = off; - // _ = try self.addInst(.{ - // .tag = .lea, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = src_addr_reg.to64(), - // .reg2 = opts.source_stack_base orelse .rbp, - // }), - // .data = .{ .disp = -off }, - // }); + try self.asmRegisterMemory(.lea, src_addr_reg.to64(), Memory.sib(.qword, .{ + .base = opts.source_stack_base orelse .rbp, + .disp = -off, + })); }, .register => |reg| { try self.asmRegisterRegister( diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index fc1e345a5a..218e1c6f55 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -221,6 +221,34 @@ fn mirEncodeGeneric(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerE const mrip = emit.mir.extraData(Mir.MemoryRip, data.payload).data; operands[0] = .{ .mem = Mir.MemoryRip.decode(mrip) }; }, + .mi_u_sib => { + const msib = emit.mir.extraData(Mir.MemorySib, data.xi_u.payload).data; + operands[0..2].* = .{ + .{ .mem = Mir.MemorySib.decode(msib) }, + .{ .imm = Immediate.u(data.xi_u.imm) }, + }; + }, + .mi_s_sib => { + const msib = emit.mir.extraData(Mir.MemorySib, data.xi_s.payload).data; + operands[0..2].* = .{ + .{ .mem = Mir.MemorySib.decode(msib) }, + .{ .imm = Immediate.s(data.xi_s.imm) }, + }; + }, + .mi_u_rip => { + const mrip = emit.mir.extraData(Mir.MemoryRip, data.xi_u.payload).data; + operands[0..2].* = .{ + .{ .mem = Mir.MemoryRip.decode(mrip) }, + .{ .imm = Immediate.u(data.xi_u.imm) }, + }; + }, + .mi_s_rip => { + const mrip = emit.mir.extraData(Mir.MemoryRip, data.xi_s.payload).data; + operands[0..2].* = .{ + .{ .mem = Mir.MemoryRip.decode(mrip) }, + .{ .imm = Immediate.s(data.xi_s.imm) }, + }; + }, .rm_sib, .mr_sib => { const msib = emit.mir.extraData(Mir.MemorySib, data.rx.payload).data; const op1 = .{ .reg = data.rx.r1 }; From d0e72125396c391172758d28c21a9b901dcecc68 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 9 Mar 2023 23:56:55 +0100 Subject: [PATCH 094/294] x86_64: finish rolling out all MIR assembly helpers --- src/arch/x86_64/CodeGen.zig | 172 ++++++++++--------- src/arch/x86_64/Emit.zig | 328 ++++++++++++------------------------ src/arch/x86_64/Mir.zig | 46 +++-- 3 files changed, 225 insertions(+), 321 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index e9e7e1875b..9056b64dce 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -491,6 +491,37 @@ fn asmRegisterImmediate(self: *Self, tag: Mir.Inst.Tag, reg: Register, imm: Imme }); } +fn asmRegisterRegisterImmediate( + self: *Self, + tag: Mir.Inst.Tag, + reg1: Register, + reg2: Register, + imm: Immediate, +) !void { + const ops: Mir.Inst.Ops = switch (imm) { + .signed => .rri_s, + .unsigned => .rri_u, + }; + const data: Mir.Inst.Data = switch (ops) { + .rri_s => .{ .rri_s = .{ + .r1 = reg1, + .r2 = reg2, + .imm = imm.signed, + } }, + .rri_u => .{ .rri_u = .{ + .r1 = reg1, + .r2 = reg2, + .imm = @intCast(u32, imm.unsigned), + } }, + else => unreachable, + }; + _ = try self.addInst(.{ + .tag = tag, + .ops = ops, + .data = data, + }); +} + fn asmMemory(self: *Self, tag: Mir.Inst.Tag, m: Memory) !void { const ops: Mir.Inst.Ops = switch (m) { .sib => .m_sib, @@ -2767,27 +2798,20 @@ fn loadMemPtrIntoRegister(self: *Self, reg: Register, ptr_ty: Type, ptr: MCValue const atom = try coff_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl); break :blk coff_file.getAtom(atom).getSymbolIndex().?; } else unreachable; - const flags: u2 = switch (load_struct.type) { - .got => 0b00, - .direct => 0b01, - .import => 0b10, + const ops: Mir.Inst.Ops = switch (load_struct.type) { + .got => .got_reloc, + .direct => .direct_reloc, + .import => .import_reloc, }; - _ = abi_size; - _ = atom_index; - _ = flags; - // _ = try self.addInst(.{ - // .tag = .lea_pic, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = registerAlias(reg, abi_size), - // .flags = flags, - // }), - // .data = .{ - // .relocation = .{ - // .atom_index = atom_index, - // .sym_index = load_struct.sym_index, - // }, - // }, - // }); + _ = try self.addInst(.{ + .tag = .lea_linker, + .ops = ops, + .data = .{ .payload = try self.addExtra(Mir.LeaRegisterReloc{ + .reg = @enumToInt(registerAlias(reg, abi_size)), + .atom_index = atom_index, + .sym_index = load_struct.sym_index, + }) }, + }); }, .memory => |addr| { // TODO: in case the address fits in an imm32 we can use [ds:imm32] @@ -3690,18 +3714,15 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M registerAlias(src_reg, abi_size), ), .immediate => |imm| { - // TODO take into account the type's ABI size when selecting the register alias - // register, immediate if (math.minInt(i32) <= imm and imm <= math.maxInt(i32)) { - // _ = try self.addInst(.{ - // .tag = .imul_complex, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = dst_reg.to32(), - // .reg2 = dst_reg.to32(), - // .flags = 0b10, - // }), - // .data = .{ .imm = @intCast(u32, imm) }, - // }); + // TODO take into account the type's ABI size when selecting the register alias + // register, immediate + try self.asmRegisterRegisterImmediate( + .imul, + dst_reg.to32(), + dst_reg.to32(), + Immediate.u(@intCast(u32, imm)), + ); } else { // TODO verify we don't spill and assign to the same register as dst_mcv const src_reg = try self.copyToTmpRegister(dst_ty, src_mcv); @@ -4034,16 +4055,14 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier const sym_index = try macho_file.getGlobalSymbol(mem.sliceTo(decl_name, 0)); const atom = try macho_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl); const atom_index = macho_file.getAtom(atom).getSymbolIndex().?; - _ = sym_index; - _ = atom_index; - // _ = try self.addInst(.{ - // .tag = .call_extern, - // .ops = undefined, - // .data = .{ .relocation = .{ - // .atom_index = atom_index, - // .sym_index = sym_index, - // } }, - // }); + _ = try self.addInst(.{ + .tag = .call_extern, + .ops = undefined, + .data = .{ .relocation = .{ + .atom_index = atom_index, + .sym_index = sym_index, + } }, + }); } else { return self.fail("TODO implement calling extern functions", .{}); } @@ -5528,7 +5547,6 @@ fn genInlineMemcpy( const index_reg = regs[2].to64(); const count_reg = regs[3].to64(); const tmp_reg = regs[4].to8(); - _ = tmp_reg; switch (dst_ptr) { .memory, .linker_load => { @@ -5575,7 +5593,6 @@ fn genInlineMemcpy( } try self.genSetReg(Type.usize, count_reg, len); - try self.asmRegisterImmediate(.mov, index_reg, Immediate.u(0)); const loop_start = try self.addInst(.{ @@ -5595,26 +5612,22 @@ fn genInlineMemcpy( } }, }); - // mov tmp, [addr + index_reg] - // _ = try self.addInst(.{ - // .tag = .mov_scale_src, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = tmp_reg.to8(), - // .reg2 = src_addr_reg, - // }), - // .data = .{ .payload = try self.addExtra(Mir.IndexRegisterDisp.encode(index_reg, 0)) }, - // }); - - // mov [stack_offset + index_reg], tmp - // _ = try self.addInst(.{ - // .tag = .mov_scale_dst, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = dst_addr_reg, - // .reg2 = tmp_reg.to8(), - // }), - // .data = .{ .payload = try self.addExtra(Mir.IndexRegisterDisp.encode(index_reg, 0)) }, - // }); - + try self.asmRegisterMemory(.mov, tmp_reg.to8(), Memory.sib(.byte, .{ + .base = src_addr_reg, + .scale_index = .{ + .scale = 1, + .index = index_reg, + }, + .disp = 0, + })); + try self.asmMemoryRegister(.mov, Memory.sib(.byte, .{ + .base = dst_addr_reg, + .scale_index = .{ + .scale = 1, + .index = index_reg, + }, + .disp = 0, + }), tmp_reg.to8()); try self.asmRegisterImmediate(.add, index_reg, Immediate.u(1)); try self.asmRegisterImmediate(.sub, count_reg, Immediate.u(1)); @@ -5655,15 +5668,10 @@ fn genInlineMemset( try self.loadMemPtrIntoRegister(addr_reg, Type.usize, dst_ptr); }, .ptr_stack_offset, .stack_offset => |off| { - _ = off; - // _ = try self.addInst(.{ - // .tag = .lea, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = addr_reg.to64(), - // .reg2 = opts.dest_stack_base orelse .rbp, - // }), - // .data = .{ .disp = -off }, - // }); + try self.asmRegisterMemory(.lea, addr_reg.to64(), Memory.sib(.qword, .{ + .base = opts.dest_stack_base orelse .rbp, + .disp = -off, + })); }, .register => |reg| { try self.asmRegisterRegister( @@ -5703,18 +5711,14 @@ fn genInlineMemset( if (x > math.maxInt(i32)) { return self.fail("TODO inline memset for value immediate larger than 32bits", .{}); } - // mov byte ptr [rbp + index_reg + stack_offset], imm - // _ = try self.addInst(.{ - // .tag = .mov_mem_index_imm, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = addr_reg, - // }), - // .data = .{ .payload = try self.addExtra(Mir.IndexRegisterDispImm.encode( - // index_reg, - // 0, - // @intCast(u32, x), - // )) }, - // }); + try self.asmMemoryImmediate(.mov, Memory.sib(.byte, .{ + .base = addr_reg, + .scale_index = .{ + .scale = 1, + .index = index_reg, + }, + .disp = 0, + }), Immediate.u(@intCast(u8, x))); }, else => return self.fail("TODO inline memset for value of type {}", .{value}), } diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index 218e1c6f55..5b3b03e8ee 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -120,6 +120,10 @@ pub fn lowerMir(emit: *Emit) InnerError!void { .jmp_reloc => try emit.mirJmpReloc(inst), + .call_extern => try emit.mirCallExtern(inst), + + .lea_linker => try emit.mirLeaLinker(inst), + .mov_moffs => try emit.mirMovMoffs(inst), .movsx => try emit.mirMovsx(inst), @@ -213,6 +217,16 @@ fn mirEncodeGeneric(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerE .{ .imm = Immediate.u(Mir.Imm64.decode(imm64)) }, }; }, + .rri_s => operands[0..3].* = .{ + .{ .reg = data.rri_s.r1 }, + .{ .reg = data.rri_s.r2 }, + .{ .imm = Immediate.s(data.rri_s.imm) }, + }, + .rri_u => operands[0..3].* = .{ + .{ .reg = data.rri_u.r1 }, + .{ .reg = data.rri_u.r2 }, + .{ .imm = Immediate.u(data.rri_u.imm) }, + }, .m_sib => { const msib = emit.mir.extraData(Mir.MemorySib, data.payload).data; operands[0] = .{ .mem = Mir.MemorySib.decode(msib) }; @@ -402,47 +416,44 @@ fn mirJmpReloc(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { } } -// fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { -// const tag = emit.mir.instructions.items(.tag)[inst]; -// assert(tag == .call_extern); -// const relocation = emit.mir.instructions.items(.data)[inst].relocation; +fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { + const relocation = emit.mir.instructions.items(.data)[inst].relocation; -// const offset = blk: { -// // callq -// try emit.encode(.call, .{ -// .op1 = .{ .imm = Immediate.s(0) }, -// }); -// break :blk @intCast(u32, emit.code.items.len) - 4; -// }; + const offset = blk: { + try emit.encode(.call, .{ + .op1 = .{ .imm = Immediate.s(0) }, + }); + break :blk @intCast(u32, emit.code.items.len) - 4; + }; -// if (emit.bin_file.cast(link.File.MachO)) |macho_file| { -// // Add relocation to the decl. -// const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?; -// const target = macho_file.getGlobalByIndex(relocation.sym_index); -// try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{ -// .type = @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_BRANCH), -// .target = target, -// .offset = offset, -// .addend = 0, -// .pcrel = true, -// .length = 2, -// }); -// } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| { -// // Add relocation to the decl. -// const atom_index = coff_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?; -// const target = coff_file.getGlobalByIndex(relocation.sym_index); -// try link.File.Coff.Atom.addRelocation(coff_file, atom_index, .{ -// .type = .direct, -// .target = target, -// .offset = offset, -// .addend = 0, -// .pcrel = true, -// .length = 2, -// }); -// } else { -// return emit.fail("TODO implement call_extern for linking backends different than MachO and COFF", .{}); -// } -// } + if (emit.bin_file.cast(link.File.MachO)) |macho_file| { + // Add relocation to the decl. + const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?; + const target = macho_file.getGlobalByIndex(relocation.sym_index); + try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{ + .type = @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_BRANCH), + .target = target, + .offset = offset, + .addend = 0, + .pcrel = true, + .length = 2, + }); + } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| { + // Add relocation to the decl. + const atom_index = coff_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?; + const target = coff_file.getGlobalByIndex(relocation.sym_index); + try link.File.Coff.Atom.addRelocation(coff_file, atom_index, .{ + .type = .direct, + .target = target, + .offset = offset, + .addend = 0, + .pcrel = true, + .length = 2, + }); + } else { + return emit.fail("TODO implement call_extern for linking backends different than MachO and COFF", .{}); + } +} fn mirPushPopRegisterList(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void { const payload = emit.mir.instructions.items(.data)[inst].payload; @@ -474,194 +485,63 @@ fn mirPushPopRegisterList(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) } } -// fn mirJmpCall(emit: *Emit, mnemonic: Instruction.Mnemonic, inst: Mir.Inst.Index) InnerError!void { -// const ops = emit.mir.instructions.items(.ops)[inst].decode(); -// switch (ops.flags) { -// 0b00 => { -// const target = emit.mir.instructions.items(.data)[inst].inst; -// const source = emit.code.items.len; -// try emit.encode(mnemonic, .{ -// .op1 = .{ .imm = Immediate.s(0) }, -// }); -// try emit.relocs.append(emit.bin_file.allocator, .{ -// .source = source, -// .target = target, -// .offset = emit.code.items.len - 4, -// .length = 5, -// }); -// }, -// 0b01 => { -// if (ops.reg1 == .none) { -// const disp = emit.mir.instructions.items(.data)[inst].disp; -// return emit.encode(mnemonic, .{ -// .op1 = .{ .mem = Memory.sib(.qword, .{ .disp = disp }) }, -// }); -// } -// return emit.encode(mnemonic, .{ -// .op1 = .{ .reg = ops.reg1 }, -// }); -// }, -// 0b10 => { -// const disp = emit.mir.instructions.items(.data)[inst].disp; -// return emit.encode(mnemonic, .{ -// .op1 = .{ .mem = Memory.sib(.qword, .{ -// .base = ops.reg1, -// .disp = disp, -// }) }, -// }); -// }, -// 0b11 => return emit.fail("TODO unused variant jmp/call 0b11", .{}), -// } -// } +fn mirLeaLinker(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { + const ops = emit.mir.instructions.items(.ops)[inst]; + const payload = emit.mir.instructions.items(.data)[inst].payload; + const metadata = emit.mir.extraData(Mir.LeaRegisterReloc, payload).data; + const reg = @intToEnum(Register, metadata.reg); -// fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { -// const tag = emit.mir.instructions.items(.tag)[inst]; -// assert(tag == .lea); -// const ops = emit.mir.instructions.items(.ops)[inst].decode(); -// switch (ops.flags) { -// 0b00 => { -// const disp = emit.mir.instructions.items(.data)[inst].disp; -// const src_reg: ?Register = if (ops.reg2 != .none) ops.reg2 else null; -// return emit.encode(.lea, .{ -// .op1 = .{ .reg = ops.reg1 }, -// .op2 = .{ .mem = Memory.sib(Memory.PtrSize.fromBitSize(ops.reg1.bitSize()), .{ -// .base = src_reg, -// .disp = disp, -// }) }, -// }); -// }, -// 0b01 => { -// const start_offset = emit.code.items.len; -// try emit.encode(.lea, .{ -// .op1 = .{ .reg = ops.reg1 }, -// .op2 = .{ .mem = Memory.rip(Memory.PtrSize.fromBitSize(ops.reg1.bitSize()), 0) }, -// }); -// const end_offset = emit.code.items.len; -// // Backpatch the displacement -// const payload = emit.mir.instructions.items(.data)[inst].payload; -// const imm = emit.mir.extraData(Mir.Imm64, payload).data.decode(); -// const disp = @intCast(i32, @intCast(i64, imm) - @intCast(i64, end_offset - start_offset)); -// mem.writeIntLittle(i32, emit.code.items[end_offset - 4 ..][0..4], disp); -// }, -// 0b10 => { -// const payload = emit.mir.instructions.items(.data)[inst].payload; -// const index_reg_disp = emit.mir.extraData(Mir.IndexRegisterDisp, payload).data.decode(); -// const src_reg: ?Register = if (ops.reg2 != .none) ops.reg2 else null; -// const scale_index = Memory.ScaleIndex{ -// .scale = 1, -// .index = index_reg_disp.index, -// }; -// return emit.encode(.lea, .{ -// .op1 = .{ .reg = ops.reg1 }, -// .op2 = .{ .mem = Memory.sib(Memory.PtrSize.fromBitSize(ops.reg1.bitSize()), .{ -// .base = src_reg, -// .scale_index = scale_index, -// .disp = index_reg_disp.disp, -// }) }, -// }); -// }, -// 0b11 => return emit.fail("TODO unused LEA variant 0b11", .{}), -// } -// } + try emit.encode(.lea, .{ + .op1 = .{ .reg = reg }, + .op2 = .{ .mem = Memory.rip(Memory.PtrSize.fromBitSize(reg.bitSize()), 0) }, + }); -// fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { -// const tag = emit.mir.instructions.items(.tag)[inst]; -// assert(tag == .lea_pic); -// const ops = emit.mir.instructions.items(.ops)[inst].decode(); -// const relocation = emit.mir.instructions.items(.data)[inst].relocation; + const end_offset = emit.code.items.len; -// switch (ops.flags) { -// 0b00, 0b01, 0b10 => {}, -// else => return emit.fail("TODO unused LEA PIC variant 0b11", .{}), -// } - -// try emit.encode(.lea, .{ -// .op1 = .{ .reg = ops.reg1 }, -// .op2 = .{ .mem = Memory.rip(Memory.PtrSize.fromBitSize(ops.reg1.bitSize()), 0) }, -// }); - -// const end_offset = emit.code.items.len; - -// if (emit.bin_file.cast(link.File.MachO)) |macho_file| { -// const reloc_type = switch (ops.flags) { -// 0b00 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_GOT), -// 0b01 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_SIGNED), -// else => unreachable, -// }; -// const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?; -// try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{ -// .type = reloc_type, -// .target = .{ .sym_index = relocation.sym_index, .file = null }, -// .offset = @intCast(u32, end_offset - 4), -// .addend = 0, -// .pcrel = true, -// .length = 2, -// }); -// } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| { -// const atom_index = coff_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?; -// try link.File.Coff.Atom.addRelocation(coff_file, atom_index, .{ -// .type = switch (ops.flags) { -// 0b00 => .got, -// 0b01 => .direct, -// 0b10 => .import, -// else => unreachable, -// }, -// .target = switch (ops.flags) { -// 0b00, 0b01 => .{ .sym_index = relocation.sym_index, .file = null }, -// 0b10 => coff_file.getGlobalByIndex(relocation.sym_index), -// else => unreachable, -// }, -// .offset = @intCast(u32, end_offset - 4), -// .addend = 0, -// .pcrel = true, -// .length = 2, -// }); -// } else { -// return emit.fail("TODO implement lea reg, [rip + reloc] for linking backends different than MachO", .{}); -// } -// } - -// fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { -// const tag = emit.mir.instructions.items(.tag)[inst]; -// assert(tag == .call_extern); -// const relocation = emit.mir.instructions.items(.data)[inst].relocation; - -// const offset = blk: { -// // callq -// try emit.encode(.call, .{ -// .op1 = .{ .imm = Immediate.s(0) }, -// }); -// break :blk @intCast(u32, emit.code.items.len) - 4; -// }; - -// if (emit.bin_file.cast(link.File.MachO)) |macho_file| { -// // Add relocation to the decl. -// const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?; -// const target = macho_file.getGlobalByIndex(relocation.sym_index); -// try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{ -// .type = @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_BRANCH), -// .target = target, -// .offset = offset, -// .addend = 0, -// .pcrel = true, -// .length = 2, -// }); -// } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| { -// // Add relocation to the decl. -// const atom_index = coff_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?; -// const target = coff_file.getGlobalByIndex(relocation.sym_index); -// try link.File.Coff.Atom.addRelocation(coff_file, atom_index, .{ -// .type = .direct, -// .target = target, -// .offset = offset, -// .addend = 0, -// .pcrel = true, -// .length = 2, -// }); -// } else { -// return emit.fail("TODO implement call_extern for linking backends different than MachO and COFF", .{}); -// } -// } + if (emit.bin_file.cast(link.File.MachO)) |macho_file| { + const reloc_type = switch (ops) { + .got_reloc => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_GOT), + .direct_reloc => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_SIGNED), + else => unreachable, + }; + const atom_index = macho_file.getAtomIndexForSymbol(.{ + .sym_index = metadata.atom_index, + .file = null, + }).?; + try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{ + .type = reloc_type, + .target = .{ .sym_index = metadata.sym_index, .file = null }, + .offset = @intCast(u32, end_offset - 4), + .addend = 0, + .pcrel = true, + .length = 2, + }); + } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| { + const atom_index = coff_file.getAtomIndexForSymbol(.{ + .sym_index = metadata.atom_index, + .file = null, + }).?; + try link.File.Coff.Atom.addRelocation(coff_file, atom_index, .{ + .type = switch (ops) { + .got_reloc => .got, + .direct_reloc => .direct, + .import_reloc => .import, + else => unreachable, + }, + .target = switch (ops) { + .got_reloc, .direct_reloc => .{ .sym_index = metadata.sym_index, .file = null }, + .import_reloc => coff_file.getGlobalByIndex(metadata.sym_index), + else => unreachable, + }, + .offset = @intCast(u32, end_offset - 4), + .addend = 0, + .pcrel = true, + .length = 2, + }); + } else { + return emit.fail("TODO implement lea reg, [rip + reloc] for linking backends different than MachO", .{}); + } +} fn mirDbgLine(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const payload = emit.mir.instructions.items(.data)[inst].payload; diff --git a/src/arch/x86_64/Mir.zig b/src/arch/x86_64/Mir.zig index 40fd1953de..6f0d578662 100644 --- a/src/arch/x86_64/Mir.zig +++ b/src/arch/x86_64/Mir.zig @@ -142,6 +142,13 @@ pub const Inst = struct { /// Jump with relocation to another local MIR instruction jmp_reloc, + /// Call to an extern symbol via linker relocation. + /// Uses `relocation` payload. + call_extern, + + /// Load effective address of a symbol not yet allocated in VM. + lea_linker, + /// End of prologue dbg_prologue_end, /// Start of epilogue @@ -169,6 +176,12 @@ pub const Inst = struct { /// Register, register, register operands. /// Uses `rrr` payload. rrr, + /// Register, register, immediate (sign-extended) operands. + /// Uses `rri_s` payload. + rri_s, + /// Register, register, immediate (unsigned) operands. + /// Uses `rri_u` payload. + rri_u, /// Register with condition code (CC). /// Uses `r_c` payload. r_c, @@ -199,12 +212,6 @@ pub const Inst = struct { /// Register, memory (RIP) operands. /// Uses `rx` payload. rm_rip, - /// Register, memory, immediate (unsigned) operands - /// Uses `rx` payload. - rmi_u, - /// Register, memory, immediate (sign-extended) operands - /// Uses `rx` payload. - rmi_s, /// Single memory (SIB) operand. /// Uses `payload` with extra data of type `MemorySib`. m_sib, @@ -250,6 +257,15 @@ pub const Inst = struct { rm_cc, /// Uses `reloc` payload. reloc, + /// Linker relocation - GOT indirection. + /// Uses `payload` payload with extra data of type `LeaRegisterReloc`. + got_reloc, + /// Linker relocation - direct reference. + /// Uses `payload` payload with extra data of type `LeaRegisterReloc`. + direct_reloc, + /// Linker relocation - imports table indirection (binding). + /// Uses `payload` payload with extra data of type `LeaRegisterReloc`. + import_reloc, }; pub const Data = union { @@ -279,6 +295,16 @@ pub const Inst = struct { r2: Register, r3: Register, }, + rri_s: struct { + r1: Register, + r2: Register, + imm: i32, + }, + rri_u: struct { + r1: Register, + r2: Register, + imm: u32, + }, /// Register with condition code (CC). r_c: struct { r1: Register, @@ -339,13 +365,7 @@ pub const Inst = struct { pub const LeaRegisterReloc = struct { /// Destination register. - reg: Register, - /// Type of the load. - load_type: enum(u2) { - got, - direct, - import, - }, + reg: u32, /// Index of the containing atom. atom_index: u32, /// Index into the linker's symbol table. From fe1fab4a8ee8d908ab592c63bbc35bbbaa1ed0bf Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 10 Mar 2023 19:23:01 +0100 Subject: [PATCH 095/294] x86_64: fix CALL emits for ELF and Plan9 --- src/arch/x86_64/CodeGen.zig | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 9056b64dce..6ba81aecdd 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -4001,7 +4001,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl); const atom = elf_file.getAtom(atom_index); const got_addr = atom.getOffsetTableAddress(elf_file); - try self.asmImmediate(.call, Immediate.s(@intCast(i32, got_addr))); + try self.asmMemory(.call, Memory.sib(.qword, .{ + .base = .ds, + .disp = @intCast(i32, got_addr), + })); } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { const atom_index = try coff_file.getOrCreateAtomForDecl(func.owner_decl); const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?; @@ -4030,7 +4033,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier const got_addr = p9.bases.data; const got_index = decl_block.got_index.?; const fn_got_addr = got_addr + got_index * ptr_bytes; - try self.asmImmediate(.call, Immediate.s(@intCast(i32, fn_got_addr))); + try self.asmMemory(.call, Memory.sib(.qword, .{ + .base = .ds, + .disp = @intCast(i32, fn_got_addr), + })); } else unreachable; } else if (func_value.castTag(.extern_fn)) |func_payload| { const extern_fn = func_payload.data; From e34e7d5ad1a61c66c145af612e11b7c6500caf79 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 10 Mar 2023 19:32:48 +0100 Subject: [PATCH 096/294] x86_64: add missing decodings for .movsx --- src/arch/x86_64/CodeGen.zig | 2 +- src/arch/x86_64/Emit.zig | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 6ba81aecdd..cd2b90051d 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -5841,7 +5841,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void if (intrinsicsAllowed(self.target.*, ty)) { const tag: Mir.Inst.Tag = switch (ty.tag()) { .f32 => .movss, - .f64 => .movsx, + .f64 => .movsd, else => return self.fail("TODO genSetReg from memory for {}", .{ty.fmtDebug()}), }; const ptr_size: Memory.PtrSize = switch (ty.tag()) { diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index 5b3b03e8ee..c490dea497 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -328,6 +328,16 @@ fn mirMovsx(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { op1 = .{ .reg = data.rr.r1 }; op2 = .{ .reg = data.rr.r2 }; }, + .rm_sib => { + const msib = emit.mir.extraData(Mir.MemorySib, data.rx.payload).data; + op1 = .{ .reg = data.rx.r1 }; + op2 = .{ .mem = Mir.MemorySib.decode(msib) }; + }, + .rm_rip => { + const mrip = emit.mir.extraData(Mir.MemoryRip, data.rx.payload).data; + op1 = .{ .reg = data.rx.r1 }; + op2 = .{ .mem = Mir.MemoryRip.decode(mrip) }; + }, else => unreachable, // TODO } From 6e1da365038856d9fbff690f187dc0a5c0933440 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 10 Mar 2023 19:38:15 +0100 Subject: [PATCH 097/294] x86_64: PtrSize.fromSize() should take into account nonexact sizes too --- src/arch/x86_64/bits.zig | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/arch/x86_64/bits.zig b/src/arch/x86_64/bits.zig index b6ac9ec5a8..1828cdc08f 100644 --- a/src/arch/x86_64/bits.zig +++ b/src/arch/x86_64/bits.zig @@ -418,14 +418,18 @@ pub const Memory = union(enum) { tbyte, pub fn fromSize(size: u32) PtrSize { - return switch (size) { - 1 => .byte, - 2 => .word, - 4 => .dword, - 8 => .qword, - 10 => .tbyte, - else => unreachable, - }; + return if (size <= 1) + .byte + else if (size <= 2) + .word + else if (size <= 4) + .dword + else if (size <= 8) + .qword + else if (size == 10) + .tbyte + else + unreachable; } pub fn fromBitSize(bit_size: u64) PtrSize { From 21630ea17f1db8791c86ccb6b5e64c7390c52f61 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 11 Mar 2023 00:09:24 +0100 Subject: [PATCH 098/294] x86_64: apply couple of tweaks and pass behavior tests --- src/arch/x86_64/CodeGen.zig | 59 ++++++++++++++++++++++++++----------- src/arch/x86_64/Emit.zig | 5 +++- src/arch/x86_64/bits.zig | 2 +- 3 files changed, 46 insertions(+), 20 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index cd2b90051d..9ad89b48ba 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -2856,10 +2856,14 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type .immediate => |imm| { switch (abi_size) { 1, 2, 4 => { + const immediate = if (value_ty.isSignedInt()) + Immediate.s(@intCast(i32, @bitCast(i64, imm))) + else + Immediate.u(@truncate(u32, imm)); try self.asmMemoryImmediate(.mov, Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = reg.to64(), .disp = 0, - }), Immediate.u(@truncate(u32, imm))); + }), immediate); }, 8 => { // TODO: optimization: if the imm is only using the lower @@ -3580,7 +3584,7 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu const actual_tag: Mir.Inst.Tag = switch (dst_ty.tag()) { .f32 => switch (mir_tag) { .add => .addss, - .cmp => .cmpss, + .cmp => .ucomiss, else => return self.fail( "TODO genBinOpMir for f32 register-register with MIR tag {}", .{mir_tag}, @@ -3588,7 +3592,7 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu }, .f64 => switch (mir_tag) { .add => .addsd, - .cmp => .cmpsd, + .cmp => .ucomisd, else => return self.fail( "TODO genBinOpMir for f64 register-register with MIR tag {}", .{mir_tag}, @@ -3599,7 +3603,7 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu .{dst_ty.fmtDebug()}, ), }; - try self.asmRegisterRegister(actual_tag, dst_reg.to128(), src_reg.to128()); + return self.asmRegisterRegister(actual_tag, dst_reg.to128(), src_reg.to128()); } return self.fail("TODO genBinOpMir for float register-register and no intrinsics", .{}); @@ -5255,11 +5259,14 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE // TODO // We have a positive stack offset value but we want a twos complement negative // offset from rbp, which is at the top of the stack frame. - // mov [rbp+offset], immediate + const immediate = if (ty.isSignedInt()) + Immediate.s(@intCast(i32, @bitCast(i64, imm))) + else + Immediate.u(@intCast(u32, imm)); try self.asmMemoryImmediate(.mov, Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = .rsp, .disp = -stack_offset, - }), Immediate.u(@intCast(u32, imm))); + }), immediate); }, 8 => { const reg = try self.copyToTmpRegister(ty, mcv); @@ -5340,10 +5347,19 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl if (!self.wantSafety()) return; // The already existing value will do just fine. // TODO Upgrade this to a memset call when we have that available. - switch (ty.abiSize(self.target.*)) { - 1 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaa }, opts), - 2 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaa }, opts), - 4 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaa }, opts), + switch (abi_size) { + 1, 2, 4 => { + const value: u64 = switch (abi_size) { + 1 => 0xaa, + 2 => 0xaaaa, + 4 => 0xaaaaaaaa, + else => unreachable, + }; + return self.asmMemoryImmediate(.mov, Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ + .base = opts.dest_stack_base orelse .rbp, + .disp = -stack_offset, + }), Immediate.u(value)); + }, 8 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaaaaaaaaaa }, opts), else => |x| return self.genInlineMemset( .{ .stack_offset = stack_offset }, @@ -5385,13 +5401,17 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl try self.asmMemoryImmediate(.mov, Memory.sib(.byte, .{ .base = base_reg, .disp = -stack_offset, - }), Immediate.u(@truncate(u32, x_big))); + }), Immediate.u(@truncate(u8, x_big))); }, 1, 2, 4 => { + const immediate = if (ty.isSignedInt()) + Immediate.s(@truncate(i32, @bitCast(i64, x_big))) + else + Immediate.u(@intCast(u32, x_big)); try self.asmMemoryImmediate(.mov, Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = base_reg, .disp = -stack_offset, - }), Immediate.u(@truncate(u32, x_big))); + }), immediate); }, 8 => { // 64 bit write to memory would take two mov's anyways so we @@ -5777,12 +5797,15 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void // register is the fastest way to zero a register. return self.asmRegisterRegister(.xor, reg.to32(), reg.to32()); } - if (ty.isSignedInt() and x <= math.maxInt(i32)) { - return self.asmRegisterImmediate( - .mov, - registerAlias(reg, abi_size), - Immediate.s(@intCast(i32, @bitCast(i64, x))), - ); + if (ty.isSignedInt()) { + const signed_x = @bitCast(i64, x); + if (math.minInt(i32) <= signed_x and signed_x <= math.maxInt(i32)) { + return self.asmRegisterImmediate( + .mov, + registerAlias(reg, abi_size), + Immediate.s(@intCast(i32, signed_x)), + ); + } } return self.asmRegisterImmediate( .mov, diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index c490dea497..a660e3f9ba 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -341,7 +341,10 @@ fn mirMovsx(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { else => unreachable, // TODO } - const mnemonic: Instruction.Mnemonic = if (op1.bitSize() == 64 and op2.bitSize() == 32) .movsxd else .movsx; + const mnemonic: Instruction.Mnemonic = switch (op1.bitSize()) { + 32, 64 => if (op2.bitSize() == 32) .movsxd else .movsx, + else => .movsx, + }; return emit.encode(mnemonic, .{ .op1 = op1, diff --git a/src/arch/x86_64/bits.zig b/src/arch/x86_64/bits.zig index 1828cdc08f..d974070e5d 100644 --- a/src/arch/x86_64/bits.zig +++ b/src/arch/x86_64/bits.zig @@ -542,7 +542,7 @@ pub const Immediate = union(enum) { .signed => |x| switch (bit_size) { 1, 8 => @bitCast(u8, @intCast(i8, x)), 16 => @bitCast(u16, @intCast(i16, x)), - 32 => @bitCast(u32, @intCast(i32, x)), + 32, 64 => @bitCast(u32, x), else => unreachable, }, .unsigned => |x| switch (bit_size) { From 621fc36b55a882c562d9378d4cf1fd8a5e1a907c Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 11 Mar 2023 09:21:41 +0100 Subject: [PATCH 099/294] x86_64: add wrapper for .jmp_reloc --- src/arch/x86_64/CodeGen.zig | 48 +++++++++++-------------------------- src/arch/x86_64/Emit.zig | 28 +++++++++------------- src/arch/x86_64/Mir.zig | 4 +--- 3 files changed, 26 insertions(+), 54 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 9ad89b48ba..fabca1d848 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -423,6 +423,14 @@ fn asmCmovccRegisterRegister(self: *Self, reg1: Register, reg2: Register, cc: bi }); } +fn asmJmpReloc(self: *Self, target: Mir.Inst.Index) !Mir.Inst.Index { + return self.addInst(.{ + .tag = .jmp_reloc, + .ops = undefined, + .data = .{ .inst = target }, + }); +} + fn asmNone(self: *Self, tag: Mir.Inst.Tag) !void { _ = try self.addInst(.{ .tag = tag, @@ -4145,11 +4153,7 @@ fn airRet(self: *Self, inst: Air.Inst.Index) !void { // TODO when implementing defer, this will need to jump to the appropriate defer expression. // TODO optimization opportunity: figure out when we can emit this as a 2 byte instruction // which is available if the jump is 127 bytes or less forward. - const jmp_reloc = try self.addInst(.{ - .tag = .jmp_reloc, - .ops = .inst, - .data = .{ .inst = undefined }, - }); + const jmp_reloc = try self.asmJmpReloc(undefined); try self.exitlude_jump_relocs.append(self.gpa, jmp_reloc); return self.finishAir(inst, .dead, .{ un_op, .none, .none }); } @@ -4181,11 +4185,7 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { // TODO when implementing defer, this will need to jump to the appropriate defer expression. // TODO optimization opportunity: figure out when we can emit this as a 2 byte instruction // which is available if the jump is 127 bytes or less forward. - const jmp_reloc = try self.addInst(.{ - .tag = .jmp_reloc, - .ops = .inst, - .data = .{ .inst = undefined }, - }); + const jmp_reloc = try self.asmJmpReloc(undefined); try self.exitlude_jump_relocs.append(self.gpa, jmp_reloc); return self.finishAir(inst, .dead, .{ un_op, .none, .none }); } @@ -4722,11 +4722,7 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void { const body = self.air.extra[loop.end..][0..loop.data.body_len]; const jmp_target = @intCast(u32, self.mir_instructions.len); try self.genBody(body); - _ = try self.addInst(.{ - .tag = .jmp_reloc, - .ops = .inst, - .data = .{ .inst = jmp_target }, - }); + _ = try self.asmJmpReloc(jmp_target); return self.finishAirBookkeeping(); } @@ -5062,11 +5058,7 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void { // Emit a jump with a relocation. It will be patched up after the block ends. try block_data.relocs.ensureUnusedCapacity(self.gpa, 1); // Leave the jump offset undefined - const jmp_reloc = try self.addInst(.{ - .tag = .jmp_reloc, - .ops = .inst, - .data = .{ .inst = undefined }, - }); + const jmp_reloc = try self.asmJmpReloc(undefined); block_data.relocs.appendAssumeCapacity(jmp_reloc); } @@ -5656,13 +5648,7 @@ fn genInlineMemcpy( }), tmp_reg.to8()); try self.asmRegisterImmediate(.add, index_reg, Immediate.u(1)); try self.asmRegisterImmediate(.sub, count_reg, Immediate.u(1)); - - _ = try self.addInst(.{ - .tag = .jmp_reloc, - .ops = .inst, - .data = .{ .inst = loop_start }, - }); - + _ = try self.asmJmpReloc(loop_start); try self.performReloc(loop_reloc); } @@ -5750,13 +5736,7 @@ fn genInlineMemset( } try self.asmRegisterImmediate(.sub, index_reg, Immediate.u(1)); - - _ = try self.addInst(.{ - .tag = .jmp_reloc, - .ops = .inst, - .data = .{ .inst = loop_start }, - }); - + _ = try self.asmJmpReloc(loop_start); try self.performReloc(loop_reloc); } diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index a660e3f9ba..6e92e81882 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -410,23 +410,17 @@ fn mirJcc(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { } fn mirJmpReloc(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { - const ops = emit.mir.instructions.items(.ops)[inst]; - switch (ops) { - .inst => { - const target = emit.mir.instructions.items(.data)[inst].inst; - const source = emit.code.items.len; - try emit.encode(.jmp, .{ - .op1 = .{ .imm = Immediate.s(0) }, - }); - try emit.relocs.append(emit.bin_file.allocator, .{ - .source = source, - .target = target, - .offset = emit.code.items.len - 4, - .length = 5, - }); - }, - else => unreachable, - } + const target = emit.mir.instructions.items(.data)[inst].inst; + const source = emit.code.items.len; + try emit.encode(.jmp, .{ + .op1 = .{ .imm = Immediate.s(0) }, + }); + try emit.relocs.append(emit.bin_file.allocator, .{ + .source = source, + .target = target, + .offset = emit.code.items.len - 4, + .length = 5, + }); } fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { diff --git a/src/arch/x86_64/Mir.zig b/src/arch/x86_64/Mir.zig index 6f0d578662..5de8ad1410 100644 --- a/src/arch/x86_64/Mir.zig +++ b/src/arch/x86_64/Mir.zig @@ -140,6 +140,7 @@ pub const Inst = struct { mov_moffs, /// Jump with relocation to another local MIR instruction + /// Uses `inst` payload. jmp_reloc, /// Call to an extern symbol via linker relocation. @@ -242,9 +243,6 @@ pub const Inst = struct { /// Memory moffs, rax. /// Uses `payload` with extra data of type `MemoryMoffs`. moffs_rax, - /// Lea into register with linker relocation. - /// Uses `payload` payload with data of type `LeaRegisterReloc`. - lea_r_reloc, /// References another Mir instruction directly. /// Uses `inst` payload. inst, From c9a153c7978b363a252f83878f75bd875fe6ae5e Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 11 Mar 2023 09:29:53 +0100 Subject: [PATCH 100/294] x86_64: add .dead pseudo-instruction to mark an unused MIR instruction --- src/arch/x86_64/CodeGen.zig | 16 ++++++++-------- src/arch/x86_64/Emit.zig | 2 ++ src/arch/x86_64/Mir.zig | 4 ++++ 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index fabca1d848..feb78ca7fc 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -630,8 +630,8 @@ fn gen(self: *Self) InnerError!void { // TODO During semantic analysis, check if there are no function calls. If there // are none, here we can omit the part where we subtract and then add rsp. const backpatch_stack_sub = try self.addInst(.{ - .tag = .nop, - .ops = .none, + .tag = .dead, + .ops = undefined, .data = undefined, }); @@ -657,8 +657,8 @@ fn gen(self: *Self) InnerError!void { // Push callee-preserved regs that were used actually in use. const backpatch_push_callee_preserved_regs = try self.addInst(.{ - .tag = .nop, - .ops = .none, + .tag = .dead, + .ops = undefined, .data = undefined, }); @@ -688,8 +688,8 @@ fn gen(self: *Self) InnerError!void { // Pop saved callee-preserved regs. const backpatch_pop_callee_preserved_regs = try self.addInst(.{ - .tag = .nop, - .ops = .none, + .tag = .dead, + .ops = undefined, .data = undefined, }); @@ -701,8 +701,8 @@ fn gen(self: *Self) InnerError!void { // Maybe add rsp, x if required. This is backpatched later. const backpatch_stack_add = try self.addInst(.{ - .tag = .nop, - .ops = .none, + .tag = .dead, + .ops = undefined, .data = undefined, }); diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index 6e92e81882..161c436323 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -137,6 +137,8 @@ pub fn lowerMir(emit: *Emit) InnerError!void { .push_regs => try emit.mirPushPopRegisterList(.push, inst), .pop_regs => try emit.mirPushPopRegisterList(.pop, inst), + + .dead => {}, } } diff --git a/src/arch/x86_64/Mir.zig b/src/arch/x86_64/Mir.zig index 5de8ad1410..4cde4dd240 100644 --- a/src/arch/x86_64/Mir.zig +++ b/src/arch/x86_64/Mir.zig @@ -163,6 +163,10 @@ pub const Inst = struct { /// Pop registers /// Uses `payload` payload with data of type `SaveRegisterList`. pop_regs, + + /// Tombstone + /// Emitter should skip this instruction. + dead, }; pub const Ops = enum(u8) { From 0a8b5c20aa2402361a4e5698100902e47bd2c7c6 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 11 Mar 2023 09:37:54 +0100 Subject: [PATCH 101/294] x86_64: add wrapper for .jcc with relocation --- src/arch/x86_64/CodeGen.zig | 64 ++++++++++--------------------------- 1 file changed, 17 insertions(+), 47 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index feb78ca7fc..21c7adec3c 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -431,6 +431,17 @@ fn asmJmpReloc(self: *Self, target: Mir.Inst.Index) !Mir.Inst.Index { }); } +fn asmJccReloc(self: *Self, target: Mir.Inst.Index, cc: bits.Condition) !Mir.Inst.Index { + return self.addInst(.{ + .tag = .jcc, + .ops = .inst_cc, + .data = .{ .inst_cc = .{ + .inst = target, + .cc = cc, + } }, + }); +} + fn asmNone(self: *Self, tag: Mir.Inst.Tag) !void { _ = try self.addInst(.{ .tag = tag, @@ -4360,29 +4371,13 @@ fn genCondBrMir(self: *Self, ty: Type, mcv: MCValue) !u32 { const abi_size = ty.abiSize(self.target.*); switch (mcv) { .eflags => |cc| { - return self.addInst(.{ - .tag = .jcc, - .ops = .inst_cc, - .data = .{ - .inst_cc = .{ - .inst = undefined, - // Here we map the opposites since the jump is to the false branch. - .cc = cc.negate(), - }, - }, - }); + // Here we map the opposites since the jump is to the false branch. + return self.asmJccReloc(undefined, cc.negate()); }, .register => |reg| { try self.spillEflagsIfOccupied(); try self.asmRegisterImmediate(.@"test", reg, Immediate.u(1)); - return self.addInst(.{ - .tag = .jcc, - .ops = .inst_cc, - .data = .{ .inst_cc = .{ - .inst = undefined, - .cc = .e, - } }, - }); + return self.asmJccReloc(undefined, .e); }, .immediate, .stack_offset, @@ -4792,15 +4787,7 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u const aliased_reg = registerAlias(cond_reg, abi_size); try self.asmRegisterRegister(.@"test", aliased_reg, aliased_reg); - - return self.addInst(.{ - .tag = .jcc, - .ops = .inst_cc, - .data = .{ .inst_cc = .{ - .inst = undefined, - .cc = .ne, - } }, - }); + return self.asmJccReloc(undefined, .ne); }, .stack_offset => { try self.spillEflagsIfOccupied(); @@ -5612,7 +5599,6 @@ fn genInlineMemcpy( try self.genSetReg(Type.usize, count_reg, len); try self.asmRegisterImmediate(.mov, index_reg, Immediate.u(0)); - const loop_start = try self.addInst(.{ .tag = .cmp, .ops = .ri_u, @@ -5621,15 +5607,7 @@ fn genInlineMemcpy( .imm = 0, } }, }); - const loop_reloc = try self.addInst(.{ - .tag = .jcc, - .ops = .inst_cc, - .data = .{ .inst_cc = .{ - .inst = undefined, - .cc = .e, - } }, - }); - + const loop_reloc = try self.asmJccReloc(undefined, .e); try self.asmRegisterMemory(.mov, tmp_reg.to8(), Memory.sib(.byte, .{ .base = src_addr_reg, .scale_index = .{ @@ -5708,15 +5686,7 @@ fn genInlineMemset( .imm = -1, } }, }); - - const loop_reloc = try self.addInst(.{ - .tag = .jcc, - .ops = .inst_cc, - .data = .{ .inst_cc = .{ - .inst = undefined, - .cc = .e, - } }, - }); + const loop_reloc = try self.asmJccReloc(undefined, .e); switch (value) { .immediate => |x| { From fb38e3d6b29f71834c7ea4ef21e4d3f607c03777 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 11 Mar 2023 16:32:30 +0100 Subject: [PATCH 102/294] x86_64: simplify immediate handling at MIR level --- src/arch/x86_64/CodeGen.zig | 77 +++++++++---------- src/arch/x86_64/Emit.zig | 142 +++++++++++++++++++----------------- src/arch/x86_64/Mir.zig | 55 +++++--------- 3 files changed, 128 insertions(+), 146 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 21c7adec3c..df06b90e4a 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -460,15 +460,13 @@ fn asmRegister(self: *Self, tag: Mir.Inst.Tag, reg: Register) !void { fn asmImmediate(self: *Self, tag: Mir.Inst.Tag, imm: Immediate) !void { const ops: Mir.Inst.Ops = if (imm == .signed) .imm_s else .imm_u; - const data: Mir.Inst.Data = switch (ops) { - .imm_s => .{ .imm_s = imm.signed }, - .imm_u => .{ .imm_u = @intCast(u32, imm.unsigned) }, - else => unreachable, - }; _ = try self.addInst(.{ .tag = tag, .ops = ops, - .data = data, + .data = .{ .imm = switch (imm) { + .signed => |x| @bitCast(u32, x), + .unsigned => |x| @intCast(u32, x), + } }, }); } @@ -489,11 +487,11 @@ fn asmRegisterImmediate(self: *Self, tag: Mir.Inst.Tag, reg: Register, imm: Imme .unsigned => |x| if (x <= math.maxInt(u32)) .ri_u else .ri64, }; const data: Mir.Inst.Data = switch (ops) { - .ri_s => .{ .ri_s = .{ + .ri_s => .{ .ri = .{ .r1 = reg, - .imm = imm.signed, + .imm = @bitCast(u32, imm.signed), } }, - .ri_u => .{ .ri_u = .{ + .ri_u => .{ .ri = .{ .r1 = reg, .imm = @intCast(u32, imm.unsigned), } }, @@ -522,12 +520,12 @@ fn asmRegisterRegisterImmediate( .unsigned => .rri_u, }; const data: Mir.Inst.Data = switch (ops) { - .rri_s => .{ .rri_s = .{ + .rri_s => .{ .rri = .{ .r1 = reg1, .r2 = reg2, - .imm = imm.signed, + .imm = @bitCast(u32, imm.signed), } }, - .rri_u => .{ .rri_u = .{ + .rri_u => .{ .rri = .{ .r1 = reg1, .r2 = reg2, .imm = @intCast(u32, imm.unsigned), @@ -547,11 +545,11 @@ fn asmMemory(self: *Self, tag: Mir.Inst.Tag, m: Memory) !void { .rip => .m_rip, else => unreachable, }; - const data: Mir.Inst.Data = switch (ops) { - .m_sib => .{ .payload = try self.addExtra(Mir.MemorySib.encode(m)) }, - .m_rip => .{ .payload = try self.addExtra(Mir.MemoryRip.encode(m)) }, + const data: Mir.Inst.Data = .{ .payload = switch (ops) { + .m_sib => try self.addExtra(Mir.MemorySib.encode(m)), + .m_rip => try self.addExtra(Mir.MemoryRip.encode(m)), else => unreachable, - }; + } }; _ = try self.addInst(.{ .tag = tag, .ops = ops, @@ -570,10 +568,11 @@ fn asmMemoryImmediate(self: *Self, tag: Mir.Inst.Tag, m: Memory, imm: Immediate) .mi_s_rip, .mi_u_rip => try self.addExtra(Mir.MemoryRip.encode(m)), else => unreachable, }; - const data: Mir.Inst.Data = switch (ops) { - .mi_s_sib, .mi_s_rip => .{ .xi_s = .{ .imm = imm.signed, .payload = payload } }, - .mi_u_sib, .mi_u_rip => .{ .xi_u = .{ .imm = @intCast(u32, imm.unsigned), .payload = payload } }, - else => unreachable, + const data: Mir.Inst.Data = .{ + .xi = .{ .payload = payload, .imm = switch (imm) { + .signed => |x| @bitCast(u32, x), + .unsigned => |x| @intCast(u32, x), + } }, }; _ = try self.addInst(.{ .tag = tag, @@ -588,16 +587,12 @@ fn asmRegisterMemory(self: *Self, tag: Mir.Inst.Tag, reg: Register, m: Memory) ! .rip => .rm_rip, else => unreachable, }; - const data: Mir.Inst.Data = switch (ops) { - .rm_sib => .{ .rx = .{ - .r1 = reg, - .payload = try self.addExtra(Mir.MemorySib.encode(m)), + const data: Mir.Inst.Data = .{ + .rx = .{ .r1 = reg, .payload = switch (ops) { + .rm_sib => try self.addExtra(Mir.MemorySib.encode(m)), + .rm_rip => try self.addExtra(Mir.MemoryRip.encode(m)), + else => unreachable, } }, - .rm_rip => .{ .rx = .{ - .r1 = reg, - .payload = try self.addExtra(Mir.MemoryRip.encode(m)), - } }, - else => unreachable, }; _ = try self.addInst(.{ .tag = tag, @@ -612,16 +607,12 @@ fn asmMemoryRegister(self: *Self, tag: Mir.Inst.Tag, m: Memory, reg: Register) ! .rip => .mr_rip, else => unreachable, }; - const data: Mir.Inst.Data = switch (ops) { - .mr_sib => .{ .rx = .{ - .r1 = reg, - .payload = try self.addExtra(Mir.MemorySib.encode(m)), + const data: Mir.Inst.Data = .{ + .rx = .{ .r1 = reg, .payload = switch (ops) { + .mr_sib => try self.addExtra(Mir.MemorySib.encode(m)), + .mr_rip => try self.addExtra(Mir.MemoryRip.encode(m)), + else => unreachable, } }, - .mr_rip => .{ .rx = .{ - .r1 = reg, - .payload = try self.addExtra(Mir.MemoryRip.encode(m)), - } }, - else => unreachable, }; _ = try self.addInst(.{ .tag = tag, @@ -733,7 +724,7 @@ fn gen(self: *Self) InnerError!void { self.mir_instructions.set(backpatch_stack_sub, .{ .tag = .sub, .ops = .ri_u, - .data = .{ .ri_u = .{ + .data = .{ .ri = .{ .r1 = .rsp, .imm = aligned_stack_end, } }, @@ -741,7 +732,7 @@ fn gen(self: *Self) InnerError!void { self.mir_instructions.set(backpatch_stack_add, .{ .tag = .add, .ops = .ri_u, - .data = .{ .ri_u = .{ + .data = .{ .ri = .{ .r1 = .rsp, .imm = aligned_stack_end, } }, @@ -5602,7 +5593,7 @@ fn genInlineMemcpy( const loop_start = try self.addInst(.{ .tag = .cmp, .ops = .ri_u, - .data = .{ .ri_u = .{ + .data = .{ .ri = .{ .r1 = count_reg, .imm = 0, } }, @@ -5681,9 +5672,9 @@ fn genInlineMemset( const loop_start = try self.addInst(.{ .tag = .cmp, .ops = .ri_s, - .data = .{ .ri_s = .{ + .data = .{ .ri = .{ .r1 = index_reg, - .imm = -1, + .imm = @bitCast(u32, @as(i32, -1)), } }, }); const loop_reloc = try self.asmJccReloc(undefined, .e); diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index 161c436323..5a7f2ef224 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -194,105 +194,115 @@ fn mirEncodeGeneric(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerE const ops = emit.mir.instructions.items(.ops)[inst]; const data = emit.mir.instructions.items(.data)[inst]; - var operands = [4]Instruction.Operand{ .none, .none, .none, .none }; + var op1: Instruction.Operand = .none; + var op2: Instruction.Operand = .none; + var op3: Instruction.Operand = .none; + var op4: Instruction.Operand = .none; + switch (ops) { .none => {}, - .imm_s => operands[0] = .{ .imm = Immediate.s(data.imm_s) }, - .imm_u => operands[0] = .{ .imm = Immediate.u(data.imm_u) }, - .r => operands[0] = .{ .reg = data.r }, - .rr => operands[0..2].* = .{ - .{ .reg = data.rr.r1 }, - .{ .reg = data.rr.r2 }, + .imm_s => op1 = .{ .imm = Immediate.s(@bitCast(i32, data.imm)) }, + .imm_u => op1 = .{ .imm = Immediate.u(data.imm) }, + .r => op1 = .{ .reg = data.r }, + .rr => { + op1 = .{ .reg = data.rr.r1 }; + op2 = .{ .reg = data.rr.r2 }; }, - .ri_s => operands[0..2].* = .{ - .{ .reg = data.ri_s.r1 }, - .{ .imm = Immediate.s(data.ri_s.imm) }, - }, - .ri_u => operands[0..2].* = .{ - .{ .reg = data.ri_u.r1 }, - .{ .imm = Immediate.u(data.ri_u.imm) }, + .ri_s, .ri_u => { + const imm = switch (ops) { + .ri_s => Immediate.s(@bitCast(i32, data.ri.imm)), + .ri_u => Immediate.u(data.ri.imm), + else => unreachable, + }; + op1 = .{ .reg = data.ri.r1 }; + op2 = .{ .imm = imm }; }, .ri64 => { const imm64 = emit.mir.extraData(Mir.Imm64, data.rx.payload).data; - operands[0..2].* = .{ - .{ .reg = data.rx.r1 }, - .{ .imm = Immediate.u(Mir.Imm64.decode(imm64)) }, + op1 = .{ .reg = data.rx.r1 }; + op2 = .{ .imm = Immediate.u(Mir.Imm64.decode(imm64)) }; + }, + .rri_s, .rri_u => { + const imm = switch (ops) { + .rri_s => Immediate.s(@bitCast(i32, data.rri.imm)), + .rri_u => Immediate.u(data.rri.imm), + else => unreachable, }; - }, - .rri_s => operands[0..3].* = .{ - .{ .reg = data.rri_s.r1 }, - .{ .reg = data.rri_s.r2 }, - .{ .imm = Immediate.s(data.rri_s.imm) }, - }, - .rri_u => operands[0..3].* = .{ - .{ .reg = data.rri_u.r1 }, - .{ .reg = data.rri_u.r2 }, - .{ .imm = Immediate.u(data.rri_u.imm) }, + op1 = .{ .reg = data.rri.r1 }; + op2 = .{ .reg = data.rri.r2 }; + op3 = .{ .imm = imm }; }, .m_sib => { const msib = emit.mir.extraData(Mir.MemorySib, data.payload).data; - operands[0] = .{ .mem = Mir.MemorySib.decode(msib) }; + op1 = .{ .mem = Mir.MemorySib.decode(msib) }; }, .m_rip => { const mrip = emit.mir.extraData(Mir.MemoryRip, data.payload).data; - operands[0] = .{ .mem = Mir.MemoryRip.decode(mrip) }; + op1 = .{ .mem = Mir.MemoryRip.decode(mrip) }; }, - .mi_u_sib => { - const msib = emit.mir.extraData(Mir.MemorySib, data.xi_u.payload).data; - operands[0..2].* = .{ - .{ .mem = Mir.MemorySib.decode(msib) }, - .{ .imm = Immediate.u(data.xi_u.imm) }, + .mi_s_sib, .mi_u_sib => { + const msib = emit.mir.extraData(Mir.MemorySib, data.xi.payload).data; + const imm = switch (ops) { + .mi_s_sib => Immediate.s(@bitCast(i32, data.xi.imm)), + .mi_u_sib => Immediate.u(data.xi.imm), + else => unreachable, }; + op1 = .{ .mem = Mir.MemorySib.decode(msib) }; + op2 = .{ .imm = imm }; }, - .mi_s_sib => { - const msib = emit.mir.extraData(Mir.MemorySib, data.xi_s.payload).data; - operands[0..2].* = .{ - .{ .mem = Mir.MemorySib.decode(msib) }, - .{ .imm = Immediate.s(data.xi_s.imm) }, - }; - }, - .mi_u_rip => { - const mrip = emit.mir.extraData(Mir.MemoryRip, data.xi_u.payload).data; - operands[0..2].* = .{ - .{ .mem = Mir.MemoryRip.decode(mrip) }, - .{ .imm = Immediate.u(data.xi_u.imm) }, - }; - }, - .mi_s_rip => { - const mrip = emit.mir.extraData(Mir.MemoryRip, data.xi_s.payload).data; - operands[0..2].* = .{ - .{ .mem = Mir.MemoryRip.decode(mrip) }, - .{ .imm = Immediate.s(data.xi_s.imm) }, + .mi_u_rip, .mi_s_rip => { + const mrip = emit.mir.extraData(Mir.MemoryRip, data.xi.payload).data; + const imm = switch (ops) { + .mi_s_rip => Immediate.s(@bitCast(i32, data.xi.imm)), + .mi_u_rip => Immediate.u(data.xi.imm), + else => unreachable, }; + op1 = .{ .mem = Mir.MemoryRip.decode(mrip) }; + op2 = .{ .imm = imm }; }, .rm_sib, .mr_sib => { const msib = emit.mir.extraData(Mir.MemorySib, data.rx.payload).data; - const op1 = .{ .reg = data.rx.r1 }; - const op2 = .{ .mem = Mir.MemorySib.decode(msib) }; + const op_r = .{ .reg = data.rx.r1 }; + const op_m = .{ .mem = Mir.MemorySib.decode(msib) }; switch (ops) { - .rm_sib => operands[0..2].* = .{ op1, op2 }, - .mr_sib => operands[0..2].* = .{ op2, op1 }, + .rm_sib => { + op1 = op_r; + op2 = op_m; + }, + .mr_sib => { + op1 = op_m; + op2 = op_r; + }, else => unreachable, } }, .rm_rip, .mr_rip => { const mrip = emit.mir.extraData(Mir.MemoryRip, data.rx.payload).data; - const op1 = .{ .reg = data.rx.r1 }; - const op2 = .{ .mem = Mir.MemoryRip.decode(mrip) }; + const op_r = .{ .reg = data.rx.r1 }; + const op_m = .{ .mem = Mir.MemoryRip.decode(mrip) }; switch (ops) { - .rm_rip => operands[0..2].* = .{ op1, op2 }, - .mr_rip => operands[0..2].* = .{ op2, op1 }, + .rm_sib => { + op1 = op_r; + op2 = op_m; + }, + .mr_sib => { + op1 = op_m; + op2 = op_r; + }, else => unreachable, } }, - else => unreachable, // TODO + else => return emit.fail("TODO handle generic encoding: {s}, {s}", .{ + @tagName(mnemonic), + @tagName(ops), + }), } return emit.encode(mnemonic, .{ - .op1 = operands[0], - .op2 = operands[1], - .op3 = operands[2], - .op4 = operands[3], + .op1 = op1, + .op2 = op2, + .op3 = op3, + .op4 = op4, }); } diff --git a/src/arch/x86_64/Mir.zig b/src/arch/x86_64/Mir.zig index 4cde4dd240..2f611258fd 100644 --- a/src/arch/x86_64/Mir.zig +++ b/src/arch/x86_64/Mir.zig @@ -182,10 +182,10 @@ pub const Inst = struct { /// Uses `rrr` payload. rrr, /// Register, register, immediate (sign-extended) operands. - /// Uses `rri_s` payload. + /// Uses `rri` payload. rri_s, /// Register, register, immediate (unsigned) operands. - /// Uses `rri_u` payload. + /// Uses `rri` payload. rri_u, /// Register with condition code (CC). /// Uses `r_c` payload. @@ -194,22 +194,22 @@ pub const Inst = struct { /// Uses `rr_c` payload. rr_c, /// Register, immediate (sign-extended) operands. - /// Uses `ri_s` payload. + /// Uses `ri` payload. ri_s, /// Register, immediate (unsigned) operands. - /// Uses `ri_u` payload. + /// Uses `ri` payload. ri_u, /// Register, 64-bit unsigned immediate operands. /// Uses `rx` payload with payload type `Imm64`. ri64, /// Immediate (sign-extended) operand. - /// Uses `imm_s` payload. + /// Uses `imm` payload. imm_s, /// Immediate (unsigned) operand. - /// Uses `imm_u` payload. + /// Uses `imm` payload. imm_u, /// Relative displacement operand. - /// Uses `rel` payload. + /// Uses `imm` payload. rel, /// Register, memory (SIB) operands. /// Uses `rx` payload. @@ -224,16 +224,16 @@ pub const Inst = struct { /// Uses `payload` with extra data of type `MemoryRip`. m_rip, /// Memory (SIB), immediate (unsigned) operands. - /// Uses `xi_u` payload with extra data of type `MemorySib`. + /// Uses `xi` payload with extra data of type `MemorySib`. mi_u_sib, /// Memory (RIP), immediate (unsigned) operands. - /// Uses `xi_u` payload with extra data of type `MemoryRip`. + /// Uses `xi` payload with extra data of type `MemoryRip`. mi_u_rip, /// Memory (SIB), immediate (sign-extend) operands. - /// Uses `xi_s` payload with extra data of type `MemorySib`. + /// Uses `xi` payload with extra data of type `MemorySib`. mi_s_sib, /// Memory (RIP), immediate (sign-extend) operands. - /// Uses `xi_s` payload with extra data of type `MemoryRip`. + /// Uses `xi` payload with extra data of type `MemoryRip`. mi_s_rip, /// Memory (SIB), register operands. /// Uses `rx` payload with extra data of type `MemorySib`. @@ -281,12 +281,8 @@ pub const Inst = struct { /// A condition code for use with EFLAGS register. cc: bits.Condition, }, - /// A 32-bit signed immediate value. - imm_s: i32, - /// A 32-bit unsigned immediate value. - imm_u: u32, - /// A 32-bit signed relative offset value. - rel: i32, + /// A 32-bit immediate value. + imm: u32, r: Register, rr: struct { r1: Register, @@ -297,12 +293,7 @@ pub const Inst = struct { r2: Register, r3: Register, }, - rri_s: struct { - r1: Register, - r2: Register, - imm: i32, - }, - rri_u: struct { + rri: struct { r1: Register, r2: Register, imm: u32, @@ -318,13 +309,8 @@ pub const Inst = struct { r2: Register, cc: bits.Condition, }, - /// Register, signed immediate. - ri_s: struct { - r1: Register, - imm: i32, - }, - /// Register, unsigned immediate. - ri_u: struct { + /// Register, immediate. + ri: struct { r1: Register, imm: u32, }, @@ -333,16 +319,11 @@ pub const Inst = struct { r1: Register, payload: u32, }, - /// Custom payload followed by an unsigned immediate. - xi_u: struct { + /// Custom payload followed by an immediate. + xi: struct { payload: u32, imm: u32, }, - /// Custom payload followed by a signed immediate. - xi_s: struct { - payload: u32, - imm: i32, - }, /// Relocation for the linker where: /// * `atom_index` is the index of the source /// * `sym_index` is the index of the target From f279ccb8078db9df92f02e23f70486f1950b92ed Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 11 Mar 2023 19:33:22 +0100 Subject: [PATCH 103/294] x86_64: rename asmNone to asmOpOnly --- src/arch/x86_64/CodeGen.zig | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index df06b90e4a..3b7dc0db57 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -442,7 +442,7 @@ fn asmJccReloc(self: *Self, target: Mir.Inst.Index, cc: bits.Condition) !Mir.Ins }); } -fn asmNone(self: *Self, tag: Mir.Inst.Tag) !void { +fn asmOpOnly(self: *Self, tag: Mir.Inst.Tag) !void { _ = try self.addInst(.{ .tag = tag, .ops = .none, @@ -709,7 +709,7 @@ fn gen(self: *Self) InnerError!void { }); try self.asmRegister(.pop, .rbp); - try self.asmNone(.ret); + try self.asmOpOnly(.ret); // Adjust the stack if (self.max_end_stack > math.maxInt(i32)) { @@ -1856,7 +1856,7 @@ fn genIntMulDivOpMir( } switch (signedness) { - .signed => try self.asmNone(.cqo), + .signed => try self.asmOpOnly(.cqo), .unsigned => try self.asmRegisterRegister(.xor, .rdx, .rdx), } @@ -3901,12 +3901,12 @@ fn genVarDbgInfo( } fn airTrap(self: *Self) !void { - try self.asmNone(.ud2); + try self.asmOpOnly(.ud2); return self.finishAirBookkeeping(); } fn airBreakpoint(self: *Self) !void { - try self.asmNone(.int3); + try self.asmOpOnly(.int3); return self.finishAirBookkeeping(); } @@ -5109,7 +5109,7 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { var iter = std.mem.tokenize(u8, asm_source, "\n\r"); while (iter.next()) |ins| { if (mem.eql(u8, ins, "syscall")) { - try self.asmNone(.syscall); + try self.asmOpOnly(.syscall); } else if (mem.indexOf(u8, ins, "push")) |_| { const arg = ins[4..]; if (mem.indexOf(u8, arg, "$")) |l| { From 433558a92f005d3ad68528c62ffd6006c48b80bd Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 11 Mar 2023 19:50:23 +0100 Subject: [PATCH 104/294] x86_64: clean up --- src/arch/x86_64/Emit.zig | 4 ++-- src/arch/x86_64/Encoding.zig | 2 +- src/arch/x86_64/Mir.zig | 11 ++++------- src/arch/x86_64/bits.zig | 7 ------- src/arch/x86_64/encoder.zig | 5 +++-- 5 files changed, 10 insertions(+), 19 deletions(-) diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index 5a7f2ef224..32699d35cb 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -177,12 +177,12 @@ fn encode(emit: *Emit, mnemonic: Instruction.Mnemonic, ops: struct { op3: Instruction.Operand = .none, op4: Instruction.Operand = .none, }) InnerError!void { - const inst = Instruction.new(mnemonic, .{ + const inst = try Instruction.new(mnemonic, .{ .op1 = ops.op1, .op2 = ops.op2, .op3 = ops.op3, .op4 = ops.op4, - }) catch unreachable; + }); return inst.encode(emit.code.writer()); } diff --git a/src/arch/x86_64/Encoding.zig b/src/arch/x86_64/Encoding.zig index 94f816eaa1..c6a8d044c3 100644 --- a/src/arch/x86_64/Encoding.zig +++ b/src/arch/x86_64/Encoding.zig @@ -121,7 +121,7 @@ pub fn findByMnemonic(mnemonic: Mnemonic, args: struct { .encoding = encoding, }; var cwriter = std.io.countingWriter(std.io.null_writer); - inst.encode(cwriter.writer()) catch unreachable; + inst.encode(cwriter.writer()) catch unreachable; // Not allowed to fail here unless OOM. return cwriter.bytes_written; } }; diff --git a/src/arch/x86_64/Mir.zig b/src/arch/x86_64/Mir.zig index 2f611258fd..3951108e3a 100644 --- a/src/arch/x86_64/Mir.zig +++ b/src/arch/x86_64/Mir.zig @@ -24,9 +24,6 @@ instructions: std.MultiArrayList(Inst).Slice, /// The meaning of this data is determined by `Inst.Tag` value. extra: []const u32, -pub const Mnemonic = encoder.Instruction.Mnemonic; -pub const Operand = encoder.Instruction.Operand; - pub const Inst = struct { tag: Tag, ops: Ops, @@ -69,8 +66,6 @@ pub const Inst = struct { imul, /// int3, - /// Conditional jump - jcc, /// Jump jmp, /// Load effective address @@ -99,8 +94,6 @@ pub const Inst = struct { sar, /// Integer subtraction with borrow sbb, - /// Set byte on condition - setcc, /// Logical shift left shl, /// Logical shift right @@ -135,6 +128,10 @@ pub const Inst = struct { /// Conditional move cmovcc, + /// Conditional jump + jcc, + /// Set byte on condition + setcc, /// Mov absolute to/from memory wrt segment register to/from rax mov_moffs, diff --git a/src/arch/x86_64/bits.zig b/src/arch/x86_64/bits.zig index d974070e5d..043e589af4 100644 --- a/src/arch/x86_64/bits.zig +++ b/src/arch/x86_64/bits.zig @@ -242,13 +242,6 @@ pub const Register = enum(u7) { }; } - pub fn isRexInvalid(reg: Register) bool { - return switch (@enumToInt(reg)) { - @enumToInt(Register.ah)...@enumToInt(Register.bh) => true, - else => false, - }; - } - pub fn enc(reg: Register) u4 { const base = switch (@enumToInt(reg)) { // zig fmt: off diff --git a/src/arch/x86_64/encoder.zig b/src/arch/x86_64/encoder.zig index 292b61ee21..9206b621bc 100644 --- a/src/arch/x86_64/encoder.zig +++ b/src/arch/x86_64/encoder.zig @@ -1,5 +1,6 @@ const std = @import("std"); const assert = std.debug.assert; +const log = std.log.scoped(.x86_64_encoder); const math = std.math; const bits = @import("bits.zig"); @@ -106,7 +107,7 @@ pub const Instruction = struct { .op3 = args.op3, .op4 = args.op4, })) orelse { - std.log.warn("{s} {s} {s} {s} {s}", .{ + log.debug("no encoding found for: {s} {s} {s} {s} {s}", .{ @tagName(mnemonic), @tagName(Encoding.Op.fromOperand(args.op1)), @tagName(Encoding.Op.fromOperand(args.op2)), @@ -115,7 +116,7 @@ pub const Instruction = struct { }); return error.InvalidInstruction; }; - std.log.debug("{}", .{encoding}); + log.debug("selected encoding: {}", .{encoding}); return .{ .op1 = args.op1, .op2 = args.op2, From 707a74655be9fd702cb1be84baa719b29435fcf7 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sun, 12 Mar 2023 08:41:44 +0100 Subject: [PATCH 105/294] x86_64: downstream encoder/assembler tests --- src/arch/x86_64/encoder.zig | 1489 +++++++++++++++++++++++++++++++++++ 1 file changed, 1489 insertions(+) diff --git a/src/arch/x86_64/encoder.zig b/src/arch/x86_64/encoder.zig index 9206b621bc..7e29f95069 100644 --- a/src/arch/x86_64/encoder.zig +++ b/src/arch/x86_64/encoder.zig @@ -2,6 +2,7 @@ const std = @import("std"); const assert = std.debug.assert; const log = std.log.scoped(.x86_64_encoder); const math = std.math; +const testing = std.testing; const bits = @import("bits.zig"); const Encoding = @import("Encoding.zig"); @@ -784,3 +785,1491 @@ pub const Rex = struct { return rex.w or rex.r or rex.x or rex.b; } }; + +// Tests +fn expectEqualHexStrings(expected: []const u8, given: []const u8, assembly: []const u8) !void { + assert(expected.len > 0); + if (std.mem.eql(u8, expected, given)) return; + const expected_fmt = try std.fmt.allocPrint(testing.allocator, "{x}", .{std.fmt.fmtSliceHexLower(expected)}); + defer testing.allocator.free(expected_fmt); + const given_fmt = try std.fmt.allocPrint(testing.allocator, "{x}", .{std.fmt.fmtSliceHexLower(given)}); + defer testing.allocator.free(given_fmt); + const idx = std.mem.indexOfDiff(u8, expected_fmt, given_fmt).?; + var padding = try testing.allocator.alloc(u8, idx + 5); + defer testing.allocator.free(padding); + std.mem.set(u8, padding, ' '); + std.debug.print("\nASM: {s}\nEXP: {s}\nGIV: {s}\n{s}^ -- first differing byte\n", .{ + assembly, + expected_fmt, + given_fmt, + padding, + }); + return error.TestFailed; +} + +const TestEncode = struct { + buffer: [32]u8 = undefined, + index: usize = 0, + + fn encode(enc: *TestEncode, mnemonic: Instruction.Mnemonic, args: struct { + op1: Instruction.Operand = .none, + op2: Instruction.Operand = .none, + op3: Instruction.Operand = .none, + op4: Instruction.Operand = .none, + }) !void { + var stream = std.io.fixedBufferStream(&enc.buffer); + var count_writer = std.io.countingWriter(stream.writer()); + const inst = try Instruction.new(mnemonic, .{ + .op1 = args.op1, + .op2 = args.op2, + .op3 = args.op3, + .op4 = args.op4, + }); + try inst.encode(count_writer.writer()); + enc.index = count_writer.bytes_written; + } + + fn code(enc: TestEncode) []const u8 { + return enc.buffer[0..enc.index]; + } +}; + +test "encode" { + var buf = std.ArrayList(u8).init(testing.allocator); + defer buf.deinit(); + + const inst = try Instruction.new(.mov, .{ + .op1 = .{ .reg = .rbx }, + .op2 = .{ .imm = Immediate.u(4) }, + }); + try inst.encode(buf.writer()); + try testing.expectEqualSlices(u8, &.{ 0x48, 0xc7, 0xc3, 0x4, 0x0, 0x0, 0x0 }, buf.items); +} + +test "lower I encoding" { + var enc = TestEncode{}; + + try enc.encode(.push, .{ .op1 = .{ .imm = Immediate.u(0x10) } }); + try expectEqualHexStrings("\x6A\x10", enc.code(), "push 0x10"); + + try enc.encode(.push, .{ .op1 = .{ .imm = Immediate.u(0x1000) } }); + try expectEqualHexStrings("\x66\x68\x00\x10", enc.code(), "push 0x1000"); + + try enc.encode(.push, .{ .op1 = .{ .imm = Immediate.u(0x10000000) } }); + try expectEqualHexStrings("\x68\x00\x00\x00\x10", enc.code(), "push 0x10000000"); + + try enc.encode(.adc, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .imm = Immediate.u(0x10000000) } }); + try expectEqualHexStrings("\x48\x15\x00\x00\x00\x10", enc.code(), "adc rax, 0x10000000"); + + try enc.encode(.add, .{ .op1 = .{ .reg = .al }, .op2 = .{ .imm = Immediate.u(0x10) } }); + try expectEqualHexStrings("\x04\x10", enc.code(), "add al, 0x10"); + + try enc.encode(.add, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .imm = Immediate.u(0x10) } }); + try expectEqualHexStrings("\x48\x83\xC0\x10", enc.code(), "add rax, 0x10"); + + try enc.encode(.sbb, .{ .op1 = .{ .reg = .ax }, .op2 = .{ .imm = Immediate.u(0x10) } }); + try expectEqualHexStrings("\x66\x1D\x10\x00", enc.code(), "sbb ax, 0x10"); + + try enc.encode(.xor, .{ .op1 = .{ .reg = .al }, .op2 = .{ .imm = Immediate.u(0x10) } }); + try expectEqualHexStrings("\x34\x10", enc.code(), "xor al, 0x10"); +} + +test "lower MI encoding" { + var enc = TestEncode{}; + + try enc.encode(.mov, .{ .op1 = .{ .reg = .r12 }, .op2 = .{ .imm = Immediate.u(0x1000) } }); + try expectEqualHexStrings("\x49\xC7\xC4\x00\x10\x00\x00", enc.code(), "mov r12, 0x1000"); + + try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.sib(.byte, .{ + .base = .r12, + .disp = 0, + }) }, .op2 = .{ .imm = Immediate.u(0x10) } }); + try expectEqualHexStrings("\x41\xC6\x04\x24\x10", enc.code(), "mov BYTE PTR [r12], 0x10"); + + try enc.encode(.mov, .{ .op1 = .{ .reg = .r12 }, .op2 = .{ .imm = Immediate.u(0x1000) } }); + try expectEqualHexStrings("\x49\xC7\xC4\x00\x10\x00\x00", enc.code(), "mov r12, 0x1000"); + + try enc.encode(.mov, .{ .op1 = .{ .reg = .r12 }, .op2 = .{ .imm = Immediate.u(0x1000) } }); + try expectEqualHexStrings("\x49\xC7\xC4\x00\x10\x00\x00", enc.code(), "mov r12, 0x1000"); + + try enc.encode(.mov, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .imm = Immediate.u(0x10) } }); + try expectEqualHexStrings("\x48\xc7\xc0\x10\x00\x00\x00", enc.code(), "mov rax, 0x10"); + + try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.sib(.dword, .{ + .base = .r11, + .disp = 0, + }) }, .op2 = .{ .imm = Immediate.u(0x10) } }); + try expectEqualHexStrings("\x41\xc7\x03\x10\x00\x00\x00", enc.code(), "mov DWORD PTR [r11], 0x10"); + + try enc.encode(.mov, .{ + .op1 = .{ .mem = Memory.rip(.qword, 0x10) }, + .op2 = .{ .imm = Immediate.u(0x10) }, + }); + try expectEqualHexStrings( + "\x48\xC7\x05\x10\x00\x00\x00\x10\x00\x00\x00", + enc.code(), + "mov QWORD PTR [rip + 0x10], 0x10", + ); + + try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.sib(.qword, .{ + .base = .rbp, + .disp = -8, + }) }, .op2 = .{ .imm = Immediate.u(0x10) } }); + try expectEqualHexStrings("\x48\xc7\x45\xf8\x10\x00\x00\x00", enc.code(), "mov QWORD PTR [rbp - 8], 0x10"); + + try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.sib(.word, .{ + .base = .rbp, + .disp = -2, + }) }, .op2 = .{ .imm = Immediate.s(-16) } }); + try expectEqualHexStrings("\x66\xC7\x45\xFE\xF0\xFF", enc.code(), "mov WORD PTR [rbp - 2], -16"); + + try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.sib(.byte, .{ + .base = .rbp, + .disp = -1, + }) }, .op2 = .{ .imm = Immediate.u(0x10) } }); + try expectEqualHexStrings("\xC6\x45\xFF\x10", enc.code(), "mov BYTE PTR [rbp - 1], 0x10"); + + try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.sib(.qword, .{ + .base = .ds, + .disp = 0x10000000, + .scale_index = .{ + .scale = 2, + .index = .rcx, + }, + }) }, .op2 = .{ .imm = Immediate.u(0x10) } }); + try expectEqualHexStrings( + "\x48\xC7\x04\x4D\x00\x00\x00\x10\x10\x00\x00\x00", + enc.code(), + "mov QWORD PTR [rcx*2 + 0x10000000], 0x10", + ); + + try enc.encode(.adc, .{ .op1 = .{ .mem = Memory.sib(.byte, .{ + .base = .rbp, + .disp = -0x10, + }) }, .op2 = .{ .imm = Immediate.u(0x10) } }); + try expectEqualHexStrings("\x80\x55\xF0\x10", enc.code(), "adc BYTE PTR [rbp - 0x10], 0x10"); + + try enc.encode(.adc, .{ .op1 = .{ .mem = Memory.rip(.qword, 0) }, .op2 = .{ .imm = Immediate.u(0x10) } }); + try expectEqualHexStrings("\x48\x83\x15\x00\x00\x00\x00\x10", enc.code(), "adc QWORD PTR [rip], 0x10"); + + try enc.encode(.adc, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .imm = Immediate.u(0x10) } }); + try expectEqualHexStrings("\x48\x83\xD0\x10", enc.code(), "adc rax, 0x10"); + + try enc.encode(.add, .{ .op1 = .{ .mem = Memory.sib(.dword, .{ + .base = .rdx, + .disp = -8, + }) }, .op2 = .{ .imm = Immediate.u(0x10) } }); + try expectEqualHexStrings("\x83\x42\xF8\x10", enc.code(), "add DWORD PTR [rdx - 8], 0x10"); + + try enc.encode(.add, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .imm = Immediate.u(0x10) } }); + try expectEqualHexStrings("\x48\x83\xC0\x10", enc.code(), "add rax, 0x10"); + + try enc.encode(.add, .{ .op1 = .{ .mem = Memory.sib(.qword, .{ + .base = .rbp, + .disp = -0x10, + }) }, .op2 = .{ .imm = Immediate.s(-0x10) } }); + try expectEqualHexStrings("\x48\x83\x45\xF0\xF0", enc.code(), "add QWORD PTR [rbp - 0x10], -0x10"); + + try enc.encode(.@"and", .{ .op1 = .{ .mem = Memory.sib(.dword, .{ + .base = .ds, + .disp = 0x10000000, + }) }, .op2 = .{ .imm = Immediate.u(0x10) } }); + try expectEqualHexStrings( + "\x83\x24\x25\x00\x00\x00\x10\x10", + enc.code(), + "and DWORD PTR ds:0x10000000, 0x10", + ); + + try enc.encode(.@"and", .{ .op1 = .{ .mem = Memory.sib(.dword, .{ + .base = .es, + .disp = 0x10000000, + }) }, .op2 = .{ .imm = Immediate.u(0x10) } }); + try expectEqualHexStrings( + "\x26\x83\x24\x25\x00\x00\x00\x10\x10", + enc.code(), + "and DWORD PTR es:0x10000000, 0x10", + ); + + try enc.encode(.@"and", .{ .op1 = .{ .mem = Memory.sib(.dword, .{ + .base = .r12, + .disp = 0x10000000, + }) }, .op2 = .{ .imm = Immediate.u(0x10) } }); + try expectEqualHexStrings( + "\x41\x83\xA4\x24\x00\x00\x00\x10\x10", + enc.code(), + "and DWORD PTR [r12 + 0x10000000], 0x10", + ); + + try enc.encode(.sub, .{ .op1 = .{ .mem = Memory.sib(.dword, .{ + .base = .r11, + .disp = 0x10000000, + }) }, .op2 = .{ .imm = Immediate.u(0x10) } }); + try expectEqualHexStrings( + "\x41\x83\xAB\x00\x00\x00\x10\x10", + enc.code(), + "sub DWORD PTR [r11 + 0x10000000], 0x10", + ); +} + +test "lower RM encoding" { + var enc = TestEncode{}; + + try enc.encode(.mov, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .mem = Memory.sib(.qword, .{ + .base = .r11, + .disp = 0, + }) } }); + try expectEqualHexStrings("\x49\x8b\x03", enc.code(), "mov rax, QWORD PTR [r11]"); + + try enc.encode(.mov, .{ .op1 = .{ .reg = .rbx }, .op2 = .{ .mem = Memory.sib(.qword, .{ + .base = .ds, + .disp = 0x10, + }) } }); + try expectEqualHexStrings("\x48\x8B\x1C\x25\x10\x00\x00\x00", enc.code(), "mov rbx, QWORD PTR ds:0x10"); + + try enc.encode(.mov, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .mem = Memory.sib(.qword, .{ + .base = .rbp, + .disp = -4, + }) } }); + try expectEqualHexStrings("\x48\x8B\x45\xFC", enc.code(), "mov rax, QWORD PTR [rbp - 4]"); + + try enc.encode(.mov, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .mem = Memory.sib(.qword, .{ + .base = .rbp, + .scale_index = .{ + .scale = 1, + .index = .rcx, + }, + .disp = -8, + }) } }); + try expectEqualHexStrings("\x48\x8B\x44\x0D\xF8", enc.code(), "mov rax, QWORD PTR [rbp + rcx*1 - 8]"); + + try enc.encode(.mov, .{ .op1 = .{ .reg = .eax }, .op2 = .{ .mem = Memory.sib(.dword, .{ + .base = .rbp, + .scale_index = .{ + .scale = 4, + .index = .rdx, + }, + .disp = -4, + }) } }); + try expectEqualHexStrings("\x8B\x44\x95\xFC", enc.code(), "mov eax, dword ptr [rbp + rdx*4 - 4]"); + + try enc.encode(.mov, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .mem = Memory.sib(.qword, .{ + .base = .rbp, + .scale_index = .{ + .scale = 8, + .index = .rcx, + }, + .disp = -8, + }) } }); + try expectEqualHexStrings("\x48\x8B\x44\xCD\xF8", enc.code(), "mov rax, QWORD PTR [rbp + rcx*8 - 8]"); + + try enc.encode(.mov, .{ .op1 = .{ .reg = .r8b }, .op2 = .{ .mem = Memory.sib(.byte, .{ + .base = .rsi, + .scale_index = .{ + .scale = 1, + .index = .rcx, + }, + .disp = -24, + }) } }); + try expectEqualHexStrings("\x44\x8A\x44\x0E\xE8", enc.code(), "mov r8b, BYTE PTR [rsi + rcx*1 - 24]"); + + // TODO this mnemonic needs cleanup as some prefixes are obsolete. + try enc.encode(.mov, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .reg = .cs } }); + try expectEqualHexStrings("\x48\x8C\xC8", enc.code(), "mov rax, cs"); + + try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.sib(.qword, .{ + .base = .rbp, + .disp = -16, + }) }, .op2 = .{ .reg = .fs } }); + try expectEqualHexStrings("\x48\x8C\x65\xF0", enc.code(), "mov QWORD PTR [rbp - 16], fs"); + + try enc.encode(.mov, .{ .op1 = .{ .reg = .r12w }, .op2 = .{ .reg = .cs } }); + try expectEqualHexStrings("\x66\x41\x8C\xCC", enc.code(), "mov r12w, cs"); + + try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.sib(.word, .{ + .base = .rbp, + .disp = -16, + }) }, .op2 = .{ .reg = .fs } }); + try expectEqualHexStrings("\x66\x8C\x65\xF0", enc.code(), "mov WORD PTR [rbp - 16], fs"); + + try enc.encode(.movsx, .{ .op1 = .{ .reg = .eax }, .op2 = .{ .reg = .bx } }); + try expectEqualHexStrings("\x0F\xBF\xC3", enc.code(), "movsx eax, bx"); + + try enc.encode(.movsx, .{ .op1 = .{ .reg = .eax }, .op2 = .{ .reg = .bl } }); + try expectEqualHexStrings("\x0F\xBE\xC3", enc.code(), "movsx eax, bl"); + + try enc.encode(.movsx, .{ .op1 = .{ .reg = .ax }, .op2 = .{ .reg = .bl } }); + try expectEqualHexStrings("\x66\x0F\xBE\xC3", enc.code(), "movsx ax, bl"); + + try enc.encode(.movsx, .{ .op1 = .{ .reg = .eax }, .op2 = .{ .mem = Memory.sib(.word, .{ + .base = .rbp, + .disp = 0, + }) } }); + try expectEqualHexStrings("\x0F\xBF\x45\x00", enc.code(), "movsx eax, BYTE PTR [rbp]"); + + try enc.encode(.movsx, .{ .op1 = .{ .reg = .eax }, .op2 = .{ .mem = Memory.sib(.byte, .{ + .base = null, + .scale_index = .{ + .index = .rax, + .scale = 2, + }, + .disp = 0, + }) } }); + try expectEqualHexStrings("\x0F\xBE\x04\x45\x00\x00\x00\x00", enc.code(), "movsx eax, BYTE PTR [rax * 2]"); + + try enc.encode(.movsx, .{ .op1 = .{ .reg = .ax }, .op2 = .{ .mem = Memory.rip(.byte, 0x10) } }); + try expectEqualHexStrings("\x66\x0F\xBE\x05\x10\x00\x00\x00", enc.code(), "movsx ax, BYTE PTR [rip + 0x10]"); + + try enc.encode(.movsx, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .reg = .bx } }); + try expectEqualHexStrings("\x48\x0F\xBF\xC3", enc.code(), "movsx rax, bx"); + + try enc.encode(.movsxd, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .reg = .ebx } }); + try expectEqualHexStrings("\x48\x63\xC3", enc.code(), "movsxd rax, ebx"); + + try enc.encode(.lea, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .mem = Memory.rip(.qword, 0x10) } }); + try expectEqualHexStrings("\x48\x8D\x05\x10\x00\x00\x00", enc.code(), "lea rax, QWORD PTR [rip + 0x10]"); + + try enc.encode(.lea, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .mem = Memory.rip(.dword, 0x10) } }); + try expectEqualHexStrings("\x48\x8D\x05\x10\x00\x00\x00", enc.code(), "lea rax, DWORD PTR [rip + 0x10]"); + + try enc.encode(.lea, .{ .op1 = .{ .reg = .eax }, .op2 = .{ .mem = Memory.rip(.dword, 0x10) } }); + try expectEqualHexStrings("\x8D\x05\x10\x00\x00\x00", enc.code(), "lea eax, DWORD PTR [rip + 0x10]"); + + try enc.encode(.lea, .{ .op1 = .{ .reg = .eax }, .op2 = .{ .mem = Memory.rip(.word, 0x10) } }); + try expectEqualHexStrings("\x8D\x05\x10\x00\x00\x00", enc.code(), "lea eax, WORD PTR [rip + 0x10]"); + + try enc.encode(.lea, .{ .op1 = .{ .reg = .ax }, .op2 = .{ .mem = Memory.rip(.byte, 0x10) } }); + try expectEqualHexStrings("\x66\x8D\x05\x10\x00\x00\x00", enc.code(), "lea ax, BYTE PTR [rip + 0x10]"); + + try enc.encode(.lea, .{ .op1 = .{ .reg = .rsi }, .op2 = .{ .mem = Memory.sib(.qword, .{ + .base = .rbp, + .scale_index = .{ + .scale = 1, + .index = .rcx, + }, + .disp = 0, + }) } }); + try expectEqualHexStrings("\x48\x8D\x74\x0D\x00", enc.code(), "lea rsi, QWORD PTR [rbp + rcx*1 + 0]"); + + try enc.encode(.add, .{ .op1 = .{ .reg = .r11 }, .op2 = .{ .mem = Memory.sib(.qword, .{ + .base = .ds, + .disp = 0x10000000, + }) } }); + try expectEqualHexStrings("\x4C\x03\x1C\x25\x00\x00\x00\x10", enc.code(), "add r11, QWORD PTR ds:0x10000000"); + + try enc.encode(.add, .{ .op1 = .{ .reg = .r12b }, .op2 = .{ .mem = Memory.sib(.byte, .{ + .base = .ds, + .disp = 0x10000000, + }) } }); + try expectEqualHexStrings("\x44\x02\x24\x25\x00\x00\x00\x10", enc.code(), "add r11b, BYTE PTR ds:0x10000000"); + + try enc.encode(.add, .{ .op1 = .{ .reg = .r12b }, .op2 = .{ .mem = Memory.sib(.byte, .{ + .base = .fs, + .disp = 0x10000000, + }) } }); + try expectEqualHexStrings("\x64\x44\x02\x24\x25\x00\x00\x00\x10", enc.code(), "add r11b, BYTE PTR fs:0x10000000"); + + try enc.encode(.sub, .{ .op1 = .{ .reg = .r11 }, .op2 = .{ .mem = Memory.sib(.qword, .{ + .base = .r13, + .disp = 0x10000000, + }) } }); + try expectEqualHexStrings("\x4D\x2B\x9D\x00\x00\x00\x10", enc.code(), "sub r11, QWORD PTR [r13 + 0x10000000]"); + + try enc.encode(.sub, .{ .op1 = .{ .reg = .r11 }, .op2 = .{ .mem = Memory.sib(.qword, .{ + .base = .r12, + .disp = 0x10000000, + }) } }); + try expectEqualHexStrings("\x4D\x2B\x9C\x24\x00\x00\x00\x10", enc.code(), "sub r11, QWORD PTR [r12 + 0x10000000]"); + + try enc.encode(.imul, .{ .op1 = .{ .reg = .r11 }, .op2 = .{ .reg = .r12 } }); + try expectEqualHexStrings("\x4D\x0F\xAF\xDC", enc.code(), "mov r11, r12"); +} + +test "lower RMI encoding" { + var enc = TestEncode{}; + + try enc.encode(.imul, .{ + .op1 = .{ .reg = .r11 }, + .op2 = .{ .reg = .r12 }, + .op3 = .{ .imm = Immediate.s(-2) }, + }); + try expectEqualHexStrings("\x4D\x6B\xDC\xFE", enc.code(), "imul r11, r12, -2"); + + try enc.encode(.imul, .{ + .op1 = .{ .reg = .r11 }, + .op2 = .{ .mem = Memory.rip(.qword, -16) }, + .op3 = .{ .imm = Immediate.s(-1024) }, + }); + try expectEqualHexStrings( + "\x4C\x69\x1D\xF0\xFF\xFF\xFF\x00\xFC\xFF\xFF", + enc.code(), + "imul r11, QWORD PTR [rip - 16], -1024", + ); + + try enc.encode(.imul, .{ + .op1 = .{ .reg = .bx }, + .op2 = .{ .mem = Memory.sib(.word, .{ + .base = .rbp, + .disp = -16, + }) }, + .op3 = .{ .imm = Immediate.s(-1024) }, + }); + try expectEqualHexStrings( + "\x66\x69\x5D\xF0\x00\xFC", + enc.code(), + "imul bx, WORD PTR [rbp - 16], -1024", + ); + + try enc.encode(.imul, .{ + .op1 = .{ .reg = .bx }, + .op2 = .{ .mem = Memory.sib(.word, .{ + .base = .rbp, + .disp = -16, + }) }, + .op3 = .{ .imm = Immediate.u(1024) }, + }); + try expectEqualHexStrings( + "\x66\x69\x5D\xF0\x00\x04", + enc.code(), + "imul bx, WORD PTR [rbp - 16], 1024", + ); +} + +test "lower MR encoding" { + var enc = TestEncode{}; + + try enc.encode(.mov, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .reg = .rbx } }); + try expectEqualHexStrings("\x48\x89\xD8", enc.code(), "mov rax, rbx"); + + try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.sib(.qword, .{ + .base = .rbp, + .disp = -4, + }) }, .op2 = .{ .reg = .r11 } }); + try expectEqualHexStrings("\x4c\x89\x5d\xfc", enc.code(), "mov QWORD PTR [rbp - 4], r11"); + + try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.rip(.qword, 0x10) }, .op2 = .{ .reg = .r12 } }); + try expectEqualHexStrings("\x4C\x89\x25\x10\x00\x00\x00", enc.code(), "mov QWORD PTR [rip + 0x10], r12"); + + try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.sib(.qword, .{ + .base = .r11, + .scale_index = .{ + .scale = 2, + .index = .r12, + }, + .disp = 0x10, + }) }, .op2 = .{ .reg = .r13 } }); + try expectEqualHexStrings("\x4F\x89\x6C\x63\x10", enc.code(), "mov QWORD PTR [r11 + 2 * r12 + 0x10], r13"); + + try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.rip(.word, -0x10) }, .op2 = .{ .reg = .r12w } }); + try expectEqualHexStrings("\x66\x44\x89\x25\xF0\xFF\xFF\xFF", enc.code(), "mov WORD PTR [rip - 0x10], r12w"); + + try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.sib(.byte, .{ + .base = .r11, + .scale_index = .{ + .scale = 2, + .index = .r12, + }, + .disp = 0x10, + }) }, .op2 = .{ .reg = .r13b } }); + try expectEqualHexStrings("\x47\x88\x6C\x63\x10", enc.code(), "mov BYTE PTR [r11 + 2 * r12 + 0x10], r13b"); + + try enc.encode(.add, .{ .op1 = .{ .mem = Memory.sib(.byte, .{ + .base = .ds, + .disp = 0x10000000, + }) }, .op2 = .{ .reg = .r12b } }); + try expectEqualHexStrings("\x44\x00\x24\x25\x00\x00\x00\x10", enc.code(), "add BYTE PTR ds:0x10000000, r12b"); + + try enc.encode(.add, .{ .op1 = .{ .mem = Memory.sib(.dword, .{ + .base = .ds, + .disp = 0x10000000, + }) }, .op2 = .{ .reg = .r12d } }); + try expectEqualHexStrings("\x44\x01\x24\x25\x00\x00\x00\x10", enc.code(), "add DWORD PTR [ds:0x10000000], r12d"); + + try enc.encode(.add, .{ .op1 = .{ .mem = Memory.sib(.dword, .{ + .base = .gs, + .disp = 0x10000000, + }) }, .op2 = .{ .reg = .r12d } }); + try expectEqualHexStrings("\x65\x44\x01\x24\x25\x00\x00\x00\x10", enc.code(), "add DWORD PTR [gs:0x10000000], r12d"); + + try enc.encode(.sub, .{ .op1 = .{ .mem = Memory.sib(.qword, .{ + .base = .r11, + .disp = 0x10000000, + }) }, .op2 = .{ .reg = .r12 } }); + try expectEqualHexStrings("\x4D\x29\xA3\x00\x00\x00\x10", enc.code(), "sub QWORD PTR [r11 + 0x10000000], r12"); +} + +test "lower M encoding" { + var enc = TestEncode{}; + + try enc.encode(.call, .{ .op1 = .{ .reg = .r12 } }); + try expectEqualHexStrings("\x41\xFF\xD4", enc.code(), "call r12"); + + try enc.encode(.call, .{ .op1 = .{ .mem = Memory.sib(.qword, .{ + .base = .r12, + .disp = 0, + }) } }); + try expectEqualHexStrings("\x41\xFF\x14\x24", enc.code(), "call QWORD PTR [r12]"); + + try enc.encode(.call, .{ .op1 = .{ .mem = Memory.sib(.qword, .{ + .base = null, + .scale_index = .{ + .index = .r11, + .scale = 2, + }, + .disp = 0, + }) } }); + try expectEqualHexStrings("\x42\xFF\x14\x5D\x00\x00\x00\x00", enc.code(), "call QWORD PTR [r11 * 2]"); + + try enc.encode(.call, .{ .op1 = .{ .mem = Memory.sib(.qword, .{ + .base = null, + .scale_index = .{ + .index = .r12, + .scale = 2, + }, + .disp = 0, + }) } }); + try expectEqualHexStrings("\x42\xFF\x14\x65\x00\x00\x00\x00", enc.code(), "call QWORD PTR [r12 * 2]"); + + try enc.encode(.call, .{ .op1 = .{ .mem = Memory.sib(.qword, .{ + .base = .gs, + .disp = 0, + }) } }); + try expectEqualHexStrings("\x65\xFF\x14\x25\x00\x00\x00\x00", enc.code(), "call gs:0x0"); + + try enc.encode(.call, .{ .op1 = .{ .imm = Immediate.s(0) } }); + try expectEqualHexStrings("\xE8\x00\x00\x00\x00", enc.code(), "call 0x0"); + + try enc.encode(.push, .{ .op1 = .{ .mem = Memory.sib(.qword, .{ + .base = .rbp, + .disp = 0, + }) } }); + try expectEqualHexStrings("\xFF\x75\x00", enc.code(), "push QWORD PTR [rbp]"); + + try enc.encode(.push, .{ .op1 = .{ .mem = Memory.sib(.word, .{ + .base = .rbp, + .disp = 0, + }) } }); + try expectEqualHexStrings("\x66\xFF\x75\x00", enc.code(), "push QWORD PTR [rbp]"); + + try enc.encode(.pop, .{ .op1 = .{ .mem = Memory.rip(.qword, 0) } }); + try expectEqualHexStrings("\x8F\x05\x00\x00\x00\x00", enc.code(), "pop QWORD PTR [rip]"); + + try enc.encode(.pop, .{ .op1 = .{ .mem = Memory.rip(.word, 0) } }); + try expectEqualHexStrings("\x66\x8F\x05\x00\x00\x00\x00", enc.code(), "pop WORD PTR [rbp]"); + + try enc.encode(.imul, .{ .op1 = .{ .reg = .rax } }); + try expectEqualHexStrings("\x48\xF7\xE8", enc.code(), "imul rax"); + + try enc.encode(.imul, .{ .op1 = .{ .reg = .r12 } }); + try expectEqualHexStrings("\x49\xF7\xEC", enc.code(), "imul r12"); +} + +test "lower O encoding" { + var enc = TestEncode{}; + + try enc.encode(.push, .{ .op1 = .{ .reg = .rax } }); + try expectEqualHexStrings("\x50", enc.code(), "push rax"); + + try enc.encode(.push, .{ .op1 = .{ .reg = .r12w } }); + try expectEqualHexStrings("\x66\x41\x54", enc.code(), "push r12w"); + + try enc.encode(.pop, .{ .op1 = .{ .reg = .r12 } }); + try expectEqualHexStrings("\x41\x5c", enc.code(), "pop r12"); +} + +test "lower OI encoding" { + var enc = TestEncode{}; + + try enc.encode(.mov, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .imm = Immediate.u(0x1000000000000000) } }); + try expectEqualHexStrings( + "\x48\xB8\x00\x00\x00\x00\x00\x00\x00\x10", + enc.code(), + "movabs rax, 0x1000000000000000", + ); + + try enc.encode(.mov, .{ .op1 = .{ .reg = .r11 }, .op2 = .{ .imm = Immediate.u(0x1000000000000000) } }); + try expectEqualHexStrings( + "\x49\xBB\x00\x00\x00\x00\x00\x00\x00\x10", + enc.code(), + "movabs r11, 0x1000000000000000", + ); + + try enc.encode(.mov, .{ .op1 = .{ .reg = .r11d }, .op2 = .{ .imm = Immediate.u(0x10000000) } }); + try expectEqualHexStrings("\x41\xBB\x00\x00\x00\x10", enc.code(), "mov r11d, 0x10000000"); + + try enc.encode(.mov, .{ .op1 = .{ .reg = .r11w }, .op2 = .{ .imm = Immediate.u(0x1000) } }); + try expectEqualHexStrings("\x66\x41\xBB\x00\x10", enc.code(), "mov r11w, 0x1000"); + + try enc.encode(.mov, .{ .op1 = .{ .reg = .r11b }, .op2 = .{ .imm = Immediate.u(0x10) } }); + try expectEqualHexStrings("\x41\xB3\x10", enc.code(), "mov r11b, 0x10"); +} + +test "lower FD/TD encoding" { + var enc = TestEncode{}; + + try enc.encode(.mov, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .mem = Memory.moffs(.cs, 0x10) } }); + try expectEqualHexStrings("\x2E\x48\xA1\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs rax, cs:0x10"); + + try enc.encode(.mov, .{ .op1 = .{ .reg = .eax }, .op2 = .{ .mem = Memory.moffs(.fs, 0x10) } }); + try expectEqualHexStrings("\x64\xA1\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs eax, fs:0x10"); + + try enc.encode(.mov, .{ .op1 = .{ .reg = .ax }, .op2 = .{ .mem = Memory.moffs(.gs, 0x10) } }); + try expectEqualHexStrings("\x65\x66\xA1\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs ax, gs:0x10"); + + try enc.encode(.mov, .{ .op1 = .{ .reg = .al }, .op2 = .{ .mem = Memory.moffs(.ds, 0x10) } }); + try expectEqualHexStrings("\xA0\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs al, ds:0x10"); + + try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.moffs(.cs, 0x10) }, .op2 = .{ .reg = .rax } }); + try expectEqualHexStrings("\x2E\x48\xA3\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs cs:0x10, rax"); + + try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.moffs(.fs, 0x10) }, .op2 = .{ .reg = .eax } }); + try expectEqualHexStrings("\x64\xA3\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs fs:0x10, eax"); + + try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.moffs(.gs, 0x10) }, .op2 = .{ .reg = .ax } }); + try expectEqualHexStrings("\x65\x66\xA3\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs gs:0x10, ax"); + + try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.moffs(.ds, 0x10) }, .op2 = .{ .reg = .al } }); + try expectEqualHexStrings("\xA2\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs ds:0x10, al"); +} + +test "lower NP encoding" { + var enc = TestEncode{}; + + try enc.encode(.int3, .{}); + try expectEqualHexStrings("\xCC", enc.code(), "int3"); + + try enc.encode(.nop, .{}); + try expectEqualHexStrings("\x90", enc.code(), "nop"); + + try enc.encode(.ret, .{}); + try expectEqualHexStrings("\xC3", enc.code(), "ret"); + + try enc.encode(.syscall, .{}); + try expectEqualHexStrings("\x0f\x05", enc.code(), "syscall"); +} + +fn invalidInstruction(mnemonic: Instruction.Mnemonic, args: struct { + op1: Instruction.Operand = .none, + op2: Instruction.Operand = .none, + op3: Instruction.Operand = .none, + op4: Instruction.Operand = .none, +}) !void { + const err = Instruction.new(mnemonic, .{ + .op1 = args.op1, + .op2 = args.op2, + .op3 = args.op3, + .op4 = args.op4, + }); + try testing.expectError(error.InvalidInstruction, err); +} + +test "invalid instruction" { + try invalidInstruction(.call, .{ .op1 = .{ .reg = .eax } }); + try invalidInstruction(.call, .{ .op1 = .{ .reg = .ax } }); + try invalidInstruction(.call, .{ .op1 = .{ .reg = .al } }); + try invalidInstruction(.call, .{ .op1 = .{ .mem = Memory.rip(.dword, 0) } }); + try invalidInstruction(.call, .{ .op1 = .{ .mem = Memory.rip(.word, 0) } }); + try invalidInstruction(.call, .{ .op1 = .{ .mem = Memory.rip(.byte, 0) } }); + try invalidInstruction(.mov, .{ .op1 = .{ .mem = Memory.rip(.word, 0x10) }, .op2 = .{ .reg = .r12 } }); + try invalidInstruction(.lea, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .reg = .rbx } }); + try invalidInstruction(.lea, .{ .op1 = .{ .reg = .al }, .op2 = .{ .mem = Memory.rip(.byte, 0) } }); + try invalidInstruction(.pop, .{ .op1 = .{ .reg = .r12b } }); + try invalidInstruction(.pop, .{ .op1 = .{ .reg = .r12d } }); + try invalidInstruction(.push, .{ .op1 = .{ .reg = .r12b } }); + try invalidInstruction(.push, .{ .op1 = .{ .reg = .r12d } }); + try invalidInstruction(.push, .{ .op1 = .{ .imm = Immediate.u(0x1000000000000000) } }); +} + +fn cannotEncode(mnemonic: Instruction.Mnemonic, args: struct { + op1: Instruction.Operand = .none, + op2: Instruction.Operand = .none, + op3: Instruction.Operand = .none, + op4: Instruction.Operand = .none, +}) !void { + try testing.expectError(error.CannotEncode, Instruction.new(mnemonic, .{ + .op1 = args.op1, + .op2 = args.op2, + .op3 = args.op3, + .op4 = args.op4, + })); +} + +test "cannot encode" { + try cannotEncode(.@"test", .{ + .op1 = .{ .mem = Memory.sib(.byte, .{ .base = .r12, .disp = 0 }) }, + .op2 = .{ .reg = .ah }, + }); + try cannotEncode(.@"test", .{ + .op1 = .{ .reg = .r11b }, + .op2 = .{ .reg = .bh }, + }); + try cannotEncode(.mov, .{ + .op1 = .{ .reg = .sil }, + .op2 = .{ .reg = .ah }, + }); +} + +const Assembler = struct { + it: Tokenizer, + + const Tokenizer = struct { + input: []const u8, + pos: usize = 0, + + const Error = error{InvalidToken}; + + const Token = struct { + id: Id, + start: usize, + end: usize, + + const Id = enum { + eof, + + space, + new_line, + + colon, + comma, + open_br, + close_br, + plus, + minus, + star, + + string, + numeral, + }; + }; + + const Iterator = struct {}; + + fn next(it: *Tokenizer) !Token { + var result = Token{ + .id = .eof, + .start = it.pos, + .end = it.pos, + }; + + var state: enum { + start, + space, + new_line, + string, + numeral, + numeral_hex, + } = .start; + + while (it.pos < it.input.len) : (it.pos += 1) { + const ch = it.input[it.pos]; + switch (state) { + .start => switch (ch) { + ',' => { + result.id = .comma; + it.pos += 1; + break; + }, + ':' => { + result.id = .colon; + it.pos += 1; + break; + }, + '[' => { + result.id = .open_br; + it.pos += 1; + break; + }, + ']' => { + result.id = .close_br; + it.pos += 1; + break; + }, + '+' => { + result.id = .plus; + it.pos += 1; + break; + }, + '-' => { + result.id = .minus; + it.pos += 1; + break; + }, + '*' => { + result.id = .star; + it.pos += 1; + break; + }, + ' ', '\t' => state = .space, + '\n', '\r' => state = .new_line, + 'a'...'z', 'A'...'Z' => state = .string, + '0'...'9' => state = .numeral, + else => return error.InvalidToken, + }, + + .space => switch (ch) { + ' ', '\t' => {}, + else => { + result.id = .space; + break; + }, + }, + + .new_line => switch (ch) { + '\n', '\r', ' ', '\t' => {}, + else => { + result.id = .new_line; + break; + }, + }, + + .string => switch (ch) { + 'a'...'z', 'A'...'Z', '0'...'9' => {}, + else => { + result.id = .string; + break; + }, + }, + + .numeral => switch (ch) { + 'x' => state = .numeral_hex, + '0'...'9' => {}, + else => { + result.id = .numeral; + break; + }, + }, + + .numeral_hex => switch (ch) { + 'a'...'f' => {}, + '0'...'9' => {}, + else => { + result.id = .numeral; + break; + }, + }, + } + } + + if (it.pos >= it.input.len) { + switch (state) { + .string => result.id = .string, + .numeral, .numeral_hex => result.id = .numeral, + else => {}, + } + } + + result.end = it.pos; + return result; + } + + fn seekTo(it: *Tokenizer, pos: usize) void { + it.pos = pos; + } + }; + + pub fn init(input: []const u8) Assembler { + return .{ + .it = Tokenizer{ .input = input }, + }; + } + + pub fn assemble(as: *Assembler, writer: anytype) !void { + while (try as.next()) |parsed_inst| { + const inst = try Instruction.new(parsed_inst.mnemonic, .{ + .op1 = parsed_inst.ops[0], + .op2 = parsed_inst.ops[1], + .op3 = parsed_inst.ops[2], + .op4 = parsed_inst.ops[3], + }); + try inst.encode(writer); + } + } + + const ParseResult = struct { + mnemonic: Instruction.Mnemonic, + ops: [4]Instruction.Operand, + }; + + const ParseError = error{ + UnexpectedToken, + InvalidMnemonic, + InvalidOperand, + InvalidRegister, + InvalidPtrSize, + InvalidMemoryOperand, + InvalidScaleIndex, + } || Tokenizer.Error || std.fmt.ParseIntError; + + fn next(as: *Assembler) ParseError!?ParseResult { + try as.skip(2, .{ .space, .new_line }); + const mnemonic_tok = as.expect(.string) catch |err| switch (err) { + error.UnexpectedToken => return if (try as.peek() == .eof) null else err, + else => return err, + }; + const mnemonic = mnemonicFromString(as.source(mnemonic_tok)) orelse + return error.InvalidMnemonic; + try as.skip(1, .{.space}); + + const rules = .{ + .{}, + .{.register}, + .{.memory}, + .{.immediate}, + .{ .register, .register }, + .{ .register, .memory }, + .{ .memory, .register }, + .{ .register, .immediate }, + .{ .memory, .immediate }, + .{ .register, .register, .immediate }, + .{ .register, .memory, .immediate }, + }; + + const pos = as.it.pos; + inline for (rules) |rule| { + var ops = [4]Instruction.Operand{ .none, .none, .none, .none }; + if (as.parseOperandRule(rule, &ops)) { + return .{ + .mnemonic = mnemonic, + .ops = ops, + }; + } else |_| { + as.it.seekTo(pos); + } + } + + return error.InvalidOperand; + } + + fn source(as: *Assembler, token: Tokenizer.Token) []const u8 { + return as.it.input[token.start..token.end]; + } + + fn peek(as: *Assembler) Tokenizer.Error!Tokenizer.Token.Id { + const pos = as.it.pos; + const next_tok = try as.it.next(); + const id = next_tok.id; + as.it.seekTo(pos); + return id; + } + + fn expect(as: *Assembler, id: Tokenizer.Token.Id) ParseError!Tokenizer.Token { + const next_tok_id = try as.peek(); + if (next_tok_id == id) return as.it.next(); + return error.UnexpectedToken; + } + + fn skip(as: *Assembler, comptime num: comptime_int, tok_ids: [num]Tokenizer.Token.Id) Tokenizer.Error!void { + outer: while (true) { + const pos = as.it.pos; + const next_tok = try as.it.next(); + inline for (tok_ids) |tok_id| { + if (next_tok.id == tok_id) continue :outer; + } + as.it.seekTo(pos); + break; + } + } + + fn mnemonicFromString(bytes: []const u8) ?Instruction.Mnemonic { + const ti = @typeInfo(Instruction.Mnemonic).Enum; + inline for (ti.fields) |field| { + if (std.mem.eql(u8, bytes, field.name)) { + return @field(Instruction.Mnemonic, field.name); + } + } + return null; + } + + fn parseOperandRule(as: *Assembler, rule: anytype, ops: *[4]Instruction.Operand) ParseError!void { + inline for (rule, 0..) |cond, i| { + comptime assert(i < 4); + if (i > 0) { + _ = try as.expect(.comma); + try as.skip(1, .{.space}); + } + if (@typeInfo(@TypeOf(cond)) != .EnumLiteral) { + @compileError("invalid condition in the rule: " ++ @typeName(@TypeOf(cond))); + } + switch (cond) { + .register => { + const reg_tok = try as.expect(.string); + const reg = registerFromString(as.source(reg_tok)) orelse + return error.InvalidOperand; + ops[i] = .{ .reg = reg }; + }, + .memory => { + const mem = try as.parseMemory(); + ops[i] = .{ .mem = mem }; + }, + .immediate => { + const is_neg = if (as.expect(.minus)) |_| true else |_| false; + const imm_tok = try as.expect(.numeral); + const imm: Immediate = if (is_neg) blk: { + const imm = try std.fmt.parseInt(i32, as.source(imm_tok), 0); + break :blk .{ .signed = imm * -1 }; + } else .{ .unsigned = try std.fmt.parseInt(u64, as.source(imm_tok), 0) }; + ops[i] = .{ .imm = imm }; + }, + else => @compileError("unhandled enum literal " ++ @tagName(cond)), + } + try as.skip(1, .{.space}); + } + + try as.skip(1, .{.space}); + const tok = try as.it.next(); + switch (tok.id) { + .new_line, .eof => {}, + else => return error.InvalidOperand, + } + } + + fn registerFromString(bytes: []const u8) ?Register { + const ti = @typeInfo(Register).Enum; + inline for (ti.fields) |field| { + if (std.mem.eql(u8, bytes, field.name)) { + return @field(Register, field.name); + } + } + return null; + } + + fn parseMemory(as: *Assembler) ParseError!Memory { + const ptr_size: ?Memory.PtrSize = blk: { + const pos = as.it.pos; + const ptr_size = as.parsePtrSize() catch |err| switch (err) { + error.UnexpectedToken => { + as.it.seekTo(pos); + break :blk null; + }, + else => return err, + }; + break :blk ptr_size; + }; + + try as.skip(1, .{.space}); + + // Supported rules and orderings. + const rules = .{ + .{ .open_br, .base, .close_br }, // [ base ] + .{ .open_br, .base, .plus, .disp, .close_br }, // [ base + disp ] + .{ .open_br, .base, .minus, .disp, .close_br }, // [ base - disp ] + .{ .open_br, .disp, .plus, .base, .close_br }, // [ disp + base ] + .{ .open_br, .base, .plus, .index, .close_br }, // [ base + index ] + .{ .open_br, .base, .plus, .index, .star, .scale, .close_br }, // [ base + index * scale ] + .{ .open_br, .index, .star, .scale, .plus, .base, .close_br }, // [ index * scale + base ] + .{ .open_br, .base, .plus, .index, .star, .scale, .plus, .disp, .close_br }, // [ base + index * scale + disp ] + .{ .open_br, .base, .plus, .index, .star, .scale, .minus, .disp, .close_br }, // [ base + index * scale - disp ] + .{ .open_br, .index, .star, .scale, .plus, .base, .plus, .disp, .close_br }, // [ index * scale + base + disp ] + .{ .open_br, .index, .star, .scale, .plus, .base, .minus, .disp, .close_br }, // [ index * scale + base - disp ] + .{ .open_br, .disp, .plus, .index, .star, .scale, .plus, .base, .close_br }, // [ disp + index * scale + base ] + .{ .open_br, .disp, .plus, .base, .plus, .index, .star, .scale, .close_br }, // [ disp + base + index * scale ] + .{ .open_br, .base, .plus, .disp, .plus, .index, .star, .scale, .close_br }, // [ base + disp + index * scale ] + .{ .open_br, .base, .minus, .disp, .plus, .index, .star, .scale, .close_br }, // [ base - disp + index * scale ] + .{ .open_br, .base, .plus, .disp, .plus, .scale, .star, .index, .close_br }, // [ base + disp + scale * index ] + .{ .open_br, .base, .minus, .disp, .plus, .scale, .star, .index, .close_br }, // [ base - disp + scale * index ] + .{ .open_br, .rip, .plus, .disp, .close_br }, // [ rip + disp ] + .{ .open_br, .rip, .minus, .disp, .close_br }, // [ rig - disp ] + .{ .base, .colon, .disp }, // seg:disp + }; + + const pos = as.it.pos; + inline for (rules) |rule| { + if (as.parseMemoryRule(rule)) |res| { + if (res.rip) { + if (res.base != null or res.scale_index != null or res.offset != null) + return error.InvalidMemoryOperand; + return Memory.rip(ptr_size orelse .qword, res.disp orelse 0); + } + if (res.base) |base| { + if (res.rip) + return error.InvalidMemoryOperand; + if (res.offset) |offset| { + if (res.scale_index != null or res.disp != null) + return error.InvalidMemoryOperand; + return Memory.moffs(base, offset); + } + return Memory.sib(ptr_size orelse .qword, .{ + .base = base, + .scale_index = res.scale_index, + .disp = res.disp orelse 0, + }); + } + return error.InvalidMemoryOperand; + } else |_| { + as.it.seekTo(pos); + } + } + + return error.InvalidOperand; + } + + const MemoryParseResult = struct { + rip: bool = false, + base: ?Register = null, + scale_index: ?Memory.ScaleIndex = null, + disp: ?i32 = null, + offset: ?u64 = null, + }; + + fn parseMemoryRule(as: *Assembler, rule: anytype) ParseError!MemoryParseResult { + var res: MemoryParseResult = .{}; + inline for (rule, 0..) |cond, i| { + if (@typeInfo(@TypeOf(cond)) != .EnumLiteral) { + @compileError("unsupported condition type in the rule: " ++ @typeName(@TypeOf(cond))); + } + switch (cond) { + .open_br, .close_br, .plus, .minus, .star, .colon => { + _ = try as.expect(cond); + }, + .base => { + const tok = try as.expect(.string); + res.base = registerFromString(as.source(tok)) orelse return error.InvalidMemoryOperand; + }, + .rip => { + const tok = try as.expect(.string); + if (!std.mem.eql(u8, as.source(tok), "rip")) return error.InvalidMemoryOperand; + res.rip = true; + }, + .index => { + const tok = try as.expect(.string); + const index = registerFromString(as.source(tok)) orelse + return error.InvalidMemoryOperand; + if (res.scale_index) |*si| { + si.index = index; + } else { + res.scale_index = .{ .scale = 1, .index = index }; + } + }, + .scale => { + const tok = try as.expect(.numeral); + const scale = try std.fmt.parseInt(u2, as.source(tok), 0); + if (res.scale_index) |*si| { + si.scale = scale; + } else { + res.scale_index = .{ .scale = scale, .index = undefined }; + } + }, + .disp => { + const tok = try as.expect(.numeral); + const is_neg = blk: { + if (i > 0) { + if (rule[i - 1] == .minus) break :blk true; + } + break :blk false; + }; + if (std.fmt.parseInt(i32, as.source(tok), 0)) |disp| { + res.disp = if (is_neg) -1 * disp else disp; + } else |err| switch (err) { + error.Overflow => { + if (is_neg) return err; + if (res.base) |base| { + if (base.class() != .segment) return err; + } + const offset = try std.fmt.parseInt(u64, as.source(tok), 0); + res.offset = offset; + }, + else => return err, + } + }, + else => @compileError("unhandled operand output type: " ++ @tagName(cond)), + } + try as.skip(1, .{.space}); + } + return res; + } + + fn parsePtrSize(as: *Assembler) ParseError!Memory.PtrSize { + const size = try as.expect(.string); + try as.skip(1, .{.space}); + const ptr = try as.expect(.string); + + const size_raw = as.source(size); + const ptr_raw = as.source(ptr); + const len = size_raw.len + ptr_raw.len + 1; + var buf: ["qword ptr".len]u8 = undefined; + if (len > buf.len) return error.InvalidPtrSize; + + for (size_raw, 0..) |c, i| { + buf[i] = std.ascii.toLower(c); + } + buf[size_raw.len] = ' '; + for (ptr_raw, 0..) |c, i| { + buf[size_raw.len + i + 1] = std.ascii.toLower(c); + } + + const slice = buf[0..len]; + if (std.mem.eql(u8, slice, "qword ptr")) return .qword; + if (std.mem.eql(u8, slice, "dword ptr")) return .dword; + if (std.mem.eql(u8, slice, "word ptr")) return .word; + if (std.mem.eql(u8, slice, "byte ptr")) return .byte; + if (std.mem.eql(u8, slice, "tbyte ptr")) return .tbyte; + return error.InvalidPtrSize; + } +}; + +test "assemble" { + const input = + \\int3 + \\mov rax, rbx + \\mov qword ptr [rbp], rax + \\mov qword ptr [rbp - 16], rax + \\mov qword ptr [16 + rbp], rax + \\mov rax, 0x10 + \\mov byte ptr [rbp - 0x10], 0x10 + \\mov word ptr [rbp + r12], r11w + \\mov word ptr [rbp + r12 * 2], r11w + \\mov word ptr [rbp + r12 * 2 - 16], r11w + \\mov dword ptr [rip - 16], r12d + \\mov rax, fs:0x0 + \\mov rax, gs:0x1000000000000000 + \\movzx r12, al + \\imul r12, qword ptr [rbp - 16], 6 + \\jmp 0x0 + \\jc 0x0 + \\jb 0x0 + \\sal rax, 1 + \\sal rax, 63 + \\shl rax, 63 + \\sar rax, 63 + \\shr rax, 63 + \\test byte ptr [rbp - 16], r12b + \\sal r12, cl + \\mul qword ptr [rip - 16] + \\div r12 + \\idiv byte ptr [rbp - 16] + \\cwde + \\cbw + \\cdqe + \\test byte ptr [rbp], ah + \\test byte ptr [r12], spl + \\cdq + \\cwd + \\cqo + \\test bl, 0x1 + \\mov rbx,0x8000000000000000 + \\movss xmm0, dword ptr [rbp] + \\movss xmm0, xmm1 + \\movss dword ptr [rbp - 16 + rax * 2], xmm7 + \\movss dword ptr [rbp - 16 + rax * 2], xmm8 + \\movss xmm15, xmm9 + \\movsd xmm8, qword ptr [rbp - 16] + \\movsd qword ptr [rbp - 8], xmm0 + \\movq xmm8, qword ptr [rbp - 16] + \\movq qword ptr [rbp - 16], xmm8 + \\ucomisd xmm0, qword ptr [rbp - 16] + \\fisttp qword ptr [rbp - 16] + \\fisttp word ptr [rip + 32] + \\fisttp dword ptr [rax] + \\fld tbyte ptr [rbp] + \\fld dword ptr [rbp] + \\xor bl, 0xff + \\ud2 + \\add rsp, -1 + \\add rsp, 0xff + \\mov sil, byte ptr [rax + rcx * 1] + \\ + ; + + // zig fmt: off + const expected = &[_]u8{ + 0xCC, + 0x48, 0x89, 0xD8, + 0x48, 0x89, 0x45, 0x00, + 0x48, 0x89, 0x45, 0xF0, + 0x48, 0x89, 0x45, 0x10, + 0x48, 0xC7, 0xC0, 0x10, 0x00, 0x00, 0x00, + 0xC6, 0x45, 0xF0, 0x10, + 0x66, 0x46, 0x89, 0x5C, 0x25, 0x00, + 0x66, 0x46, 0x89, 0x5C, 0x65, 0x00, + 0x66, 0x46, 0x89, 0x5C, 0x65, 0xF0, + 0x44, 0x89, 0x25, 0xF0, 0xFF, 0xFF, 0xFF, + 0x64, 0x48, 0x8B, 0x04, 0x25, 0x00, 0x00, 0x00, 0x00, + 0x65, 0x48, 0xA1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, + 0x4C, 0x0F, 0xB6, 0xE0, + 0x4C, 0x6B, 0x65, 0xF0, 0x06, + 0xE9, 0x00, 0x00, 0x00, 0x00, + 0x0F, 0x82, 0x00, 0x00, 0x00, 0x00, + 0x0F, 0x82, 0x00, 0x00, 0x00, 0x00, + 0x48, 0xD1, 0xE0, + 0x48, 0xC1, 0xE0, 0x3F, + 0x48, 0xC1, 0xE0, 0x3F, + 0x48, 0xC1, 0xF8, 0x3F, + 0x48, 0xC1, 0xE8, 0x3F, + 0x44, 0x84, 0x65, 0xF0, + 0x49, 0xD3, 0xE4, + 0x48, 0xF7, 0x25, 0xF0, 0xFF, 0xFF, 0xFF, + 0x49, 0xF7, 0xF4, + 0xF6, 0x7D, 0xF0, + 0x98, + 0x66, 0x98, + 0x48, 0x98, + 0x84, 0x65, 0x00, + 0x41, 0x84, 0x24, 0x24, + 0x99, + 0x66, 0x99, + 0x48, 0x99, + 0xF6, 0xC3, 0x01, + 0x48, 0xBB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0xF3, 0x0F, 0x10, 0x45, 0x00, + 0xF3, 0x0F, 0x10, 0xC1, + 0xF3, 0x0F, 0x11, 0x7C, 0x45, 0xF0, + 0xF3, 0x44, 0x0F, 0x11, 0x44, 0x45, 0xF0, + 0xF3, 0x45, 0x0F, 0x10, 0xF9, + 0xF2, 0x44, 0x0F, 0x10, 0x45, 0xF0, + 0xF2, 0x0F, 0x11, 0x45, 0xF8, + 0xF3, 0x44, 0x0F, 0x7E, 0x45, 0xF0, + 0x66, 0x44, 0x0F, 0xD6, 0x45, 0xF0, + 0x66, 0x0F, 0x2E, 0x45, 0xF0, + 0xDD, 0x4D, 0xF0, + 0xDF, 0x0D, 0x20, 0x00, 0x00, 0x00, + 0xDB, 0x08, + 0xDB, 0x6D, 0x00, + 0xD9, 0x45, 0x00, + 0x80, 0xF3, 0xFF, + 0x0F, 0x0B, + 0x48, 0x83, 0xC4, 0xFF, + 0x48, 0x81, 0xC4, 0xFF, 0x00, 0x00, 0x00, + 0x40, 0x8A, 0x34, 0x08, + }; + // zig fmt: on + + var as = Assembler.init(input); + var output = std.ArrayList(u8).init(testing.allocator); + defer output.deinit(); + try as.assemble(output.writer()); + try expectEqualHexStrings(expected, output.items, input); +} + +test "assemble - Jcc" { + const mnemonics = [_]struct { Instruction.Mnemonic, u8 }{ + .{ .ja, 0x87 }, + .{ .jae, 0x83 }, + .{ .jb, 0x82 }, + .{ .jbe, 0x86 }, + .{ .jc, 0x82 }, + .{ .je, 0x84 }, + .{ .jg, 0x8f }, + .{ .jge, 0x8d }, + .{ .jl, 0x8c }, + .{ .jle, 0x8e }, + .{ .jna, 0x86 }, + .{ .jnae, 0x82 }, + .{ .jnb, 0x83 }, + .{ .jnbe, 0x87 }, + .{ .jnc, 0x83 }, + .{ .jne, 0x85 }, + .{ .jng, 0x8e }, + .{ .jnge, 0x8c }, + .{ .jnl, 0x8d }, + .{ .jnle, 0x8f }, + .{ .jno, 0x81 }, + .{ .jnp, 0x8b }, + .{ .jns, 0x89 }, + .{ .jnz, 0x85 }, + .{ .jo, 0x80 }, + .{ .jp, 0x8a }, + .{ .jpe, 0x8a }, + .{ .jpo, 0x8b }, + .{ .js, 0x88 }, + .{ .jz, 0x84 }, + }; + + inline for (&mnemonics) |mnemonic| { + const input = @tagName(mnemonic[0]) ++ " 0x0"; + const expected = [_]u8{ 0x0f, mnemonic[1], 0x0, 0x0, 0x0, 0x0 }; + var as = Assembler.init(input); + var output = std.ArrayList(u8).init(testing.allocator); + defer output.deinit(); + try as.assemble(output.writer()); + try expectEqualHexStrings(&expected, output.items, input); + } +} + +test "assemble - SETcc" { + const mnemonics = [_]struct { Instruction.Mnemonic, u8 }{ + .{ .seta, 0x97 }, + .{ .setae, 0x93 }, + .{ .setb, 0x92 }, + .{ .setbe, 0x96 }, + .{ .setc, 0x92 }, + .{ .sete, 0x94 }, + .{ .setg, 0x9f }, + .{ .setge, 0x9d }, + .{ .setl, 0x9c }, + .{ .setle, 0x9e }, + .{ .setna, 0x96 }, + .{ .setnae, 0x92 }, + .{ .setnb, 0x93 }, + .{ .setnbe, 0x97 }, + .{ .setnc, 0x93 }, + .{ .setne, 0x95 }, + .{ .setng, 0x9e }, + .{ .setnge, 0x9c }, + .{ .setnl, 0x9d }, + .{ .setnle, 0x9f }, + .{ .setno, 0x91 }, + .{ .setnp, 0x9b }, + .{ .setns, 0x99 }, + .{ .setnz, 0x95 }, + .{ .seto, 0x90 }, + .{ .setp, 0x9a }, + .{ .setpe, 0x9a }, + .{ .setpo, 0x9b }, + .{ .sets, 0x98 }, + .{ .setz, 0x94 }, + }; + + inline for (&mnemonics) |mnemonic| { + const input = @tagName(mnemonic[0]) ++ " al"; + const expected = [_]u8{ 0x0f, mnemonic[1], 0xC0 }; + var as = Assembler.init(input); + var output = std.ArrayList(u8).init(testing.allocator); + defer output.deinit(); + try as.assemble(output.writer()); + try expectEqualHexStrings(&expected, output.items, input); + } +} + +test "assemble - CMOVcc" { + const mnemonics = [_]struct { Instruction.Mnemonic, u8 }{ + .{ .cmova, 0x47 }, + .{ .cmovae, 0x43 }, + .{ .cmovb, 0x42 }, + .{ .cmovbe, 0x46 }, + .{ .cmovc, 0x42 }, + .{ .cmove, 0x44 }, + .{ .cmovg, 0x4f }, + .{ .cmovge, 0x4d }, + .{ .cmovl, 0x4c }, + .{ .cmovle, 0x4e }, + .{ .cmovna, 0x46 }, + .{ .cmovnae, 0x42 }, + .{ .cmovnb, 0x43 }, + .{ .cmovnbe, 0x47 }, + .{ .cmovnc, 0x43 }, + .{ .cmovne, 0x45 }, + .{ .cmovng, 0x4e }, + .{ .cmovnge, 0x4c }, + .{ .cmovnl, 0x4d }, + .{ .cmovnle, 0x4f }, + .{ .cmovno, 0x41 }, + .{ .cmovnp, 0x4b }, + .{ .cmovns, 0x49 }, + .{ .cmovnz, 0x45 }, + .{ .cmovo, 0x40 }, + .{ .cmovp, 0x4a }, + .{ .cmovpe, 0x4a }, + .{ .cmovpo, 0x4b }, + .{ .cmovs, 0x48 }, + .{ .cmovz, 0x44 }, + }; + + inline for (&mnemonics) |mnemonic| { + const input = @tagName(mnemonic[0]) ++ " rax, rbx"; + const expected = [_]u8{ 0x48, 0x0f, mnemonic[1], 0xC3 }; + var as = Assembler.init(input); + var output = std.ArrayList(u8).init(testing.allocator); + defer output.deinit(); + try as.assemble(output.writer()); + try expectEqualHexStrings(&expected, output.items, input); + } +} From 955e394792af69fc8c795802c0165e5c86f5ea73 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sun, 12 Mar 2023 08:47:23 +0100 Subject: [PATCH 106/294] x86_64: fix 32bit build issues in the encoder --- src/arch/x86_64/Encoding.zig | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/arch/x86_64/Encoding.zig b/src/arch/x86_64/Encoding.zig index c6a8d044c3..7b00679b92 100644 --- a/src/arch/x86_64/Encoding.zig +++ b/src/arch/x86_64/Encoding.zig @@ -105,6 +105,7 @@ pub fn findByMnemonic(mnemonic: Mnemonic, args: struct { } if (count == 0) return null; + if (count == 1) return candidates[0]; const EncodingLength = struct { fn estimate(encoding: Encoding, params: struct { @@ -112,7 +113,7 @@ pub fn findByMnemonic(mnemonic: Mnemonic, args: struct { op2: Instruction.Operand, op3: Instruction.Operand, op4: Instruction.Operand, - }) !usize { + }) usize { var inst = Instruction{ .op1 = params.op1, .op2 = params.op2, @@ -122,7 +123,7 @@ pub fn findByMnemonic(mnemonic: Mnemonic, args: struct { }; var cwriter = std.io.countingWriter(std.io.null_writer); inst.encode(cwriter.writer()) catch unreachable; // Not allowed to fail here unless OOM. - return cwriter.bytes_written; + return @intCast(usize, cwriter.bytes_written); } }; @@ -138,7 +139,7 @@ pub fn findByMnemonic(mnemonic: Mnemonic, args: struct { else => {}, } - const len = try EncodingLength.estimate(candidate, .{ + const len = EncodingLength.estimate(candidate, .{ .op1 = args.op1, .op2 = args.op2, .op3 = args.op3, From a097779b611577b75475336ee282615984f77edf Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Sat, 11 Mar 2023 14:52:38 +0100 Subject: [PATCH 107/294] std: Add ArrayList.insertAssumeCapacity() Also test and document that inserting at list.items.len is allowed. --- lib/std/array_list.zig | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/lib/std/array_list.zig b/lib/std/array_list.zig index 13aad53019..fb11e2e755 100644 --- a/lib/std/array_list.zig +++ b/lib/std/array_list.zig @@ -141,11 +141,21 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { return cloned; } - /// Insert `item` at index `n` by moving `list[n .. list.len]` to make room. + /// Insert `item` at index `n`. Moves `list[n .. list.len]` to higher indices to make room. + /// If `n` is equal to the length of the list this operation is equivalent to append. /// This operation is O(N). /// Invalidates pointers if additional memory is needed. pub fn insert(self: *Self, n: usize, item: T) Allocator.Error!void { try self.ensureUnusedCapacity(1); + self.insertAssumeCapacity(n, item); + } + + /// Insert `item` at index `n`. Moves `list[n .. list.len]` to higher indices to make room. + /// If `n` is equal to the length of the list this operation is equivalent to append. + /// This operation is O(N). + /// Asserts that there is enough capacity for the new item. + pub fn insertAssumeCapacity(self: *Self, n: usize, item: T) void { + assert(self.items.len < self.capacity); self.items.len += 1; mem.copyBackwards(T, self.items[n + 1 .. self.items.len], self.items[n .. self.items.len - 1]); @@ -609,12 +619,21 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ return cloned; } - /// Insert `item` at index `n`. Moves `list[n .. list.len]` - /// to higher indices to make room. + /// Insert `item` at index `n`. Moves `list[n .. list.len]` to higher indices to make room. + /// If `n` is equal to the length of the list this operation is equivalent to append. /// This operation is O(N). /// Invalidates pointers if additional memory is needed. pub fn insert(self: *Self, allocator: Allocator, n: usize, item: T) Allocator.Error!void { try self.ensureUnusedCapacity(allocator, 1); + self.insertAssumeCapacity(n, item); + } + + /// Insert `item` at index `n`. Moves `list[n .. list.len]` to higher indices to make room. + /// If `n` is equal to the length of the list this operation is equivalent to append. + /// This operation is O(N). + /// Asserts that there is enough capacity for the new item. + pub fn insertAssumeCapacity(self: *Self, n: usize, item: T) void { + assert(self.items.len < self.capacity); self.items.len += 1; mem.copyBackwards(T, self.items[n + 1 .. self.items.len], self.items[n .. self.items.len - 1]); @@ -1309,9 +1328,9 @@ test "std.ArrayList/ArrayListUnmanaged.insert" { var list = ArrayList(i32).init(a); defer list.deinit(); - try list.append(1); + try list.insert(0, 1); try list.append(2); - try list.append(3); + try list.insert(2, 3); try list.insert(0, 5); try testing.expect(list.items[0] == 5); try testing.expect(list.items[1] == 1); @@ -1322,9 +1341,9 @@ test "std.ArrayList/ArrayListUnmanaged.insert" { var list = ArrayListUnmanaged(i32){}; defer list.deinit(a); - try list.append(a, 1); + try list.insert(a, 0, 1); try list.append(a, 2); - try list.append(a, 3); + try list.insert(a, 2, 3); try list.insert(a, 0, 5); try testing.expect(list.items[0] == 5); try testing.expect(list.items[1] == 1); From 948926c513befd95dc1ff90fe05329245f1c81db Mon Sep 17 00:00:00 2001 From: mlugg Date: Sat, 11 Mar 2023 14:26:56 +0000 Subject: [PATCH 108/294] Sema: improve error message when calling non-member function as method Resolves: #14880 --- src/Sema.zig | 21 +++++++++++++------ ...hod_call_with_first_arg_type_primitive.zig | 3 ++- ...ll_with_first_arg_type_wrong_container.zig | 1 + 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 41685890f7..6c24746da8 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -23605,10 +23605,13 @@ fn fieldCallBind( } // If we get here, we need to look for a decl in the struct type instead. - switch (concrete_ty.zigTypeTag()) { - .Struct, .Opaque, .Union, .Enum => { + const found_decl = switch (concrete_ty.zigTypeTag()) { + .Struct, .Opaque, .Union, .Enum => found_decl: { if (concrete_ty.getNamespace()) |namespace| { - if (try sema.namespaceLookupRef(block, src, namespace, field_name)) |inst| { + if (try sema.namespaceLookup(block, src, namespace, field_name)) |decl_idx| { + try sema.addReferencedBy(block, src, decl_idx); + const inst = try sema.analyzeDeclRef(decl_idx); + const decl_val = try sema.analyzeLoad(block, src, inst, src); const decl_type = sema.typeOf(decl_val); if (decl_type.zigTypeTag() == .Fn and @@ -23625,7 +23628,7 @@ fn fieldCallBind( first_param_type.ptrSize() == .C) and first_param_type.childType().eql(concrete_ty, sema.mod))) { - // zig fmt: on + // zig fmt: on // TODO: bound fn calls on rvalues should probably // generate a by-value argument somehow. const ty = Type.Tag.bound_fn.init(); @@ -23664,16 +23667,22 @@ fn fieldCallBind( return sema.addConstant(ty, value); } } + break :found_decl decl_idx; } } + break :found_decl null; }, - else => {}, - } + else => null, + }; const msg = msg: { const msg = try sema.errMsg(block, src, "no field or member function named '{s}' in '{}'", .{ field_name, concrete_ty.fmt(sema.mod) }); errdefer msg.destroy(sema.gpa); try sema.addDeclaredHereNote(msg, concrete_ty); + if (found_decl) |decl_idx| { + const decl = sema.mod.declPtr(decl_idx); + try sema.mod.errNoteNonLazy(decl.srcLoc(), msg, "'{s}' is not a member function", .{field_name}); + } break :msg msg; }; return sema.failWithOwnedErrorMsg(msg); diff --git a/test/cases/compile_errors/method_call_with_first_arg_type_primitive.zig b/test/cases/compile_errors/method_call_with_first_arg_type_primitive.zig index 1cecac6fac..2a5167adf2 100644 --- a/test/cases/compile_errors/method_call_with_first_arg_type_primitive.zig +++ b/test/cases/compile_errors/method_call_with_first_arg_type_primitive.zig @@ -2,7 +2,7 @@ const Foo = struct { x: i32, fn init(x: i32) Foo { - return Foo { + return Foo{ .x = x, }; } @@ -20,3 +20,4 @@ export fn f() void { // // :14:9: error: no field or member function named 'init' in 'tmp.Foo' // :1:13: note: struct declared here +// :4:5: note: 'init' is not a member function diff --git a/test/cases/compile_errors/method_call_with_first_arg_type_wrong_container.zig b/test/cases/compile_errors/method_call_with_first_arg_type_wrong_container.zig index ad481a6158..0653bda3ea 100644 --- a/test/cases/compile_errors/method_call_with_first_arg_type_wrong_container.zig +++ b/test/cases/compile_errors/method_call_with_first_arg_type_wrong_container.zig @@ -29,3 +29,4 @@ export fn foo() void { // // :23:6: error: no field or member function named 'init' in 'tmp.List' // :1:18: note: struct declared here +// :5:9: note: 'init' is not a member function From c93e0d86187cb589d6726acd36f741f3d87a96be Mon Sep 17 00:00:00 2001 From: mlugg Date: Thu, 9 Mar 2023 21:34:06 +0000 Subject: [PATCH 109/294] Sema: @extern fixes * There was an edge case where the arena could be destroyed twice on error: once from the arena itself and once from the decl destruction. * The type of the created decl was incorrect (it should have been the pointer child type), but it's not required anyway, so it's now just initialized to anyopaque (which more accurately reflects what's actually at that memory, since e.g. [*]T may correspond to nothing). * A runtime bitcast of the pointer was performed, meaning @extern didn't work at comptime. This is unnecessary: the decl_ref can just be initialized with the correct pointer type. --- src/Sema.zig | 59 +++++++++++++++--------------- test/standalone.zig | 1 + test/standalone/extern/build.zig | 20 ++++++++++ test/standalone/extern/exports.zig | 12 ++++++ test/standalone/extern/main.zig | 21 +++++++++++ 5 files changed, 84 insertions(+), 29 deletions(-) create mode 100644 test/standalone/extern/build.zig create mode 100644 test/standalone/extern/exports.zig create mode 100644 test/standalone/extern/main.zig diff --git a/src/Sema.zig b/src/Sema.zig index 6c24746da8..aef07b7988 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -22287,7 +22287,6 @@ fn zirBuiltinExtern( extended: Zir.Inst.Extended.InstData, ) CompileError!Air.Inst.Ref { const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; - const src = LazySrcLoc.nodeOffset(extra.node); const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; const options_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node }; @@ -22315,39 +22314,41 @@ fn zirBuiltinExtern( const new_decl = sema.mod.declPtr(new_decl_index); new_decl.name = try sema.gpa.dupeZ(u8, options.name); - var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa); - errdefer new_decl_arena.deinit(); - const new_decl_arena_allocator = new_decl_arena.allocator(); + { + var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa); + errdefer new_decl_arena.deinit(); + const new_decl_arena_allocator = new_decl_arena.allocator(); - const new_var = try new_decl_arena_allocator.create(Module.Var); - errdefer new_decl_arena_allocator.destroy(new_var); + const new_var = try new_decl_arena_allocator.create(Module.Var); + new_var.* = .{ + .owner_decl = sema.owner_decl_index, + .init = Value.initTag(.unreachable_value), + .is_extern = true, + .is_mutable = false, + .is_threadlocal = options.is_thread_local, + .is_weak_linkage = options.linkage == .Weak, + .lib_name = null, + }; - new_var.* = .{ - .owner_decl = sema.owner_decl_index, - .init = Value.initTag(.unreachable_value), - .is_extern = true, - .is_mutable = false, - .is_threadlocal = options.is_thread_local, - .is_weak_linkage = options.linkage == .Weak, - .lib_name = null, - }; + new_decl.src_line = sema.owner_decl.src_line; + // We only access this decl through the decl_ref with the correct type created + // below, so this type doesn't matter + new_decl.ty = Type.Tag.init(.anyopaque); + new_decl.val = try Value.Tag.variable.create(new_decl_arena_allocator, new_var); + new_decl.@"align" = 0; + new_decl.@"linksection" = null; + new_decl.has_tv = true; + new_decl.analysis = .complete; + new_decl.generation = sema.mod.generation; - new_decl.src_line = sema.owner_decl.src_line; - new_decl.ty = try ty.copy(new_decl_arena_allocator); - new_decl.val = try Value.Tag.variable.create(new_decl_arena_allocator, new_var); - new_decl.@"align" = 0; - new_decl.@"linksection" = null; - new_decl.has_tv = true; - new_decl.analysis = .complete; - new_decl.generation = sema.mod.generation; + try new_decl.finalizeNewArena(&new_decl_arena); + } - const arena_state = try new_decl_arena_allocator.create(std.heap.ArenaAllocator.State); - arena_state.* = new_decl_arena.state; - new_decl.value_arena = arena_state; + try sema.mod.declareDeclDependency(sema.owner_decl_index, new_decl_index); + try sema.ensureDeclAnalyzed(new_decl_index); - const ref = try sema.analyzeDeclRef(new_decl_index); - try sema.requireRuntimeBlock(block, src, null); - return block.addBitCast(ty, ref); + const ref = try Value.Tag.decl_ref.create(sema.arena, new_decl_index); + return sema.addConstant(ty, ref); } fn requireRuntimeBlock(sema: *Sema, block: *Block, src: LazySrcLoc, runtime_src: ?LazySrcLoc) !void { diff --git a/test/standalone.zig b/test/standalone.zig index 965139235c..7aa4d81f97 100644 --- a/test/standalone.zig +++ b/test/standalone.zig @@ -107,6 +107,7 @@ pub fn addCases(cases: *tests.StandaloneContext) void { cases.addBuildFile("test/standalone/emit_asm_and_bin/build.zig", .{}); cases.addBuildFile("test/standalone/issue_12588/build.zig", .{}); cases.addBuildFile("test/standalone/embed_generated_file/build.zig", .{}); + cases.addBuildFile("test/standalone/extern/build.zig", .{}); cases.addBuildFile("test/standalone/dep_diamond/build.zig", .{}); cases.addBuildFile("test/standalone/dep_triangle/build.zig", .{}); diff --git a/test/standalone/extern/build.zig b/test/standalone/extern/build.zig new file mode 100644 index 0000000000..8a44a6ca8f --- /dev/null +++ b/test/standalone/extern/build.zig @@ -0,0 +1,20 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + const optimize = b.standardOptimizeOption(.{}); + + const obj = b.addObject(.{ + .name = "exports", + .root_source_file = .{ .path = "exports.zig" }, + .target = .{}, + .optimize = optimize, + }); + const main = b.addTest(.{ + .root_source_file = .{ .path = "main.zig" }, + .optimize = optimize, + }); + main.addObject(obj); + + const test_step = b.step("test", "Test it"); + test_step.dependOn(&main.step); +} diff --git a/test/standalone/extern/exports.zig b/test/standalone/extern/exports.zig new file mode 100644 index 0000000000..93351c4581 --- /dev/null +++ b/test/standalone/extern/exports.zig @@ -0,0 +1,12 @@ +var hidden: u32 = 0; +export fn updateHidden(val: u32) void { + hidden = val; +} +export fn getHidden() u32 { + return hidden; +} + +const T = extern struct { x: u32 }; + +export var mut_val: f64 = 1.23; +export const const_val: T = .{ .x = 42 }; diff --git a/test/standalone/extern/main.zig b/test/standalone/extern/main.zig new file mode 100644 index 0000000000..4cbed184c3 --- /dev/null +++ b/test/standalone/extern/main.zig @@ -0,0 +1,21 @@ +const assert = @import("std").debug.assert; + +const updateHidden = @extern(*const fn (u32) callconv(.C) void, .{ .name = "updateHidden" }); +const getHidden = @extern(*const fn () callconv(.C) u32, .{ .name = "getHidden" }); + +const T = extern struct { x: u32 }; + +test { + var mut_val_ptr = @extern(*f64, .{ .name = "mut_val" }); + var const_val_ptr = @extern(*const T, .{ .name = "const_val" }); + + assert(getHidden() == 0); + updateHidden(123); + assert(getHidden() == 123); + + assert(mut_val_ptr.* == 1.23); + mut_val_ptr.* = 10.0; + assert(mut_val_ptr.* == 10.0); + + assert(const_val_ptr.x == 42); +} From a8bd55e0853f7f80c9cd843ec54813425e4276bc Mon Sep 17 00:00:00 2001 From: mlugg Date: Tue, 7 Mar 2023 22:24:50 +0000 Subject: [PATCH 110/294] translate-c: translate extern unknown-length arrays using @extern Resolves: #14743 --- src/translate_c.zig | 16 +++++++++++++++- src/translate_c/ast.zig | 28 ++++++++++++++++++++++++++++ test/translate_c.zig | 8 ++++++++ 3 files changed, 51 insertions(+), 1 deletion(-) diff --git a/src/translate_c.zig b/src/translate_c.zig index 5b2b1c2df5..698f750afb 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -784,9 +784,9 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co const qual_type = var_decl.getTypeSourceInfo_getType(); const storage_class = var_decl.getStorageClass(); - const is_const = qual_type.isConstQualified(); const has_init = var_decl.hasInit(); const decl_init = var_decl.getInit(); + var is_const = qual_type.isConstQualified(); // In C extern variables with initializers behave like Zig exports. // extern int foo = 2; @@ -843,6 +843,20 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co // std.mem.zeroes(T) init_node = try Tag.std_mem_zeroes.create(c.arena, type_node); + } else if (qual_type.getTypeClass() == .IncompleteArray) { + // Oh no, an extern array of unknown size! These are really fun because there's no + // direct equivalent in Zig. To translate correctly, we'll have to create a C-pointer + // to the data initialized via @extern. + + const name_str = try std.fmt.allocPrint(c.arena, "\"{s}\"", .{var_name}); + init_node = try Tag.builtin_extern.create(c.arena, .{ + .type = type_node, + .name = try Tag.string_literal.create(c.arena, name_str), + }); + + // Since this is really a pointer to the underlying data, we tweak a few properties. + is_extern = false; + is_const = true; } const linksection_string = blk: { diff --git a/src/translate_c/ast.zig b/src/translate_c/ast.zig index 81a19eb39d..688235c2d3 100644 --- a/src/translate_c/ast.zig +++ b/src/translate_c/ast.zig @@ -158,6 +158,8 @@ pub const Node = extern union { vector_zero_init, /// @shuffle(type, a, b, mask) shuffle, + /// @extern(ty, .{ .name = n }) + builtin_extern, /// @import("std").zig.c_translation.MacroArithmetic.(lhs, rhs) macro_arithmetic, @@ -373,6 +375,7 @@ pub const Node = extern union { .field_access => Payload.FieldAccess, .string_slice => Payload.StringSlice, .shuffle => Payload.Shuffle, + .builtin_extern => Payload.Extern, .macro_arithmetic => Payload.MacroArithmetic, }; } @@ -718,6 +721,14 @@ pub const Payload = struct { }, }; + pub const Extern = struct { + base: Payload, + data: struct { + type: Node, + name: Node, + }, + }; + pub const MacroArithmetic = struct { base: Payload, data: struct { @@ -1409,6 +1420,22 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { payload.mask_vector, }); }, + .builtin_extern => { + const payload = node.castTag(.builtin_extern).?.data; + + var info_inits: [1]Payload.ContainerInitDot.Initializer = .{ + .{ .name = "name", .value = payload.name }, + }; + var info_payload: Payload.ContainerInitDot = .{ + .base = .{ .tag = .container_init_dot }, + .data = &info_inits, + }; + + return renderBuiltinCall(c, "@extern", &.{ + payload.type, + .{ .ptr_otherwise = &info_payload.base }, + }); + }, .macro_arithmetic => { const payload = node.castTag(.macro_arithmetic).?.data; const op = @tagName(payload.op); @@ -2348,6 +2375,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex { .div_exact, .offset_of, .shuffle, + .builtin_extern, .static_local_var, .mut_str, .macro_arithmetic, diff --git a/test/translate_c.zig b/test/translate_c.zig index 92dc3038c0..4d65bddb39 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -3948,4 +3948,12 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); } + + cases.add("extern array of unknown length", + \\extern int foo[]; + , &[_][]const u8{ + \\const foo: [*c]c_int = @extern([*c]c_int, .{ + \\ .name = "foo", + \\}); + }); } From ac434fd8cc1105912d29f209e2f9f67e5af3a744 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sun, 12 Mar 2023 22:06:22 +0100 Subject: [PATCH 111/294] x86_64: avoid inline for-loops when scanning the encodings table --- src/arch/x86_64/Encoding.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/arch/x86_64/Encoding.zig b/src/arch/x86_64/Encoding.zig index 7b00679b92..a51f954aed 100644 --- a/src/arch/x86_64/Encoding.zig +++ b/src/arch/x86_64/Encoding.zig @@ -68,7 +68,7 @@ pub fn findByMnemonic(mnemonic: Mnemonic, args: struct { // TODO work out what is the maximum number of variants we can actually find in one swoop. var candidates: [10]Encoding = undefined; var count: usize = 0; - inline for (table) |entry| { + for (table) |entry| { const enc = Encoding{ .mnemonic = entry[0], .op_en = entry[1], @@ -162,7 +162,7 @@ pub fn findByOpcode(opc: []const u8, prefixes: struct { legacy: LegacyPrefixes, rex: Rex, }, modrm_ext: ?u3) ?Encoding { - inline for (table) |entry| { + for (table) |entry| { const enc = Encoding{ .mnemonic = entry[0], .op_en = entry[1], From e9fc0aba4c3b0855e580ff3afe7251920ae3dcf9 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sun, 12 Mar 2023 22:08:29 +0100 Subject: [PATCH 112/294] x86_64: add missing source files to CMakeLists.txt --- CMakeLists.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 46d9c701b0..5afea9354e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -559,9 +559,12 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/src/arch/wasm/Mir.zig" "${CMAKE_SOURCE_DIR}/src/arch/x86_64/CodeGen.zig" "${CMAKE_SOURCE_DIR}/src/arch/x86_64/Emit.zig" + "${CMAKE_SOURCE_DIR}/src/arch/x86_64/Encoding.zig" "${CMAKE_SOURCE_DIR}/src/arch/x86_64/Mir.zig" - "${CMAKE_SOURCE_DIR}/src/arch/x86_64/bits.zig" "${CMAKE_SOURCE_DIR}/src/arch/x86_64/abi.zig" + "${CMAKE_SOURCE_DIR}/src/arch/x86_64/bits.zig" + "${CMAKE_SOURCE_DIR}/src/arch/x86_64/encoder.zig" + "${CMAKE_SOURCE_DIR}/src/arch/x86_64/encodings.zig" "${CMAKE_SOURCE_DIR}/src/clang.zig" "${CMAKE_SOURCE_DIR}/src/clang_options.zig" "${CMAKE_SOURCE_DIR}/src/clang_options_data.zig" From 10c74631b381b6b7d84def90a3a747192f2793a0 Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Sun, 12 Mar 2023 16:50:04 +0100 Subject: [PATCH 113/294] langref: add missing comma in assembly expressions --- doc/langref.html.in | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 7044fe977f..9cee35caa2 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -7457,20 +7457,20 @@ pub const STDOUT_FILENO = 1; pub fn syscall1(number: usize, arg1: usize) usize { return asm volatile ("syscall" - : [ret] "={rax}" (-> usize) + : [ret] "={rax}" (-> usize), : [number] "{rax}" (number), - [arg1] "{rdi}" (arg1) + [arg1] "{rdi}" (arg1), : "rcx", "r11" ); } pub fn syscall3(number: usize, arg1: usize, arg2: usize, arg3: usize) usize { return asm volatile ("syscall" - : [ret] "={rax}" (-> usize) + : [ret] "={rax}" (-> usize), : [number] "{rax}" (number), [arg1] "{rdi}" (arg1), [arg2] "{rsi}" (arg2), - [arg3] "{rdx}" (arg3) + [arg3] "{rdx}" (arg3), : "rcx", "r11" ); } @@ -7519,14 +7519,14 @@ pub fn syscall1(number: usize, arg1: usize) usize { // type is the result type of the inline assembly expression. // If it is a value binding, then `%[ret]` syntax would be used // to refer to the register bound to the value. - (-> usize) + (-> usize), // Next is the list of inputs. // The constraint for these inputs means, "when the assembly code is // executed, $rax shall have the value of `number` and $rdi shall have // the value of `arg1`". Any number of input parameters is allowed, // including none. : [number] "{rax}" (number), - [arg1] "{rdi}" (arg1) + [arg1] "{rdi}" (arg1), // Next is the list of clobbers. These declare a set of registers whose // values will not be preserved by the execution of this assembly code. // These do not include output or input registers. The special clobber From 1d96a17af473d5ca79ecc7b64bbf2e899b5de3b4 Mon Sep 17 00:00:00 2001 From: Frank Denis <124872+jedisct1@users.noreply.github.com> Date: Mon, 13 Mar 2023 08:06:27 +0100 Subject: [PATCH 114/294] crypto.aescrypto.encrypt: do not add the round key in an asm block (#14899) Apple M1/M2 have an EOR3 instruction that can XOR 2 operands with another one, and LLVM knows how to take advantage of it. However, two EOR can't be automatically combined into an EOR3 if one of them is in an assembly block. That simple change speeds up ciphers doing an AES round immediately followed by a XOR operation on Apple Silicon. Before: aegis-128l mac: 12534 MiB/s aegis-256 mac: 6722 MiB/s aegis-128l: 10634 MiB/s aegis-256: 6133 MiB/s aes128-gcm: 3890 MiB/s aes256-gcm: 3122 MiB/s aes128-ocb: 2832 MiB/s aes256-ocb: 2057 MiB/s After: aegis-128l mac: 15667 MiB/s aegis-256 mac: 8240 MiB/s aegis-128l: 12656 MiB/s aegis-256: 7214 MiB/s aes128-gcm: 3976 MiB/s aes256-gcm: 3202 MiB/s aes128-ocb: 2835 MiB/s aes256-ocb: 2118 MiB/s --- lib/std/crypto/aes/armcrypto.zig | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/lib/std/crypto/aes/armcrypto.zig b/lib/std/crypto/aes/armcrypto.zig index 3f4faf1b14..a6574c372a 100644 --- a/lib/std/crypto/aes/armcrypto.zig +++ b/lib/std/crypto/aes/armcrypto.zig @@ -32,62 +32,54 @@ pub const Block = struct { /// Encrypt a block with a round key. pub inline fn encrypt(block: Block, round_key: Block) Block { return Block{ - .repr = asm ( + .repr = (asm ( \\ mov %[out].16b, %[in].16b \\ aese %[out].16b, %[zero].16b \\ aesmc %[out].16b, %[out].16b - \\ eor %[out].16b, %[out].16b, %[rk].16b : [out] "=&x" (-> BlockVec), : [in] "x" (block.repr), - [rk] "x" (round_key.repr), [zero] "x" (zero), - ), + )) ^ round_key.repr, }; } /// Encrypt a block with the last round key. pub inline fn encryptLast(block: Block, round_key: Block) Block { return Block{ - .repr = asm ( + .repr = (asm ( \\ mov %[out].16b, %[in].16b \\ aese %[out].16b, %[zero].16b - \\ eor %[out].16b, %[out].16b, %[rk].16b : [out] "=&x" (-> BlockVec), : [in] "x" (block.repr), - [rk] "x" (round_key.repr), [zero] "x" (zero), - ), + )) ^ round_key.repr, }; } /// Decrypt a block with a round key. pub inline fn decrypt(block: Block, inv_round_key: Block) Block { return Block{ - .repr = asm ( + .repr = (asm ( \\ mov %[out].16b, %[in].16b \\ aesd %[out].16b, %[zero].16b \\ aesimc %[out].16b, %[out].16b - \\ eor %[out].16b, %[out].16b, %[rk].16b : [out] "=&x" (-> BlockVec), : [in] "x" (block.repr), - [rk] "x" (inv_round_key.repr), [zero] "x" (zero), - ), + )) ^ inv_round_key.repr, }; } /// Decrypt a block with the last round key. pub inline fn decryptLast(block: Block, inv_round_key: Block) Block { return Block{ - .repr = asm ( + .repr = (asm ( \\ mov %[out].16b, %[in].16b \\ aesd %[out].16b, %[zero].16b - \\ eor %[out].16b, %[out].16b, %[rk].16b : [out] "=&x" (-> BlockVec), : [in] "x" (block.repr), - [rk] "x" (inv_round_key.repr), [zero] "x" (zero), - ), + )) ^ inv_round_key.repr, }; } From adc6dec26b8ba9f79aabc4b69ae689acf4c6767d Mon Sep 17 00:00:00 2001 From: Ian Johnson Date: Sun, 12 Mar 2023 13:08:15 -0400 Subject: [PATCH 115/294] Sema: avoid panic on callconv(.C) generic return type Fixes #14854 --- src/Sema.zig | 2 +- test/behavior.zig | 1 + test/behavior/bugs/14854.zig | 13 +++++++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 test/behavior/bugs/14854.zig diff --git a/src/Sema.zig b/src/Sema.zig index aef07b7988..e6652a5d66 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -8782,7 +8782,7 @@ fn funcCommon( }; return sema.failWithOwnedErrorMsg(msg); } - if (!Type.fnCallingConventionAllowsZigTypes(cc_resolved) and !try sema.validateExternType(return_type, .ret_ty)) { + if (!ret_poison and !Type.fnCallingConventionAllowsZigTypes(cc_resolved) and !try sema.validateExternType(return_type, .ret_ty)) { const msg = msg: { const msg = try sema.errMsg(block, ret_ty_src, "return type '{}' not allowed in function with calling convention '{s}'", .{ return_type.fmt(sema.mod), @tagName(cc_resolved), diff --git a/test/behavior.zig b/test/behavior.zig index 4f8ad67203..ed731377d8 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -141,6 +141,7 @@ test { _ = @import("behavior/bugs/13664.zig"); _ = @import("behavior/bugs/13714.zig"); _ = @import("behavior/bugs/13785.zig"); + _ = @import("behavior/bugs/14854.zig"); _ = @import("behavior/byteswap.zig"); _ = @import("behavior/byval_arg_var.zig"); _ = @import("behavior/call.zig"); diff --git a/test/behavior/bugs/14854.zig b/test/behavior/bugs/14854.zig new file mode 100644 index 0000000000..b34dd49406 --- /dev/null +++ b/test/behavior/bugs/14854.zig @@ -0,0 +1,13 @@ +const testing = @import("std").testing; + +test { + try testing.expect(getGeneric(u8, getU8) == 123); +} + +fn getU8() callconv(.C) u8 { + return 123; +} + +fn getGeneric(comptime T: type, supplier: fn () callconv(.C) T) T { + return supplier(); +} From 4942e4e8701b9a137d7f30fcb8ac1bc2d46f2a99 Mon Sep 17 00:00:00 2001 From: Hashi364 <49736221+Kiyoshi364@users.noreply.github.com> Date: Mon, 13 Mar 2023 11:47:20 -0300 Subject: [PATCH 116/294] Resolve docs inconsistency with Overflow builtins In 41 (Undefined Behavior) . 5 (Integer Overflow) . 3 (Builtin Overflow Functions), it is stated that > These builtins return a bool of whether or not overflow occurred, as well as returning the overflowed bits: > * @addWithOverflow > * @subWithOverflow > * @mulWithOverflow > * @shlWithOverflow but in their definition says that it returns a `tuple`/`struct`. Example; `@addWithOverflow(a: anytype, b: anytype) struct { @TypeOf(a, b), u1 }` Co-authored-by: zooster --- doc/langref.html.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 9cee35caa2..991fd0c3e6 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -9804,8 +9804,8 @@ pub fn main() !void { {#header_close#} {#header_open|Builtin Overflow Functions#}

- These builtins return a {#syntax#}bool{#endsyntax#} of whether or not overflow - occurred, as well as returning the overflowed bits: + These builtins return a tuple containing whether there was an overflow + (as a {#syntax#}u1{#endsyntax#}) and the possibly overflowed bits of the operation:

  • {#link|@addWithOverflow#}
  • From 962299157840979ba659d478785f5ed0759d5401 Mon Sep 17 00:00:00 2001 From: Frank Denis <124872+jedisct1@users.noreply.github.com> Date: Mon, 13 Mar 2023 22:18:26 +0100 Subject: [PATCH 117/294] Add configurable side channels mitigations; enable them on soft AES (#13739) * Add configurable side channels mitigations; enable them on soft AES Our software AES implementation doesn't have any mitigations against side channels. Go's generic implementation is not protected at all either, and even OpenSSL only has minimal mitigations. Full mitigations against cache-based attacks (bitslicing, fixslicing) come at a huge performance cost, making AES-based primitives pretty much useless for many applications. They also don't offer any protection against other classes of side channel attacks. In practice, partially protected, or even unprotected implementations are not as bad as it sounds. Exploiting these side channels requires an attacker that is able to submit many plaintexts/ciphertexts and perform accurate measurements. Noisy measurements can still be exploited, but require a significant amount of attempts. Wether this is exploitable or not depends on the platform, application and the attacker's proximity. So, some libraries made the choice of minimal mitigations and some use better mitigations in spite of the performance hit. It's a tradeoff (security vs performance), and there's no one-size-fits all implementation. What applies to AES applies to other cryptographic primitives. For example, RSA signatures are very sensible to fault attacks, regardless of them using the CRT or not. A mitigation is to verify every produced signature. That also comes with a performance cost. Wether to do it or not depends on wether fault attacks are part of the threat model or not. Thanks to Zig's comptime, we can try to address these different requirements. This PR adds a `side_channels_protection` global, that can later be complemented with `fault_attacks_protection` and possibly other knobs. It can have 4 different values: - `none`: which doesn't enable additional mitigations. "Additional", because it only disables mitigations that don't have a big performance cost. For example, checking authentication tags will still be done in constant time. - `basic`: which enables mitigations protecting against attacks in a common scenario, where an attacker doesn't have physical access to the device, cannot run arbitrary code on the same thread, and cannot conduct brute-force attacks without being throttled. - `medium`: which enables additional mitigations, offering practical protection in a shared environement. - `full`: which enables all the mitigations we have. The tradeoff is that the more mitigations we enable, the bigger the performance hit will be. But this let applications choose what's best for their use case. `medium` is the default. Currently, this only affects software AES, but that setting can later be used by other primitives. For AES, our implementation is a traditional table-based, with 4 32-bit tables and a sbox. Lookups in that table have been replaced by function calls. These functions can add a configurable noise level, making cache-based attacks more difficult to conduct. In the `none` mitigation level, the behavior is exactly the same as before. Performance also remains the same. In other levels, we compress the T tables into a single one, and read data from multiple cache lines (all of them in `full` mode), for all bytes in parallel. More precise measurements and way more attempts become necessary in order to find correlations. In addition, we use distinct copies of the sbox for key expansion and encryption, so that they don't share the same L1 cache entries. The best known attacks target the first two AES round, or the last one. While future attacks may improve on this, AES achieves full diffusion after 4 rounds. So, we can relax the mitigations after that. This is what this implementation does, enabling mitigations again for the last two rounds. In `full` mode, all the rounds are protected. The protection assumes that lookups within a cache line are secret. The cachebleed attack showed that it can be circumvented, but that requires an attacker to be able to abuse hyperthreading and run code on the same core as the encryption, which is rarely a practical scenario. Still, the current AES API allows us to transparently switch to using fixslicing/bitslicing later when the `full` mitigation level is enabled. * Software AES: use little-endian representation. Virtually all platforms are little-endian these days, so optimizing for big-endian CPUs doesn't make sense any more. --- lib/std/crypto.zig | 27 +++ lib/std/crypto/aes/soft.zig | 363 ++++++++++++++++++++++++++++++------ 2 files changed, 328 insertions(+), 62 deletions(-) diff --git a/lib/std/crypto.zig b/lib/std/crypto.zig index f46e7b1022..b469620002 100644 --- a/lib/std/crypto.zig +++ b/lib/std/crypto.zig @@ -1,3 +1,5 @@ +const root = @import("root"); + /// Authenticated Encryption with Associated Data pub const aead = struct { pub const aegis = struct { @@ -183,6 +185,31 @@ pub const errors = @import("crypto/errors.zig"); pub const tls = @import("crypto/tls.zig"); pub const Certificate = @import("crypto/Certificate.zig"); +/// Global configuration of cryptographic implementations in the standard library. +pub const config = struct { + /// Side-channels mitigations. + pub const SideChannelsMitigations = enum { + /// No additional side-channel mitigations are applied. + /// This is the fastest mode. + none, + /// The `basic` mode protects against most practical attacks, provided that the + /// application or implements proper defenses against brute-force attacks. + /// It offers a good balance between performance and security. + basic, + /// The `medium` mode offers increased resilience against side-channel attacks, + /// making most attacks unpractical even on shared/low latency environements. + /// This is the default mode. + medium, + /// The `full` mode offers the highest level of protection against side-channel attacks. + /// Note that this doesn't cover all possible attacks (especially power analysis or + /// thread-local attacks such as cachebleed), and that the performance impact is significant. + full, + }; + + /// This is a global configuration that applies to all cryptographic implementations. + pub const side_channels_mitigations: SideChannelsMitigations = if (@hasDecl(root, "side_channels_mitigations")) root.side_channels_mitigations else .medium; +}; + test { _ = aead.aegis.Aegis128L; _ = aead.aegis.Aegis256; diff --git a/lib/std/crypto/aes/soft.zig b/lib/std/crypto/aes/soft.zig index d8bd3d4ac0..4a300961c6 100644 --- a/lib/std/crypto/aes/soft.zig +++ b/lib/std/crypto/aes/soft.zig @@ -1,11 +1,11 @@ -// Based on Go stdlib implementation - const std = @import("../../std.zig"); const math = std.math; const mem = std.mem; const BlockVec = [4]u32; +const side_channels_mitigations = std.crypto.config.side_channels_mitigations; + /// A single AES block. pub const Block = struct { pub const block_length: usize = 16; @@ -15,20 +15,20 @@ pub const Block = struct { /// Convert a byte sequence into an internal representation. pub inline fn fromBytes(bytes: *const [16]u8) Block { - const s0 = mem.readIntBig(u32, bytes[0..4]); - const s1 = mem.readIntBig(u32, bytes[4..8]); - const s2 = mem.readIntBig(u32, bytes[8..12]); - const s3 = mem.readIntBig(u32, bytes[12..16]); + const s0 = mem.readIntLittle(u32, bytes[0..4]); + const s1 = mem.readIntLittle(u32, bytes[4..8]); + const s2 = mem.readIntLittle(u32, bytes[8..12]); + const s3 = mem.readIntLittle(u32, bytes[12..16]); return Block{ .repr = BlockVec{ s0, s1, s2, s3 } }; } /// Convert the internal representation of a block into a byte sequence. pub inline fn toBytes(block: Block) [16]u8 { var bytes: [16]u8 = undefined; - mem.writeIntBig(u32, bytes[0..4], block.repr[0]); - mem.writeIntBig(u32, bytes[4..8], block.repr[1]); - mem.writeIntBig(u32, bytes[8..12], block.repr[2]); - mem.writeIntBig(u32, bytes[12..16], block.repr[3]); + mem.writeIntLittle(u32, bytes[0..4], block.repr[0]); + mem.writeIntLittle(u32, bytes[4..8], block.repr[1]); + mem.writeIntLittle(u32, bytes[8..12], block.repr[2]); + mem.writeIntLittle(u32, bytes[12..16], block.repr[3]); return bytes; } @@ -50,32 +50,93 @@ pub const Block = struct { const s2 = block.repr[2]; const s3 = block.repr[3]; - const t0 = round_key.repr[0] ^ table_encrypt[0][@truncate(u8, s0 >> 24)] ^ table_encrypt[1][@truncate(u8, s1 >> 16)] ^ table_encrypt[2][@truncate(u8, s2 >> 8)] ^ table_encrypt[3][@truncate(u8, s3)]; - const t1 = round_key.repr[1] ^ table_encrypt[0][@truncate(u8, s1 >> 24)] ^ table_encrypt[1][@truncate(u8, s2 >> 16)] ^ table_encrypt[2][@truncate(u8, s3 >> 8)] ^ table_encrypt[3][@truncate(u8, s0)]; - const t2 = round_key.repr[2] ^ table_encrypt[0][@truncate(u8, s2 >> 24)] ^ table_encrypt[1][@truncate(u8, s3 >> 16)] ^ table_encrypt[2][@truncate(u8, s0 >> 8)] ^ table_encrypt[3][@truncate(u8, s1)]; - const t3 = round_key.repr[3] ^ table_encrypt[0][@truncate(u8, s3 >> 24)] ^ table_encrypt[1][@truncate(u8, s0 >> 16)] ^ table_encrypt[2][@truncate(u8, s1 >> 8)] ^ table_encrypt[3][@truncate(u8, s2)]; + var x: [4]u32 = undefined; + x = table_lookup(&table_encrypt, @truncate(u8, s0), @truncate(u8, s1 >> 8), @truncate(u8, s2 >> 16), @truncate(u8, s3 >> 24)); + var t0 = x[0] ^ x[1] ^ x[2] ^ x[3]; + x = table_lookup(&table_encrypt, @truncate(u8, s1), @truncate(u8, s2 >> 8), @truncate(u8, s3 >> 16), @truncate(u8, s0 >> 24)); + var t1 = x[0] ^ x[1] ^ x[2] ^ x[3]; + x = table_lookup(&table_encrypt, @truncate(u8, s2), @truncate(u8, s3 >> 8), @truncate(u8, s0 >> 16), @truncate(u8, s1 >> 24)); + var t2 = x[0] ^ x[1] ^ x[2] ^ x[3]; + x = table_lookup(&table_encrypt, @truncate(u8, s3), @truncate(u8, s0 >> 8), @truncate(u8, s1 >> 16), @truncate(u8, s2 >> 24)); + var t3 = x[0] ^ x[1] ^ x[2] ^ x[3]; + + t0 ^= round_key.repr[0]; + t1 ^= round_key.repr[1]; + t2 ^= round_key.repr[2]; + t3 ^= round_key.repr[3]; + + return Block{ .repr = BlockVec{ t0, t1, t2, t3 } }; + } + + /// Encrypt a block with a round key *WITHOUT ANY PROTECTION AGAINST SIDE CHANNELS* + pub inline fn encryptUnprotected(block: Block, round_key: Block) Block { + const s0 = block.repr[0]; + const s1 = block.repr[1]; + const s2 = block.repr[2]; + const s3 = block.repr[3]; + + var x: [4]u32 = undefined; + x = .{ + table_encrypt[0][@truncate(u8, s0)], + table_encrypt[1][@truncate(u8, s1 >> 8)], + table_encrypt[2][@truncate(u8, s2 >> 16)], + table_encrypt[3][@truncate(u8, s3 >> 24)], + }; + var t0 = x[0] ^ x[1] ^ x[2] ^ x[3]; + x = .{ + table_encrypt[0][@truncate(u8, s1)], + table_encrypt[1][@truncate(u8, s2 >> 8)], + table_encrypt[2][@truncate(u8, s3 >> 16)], + table_encrypt[3][@truncate(u8, s0 >> 24)], + }; + var t1 = x[0] ^ x[1] ^ x[2] ^ x[3]; + x = .{ + table_encrypt[0][@truncate(u8, s2)], + table_encrypt[1][@truncate(u8, s3 >> 8)], + table_encrypt[2][@truncate(u8, s0 >> 16)], + table_encrypt[3][@truncate(u8, s1 >> 24)], + }; + var t2 = x[0] ^ x[1] ^ x[2] ^ x[3]; + x = .{ + table_encrypt[0][@truncate(u8, s3)], + table_encrypt[1][@truncate(u8, s0 >> 8)], + table_encrypt[2][@truncate(u8, s1 >> 16)], + table_encrypt[3][@truncate(u8, s2 >> 24)], + }; + var t3 = x[0] ^ x[1] ^ x[2] ^ x[3]; + + t0 ^= round_key.repr[0]; + t1 ^= round_key.repr[1]; + t2 ^= round_key.repr[2]; + t3 ^= round_key.repr[3]; return Block{ .repr = BlockVec{ t0, t1, t2, t3 } }; } /// Encrypt a block with the last round key. pub inline fn encryptLast(block: Block, round_key: Block) Block { - const t0 = block.repr[0]; - const t1 = block.repr[1]; - const t2 = block.repr[2]; - const t3 = block.repr[3]; + const s0 = block.repr[0]; + const s1 = block.repr[1]; + const s2 = block.repr[2]; + const s3 = block.repr[3]; // Last round uses s-box directly and XORs to produce output. - var s0 = @as(u32, sbox_encrypt[t0 >> 24]) << 24 | @as(u32, sbox_encrypt[t1 >> 16 & 0xff]) << 16 | @as(u32, sbox_encrypt[t2 >> 8 & 0xff]) << 8 | @as(u32, sbox_encrypt[t3 & 0xff]); - var s1 = @as(u32, sbox_encrypt[t1 >> 24]) << 24 | @as(u32, sbox_encrypt[t2 >> 16 & 0xff]) << 16 | @as(u32, sbox_encrypt[t3 >> 8 & 0xff]) << 8 | @as(u32, sbox_encrypt[t0 & 0xff]); - var s2 = @as(u32, sbox_encrypt[t2 >> 24]) << 24 | @as(u32, sbox_encrypt[t3 >> 16 & 0xff]) << 16 | @as(u32, sbox_encrypt[t0 >> 8 & 0xff]) << 8 | @as(u32, sbox_encrypt[t1 & 0xff]); - var s3 = @as(u32, sbox_encrypt[t3 >> 24]) << 24 | @as(u32, sbox_encrypt[t0 >> 16 & 0xff]) << 16 | @as(u32, sbox_encrypt[t1 >> 8 & 0xff]) << 8 | @as(u32, sbox_encrypt[t2 & 0xff]); - s0 ^= round_key.repr[0]; - s1 ^= round_key.repr[1]; - s2 ^= round_key.repr[2]; - s3 ^= round_key.repr[3]; + var x: [4]u8 = undefined; + x = sbox_lookup(&sbox_encrypt, @truncate(u8, s3 >> 24), @truncate(u8, s2 >> 16), @truncate(u8, s1 >> 8), @truncate(u8, s0)); + var t0 = @as(u32, x[0]) << 24 | @as(u32, x[1]) << 16 | @as(u32, x[2]) << 8 | @as(u32, x[3]); + x = sbox_lookup(&sbox_encrypt, @truncate(u8, s0 >> 24), @truncate(u8, s3 >> 16), @truncate(u8, s2 >> 8), @truncate(u8, s1)); + var t1 = @as(u32, x[0]) << 24 | @as(u32, x[1]) << 16 | @as(u32, x[2]) << 8 | @as(u32, x[3]); + x = sbox_lookup(&sbox_encrypt, @truncate(u8, s1 >> 24), @truncate(u8, s0 >> 16), @truncate(u8, s3 >> 8), @truncate(u8, s2)); + var t2 = @as(u32, x[0]) << 24 | @as(u32, x[1]) << 16 | @as(u32, x[2]) << 8 | @as(u32, x[3]); + x = sbox_lookup(&sbox_encrypt, @truncate(u8, s2 >> 24), @truncate(u8, s1 >> 16), @truncate(u8, s0 >> 8), @truncate(u8, s3)); + var t3 = @as(u32, x[0]) << 24 | @as(u32, x[1]) << 16 | @as(u32, x[2]) << 8 | @as(u32, x[3]); - return Block{ .repr = BlockVec{ s0, s1, s2, s3 } }; + t0 ^= round_key.repr[0]; + t1 ^= round_key.repr[1]; + t2 ^= round_key.repr[2]; + t3 ^= round_key.repr[3]; + + return Block{ .repr = BlockVec{ t0, t1, t2, t3 } }; } /// Decrypt a block with a round key. @@ -85,32 +146,93 @@ pub const Block = struct { const s2 = block.repr[2]; const s3 = block.repr[3]; - const t0 = round_key.repr[0] ^ table_decrypt[0][@truncate(u8, s0 >> 24)] ^ table_decrypt[1][@truncate(u8, s3 >> 16)] ^ table_decrypt[2][@truncate(u8, s2 >> 8)] ^ table_decrypt[3][@truncate(u8, s1)]; - const t1 = round_key.repr[1] ^ table_decrypt[0][@truncate(u8, s1 >> 24)] ^ table_decrypt[1][@truncate(u8, s0 >> 16)] ^ table_decrypt[2][@truncate(u8, s3 >> 8)] ^ table_decrypt[3][@truncate(u8, s2)]; - const t2 = round_key.repr[2] ^ table_decrypt[0][@truncate(u8, s2 >> 24)] ^ table_decrypt[1][@truncate(u8, s1 >> 16)] ^ table_decrypt[2][@truncate(u8, s0 >> 8)] ^ table_decrypt[3][@truncate(u8, s3)]; - const t3 = round_key.repr[3] ^ table_decrypt[0][@truncate(u8, s3 >> 24)] ^ table_decrypt[1][@truncate(u8, s2 >> 16)] ^ table_decrypt[2][@truncate(u8, s1 >> 8)] ^ table_decrypt[3][@truncate(u8, s0)]; + var x: [4]u32 = undefined; + x = table_lookup(&table_decrypt, @truncate(u8, s0), @truncate(u8, s3 >> 8), @truncate(u8, s2 >> 16), @truncate(u8, s1 >> 24)); + var t0 = x[0] ^ x[1] ^ x[2] ^ x[3]; + x = table_lookup(&table_decrypt, @truncate(u8, s1), @truncate(u8, s0 >> 8), @truncate(u8, s3 >> 16), @truncate(u8, s2 >> 24)); + var t1 = x[0] ^ x[1] ^ x[2] ^ x[3]; + x = table_lookup(&table_decrypt, @truncate(u8, s2), @truncate(u8, s1 >> 8), @truncate(u8, s0 >> 16), @truncate(u8, s3 >> 24)); + var t2 = x[0] ^ x[1] ^ x[2] ^ x[3]; + x = table_lookup(&table_decrypt, @truncate(u8, s3), @truncate(u8, s2 >> 8), @truncate(u8, s1 >> 16), @truncate(u8, s0 >> 24)); + var t3 = x[0] ^ x[1] ^ x[2] ^ x[3]; + + t0 ^= round_key.repr[0]; + t1 ^= round_key.repr[1]; + t2 ^= round_key.repr[2]; + t3 ^= round_key.repr[3]; + + return Block{ .repr = BlockVec{ t0, t1, t2, t3 } }; + } + + /// Decrypt a block with a round key *WITHOUT ANY PROTECTION AGAINST SIDE CHANNELS* + pub inline fn decryptUnprotected(block: Block, round_key: Block) Block { + const s0 = block.repr[0]; + const s1 = block.repr[1]; + const s2 = block.repr[2]; + const s3 = block.repr[3]; + + var x: [4]u32 = undefined; + x = .{ + table_decrypt[0][@truncate(u8, s0)], + table_decrypt[1][@truncate(u8, s3 >> 8)], + table_decrypt[2][@truncate(u8, s2 >> 16)], + table_decrypt[3][@truncate(u8, s1 >> 24)], + }; + var t0 = x[0] ^ x[1] ^ x[2] ^ x[3]; + x = .{ + table_decrypt[0][@truncate(u8, s1)], + table_decrypt[1][@truncate(u8, s0 >> 8)], + table_decrypt[2][@truncate(u8, s3 >> 16)], + table_decrypt[3][@truncate(u8, s2 >> 24)], + }; + var t1 = x[0] ^ x[1] ^ x[2] ^ x[3]; + x = .{ + table_decrypt[0][@truncate(u8, s2)], + table_decrypt[1][@truncate(u8, s1 >> 8)], + table_decrypt[2][@truncate(u8, s0 >> 16)], + table_decrypt[3][@truncate(u8, s3 >> 24)], + }; + var t2 = x[0] ^ x[1] ^ x[2] ^ x[3]; + x = .{ + table_decrypt[0][@truncate(u8, s3)], + table_decrypt[1][@truncate(u8, s2 >> 8)], + table_decrypt[2][@truncate(u8, s1 >> 16)], + table_decrypt[3][@truncate(u8, s0 >> 24)], + }; + var t3 = x[0] ^ x[1] ^ x[2] ^ x[3]; + + t0 ^= round_key.repr[0]; + t1 ^= round_key.repr[1]; + t2 ^= round_key.repr[2]; + t3 ^= round_key.repr[3]; return Block{ .repr = BlockVec{ t0, t1, t2, t3 } }; } /// Decrypt a block with the last round key. pub inline fn decryptLast(block: Block, round_key: Block) Block { - const t0 = block.repr[0]; - const t1 = block.repr[1]; - const t2 = block.repr[2]; - const t3 = block.repr[3]; + const s0 = block.repr[0]; + const s1 = block.repr[1]; + const s2 = block.repr[2]; + const s3 = block.repr[3]; // Last round uses s-box directly and XORs to produce output. - var s0 = @as(u32, sbox_decrypt[t0 >> 24]) << 24 | @as(u32, sbox_decrypt[t3 >> 16 & 0xff]) << 16 | @as(u32, sbox_decrypt[t2 >> 8 & 0xff]) << 8 | @as(u32, sbox_decrypt[t1 & 0xff]); - var s1 = @as(u32, sbox_decrypt[t1 >> 24]) << 24 | @as(u32, sbox_decrypt[t0 >> 16 & 0xff]) << 16 | @as(u32, sbox_decrypt[t3 >> 8 & 0xff]) << 8 | @as(u32, sbox_decrypt[t2 & 0xff]); - var s2 = @as(u32, sbox_decrypt[t2 >> 24]) << 24 | @as(u32, sbox_decrypt[t1 >> 16 & 0xff]) << 16 | @as(u32, sbox_decrypt[t0 >> 8 & 0xff]) << 8 | @as(u32, sbox_decrypt[t3 & 0xff]); - var s3 = @as(u32, sbox_decrypt[t3 >> 24]) << 24 | @as(u32, sbox_decrypt[t2 >> 16 & 0xff]) << 16 | @as(u32, sbox_decrypt[t1 >> 8 & 0xff]) << 8 | @as(u32, sbox_decrypt[t0 & 0xff]); - s0 ^= round_key.repr[0]; - s1 ^= round_key.repr[1]; - s2 ^= round_key.repr[2]; - s3 ^= round_key.repr[3]; + var x: [4]u8 = undefined; + x = sbox_lookup(&sbox_decrypt, @truncate(u8, s1 >> 24), @truncate(u8, s2 >> 16), @truncate(u8, s3 >> 8), @truncate(u8, s0)); + var t0 = @as(u32, x[0]) << 24 | @as(u32, x[1]) << 16 | @as(u32, x[2]) << 8 | @as(u32, x[3]); + x = sbox_lookup(&sbox_decrypt, @truncate(u8, s2 >> 24), @truncate(u8, s3 >> 16), @truncate(u8, s0 >> 8), @truncate(u8, s1)); + var t1 = @as(u32, x[0]) << 24 | @as(u32, x[1]) << 16 | @as(u32, x[2]) << 8 | @as(u32, x[3]); + x = sbox_lookup(&sbox_decrypt, @truncate(u8, s3 >> 24), @truncate(u8, s0 >> 16), @truncate(u8, s1 >> 8), @truncate(u8, s2)); + var t2 = @as(u32, x[0]) << 24 | @as(u32, x[1]) << 16 | @as(u32, x[2]) << 8 | @as(u32, x[3]); + x = sbox_lookup(&sbox_decrypt, @truncate(u8, s0 >> 24), @truncate(u8, s1 >> 16), @truncate(u8, s2 >> 8), @truncate(u8, s3)); + var t3 = @as(u32, x[0]) << 24 | @as(u32, x[1]) << 16 | @as(u32, x[2]) << 8 | @as(u32, x[3]); - return Block{ .repr = BlockVec{ s0, s1, s2, s3 } }; + t0 ^= round_key.repr[0]; + t1 ^= round_key.repr[1]; + t2 ^= round_key.repr[2]; + t3 ^= round_key.repr[3]; + + return Block{ .repr = BlockVec{ t0, t1, t2, t3 } }; } /// Apply the bitwise XOR operation to the content of two blocks. @@ -226,7 +348,8 @@ fn KeySchedule(comptime Aes: type) type { const subw = struct { // Apply sbox_encrypt to each byte in w. fn func(w: u32) u32 { - return @as(u32, sbox_encrypt[w >> 24]) << 24 | @as(u32, sbox_encrypt[w >> 16 & 0xff]) << 16 | @as(u32, sbox_encrypt[w >> 8 & 0xff]) << 8 | @as(u32, sbox_encrypt[w & 0xff]); + const x = sbox_lookup(&sbox_key_schedule, @truncate(u8, w), @truncate(u8, w >> 8), @truncate(u8, w >> 16), @truncate(u8, w >> 24)); + return @as(u32, x[3]) << 24 | @as(u32, x[2]) << 16 | @as(u32, x[1]) << 8 | @as(u32, x[0]); } }.func; @@ -244,6 +367,10 @@ fn KeySchedule(comptime Aes: type) type { } round_keys[i / 4].repr[i % 4] = round_keys[(i - words_in_key) / 4].repr[(i - words_in_key) % 4] ^ t; } + i = 0; + inline while (i < round_keys.len * 4) : (i += 1) { + round_keys[i / 4].repr[i % 4] = @byteSwap(round_keys[i / 4].repr[i % 4]); + } return Self{ .round_keys = round_keys }; } @@ -257,11 +384,13 @@ fn KeySchedule(comptime Aes: type) type { const ei = total_words - i - 4; comptime var j: usize = 0; inline while (j < 4) : (j += 1) { - var x = round_keys[(ei + j) / 4].repr[(ei + j) % 4]; + var rk = round_keys[(ei + j) / 4].repr[(ei + j) % 4]; if (i > 0 and i + 4 < total_words) { - x = table_decrypt[0][sbox_encrypt[x >> 24]] ^ table_decrypt[1][sbox_encrypt[x >> 16 & 0xff]] ^ table_decrypt[2][sbox_encrypt[x >> 8 & 0xff]] ^ table_decrypt[3][sbox_encrypt[x & 0xff]]; + const x = sbox_lookup(&sbox_key_schedule, @truncate(u8, rk >> 24), @truncate(u8, rk >> 16), @truncate(u8, rk >> 8), @truncate(u8, rk)); + const y = table_lookup(&table_decrypt, x[3], x[2], x[1], x[0]); + rk = y[0] ^ y[1] ^ y[2] ^ y[3]; } - inv_round_keys[(i + j) / 4].repr[(i + j) % 4] = x; + inv_round_keys[(i + j) / 4].repr[(i + j) % 4] = rk; } } return Self{ .round_keys = inv_round_keys }; @@ -293,7 +422,17 @@ pub fn AesEncryptCtx(comptime Aes: type) type { const round_keys = ctx.key_schedule.round_keys; var t = Block.fromBytes(src).xorBlocks(round_keys[0]); comptime var i = 1; - inline while (i < rounds) : (i += 1) { + if (side_channels_mitigations == .full) { + inline while (i < rounds) : (i += 1) { + t = t.encrypt(round_keys[i]); + } + } else { + inline while (i < 5) : (i += 1) { + t = t.encrypt(round_keys[i]); + } + inline while (i < rounds - 1) : (i += 1) { + t = t.encryptUnprotected(round_keys[i]); + } t = t.encrypt(round_keys[i]); } t = t.encryptLast(round_keys[rounds]); @@ -305,7 +444,17 @@ pub fn AesEncryptCtx(comptime Aes: type) type { const round_keys = ctx.key_schedule.round_keys; var t = Block.fromBytes(&counter).xorBlocks(round_keys[0]); comptime var i = 1; - inline while (i < rounds) : (i += 1) { + if (side_channels_mitigations == .full) { + inline while (i < rounds) : (i += 1) { + t = t.encrypt(round_keys[i]); + } + } else { + inline while (i < 5) : (i += 1) { + t = t.encrypt(round_keys[i]); + } + inline while (i < rounds - 1) : (i += 1) { + t = t.encryptUnprotected(round_keys[i]); + } t = t.encrypt(round_keys[i]); } t = t.encryptLast(round_keys[rounds]); @@ -359,7 +508,17 @@ pub fn AesDecryptCtx(comptime Aes: type) type { const inv_round_keys = ctx.key_schedule.round_keys; var t = Block.fromBytes(src).xorBlocks(inv_round_keys[0]); comptime var i = 1; - inline while (i < rounds) : (i += 1) { + if (side_channels_mitigations == .full) { + inline while (i < rounds) : (i += 1) { + t = t.decrypt(inv_round_keys[i]); + } + } else { + inline while (i < 5) : (i += 1) { + t = t.decrypt(inv_round_keys[i]); + } + inline while (i < rounds - 1) : (i += 1) { + t = t.decryptUnprotected(inv_round_keys[i]); + } t = t.decrypt(inv_round_keys[i]); } t = t.decryptLast(inv_round_keys[rounds]); @@ -428,10 +587,11 @@ const powx = init: { break :init array; }; -const sbox_encrypt align(64) = generateSbox(false); -const sbox_decrypt align(64) = generateSbox(true); -const table_encrypt align(64) = generateTable(false); -const table_decrypt align(64) = generateTable(true); +const sbox_encrypt align(64) = generateSbox(false); // S-box for encryption +const sbox_key_schedule align(64) = generateSbox(false); // S-box only for key schedule, so that it uses distinct L1 cache entries than the S-box used for encryption +const sbox_decrypt align(64) = generateSbox(true); // S-box for decryption +const table_encrypt align(64) = generateTable(false); // 4-byte LUTs for encryption +const table_decrypt align(64) = generateTable(true); // 4-byte LUTs for decryption // Generate S-box substitution values. fn generateSbox(invert: bool) [256]u8 { @@ -472,14 +632,14 @@ fn generateTable(invert: bool) [4][256]u32 { var table: [4][256]u32 = undefined; for (generateSbox(invert), 0..) |value, index| { - table[0][index] = mul(value, if (invert) 0xb else 0x3); - table[0][index] |= math.shl(u32, mul(value, if (invert) 0xd else 0x1), 8); - table[0][index] |= math.shl(u32, mul(value, if (invert) 0x9 else 0x1), 16); - table[0][index] |= math.shl(u32, mul(value, if (invert) 0xe else 0x2), 24); + table[0][index] = math.shl(u32, mul(value, if (invert) 0xb else 0x3), 24); + table[0][index] |= math.shl(u32, mul(value, if (invert) 0xd else 0x1), 16); + table[0][index] |= math.shl(u32, mul(value, if (invert) 0x9 else 0x1), 8); + table[0][index] |= mul(value, if (invert) 0xe else 0x2); - table[1][index] = math.rotr(u32, table[0][index], 8); - table[2][index] = math.rotr(u32, table[0][index], 16); - table[3][index] = math.rotr(u32, table[0][index], 24); + table[1][index] = math.rotl(u32, table[0][index], 8); + table[2][index] = math.rotl(u32, table[0][index], 16); + table[3][index] = math.rotl(u32, table[0][index], 24); } return table; @@ -506,3 +666,82 @@ fn mul(a: u8, b: u8) u8 { return @truncate(u8, s); } + +const cache_line_bytes = 64; + +inline fn sbox_lookup(sbox: *align(64) const [256]u8, idx0: u8, idx1: u8, idx2: u8, idx3: u8) [4]u8 { + if (side_channels_mitigations == .none) { + return [4]u8{ + sbox[idx0], + sbox[idx1], + sbox[idx2], + sbox[idx3], + }; + } else { + const stride = switch (side_channels_mitigations) { + .none => unreachable, + .basic => sbox.len / 4, + .medium => sbox.len / (sbox.len / cache_line_bytes) * 2, + .full => sbox.len / (sbox.len / cache_line_bytes), + }; + const of0 = idx0 % stride; + const of1 = idx1 % stride; + const of2 = idx2 % stride; + const of3 = idx3 % stride; + var t: [4][sbox.len / stride]u8 align(64) = undefined; + var i: usize = 0; + while (i < t[0].len) : (i += 1) { + const tx = sbox[i * stride ..]; + t[0][i] = tx[of0]; + t[1][i] = tx[of1]; + t[2][i] = tx[of2]; + t[3][i] = tx[of3]; + } + std.mem.doNotOptimizeAway(t); + return [4]u8{ + t[0][idx0 / stride], + t[1][idx1 / stride], + t[2][idx2 / stride], + t[3][idx3 / stride], + }; + } +} + +inline fn table_lookup(table: *align(64) const [4][256]u32, idx0: u8, idx1: u8, idx2: u8, idx3: u8) [4]u32 { + if (side_channels_mitigations == .none) { + return [4]u32{ + table[0][idx0], + table[1][idx1], + table[2][idx2], + table[3][idx3], + }; + } else { + const table_bytes = @sizeOf(@TypeOf(table[0])); + const stride = switch (side_channels_mitigations) { + .none => unreachable, + .basic => table[0].len / 4, + .medium => table[0].len / (table_bytes / cache_line_bytes) * 2, + .full => table[0].len / (table_bytes / cache_line_bytes), + }; + const of0 = idx0 % stride; + const of1 = idx1 % stride; + const of2 = idx2 % stride; + const of3 = idx3 % stride; + var t: [4][table[0].len / stride]u32 align(64) = undefined; + var i: usize = 0; + while (i < t[0].len) : (i += 1) { + const tx = table[0][i * stride ..]; + t[0][i] = tx[of0]; + t[1][i] = tx[of1]; + t[2][i] = tx[of2]; + t[3][i] = tx[of3]; + } + std.mem.doNotOptimizeAway(t); + return [4]u32{ + t[0][idx0 / stride], + math.rotl(u32, t[1][idx1 / stride], 8), + math.rotl(u32, t[2][idx2 / stride], 16), + math.rotl(u32, t[3][idx3 / stride], 24), + }; + } +} From 5a12d00708df019fa510076f8af40d6efcb7c608 Mon Sep 17 00:00:00 2001 From: Frank Denis <124872+jedisct1@users.noreply.github.com> Date: Tue, 14 Mar 2023 07:40:23 +0100 Subject: [PATCH 118/294] Move std.crypto.config options to std.options (#14906) Options have been moved to a single namespace. --- lib/std/crypto.zig | 42 +++++++++++++++++-------------------- lib/std/crypto/aes/soft.zig | 2 +- lib/std/std.zig | 5 +++++ 3 files changed, 25 insertions(+), 24 deletions(-) diff --git a/lib/std/crypto.zig b/lib/std/crypto.zig index b469620002..d70958f6dd 100644 --- a/lib/std/crypto.zig +++ b/lib/std/crypto.zig @@ -185,31 +185,27 @@ pub const errors = @import("crypto/errors.zig"); pub const tls = @import("crypto/tls.zig"); pub const Certificate = @import("crypto/Certificate.zig"); -/// Global configuration of cryptographic implementations in the standard library. -pub const config = struct { - /// Side-channels mitigations. - pub const SideChannelsMitigations = enum { - /// No additional side-channel mitigations are applied. - /// This is the fastest mode. - none, - /// The `basic` mode protects against most practical attacks, provided that the - /// application or implements proper defenses against brute-force attacks. - /// It offers a good balance between performance and security. - basic, - /// The `medium` mode offers increased resilience against side-channel attacks, - /// making most attacks unpractical even on shared/low latency environements. - /// This is the default mode. - medium, - /// The `full` mode offers the highest level of protection against side-channel attacks. - /// Note that this doesn't cover all possible attacks (especially power analysis or - /// thread-local attacks such as cachebleed), and that the performance impact is significant. - full, - }; - - /// This is a global configuration that applies to all cryptographic implementations. - pub const side_channels_mitigations: SideChannelsMitigations = if (@hasDecl(root, "side_channels_mitigations")) root.side_channels_mitigations else .medium; +/// Side-channels mitigations. +pub const SideChannelsMitigations = enum { + /// No additional side-channel mitigations are applied. + /// This is the fastest mode. + none, + /// The `basic` mode protects against most practical attacks, provided that the + /// application or implements proper defenses against brute-force attacks. + /// It offers a good balance between performance and security. + basic, + /// The `medium` mode offers increased resilience against side-channel attacks, + /// making most attacks unpractical even on shared/low latency environements. + /// This is the default mode. + medium, + /// The `full` mode offers the highest level of protection against side-channel attacks. + /// Note that this doesn't cover all possible attacks (especially power analysis or + /// thread-local attacks such as cachebleed), and that the performance impact is significant. + full, }; +pub const default_side_channels_mitigations = .medium; + test { _ = aead.aegis.Aegis128L; _ = aead.aegis.Aegis256; diff --git a/lib/std/crypto/aes/soft.zig b/lib/std/crypto/aes/soft.zig index 4a300961c6..7a8e7ff0ec 100644 --- a/lib/std/crypto/aes/soft.zig +++ b/lib/std/crypto/aes/soft.zig @@ -4,7 +4,7 @@ const mem = std.mem; const BlockVec = [4]u32; -const side_channels_mitigations = std.crypto.config.side_channels_mitigations; +const side_channels_mitigations = std.options.side_channels_mitigations; /// A single AES block. pub const Block = struct { diff --git a/lib/std/std.zig b/lib/std/std.zig index e888ade659..92ebdf595b 100644 --- a/lib/std/std.zig +++ b/lib/std/std.zig @@ -190,6 +190,11 @@ pub const options = struct { options_override.http_connection_pool_size else http.Client.default_connection_pool_size; + + pub const side_channels_mitigations: crypto.SideChannelsMitigations = if (@hasDecl(options_override, "side_channels_mitigations")) + options_override.side_channels_mitigations + else + crypto.default_side_channels_mitigations; }; // This forces the start.zig file to be imported, and the comptime logic inside that From 1e6d7f77639d52b61b2852cf0de19e2b5a50f31f Mon Sep 17 00:00:00 2001 From: mlugg Date: Fri, 3 Mar 2023 21:13:31 +0000 Subject: [PATCH 119/294] Sema: allow comptime mutation of multiple array elements Previously, if you had a pointer to multiple array elements and tried to write to it at comptime, it was incorrectly treated as a pointer to one specific array value, leading to an assertion down the line. If we try to mutate a value at an elem_ptr larger than the element type, we need to perform a modification to multiple array elements. This solution isn't ideal, since it will result in storePtrVal serializing the whole array, modifying the relevant parts, and storing it back. Ideally, it would only take the required elements. However, this change would have been more complex, and this is a fairly rare operation (nobody ever ran into the bug before after all), so it doesn't matter all that much. --- src/Sema.zig | 17 +++++++++++++++++ test/behavior/array.zig | 27 +++++++++++++++++---------- test/behavior/comptime_memory.zig | 8 ++++++++ 3 files changed, 42 insertions(+), 10 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index e6652a5d66..8b6c269246 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -26648,6 +26648,23 @@ fn beginComptimePtrMutation( }); } const elem_ty = parent.ty.childType(); + + // We might have a pointer to multiple elements of the array (e.g. a pointer + // to a sub-array). In this case, we just have to reinterpret the relevant + // bytes of the whole array rather than any single element. + const elem_abi_size_u64 = try sema.typeAbiSize(elem_ptr.elem_ty); + if (elem_abi_size_u64 < try sema.typeAbiSize(ptr_elem_ty)) { + const elem_abi_size = try sema.usizeCast(block, src, elem_abi_size_u64); + return .{ + .decl_ref_mut = parent.decl_ref_mut, + .pointee = .{ .reinterpret = .{ + .val_ptr = val_ptr, + .byte_offset = elem_abi_size * elem_ptr.index, + } }, + .ty = parent.ty, + }; + } + switch (val_ptr.tag()) { .undef => { // An array has been initialized to undefined at comptime and now we diff --git a/test/behavior/array.zig b/test/behavior/array.zig index a5ecd6f115..3d711357f3 100644 --- a/test/behavior/array.zig +++ b/test/behavior/array.zig @@ -48,16 +48,23 @@ fn getArrayLen(a: []const u32) usize { test "array concat with undefined" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - { - var array = "hello".* ++ @as([5]u8, undefined); - array[5..10].* = "world".*; - try std.testing.expect(std.mem.eql(u8, &array, "helloworld")); - } - { - var array = @as([5]u8, undefined) ++ "world".*; - array[0..5].* = "hello".*; - try std.testing.expect(std.mem.eql(u8, &array, "helloworld")); - } + const S = struct { + fn doTheTest() !void { + { + var array = "hello".* ++ @as([5]u8, undefined); + array[5..10].* = "world".*; + try std.testing.expect(std.mem.eql(u8, &array, "helloworld")); + } + { + var array = @as([5]u8, undefined) ++ "world".*; + array[0..5].* = "hello".*; + try std.testing.expect(std.mem.eql(u8, &array, "helloworld")); + } + } + }; + + try S.doTheTest(); + comptime try S.doTheTest(); } test "array concat with tuple" { diff --git a/test/behavior/comptime_memory.zig b/test/behavior/comptime_memory.zig index a4f9f2f7a9..71d177395b 100644 --- a/test/behavior/comptime_memory.zig +++ b/test/behavior/comptime_memory.zig @@ -412,3 +412,11 @@ test "bitcast packed union to integer" { try testing.expectEqual(@as(u2, 2), cast_b); } } + +test "mutate entire slice at comptime" { + comptime { + var buf: [3]u8 = undefined; + const x: [2]u8 = .{ 1, 2 }; // Avoid RLS + buf[1..3].* = x; + } +} From 9ecdcb8e300a4f6aac050e3118ceea0b7d35f899 Mon Sep 17 00:00:00 2001 From: Kotaro Inoue Date: Tue, 14 Mar 2023 20:07:25 +0900 Subject: [PATCH 120/294] Fix to use '/' for a empty path (#14884) Signed-off-by: Kotaro Inoue --- lib/std/http/Client.zig | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/std/http/Client.zig b/lib/std/http/Client.zig index baf0239388..76073c0ce3 100644 --- a/lib/std/http/Client.zig +++ b/lib/std/http/Client.zig @@ -333,7 +333,11 @@ pub fn request(client: *Client, uri: Uri, headers: Request.Headers, options: Req try writer.writeAll(@tagName(headers.method)); try writer.writeByte(' '); - try writer.writeAll(escaped_path); + if (escaped_path.len == 0) { + try writer.writeByte('/'); + } else { + try writer.writeAll(escaped_path); + } if (escaped_query) |q| { try writer.writeByte('?'); try writer.writeAll(q); From d6e48abde87400a8a4851c7ab8c918005d81d058 Mon Sep 17 00:00:00 2001 From: DerryAlex <105348204+DerryAlex@users.noreply.github.com> Date: Tue, 14 Mar 2023 19:08:56 +0800 Subject: [PATCH 121/294] Implement readFromMemory/writeToMemory for ptrLikeOptional --- src/value.zig | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/value.zig b/src/value.zig index b6d27d620d..e5283d1270 100644 --- a/src/value.zig +++ b/src/value.zig @@ -1365,6 +1365,17 @@ pub const Value = extern union { if (val.isDeclRef()) return error.ReinterpretDeclRef; return val.writeToMemory(Type.usize, mod, buffer); }, + .Optional => { + assert(ty.isPtrLikeOptional()); + var buf: Type.Payload.ElemType = undefined; + const child = ty.optionalChild(&buf); + const opt_val = val.optionalValue(); + if (opt_val) |some| { + return some.writeToMemory(child, mod, buffer); + } else { + return writeToMemory(Value.zero, Type.usize, mod, buffer); + } + }, else => @panic("TODO implement writeToMemory for more types"), } } @@ -1471,6 +1482,17 @@ pub const Value = extern union { if (val.isDeclRef()) return error.ReinterpretDeclRef; return val.writeToPackedMemory(Type.usize, mod, buffer, bit_offset); }, + .Optional => { + assert(ty.isPtrLikeOptional()); + var buf: Type.Payload.ElemType = undefined; + const child = ty.optionalChild(&buf); + const opt_val = val.optionalValue(); + if (opt_val) |some| { + return some.writeToPackedMemory(child, mod, buffer, bit_offset); + } else { + return writeToPackedMemory(Value.zero, Type.usize, mod, buffer, bit_offset); + } + }, else => @panic("TODO implement writeToPackedMemory for more types"), } } @@ -1579,6 +1601,12 @@ pub const Value = extern union { assert(!ty.isSlice()); // No well defined layout. return readFromMemory(Type.usize, mod, buffer, arena); }, + .Optional => { + assert(ty.isPtrLikeOptional()); + var buf: Type.Payload.ElemType = undefined; + const child = ty.optionalChild(&buf); + return readFromMemory(child, mod, buffer, arena); + }, else => @panic("TODO implement readFromMemory for more types"), } } @@ -1670,6 +1698,12 @@ pub const Value = extern union { assert(!ty.isSlice()); // No well defined layout. return readFromPackedMemory(Type.usize, mod, buffer, bit_offset, arena); }, + .Optional => { + assert(ty.isPtrLikeOptional()); + var buf: Type.Payload.ElemType = undefined; + const child = ty.optionalChild(&buf); + return readFromPackedMemory(child, mod, buffer, bit_offset, arena); + }, else => @panic("TODO implement readFromPackedMemory for more types"), } } From e17998b39655e8af5d2a1134fee7ca4850ad4389 Mon Sep 17 00:00:00 2001 From: Frank Denis <124872+jedisct1@users.noreply.github.com> Date: Tue, 14 Mar 2023 22:40:02 +0100 Subject: [PATCH 122/294] Argon2: properly handle outputs > 64 bytes in blake2Long() (#14914) Fixes #14912 --- lib/std/crypto/argon2.zig | 53 +++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/lib/std/crypto/argon2.zig b/lib/std/crypto/argon2.zig index a95e75e538..0112e81c6a 100644 --- a/lib/std/crypto/argon2.zig +++ b/lib/std/crypto/argon2.zig @@ -138,40 +138,39 @@ fn initHash( } fn blake2bLong(out: []u8, in: []const u8) void { - var b2 = Blake2b512.init(.{ .expected_out_bits = math.min(512, out.len * 8) }); + const H = Blake2b512; + var outlen_bytes: [4]u8 = undefined; + mem.writeIntLittle(u32, &outlen_bytes, @intCast(u32, out.len)); - var buffer: [Blake2b512.digest_length]u8 = undefined; - mem.writeIntLittle(u32, buffer[0..4], @intCast(u32, out.len)); - b2.update(buffer[0..4]); - b2.update(in); - b2.final(&buffer); + var out_buf: [H.digest_length]u8 = undefined; - if (out.len <= Blake2b512.digest_length) { - mem.copy(u8, out, buffer[0..out.len]); + if (out.len <= H.digest_length) { + var h = H.init(.{ .expected_out_bits = out.len * 8 }); + h.update(&outlen_bytes); + h.update(in); + h.final(&out_buf); + mem.copy(u8, out, out_buf[0..out.len]); return; } - b2 = Blake2b512.init(.{}); - mem.copy(u8, out, buffer[0..32]); - var out_slice = out[32..]; - while (out_slice.len > Blake2b512.digest_length) : ({ - out_slice = out_slice[32..]; - b2 = Blake2b512.init(.{}); - }) { - b2.update(&buffer); - b2.final(&buffer); - mem.copy(u8, out_slice, buffer[0..32]); - } + var h = H.init(.{}); + h.update(&outlen_bytes); + h.update(in); + h.final(&out_buf); + var out_slice = out; + mem.copy(u8, out_slice, out_buf[0 .. H.digest_length / 2]); + out_slice = out_slice[H.digest_length / 2 ..]; - var r = Blake2b512.digest_length; - if (out.len % Blake2b512.digest_length > 0) { - r = ((out.len + 31) / 32) - 2; - b2 = Blake2b512.init(.{ .expected_out_bits = r * 8 }); + var in_buf: [H.digest_length]u8 = undefined; + while (out_slice.len > H.digest_length) { + mem.copy(u8, &in_buf, &out_buf); + H.hash(&in_buf, &out_buf, .{}); + mem.copy(u8, out_slice, out_buf[0 .. H.digest_length / 2]); + out_slice = out_slice[H.digest_length / 2 ..]; } - - b2.update(&buffer); - b2.final(&buffer); - mem.copy(u8, out_slice, buffer[0..r]); + mem.copy(u8, &in_buf, &out_buf); + H.hash(&in_buf, &out_buf, .{ .expected_out_bits = out_slice.len * 8 }); + mem.copy(u8, out_slice, out_buf[0..out_slice.len]); } fn initBlocks( From 4414f9c46e778a58c6d08df3c6ab604449abf38d Mon Sep 17 00:00:00 2001 From: Bas Westerbaan Date: Wed, 15 Mar 2023 04:50:45 +0100 Subject: [PATCH 123/294] Add Kyber post-quantum key encapsulation mechanism (#14902) Implementation of the IND-CCA2 post-quantum secure key encapsulation mechanism (KEM) CRYSTALS-Kyber, as submitted to the third round of the NIST Post-Quantum Cryptography (v3.02/"draft00"), and selected for standardisation. Co-authored-by: Frank Denis <124872+jedisct1@users.noreply.github.com> --- lib/std/crypto.zig | 7 + lib/std/crypto/benchmark.zig | 87 ++ lib/std/crypto/kyber_d00.zig | 1780 ++++++++++++++++++++++++++++++++++ 3 files changed, 1874 insertions(+) create mode 100644 lib/std/crypto/kyber_d00.zig diff --git a/lib/std/crypto.zig b/lib/std/crypto.zig index d70958f6dd..9b995480aa 100644 --- a/lib/std/crypto.zig +++ b/lib/std/crypto.zig @@ -68,6 +68,11 @@ pub const dh = struct { pub const X25519 = @import("crypto/25519/x25519.zig").X25519; }; +/// Key Encapsulation Mechanisms. +pub const kem = struct { + pub const kyber_d00 = @import("crypto/kyber_d00.zig"); +}; + /// Elliptic-curve arithmetic. pub const ecc = struct { pub const Curve25519 = @import("crypto/25519/curve25519.zig").Curve25519; @@ -240,6 +245,8 @@ test { _ = dh.X25519; + _ = kem.kyber_d00; + _ = ecc.Curve25519; _ = ecc.Edwards25519; _ = ecc.P256; diff --git a/lib/std/crypto/benchmark.zig b/lib/std/crypto/benchmark.zig index 47ca24aa66..ff098c7804 100644 --- a/lib/std/crypto/benchmark.zig +++ b/lib/std/crypto/benchmark.zig @@ -203,6 +203,72 @@ pub fn benchmarkBatchSignatureVerification(comptime Signature: anytype, comptime return throughput; } +const kems = [_]Crypto{ + Crypto{ .ty = crypto.kem.kyber_d00.Kyber512, .name = "kyber512d00" }, + Crypto{ .ty = crypto.kem.kyber_d00.Kyber768, .name = "kyber768d00" }, + Crypto{ .ty = crypto.kem.kyber_d00.Kyber1024, .name = "kyber1024d00" }, +}; + +pub fn benchmarkKem(comptime Kem: anytype, comptime kems_count: comptime_int) !u64 { + const key_pair = try Kem.KeyPair.create(null); + + var timer = try Timer.start(); + const start = timer.lap(); + { + var i: usize = 0; + while (i < kems_count) : (i += 1) { + const e = key_pair.public_key.encaps(null); + mem.doNotOptimizeAway(&e); + } + } + const end = timer.read(); + + const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s; + const throughput = @floatToInt(u64, kems_count / elapsed_s); + + return throughput; +} + +pub fn benchmarkKemDecaps(comptime Kem: anytype, comptime kems_count: comptime_int) !u64 { + const key_pair = try Kem.KeyPair.create(null); + + const e = key_pair.public_key.encaps(null); + + var timer = try Timer.start(); + const start = timer.lap(); + { + var i: usize = 0; + while (i < kems_count) : (i += 1) { + const ss2 = try key_pair.secret_key.decaps(&e.ciphertext); + mem.doNotOptimizeAway(&ss2); + } + } + const end = timer.read(); + + const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s; + const throughput = @floatToInt(u64, kems_count / elapsed_s); + + return throughput; +} + +pub fn benchmarkKemKeyGen(comptime Kem: anytype, comptime kems_count: comptime_int) !u64 { + var timer = try Timer.start(); + const start = timer.lap(); + { + var i: usize = 0; + while (i < kems_count) : (i += 1) { + const key_pair = try Kem.KeyPair.create(null); + mem.doNotOptimizeAway(&key_pair); + } + } + const end = timer.read(); + + const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s; + const throughput = @floatToInt(u64, kems_count / elapsed_s); + + return throughput; +} + const aeads = [_]Crypto{ Crypto{ .ty = crypto.aead.chacha_poly.ChaCha20Poly1305, .name = "chacha20Poly1305" }, Crypto{ .ty = crypto.aead.chacha_poly.XChaCha20Poly1305, .name = "xchacha20Poly1305" }, @@ -485,4 +551,25 @@ pub fn main() !void { try stdout.print("{s:>17}: {d:10.3} s/ops\n", .{ H.name, throughput }); } } + + inline for (kems) |E| { + if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) { + const throughput = try benchmarkKem(E.ty, mode(1000)); + try stdout.print("{s:>17}: {:10} encaps/s\n", .{ E.name, throughput }); + } + } + + inline for (kems) |E| { + if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) { + const throughput = try benchmarkKemDecaps(E.ty, mode(25000)); + try stdout.print("{s:>17}: {:10} decaps/s\n", .{ E.name, throughput }); + } + } + + inline for (kems) |E| { + if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) { + const throughput = try benchmarkKemKeyGen(E.ty, mode(25000)); + try stdout.print("{s:>17}: {:10} keygen/s\n", .{ E.name, throughput }); + } + } } diff --git a/lib/std/crypto/kyber_d00.zig b/lib/std/crypto/kyber_d00.zig new file mode 100644 index 0000000000..21fdb6ff17 --- /dev/null +++ b/lib/std/crypto/kyber_d00.zig @@ -0,0 +1,1780 @@ +//! Implementation of the IND-CCA2 post-quantum secure key encapsulation +//! mechanism (KEM) CRYSTALS-Kyber, as submitted to the third round of the NIST +//! Post-Quantum Cryptography (v3.02/"draft00"), and selected for standardisation. +//! +//! Kyber will likely change before final standardisation. +//! +//! The namespace suffix (currently `_d00`) refers to the version currently +//! implemented, in accordance with the draft. It may not be updated if new +//! versions of the draft only include editorial changes. +//! +//! The suffix will eventually be removed once Kyber is finalized. +//! +//! Quoting from the CFRG I-D: +//! +//! Kyber is not a Diffie-Hellman (DH) style non-interactive key +//! agreement, but instead, Kyber is a Key Encapsulation Method (KEM). +//! In essence, a KEM is a Public-Key Encryption (PKE) scheme where the +//! plaintext cannot be specified, but is generated as a random key as +//! part of the encryption. A KEM can be transformed into an unrestricted +//! PKE using HPKE (RFC9180). On its own, a KEM can be used as a key +//! agreement method in TLS. +//! +//! Kyber is an IND-CCA2 secure KEM. It is constructed by applying a +//! Fujisaki--Okamato style transformation on InnerPKE, which is the +//! underlying IND-CPA secure Public Key Encryption scheme. We cannot +//! use InnerPKE directly, as its ciphertexts are malleable. +//! +//! ``` +//! F.O. transform +//! InnerPKE ----------------------> Kyber +//! IND-CPA IND-CCA2 +//! ``` +//! +//! Kyber is a lattice-based scheme. More precisely, its security is +//! based on the learning-with-errors-and-rounding problem in module +//! lattices (MLWER). The underlying polynomial ring R (defined in +//! Section 5) is chosen such that multiplication is very fast using the +//! number theoretic transform (NTT, see Section 5.1.3). +//! +//! An InnerPKE private key is a vector _s_ over R of length k which is +//! _small_ in a particular way. Here k is a security parameter akin to +//! the size of a prime modulus. For Kyber512, which targets AES-128's +//! security level, the value of k is 2. +//! +//! The public key consists of two values: +//! +//! * _A_ a uniformly sampled k by k matrix over R _and_ +//! +//! * _t = A s + e_, where e is a suitably small masking vector. +//! +//! Distinguishing between such A s + e and a uniformly sampled t is the +//! module learning-with-errors (MLWE) problem. If that is hard, then it +//! is also hard to recover the private key from the public key as that +//! would allow you to distinguish between those two. +//! +//! To save space in the public key, A is recomputed deterministically +//! from a seed _rho_. +//! +//! A ciphertext for a message m under this public key is a pair (c_1, +//! c_2) computed roughly as follows: +//! +//! c_1 = Compress(A^T r + e_1, d_u) +//! c_2 = Compress(t^T r + e_2 + Decompress(m, 1), d_v) +//! +//! where +//! +//! * e_1, e_2 and r are small blinds; +//! +//! * Compress(-, d) removes some information, leaving d bits per +//! coefficient and Decompress is such that Compress after Decompress +//! does nothing and +//! +//! * d_u, d_v are scheme parameters. +//! +//! Distinguishing such a ciphertext and uniformly sampled (c_1, c_2) is +//! an example of the full MLWER problem, see section 4.4 of [KyberV302]. +//! +//! To decrypt the ciphertext, one computes +//! +//! m = Compress(Decompress(c_2, d_v) - s^T Decompress(c_1, d_u), 1). +//! +//! It it not straight-forward to see that this formula is correct. In +//! fact, there is negligable but non-zero probability that a ciphertext +//! does not decrypt correctly given by the DFP column in Table 4. This +//! failure probability can be computed by a careful automated analysis +//! of the probabilities involved, see kyber_failure.py of [SecEst]. +//! +//! [KyberV302](https://pq-crystals.org/kyber/data/kyber-specification-round3-20210804.pdf) +//! [I-D](https://github.com/bwesterb/draft-schwabe-cfrg-kyber) +//! [SecEst](https://github.com/pq-crystals/security-estimates) + +// TODO +// +// - The bottleneck in Kyber are the various hash/xof calls: +// - Optimize Zig's keccak implementation. +// - Use SIMD to compute keccak in parallel. +// - Can we track bounds of coefficients using comptime types without +// duplicating code? +// - Would be neater to have tests closer to the thing under test. +// - When generating a keypair, we have a copy of the inner public key with +// its large matrix A in both the public key and the private key. In Go we +// can just have a pointer in the private key to the public key, but +// how do we do this elegantly in Zig? + +const std = @import("std"); +const builtin = @import("builtin"); + +const testing = std.testing; +const assert = std.debug.assert; +const crypto = std.crypto; +const math = std.math; +const mem = std.mem; +const RndGen = std.rand.DefaultPrng; +const sha3 = crypto.hash.sha3; + +// Q is the parameter q ≡ 3329 = 2¹¹ + 2¹⁰ + 2⁸ + 1. +const Q: i16 = 3329; + +// Montgomery R +const R: i32 = 1 << 16; + +// Parameter n, degree of polynomials. +const N: usize = 256; + +// Size of "small" vectors used in encryption blinds. +const eta2: u8 = 2; + +const Params = struct { + name: []const u8, + + // Width and height of the matrix A. + k: u8, + + // Size of "small" vectors used in private key and encryption blinds. + eta1: u8, + + // How many bits to retain of u, the private-key independent part + // of the ciphertext. + du: u8, + + // How many bits to retain of v, the private-key dependent part + // of the ciphertext. + dv: u8, +}; + +pub const Kyber512 = Kyber(.{ + .name = "Kyber512", + .k = 2, + .eta1 = 3, + .du = 10, + .dv = 4, +}); + +pub const Kyber768 = Kyber(.{ + .name = "Kyber768", + .k = 3, + .eta1 = 2, + .du = 10, + .dv = 4, +}); + +pub const Kyber1024 = Kyber(.{ + .name = "Kyber1024", + .k = 4, + .eta1 = 2, + .du = 11, + .dv = 5, +}); + +const modes = [_]type{ Kyber512, Kyber768, Kyber1024 }; +const h_length: usize = 32; +const inner_seed_length: usize = 32; +const common_encaps_seed_length: usize = 32; +const common_shared_key_size: usize = 32; + +fn Kyber(comptime p: Params) type { + return struct { + // Size of a ciphertext, in bytes. + pub const ciphertext_length = Poly.compressedSize(p.du) * p.k + Poly.compressedSize(p.dv); + + const Self = @This(); + const V = Vec(p.k); + const M = Mat(p.k); + + /// Length (in bytes) of a shared secret. + pub const shared_length = common_shared_key_size; + /// Length (in bytes) of a seed for deterministic encapsulation. + pub const encaps_seed_length = common_encaps_seed_length; + /// Length (in bytes) of a seed for key generation. + pub const seed_length: usize = inner_seed_length + shared_length; + /// Algorithm name. + pub const name = p.name; + + /// A shared secret, and an encapsulated (encrypted) representation of it. + pub const EncapsulatedSecret = struct { + shared_secret: [shared_length]u8, + ciphertext: [ciphertext_length]u8, + }; + + /// A Kyber public key. + pub const PublicKey = struct { + pk: InnerPk, + + // Cached + hpk: [h_length]u8, // H(pk) + + /// Size of a serialized representation of the key, in bytes. + pub const bytes_length = InnerPk.bytes_length; + + /// Generates a shared secret, and encapsulates it for the public key. + /// If `seed` is `null`, a random seed is used. This is recommended. + /// If `seed` is set, encapsulation is deterministic. + pub fn encaps(pk: PublicKey, seed_: ?[encaps_seed_length]u8) EncapsulatedSecret { + const seed = seed_ orelse seed: { + var random_seed: [encaps_seed_length]u8 = undefined; + crypto.random.bytes(&random_seed); + break :seed random_seed; + }; + + var m: [inner_plaintext_length]u8 = undefined; + + // m = H(seed) + var h = sha3.Sha3_256.init(.{}); + h.update(&seed); + h.final(&m); + + // (K', r) = G(m ‖ H(pk)) + var kr: [inner_plaintext_length + h_length]u8 = undefined; + var g = sha3.Sha3_512.init(.{}); + g.update(&m); + g.update(&pk.hpk); + g.final(&kr); + + // c = innerEncrypy(pk, m, r) + const ct = pk.pk.encrypt(&m, kr[32..64]); + + // Compute H(c) and put in second slot of kr, which will be (K', H(c)). + h = sha3.Sha3_256.init(.{}); + h.update(&ct); + h.final(kr[32..64]); + + // K = KDF(K' ‖ H(c)) + var kdf = sha3.Shake256.init(.{}); + kdf.update(&kr); + var ss: [shared_length]u8 = undefined; + kdf.squeeze(&ss); + + return EncapsulatedSecret{ + .shared_secret = ss, + .ciphertext = ct, + }; + } + + /// Serializes the key into a byte array. + pub fn toBytes(pk: PublicKey) [bytes_length]u8 { + return pk.pk.toBytes(); + } + + /// Deserializes the key from a byte array. + pub fn fromBytes(buf: *const [bytes_length]u8) !PublicKey { + var ret: PublicKey = undefined; + ret.pk = InnerPk.fromBytes(buf[0..InnerPk.bytes_length]); + + var h = sha3.Sha3_256.init(.{}); + h.update(buf); + h.final(&ret.hpk); + return ret; + } + }; + + /// A Kyber secret key. + pub const SecretKey = struct { + sk: InnerSk, + pk: InnerPk, + hpk: [h_length]u8, // H(pk) + z: [shared_length]u8, + + /// Size of a serialized representation of the key, in bytes. + pub const bytes_length: usize = + InnerSk.bytes_length + InnerPk.bytes_length + h_length + shared_length; + + /// Decapsulates the shared secret within ct using the private key. + pub fn decaps(sk: SecretKey, ct: *const [ciphertext_length]u8) ![shared_length]u8 { + // m' = innerDec(ct) + const m2 = sk.sk.decrypt(ct); + + // (K'', r') = G(m' ‖ H(pk)) + var kr2: [64]u8 = undefined; + var g = sha3.Sha3_512.init(.{}); + g.update(&m2); + g.update(&sk.hpk); + g.final(&kr2); + + // ct' = innerEnc(pk, m', r') + const ct2 = sk.pk.encrypt(&m2, kr2[32..64]); + + // Compute H(ct) and put in the second slot of kr2 which will be (K'', H(ct)). + var h = sha3.Sha3_256.init(.{}); + h.update(ct); + h.final(kr2[32..64]); + + // Replace K'' by z in the first slot of kr2 if ct ≠ ct'. + cmov(32, kr2[0..32], sk.z, ctneq(ciphertext_length, ct.*, ct2)); + + // K = KDF(K''/z, H(c)) + var kdf = sha3.Shake256.init(.{}); + var ss: [shared_length]u8 = undefined; + kdf.update(&kr2); + kdf.squeeze(&ss); + return ss; + } + + /// Serializes the key into a byte array. + pub fn toBytes(sk: SecretKey) [bytes_length]u8 { + return sk.sk.toBytes() ++ sk.pk.toBytes() ++ sk.hpk ++ sk.z; + } + + /// Deserializes the key from a byte array. + pub fn fromBytes(buf: *const [bytes_length]u8) !SecretKey { + var ret: SecretKey = undefined; + comptime var s: usize = 0; + ret.sk = InnerSk.fromBytes(buf[s .. s + InnerSk.bytes_length]); + s += InnerSk.bytes_length; + ret.pk = InnerPk.fromBytes(buf[s .. s + InnerPk.bytes_length]); + s += InnerPk.bytes_length; + mem.copy(u8, &ret.hpk, buf[s .. s + h_length]); + s += h_length; + mem.copy(u8, &ret.z, buf[s .. s + shared_length]); + return ret; + } + }; + + /// A Kyber key pair. + pub const KeyPair = struct { + secret_key: SecretKey, + public_key: PublicKey, + + /// Create a new key pair. + /// If seed is null, a random seed will be generated. + /// If a seed is provided, the key pair will be determinsitic. + pub fn create(seed_: ?[seed_length]u8) !KeyPair { + const seed = seed_ orelse sk: { + var random_seed: [seed_length]u8 = undefined; + crypto.random.bytes(&random_seed); + break :sk random_seed; + }; + var ret: KeyPair = undefined; + mem.copy(u8, &ret.secret_key.z, seed[inner_seed_length..seed_length]); + + // Generate inner key + innerKeyFromSeed( + seed[0..inner_seed_length].*, + &ret.public_key.pk, + &ret.secret_key.sk, + ); + ret.secret_key.pk = ret.public_key.pk; + + // Copy over z from seed. + mem.copy(u8, &ret.secret_key.z, seed[inner_seed_length..seed_length]); + + // Compute H(pk) + var h = sha3.Sha3_256.init(.{}); + h.update(&ret.public_key.pk.toBytes()); + h.final(&ret.secret_key.hpk); + ret.public_key.hpk = ret.secret_key.hpk; + + return ret; + } + }; + + // Size of plaintexts of the in + const inner_plaintext_length: usize = Poly.compressedSize(1); + + const InnerPk = struct { + rho: [32]u8, // ρ, the seed for the matrix A + th: V, // NTT(t), normalized + + // Cached values + aT: M, + + const bytes_length = V.bytes_length + 32; + + fn encrypt( + pk: InnerPk, + pt: *const [inner_plaintext_length]u8, + seed: *const [32]u8, + ) [ciphertext_length]u8 { + // Sample r, e₁ and e₂ appropriately + const rh = V.noise(p.eta1, 0, seed).ntt().barrettReduce(); + const e1 = V.noise(eta2, p.k, seed); + const e2 = Poly.noise(eta2, 2 * p.k, seed); + + // Next we compute u = Aᵀ r + e₁. First Aᵀ. + var u: V = undefined; + for (0..p.k) |i| { + // Note that coefficients of r are bounded by q and those of Aᵀ + // are bounded by 4.5q and so their product is bounded by 2¹⁵q + // as required for multiplication. + u.ps[i] = pk.aT.vs[i].dotHat(rh); + } + + // Aᵀ and r were not in Montgomery form, so the Montgomery + // multiplications in the inner product added a factor R⁻¹ which + // the InvNTT cancels out. + u = u.barrettReduce().invNTT().add(e1).normalize(); + + // Next, compute v = + e₂ + Decompress_q(m, 1) + const v = pk.th.dotHat(rh).barrettReduce().invNTT() + .add(Poly.decompress(1, pt)).add(e2).normalize(); + + return u.compress(p.du) ++ v.compress(p.dv); + } + + fn toBytes(pk: InnerPk) [bytes_length]u8 { + return pk.th.toBytes() ++ pk.rho; + } + + fn fromBytes(buf: *const [bytes_length]u8) InnerPk { + var ret: InnerPk = undefined; + ret.th = V.fromBytes(buf[0..V.bytes_length]).normalize(); + mem.copy(u8, &ret.rho, buf[V.bytes_length..bytes_length]); + ret.aT = M.uniform(ret.rho, true); + return ret; + } + }; + + // Private key of the inner PKE + const InnerSk = struct { + sh: V, // NTT(s), normalized + const bytes_length = V.bytes_length; + + fn decrypt(sk: InnerSk, ct: *const [ciphertext_length]u8) [inner_plaintext_length]u8 { + const u = V.decompress(p.du, ct[0..comptime V.compressedSize(p.du)]); + const v = Poly.decompress( + p.dv, + ct[comptime V.compressedSize(p.du)..ciphertext_length], + ); + + // Compute m = v - + return v.sub(sk.sh.dotHat(u.ntt()).barrettReduce().invNTT()) + .normalize().compress(1); + } + + fn toBytes(sk: InnerSk) [bytes_length]u8 { + return sk.sh.toBytes(); + } + + fn fromBytes(buf: *const [bytes_length]u8) InnerSk { + var ret: InnerSk = undefined; + ret.sh = V.fromBytes(buf).normalize(); + return ret; + } + }; + + // Derives inner PKE keypair from given seed. + fn innerKeyFromSeed(seed: [inner_seed_length]u8, pk: *InnerPk, sk: *InnerSk) void { + var expanded_seed: [64]u8 = undefined; + + var h = sha3.Sha3_512.init(.{}); + h.update(&seed); + h.final(&expanded_seed); + mem.copy(u8, &pk.rho, expanded_seed[0..32]); + const sigma = expanded_seed[32..64]; + pk.aT = M.uniform(pk.rho, false); // Expand ρ to A; we'll transpose later on + + // Sample secret vector s. + sk.sh = V.noise(p.eta1, 0, sigma).ntt().normalize(); + + const eh = Vec(p.k).noise(p.eta1, p.k, sigma).ntt(); // sample blind e. + var th: V = undefined; + + // Next, we compute t = A s + e. + for (0..p.k) |i| { + // Note that coefficients of s are bounded by q and those of A + // are bounded by 4.5q and so their product is bounded by 2¹⁵q + // as required for multiplication. + // A and s were not in Montgomery form, so the Montgomery + // multiplications in the inner product added a factor R⁻¹ which + // we'll cancel out with toMont(). This will also ensure the + // coefficients of th are bounded in absolute value by q. + th.ps[i] = pk.aT.vs[i].dotHat(sk.sh).toMont(); + } + + pk.th = th.add(eh).normalize(); // bounded by 8q + pk.aT = pk.aT.transpose(); + } + }; +} + +// R mod q +const r_mod_q: i32 = @rem(@as(i32, R), Q); + +// R² mod q +const r2_mod_q: i32 = @rem(r_mod_q * r_mod_q, Q); + +// ζ is the degree 256 primitive root of unity used for the NTT. +const zeta: i16 = 17; + +// (128)⁻¹ R². Used in inverse NTT. +const r2_over_128: i32 = @mod(invertMod(128, Q) * r2_mod_q, Q); + +// zetas lists precomputed powers of the primitive root of unity in +// Montgomery representation used for the NTT: +// +// zetas[i] = ζᵇʳᵛ⁽ⁱ⁾ R mod q +// +// where ζ = 17, brv(i) is the bitreversal of a 7-bit number and R=2¹⁶ mod q. +const zetas = computeZetas(); + +// invNTTReductions keeps track of which coefficients to apply Barrett +// reduction to in Poly.invNTT(). +// +// Generated lazily: once a butterfly is computed which is about to +// overflow the i16, the largest coefficient is reduced. If that is +// not enough, the other coefficient is reduced as well. +// +// This is actually optimal, as proven in https://eprint.iacr.org/2020/1377.pdf +// TODO generate comptime? +const inv_ntt_reductions = [_]i16{ + -1, // after layer 1 + -1, // after layer 2 + 16, + 17, + 48, + 49, + 80, + 81, + 112, + 113, + 144, + 145, + 176, + 177, + 208, + 209, + 240, 241, -1, // after layer 3 + 0, 1, 32, + 33, 34, 35, + 64, 65, 96, + 97, 98, 99, + 128, 129, + 160, 161, 162, 163, 192, 193, 224, 225, 226, 227, -1, // after layer 4 + 2, 3, 66, 67, 68, 69, 70, 71, 130, 131, 194, + 195, 196, 197, + 198, 199, -1, // after layer 5 + 4, 5, 6, + 7, 132, 133, + 134, 135, 136, + 137, 138, 139, + 140, 141, + 142, 143, -1, // after layer 6 + -1, // after layer 7 +}; + +test "invNTTReductions bounds" { + // Checks whether the reductions proposed by invNTTReductions + // don't overflow during invNTT(). + var xs = [_]i32{1} ** 256; // start at |x| ≤ q + + var r: usize = 0; + var layer: math.Log2Int(usize) = 1; + while (layer < 8) : (layer += 1) { + const w = @as(usize, 1) << layer; + var i: usize = 0; + + while (i + w < 256) { + xs[i] = xs[i] + xs[i + w]; + try testing.expect(xs[i] <= 9); // we can't exceed 9q + xs[i + w] = 1; + i += 1; + if (@mod(i, w) == 0) { + i += w; + } + } + + while (true) { + const j = inv_ntt_reductions[r]; + r += 1; + if (j < 0) { + break; + } + xs[@intCast(usize, j)] = 1; + } + } +} + +// Extended euclidean algorithm. +// +// For a, b finds x, y such that x a + y b = gcd(a, b). Used to compute +// modular inverse. +fn eea(a: anytype, b: @TypeOf(a)) EeaResult(@TypeOf(a)) { + if (a == 0) { + return .{ .gcd = b, .x = 0, .y = 1 }; + } + const r = eea(@rem(b, a), a); + return .{ .gcd = r.gcd, .x = r.y - @divTrunc(b, a) * r.x, .y = r.x }; +} + +fn EeaResult(comptime T: type) type { + return struct { gcd: T, x: T, y: T }; +} + +// Returns least common multiple of a and b. +fn lcm(a: anytype, b: @TypeOf(a)) @TypeOf(a) { + const r = eea(a, b); + return a * b / r.gcd; +} + +// Invert modulo p. +fn invertMod(a: anytype, p: @TypeOf(a)) @TypeOf(a) { + const r = eea(a, p); + assert(r.gcd == 1); + return r.x; +} + +// Reduce mod q for testing. +fn modQ32(x: i32) i16 { + var y = @intCast(i16, @rem(x, @as(i32, Q))); + if (y < 0) { + y += Q; + } + return y; +} + +// Given -2¹⁵ q ≤ x < 2¹⁵ q, returns -q < y < q with x 2⁻¹⁶ = y (mod q). +fn montReduce(x: i32) i16 { + const qInv = comptime invertMod(@as(i32, Q), R); + // This is Montgomery reduction with R=2¹⁶. + // + // Note gcd(2¹⁶, q) = 1 as q is prime. Write q' := 62209 = q⁻¹ mod R. + // First we compute + // + // m := ((x mod R) q') mod R + // = x q' mod R + // = int16(x q') + // = int16(int32(x) * int32(q')) + // + // Note that x q' might be as big as 2³² and could overflow the int32 + // multiplication in the last line. However for any int32s a and b, + // we have int32(int64(a)*int64(b)) = int32(a*b) and so the result is ok. + const m = @truncate(i16, @truncate(i32, x *% qInv)); + + // Note that x - m q is divisable by R; indeed modulo R we have + // + // x - m q ≡ x - x q' q ≡ x - x q⁻¹ q ≡ x - x = 0. + // + // We return y := (x - m q) / R. Note that y is indeed correct as + // modulo q we have + // + // y ≡ x R⁻¹ - m q R⁻¹ = x R⁻¹ + // + // and as both 2¹⁵ q ≤ m q, x < 2¹⁵ q, we have + // 2¹⁶ q ≤ x - m q < 2¹⁶ and so q ≤ (x - m q) / R < q as desired. + const yR = x - @as(i32, m) * @as(i32, Q); + return @bitCast(i16, @truncate(u16, @bitCast(u32, yR) >> 16)); +} + +test "Test montReduce" { + var rnd = RndGen.init(0); + for (0..1000) |_| { + const bound = comptime @as(i32, Q) * (1 << 15); + const x = rnd.random().intRangeLessThan(i32, -bound, bound); + const y = montReduce(x); + try testing.expect(-Q < y and y < Q); + try testing.expectEqual(modQ32(x), modQ32(@as(i32, y) * R)); + } +} + +// Given any x, return x R mod q where R=2¹⁶. +fn feToMont(x: i16) i16 { + // Note |1353 x| ≤ 1353 2¹⁵ ≤ 13318 q ≤ 2¹⁵ q and so we're within + // the bounds of montReduce. + return montReduce(@as(i32, x) * r2_mod_q); +} + +test "Test feToMont" { + var x: i32 = -(1 << 15); + while (x < 1 << 15) : (x += 1) { + const y = feToMont(@intCast(i16, x)); + try testing.expectEqual(modQ32(@as(i32, y)), modQ32(x * r_mod_q)); + } +} + +// Given any x, compute 0 ≤ y ≤ q with x = y (mod q). +// +// Beware: we might have feBarrettReduce(x) = q ≠ 0 for some x. In fact, +// this happens if and only if x = -nq for some positive integer n. +fn feBarrettReduce(x: i16) i16 { + // This is standard Barrett reduction. + // + // For any x we have x mod q = x - ⌊x/q⌋ q. We will use 20159/2²⁶ as + // an approximation of 1/q. Note that 0 ≤ 20159/2²⁶ - 1/q ≤ 0.135/2²⁶ + // and so | x 20156/2²⁶ - x/q | ≤ 2⁻¹⁰ for |x| ≤ 2¹⁶. For all x + // not a multiple of q, the number x/q is further than 1/q from any integer + // and so ⌊x 20156/2²⁶⌋ = ⌊x/q⌋. If x is a multiple of q and x is positive, + // then x 20156/2²⁶ is larger than x/q so ⌊x 20156/2²⁶⌋ = ⌊x/q⌋ as well. + // Finally, if x is negative multiple of q, then ⌊x 20156/2²⁶⌋ = ⌊x/q⌋-1. + // Thus + // [ q if x=-nq for pos. integer n + // x - ⌊x 20156/2²⁶⌋ q = [ + // [ x mod q otherwise + // + // To actually compute this, note that + // + // ⌊x 20156/2²⁶⌋ = (20159 x) >> 26. + return x -% @intCast(i16, (@as(i32, x) * 20159) >> 26) *% Q; +} + +test "Test Barrett reduction" { + var x: i32 = -(1 << 15); + while (x < 1 << 15) : (x += 1) { + var y1 = feBarrettReduce(@intCast(i16, x)); + const y2 = @mod(@intCast(i16, x), Q); + if (x < 0 and @rem(-x, Q) == 0) { + y1 -= Q; + } + try testing.expectEqual(y1, y2); + } +} + +// Returns x if x < q and x - q otherwise. Assumes x ≥ -29439. +fn csubq(x: i16) i16 { + var r = x; + r -= Q; + r += (r >> 15) & Q; + return r; +} + +test "Test csubq" { + var x: i32 = -29439; + while (x < 1 << 15) : (x += 1) { + const y1 = csubq(@intCast(i16, x)); + var y2 = @intCast(i16, x); + if (@intCast(i16, x) >= Q) { + y2 -= Q; + } + try testing.expectEqual(y1, y2); + } +} + +// Compute a^s mod p. +fn mpow(a: anytype, s: @TypeOf(a), p: @TypeOf(a)) @TypeOf(a) { + var ret: @TypeOf(a) = 1; + var s2 = s; + var a2 = a; + + while (true) { + if (s2 & 1 == 1) { + ret = @mod(ret * a2, p); + } + s2 >>= 1; + if (s2 == 0) { + break; + } + a2 = @mod(a2 * a2, p); + } + return ret; +} + +// Computes zetas table used by ntt and invNTT. +fn computeZetas() [128]i16 { + @setEvalBranchQuota(10000); + var ret: [128]i16 = undefined; + for (&ret, 0..) |*r, i| { + const t = @intCast(i16, mpow(@as(i32, zeta), @bitReverse(@intCast(u7, i)), Q)); + r.* = csubq(feBarrettReduce(feToMont(t))); + } + return ret; +} + +// An element of our base ring R which are polynomials over ℤ_q +// modulo the equation Xᴺ = -1, where q=3329 and N=256. +// +// This type is also used to store NTT-transformed polynomials, +// see Poly.NTT(). +// +// Coefficients aren't always reduced. See Normalize(). +const Poly = struct { + cs: [N]i16, + + const bytes_length = N / 2 * 3; + const zero: Poly = .{ .cs = .{0} ** N }; + + fn add(a: Poly, b: Poly) Poly { + var ret: Poly = undefined; + for (0..N) |i| { + ret.cs[i] = a.cs[i] + b.cs[i]; + } + return ret; + } + + fn sub(a: Poly, b: Poly) Poly { + var ret: Poly = undefined; + for (0..N) |i| { + ret.cs[i] = a.cs[i] - b.cs[i]; + } + return ret; + } + + // For testing, generates a random polynomial with for each + // coefficient |x| ≤ q. + fn randAbsLeqQ(rnd: anytype) Poly { + var ret: Poly = undefined; + for (0..N) |i| { + ret.cs[i] = rnd.random().intRangeAtMost(i16, -Q, Q); + } + return ret; + } + + // For testing, generates a random normalized polynomial. + fn randNormalized(rnd: anytype) Poly { + var ret: Poly = undefined; + for (0..N) |i| { + ret.cs[i] = rnd.random().intRangeLessThan(i16, 0, Q); + } + return ret; + } + + // Executes a forward "NTT" on p. + // + // Assumes the coefficients are in absolute value ≤q. The resulting + // coefficients are in absolute value ≤7q. If the input is in Montgomery + // form, then the result is in Montgomery form and so (by linearity of the NTT) + // if the input is in regular form, then the result is also in regular form. + fn ntt(a: Poly) Poly { + // Note that ℤ_q does not have a primitive 512ᵗʰ root of unity (as 512 + // does not divide into q-1) and so we cannot do a regular NTT. ℤ_q + // does have a primitive 256ᵗʰ root of unity, the smallest of which + // is ζ := 17. + // + // Recall that our base ring R := ℤ_q[x] / (x²⁵⁶ + 1). The polynomial + // x²⁵⁶+1 will not split completely (as its roots would be 512ᵗʰ roots + // of unity.) However, it does split almost (using ζ¹²⁸ = -1): + // + // x²⁵⁶ + 1 = (x²)¹²⁸ - ζ¹²⁸ + // = ((x²)⁶⁴ - ζ⁶⁴)((x²)⁶⁴ + ζ⁶⁴) + // = ((x²)³² - ζ³²)((x²)³² + ζ³²)((x²)³² - ζ⁹⁶)((x²)³² + ζ⁹⁶) + // ⋮ + // = (x² - ζ)(x² + ζ)(x² - ζ⁶⁵)(x² + ζ⁶⁵) … (x² + ζ¹²⁷) + // + // Note that the powers of ζ that appear (from the second line down) are + // in binary + // + // 0100000 1100000 + // 0010000 1010000 0110000 1110000 + // 0001000 1001000 0101000 1101000 0011000 1011000 0111000 1111000 + // … + // + // That is: brv(2), brv(3), brv(4), …, where brv(x) denotes the 7-bit + // bitreversal of x. These powers of ζ are given by the Zetas array. + // + // The polynomials x² ± ζⁱ are irreducible and coprime, hence by + // the Chinese Remainder Theorem we know + // + // ℤ_q[x]/(x²⁵⁶+1) → ℤ_q[x]/(x²-ζ) x … x ℤ_q[x]/(x²+ζ¹²⁷) + // + // given by a ↦ ( a mod x²-ζ, …, a mod x²+ζ¹²⁷ ) + // is an isomorphism, which is the "NTT". It can be efficiently computed by + // + // + // a ↦ ( a mod (x²)⁶⁴ - ζ⁶⁴, a mod (x²)⁶⁴ + ζ⁶⁴ ) + // ↦ ( a mod (x²)³² - ζ³², a mod (x²)³² + ζ³², + // a mod (x²)⁹⁶ - ζ⁹⁶, a mod (x²)⁹⁶ + ζ⁹⁶ ) + // + // et cetera + // If N was 8 then this can be pictured in the following diagram: + // + // https://cnx.org/resources/17ee4dfe517a6adda05377b25a00bf6e6c93c334/File0026.png + // + // Each cross is a Cooley-Tukey butterfly: it's the map + // + // (a, b) ↦ (a + ζb, a - ζb) + // + // for the appropriate power ζ for that column and row group. + var p = a; + var k: usize = 0; // index into zetas + + var l = N >> 1; + while (l > 1) : (l >>= 1) { + // On the nᵗʰ iteration of the l-loop, the absolute value of the + // coefficients are bounded by nq. + + // offset effectively loops over the row groups in this column; it is + // the first row in the row group. + var offset: usize = 0; + while (offset < N - l) : (offset += 2 * l) { + k += 1; + const z = @as(i32, zetas[k]); + + // j loops over each butterfly in the row group. + for (offset..offset + l) |j| { + const t = montReduce(z * @as(i32, p.cs[j + l])); + p.cs[j + l] = p.cs[j] - t; + p.cs[j] += t; + } + } + } + + return p; + } + + // Executes an inverse "NTT" on p and multiply by the Montgomery factor R. + // + // Assumes the coefficients are in absolute value ≤q. The resulting + // coefficients are in absolute value ≤q. If the input is in Montgomery + // form, then the result is in Montgomery form and so (by linearity) + // if the input is in regular form, then the result is also in regular form. + fn invNTT(a: Poly) Poly { + var k: usize = 127; // index into zetas + var r: usize = 0; // index into invNTTReductions + var p = a; + + // We basically do the oppposite of NTT, but postpone dividing by 2 in the + // inverse of the Cooley-Tukey butterfly and accumulate that into a big + // division by 2⁷ at the end. See the comments in the ntt() function. + + var l: usize = 2; + while (l < N) : (l <<= 1) { + var offset: usize = 0; + while (offset < N - l) : (offset += 2 * l) { + // As we're inverting, we need powers of ζ⁻¹ (instead of ζ). + // To be precise, we need ζᵇʳᵛ⁽ᵏ⁾⁻¹²⁸. However, as ζ⁻¹²⁸ = -1, + // we can use the existing zetas table instead of + // keeping a separate invZetas table as in Dilithium. + + const minZeta = @as(i32, zetas[k]); + k -= 1; + + for (offset..offset + l) |j| { + // Gentleman-Sande butterfly: (a, b) ↦ (a + b, ζ(a-b)) + const t = p.cs[j + l] - p.cs[j]; + p.cs[j] += p.cs[j + l]; + p.cs[j + l] = montReduce(minZeta * @as(i32, t)); + + // Note that if we had |a| < αq and |b| < βq before the + // butterfly, then now we have |a| < (α+β)q and |b| < q. + } + } + + // We let the invNTTReductions instruct us which coefficients to + // Barrett reduce. + while (true) { + const i = inv_ntt_reductions[r]; + r += 1; + if (i < 0) { + break; + } + p.cs[@intCast(usize, i)] = feBarrettReduce(p.cs[@intCast(usize, i)]); + } + } + + for (0..N) |j| { + // Note 1441 = (128)⁻¹ R². The coefficients are bounded by 9q, so + // as 1441 * 9 ≈ 2¹⁴ < 2¹⁵, we're within the required bounds + // for montReduce(). + p.cs[j] = montReduce(r2_over_128 * @as(i32, p.cs[j])); + } + + return p; + } + + // Normalizes coefficients. + // + // Ensures each coefficient is in {0, …, q-1}. + fn normalize(a: Poly) Poly { + var ret: Poly = undefined; + for (0..N) |i| { + ret.cs[i] = csubq(feBarrettReduce(a.cs[i])); + } + return ret; + } + + // Put p in Montgomery form. + fn toMont(a: Poly) Poly { + var ret: Poly = undefined; + for (0..N) |i| { + ret.cs[i] = feToMont(a.cs[i]); + } + return ret; + } + + // Barret reduce coefficients. + // + // Beware, this does not fully normalize coefficients. + fn barrettReduce(a: Poly) Poly { + var ret: Poly = undefined; + for (0..N) |i| { + ret.cs[i] = feBarrettReduce(a.cs[i]); + } + return ret; + } + + fn compressedSize(comptime d: u8) usize { + return @divTrunc(N * d, 8); + } + + // Returns packed Compress_q(p, d). + // + // Assumes p is normalized. + fn compress(p: Poly, comptime d: u8) [compressedSize(d)]u8 { + @setEvalBranchQuota(10000); + const q_over_2: u32 = comptime @divTrunc(Q, 2); // (q-1)/2 + const two_d_min_1: u32 = comptime (1 << d) - 1; // 2ᵈ-1 + var in_off: usize = 0; + var out_off: usize = 0; + + const batch_size: usize = comptime lcm(@as(i16, d), 8); + const in_batch_size: usize = comptime batch_size / d; + const out_batch_size: usize = comptime batch_size / 8; + + const out_length: usize = comptime @divTrunc(N * d, 8); + comptime assert(out_length * 8 == d * N); + var out = [_]u8{0} ** out_length; + + while (in_off < N) { + // First we compress into in. + var in: [in_batch_size]u16 = undefined; + inline for (0..in_batch_size) |i| { + // Compress_q(x, d) = ⌈(2ᵈ/q)x⌋ mod⁺ 2ᵈ + // = ⌊(2ᵈ/q)x+½⌋ mod⁺ 2ᵈ + // = ⌊((x << d) + q/2) / q⌋ mod⁺ 2ᵈ + // = DIV((x << d) + q/2, q) & ((1< 0) { + const out_shift = comptime 8 - todo; + out[out_off + j] |= @truncate(u8, (in[i] >> in_shift) << out_shift); + + const done = comptime @min(@min(d, todo), d - in_shift); + todo -= done; + in_shift += done; + + if (in_shift == d) { + in_shift = 0; + i += 1; + } + } + } + + in_off += in_batch_size; + out_off += out_batch_size; + } + + return out; + } + + // Set p to Decompress_q(m, d). + fn decompress(comptime d: u8, in: *const [compressedSize(d)]u8) Poly { + @setEvalBranchQuota(10000); + const inLen = comptime @divTrunc(N * d, 8); + comptime assert(inLen * 8 == d * N); + var ret: Poly = undefined; + var in_off: usize = 0; + var out_off: usize = 0; + + const batch_size: usize = comptime lcm(@as(i16, d), 8); + const in_batch_size: usize = comptime batch_size / 8; + const out_batch_size: usize = comptime batch_size / d; + + while (out_off < N) { + comptime var in_shift: usize = 0; + comptime var j: usize = 0; + comptime var i: usize = 0; + inline while (i < out_batch_size) : (i += 1) { + // First, unpack next coefficient. + comptime var todo = d; + var out: u16 = 0; + + inline while (todo > 0) { + const out_shift = comptime d - todo; + const m = comptime (1 << d) - 1; + out |= (@as(u16, in[in_off + j] >> in_shift) << out_shift) & m; + + const done = comptime @min(@min(8, todo), 8 - in_shift); + todo -= done; + in_shift += done; + + if (in_shift == 8) { + in_shift = 0; + j += 1; + } + } + + // Decompress_q(x, d) = ⌈(q/2ᵈ)x⌋ + // = ⌊(q/2ᵈ)x+½⌋ + // = ⌊(qx + 2ᵈ⁻¹)/2ᵈ⌋ + // = (qx + (1<<(d-1))) >> d + const qx = @as(u32, out) * @as(u32, Q); + ret.cs[out_off + i] = @intCast(i16, (qx + (1 << (d - 1))) >> d); + } + + in_off += in_batch_size; + out_off += out_batch_size; + } + + return ret; + } + + // Returns the "pointwise" multiplication a o b. + // + // That is: invNTT(a o b) = invNTT(a) * invNTT(b). Assumes a and b are in + // Montgomery form. Products between coefficients of a and b must be strictly + // bounded in absolute value by 2¹⁵q. a o b will be in Montgomery form and + // bounded in absolute value by 2q. + fn mulHat(a: Poly, b: Poly) Poly { + // Recall from the discussion in ntt(), that a transformed polynomial is + // an element of ℤ_q[x]/(x²-ζ) x … x ℤ_q[x]/(x²+ζ¹²⁷); + // that is: 128 degree-one polynomials instead of simply 256 elements + // from ℤ_q as in the regular NTT. So instead of pointwise multiplication, + // we multiply the 128 pairs of degree-one polynomials modulo the + // right equation: + // + // (a₁ + a₂x)(b₁ + b₂x) = a₁b₁ + a₂b₂ζ' + (a₁b₂ + a₂b₁)x, + // + // where ζ' is the appropriate power of ζ. + + var p: Poly = undefined; + var k: usize = 64; + var i: usize = 0; + while (i < N) : (i += 4) { + const z = @as(i32, zetas[k]); + k += 1; + + const a1b1 = montReduce(@as(i32, a.cs[i + 1]) * @as(i32, b.cs[i + 1])); + const a0b0 = montReduce(@as(i32, a.cs[i]) * @as(i32, b.cs[i])); + const a1b0 = montReduce(@as(i32, a.cs[i + 1]) * @as(i32, b.cs[i])); + const a0b1 = montReduce(@as(i32, a.cs[i]) * @as(i32, b.cs[i + 1])); + + p.cs[i] = montReduce(a1b1 * z) + a0b0; + p.cs[i + 1] = a0b1 + a1b0; + + const a3b3 = montReduce(@as(i32, a.cs[i + 3]) * @as(i32, b.cs[i + 3])); + const a2b2 = montReduce(@as(i32, a.cs[i + 2]) * @as(i32, b.cs[i + 2])); + const a3b2 = montReduce(@as(i32, a.cs[i + 3]) * @as(i32, b.cs[i + 2])); + const a2b3 = montReduce(@as(i32, a.cs[i + 2]) * @as(i32, b.cs[i + 3])); + + p.cs[i + 2] = a2b2 - montReduce(a3b3 * z); + p.cs[i + 3] = a2b3 + a3b2; + } + + return p; + } + + // Sample p from a centered binomial distribution with n=2η and p=½ - viz: + // coefficients are in {-η, …, η} with probabilities + // + // {ncr(0, 2η)/2^2η, ncr(1, 2η)/2^2η, …, ncr(2η,2η)/2^2η} + fn noise(comptime eta: u8, nonce: u8, seed: *const [32]u8) Poly { + var h = sha3.Shake256.init(.{}); + const suffix: [1]u8 = .{nonce}; + h.update(seed); + h.update(&suffix); + + // The distribution at hand is exactly the same as that + // of (a₁ + a₂ + … + a_η) - (b₁ + … + b_η) where a_i,b_i~U(1). + // Thus we need 2η bits per coefficient. + const buf_len = comptime 2 * eta * N / 8; + var buf: [buf_len]u8 = undefined; + h.squeeze(&buf); + + // buf is interpreted as a₁…a_ηb₁…b_ηa₁…a_ηb₁…b_η…. We process + // multiple coefficients in one batch. + + const T = switch (builtin.target.cpu.arch) { + .x86_64, .x86 => u32, // Generates better code on Intel CPUs + else => u64, // u128 might be faster on some other CPUs. + }; + + comptime var batch_count: usize = undefined; + comptime var batch_bytes: usize = undefined; + comptime var mask: T = 0; + comptime { + batch_count = @bitSizeOf(T) / @as(usize, 2 * eta); + while (@rem(N, batch_count) != 0 and batch_count > 0) : (batch_count -= 1) {} + assert(batch_count > 0); + assert(@rem(2 * eta * batch_count, 8) == 0); + batch_bytes = 2 * eta * batch_count / 8; + + for (0..2 * eta * batch_count) |_| { + mask <<= eta; + mask |= 1; + } + } + + var ret: Poly = undefined; + for (0..comptime N / batch_count) |i| { + // Read coefficients into t. In the case of η=3, + // we have t = a₁ + 2a₂ + 4a₃ + 8b₁ + 16b₂ + … + var t: T = 0; + inline for (0..batch_bytes) |j| { + t |= @as(T, buf[batch_bytes * i + j]) << (8 * j); + } + + // Accumelate `a's and `b's together by masking them out, shifting + // and adding. For η=3, we have d = a₁ + a₂ + a₃ + 8(b₁ + b₂ + b₃) + … + var d: T = 0; + inline for (0..eta) |j| { + d += (t >> j) & mask; + } + + // Extract each a and b separately and set coefficient in polynomial. + inline for (0..batch_count) |j| { + const mask2 = comptime (1 << eta) - 1; + const a = @intCast(i16, (d >> (comptime (2 * j * eta))) & mask2); + const b = @intCast(i16, (d >> (comptime ((2 * j + 1) * eta))) & mask2); + ret.cs[batch_count * i + j] = a - b; + } + } + + return ret; + } + + // Sample p uniformly from the given seed and x and y coordinates. + fn uniform(seed: [32]u8, x: u8, y: u8) Poly { + var h = sha3.Shake128.init(.{}); + const suffix: [2]u8 = .{ x, y }; + h.update(&seed); + h.update(&suffix); + + const buf_len = sha3.Shake128.block_length; // rate SHAKE-128 + var buf: [buf_len]u8 = undefined; + + var ret: Poly = undefined; + var i: usize = 0; // index into ret.cs + outer: while (true) { + h.squeeze(&buf); + + var j: usize = 0; // index into buf + while (j < buf_len) : (j += 3) { + const b0 = @as(u16, buf[j]); + const b1 = @as(u16, buf[j + 1]); + const b2 = @as(u16, buf[j + 2]); + + const ts: [2]u16 = .{ + b0 | ((b1 & 0xf) << 8), + (b1 >> 4) | (b2 << 4), + }; + + inline for (ts) |t| { + if (t < Q) { + ret.cs[i] = @intCast(i16, t); + i += 1; + + if (i == N) { + break :outer; + } + } + } + } + } + + return ret; + } + + // Packs p. + // + // Assumes p is normalized (and not just Barrett reduced). + fn toBytes(p: Poly) [bytes_length]u8 { + var ret: [bytes_length]u8 = undefined; + for (0..comptime N / 2) |i| { + const t0 = @intCast(u16, p.cs[2 * i]); + const t1 = @intCast(u16, p.cs[2 * i + 1]); + ret[3 * i] = @truncate(u8, t0); + ret[3 * i + 1] = @truncate(u8, (t0 >> 8) | (t1 << 4)); + ret[3 * i + 2] = @truncate(u8, t1 >> 4); + } + return ret; + } + + // Unpacks a Poly from buf. + // + // p will not be normalized; instead 0 ≤ p[i] < 4096. + fn fromBytes(buf: *const [bytes_length]u8) Poly { + var ret: Poly = undefined; + for (0..comptime N / 2) |i| { + const b0 = @as(i16, buf[3 * i]); + const b1 = @as(i16, buf[3 * i + 1]); + const b2 = @as(i16, buf[3 * i + 2]); + ret.cs[2 * i] = b0 | ((b1 & 0xf) << 8); + ret.cs[2 * i + 1] = (b1 >> 4) | b2 << 4; + } + return ret; + } +}; + +// A vector of K polynomials. +fn Vec(comptime K: u8) type { + return struct { + ps: [K]Poly, + + const Self = @This(); + const bytes_length = K * Poly.bytes_length; + + fn compressedSize(comptime d: u8) usize { + return Poly.compressedSize(d) * K; + } + + fn ntt(a: Self) Self { + var ret: Self = undefined; + for (0..K) |i| { + ret.ps[i] = a.ps[i].ntt(); + } + return ret; + } + + fn invNTT(a: Self) Self { + var ret: Self = undefined; + for (0..K) |i| { + ret.ps[i] = a.ps[i].invNTT(); + } + return ret; + } + + fn normalize(a: Self) Self { + var ret: Self = undefined; + for (0..K) |i| { + ret.ps[i] = a.ps[i].normalize(); + } + return ret; + } + + fn barrettReduce(a: Self) Self { + var ret: Self = undefined; + for (0..K) |i| { + ret.ps[i] = a.ps[i].barrettReduce(); + } + return ret; + } + + fn add(a: Self, b: Self) Self { + var ret: Self = undefined; + for (0..K) |i| { + ret.ps[i] = a.ps[i].add(b.ps[i]); + } + return ret; + } + + fn sub(a: Self, b: Self) Self { + var ret: Self = undefined; + for (0..K) |i| { + ret.ps[i] = a.ps[i].sub(b.ps[i]); + } + return ret; + } + + // Samples v[i] from centered binomial distribution with the given η, + // seed and nonce+i. + fn noise(comptime eta: u8, nonce: u8, seed: *const [32]u8) Self { + var ret: Self = undefined; + for (0..K) |i| { + ret.ps[i] = Poly.noise(eta, nonce + @intCast(u8, i), seed); + } + return ret; + } + + // Sets p to the inner product of a and b using "pointwise" multiplication. + // + // See MulHat() and NTT() for a description of the multiplication. + // Assumes a and b are in Montgomery form. p will be in Montgomery form, + // and its coefficients will be bounded in absolute value by 2kq. + // If a and b are not in Montgomery form, then the action is the same + // as "pointwise" multiplication followed by multiplying by R⁻¹, the inverse + // of the Montgomery factor. + fn dotHat(a: Self, b: Self) Poly { + var ret: Poly = Poly.zero; + for (0..K) |i| { + ret = ret.add(a.ps[i].mulHat(b.ps[i])); + } + return ret; + } + + fn compress(v: Self, comptime d: u8) [compressedSize(d)]u8 { + const cs = comptime Poly.compressedSize(d); + var ret: [compressedSize(d)]u8 = undefined; + inline for (0..K) |i| { + mem.copy(u8, ret[i * cs .. (i + 1) * cs], &v.ps[i].compress(d)); + } + return ret; + } + + fn decompress(comptime d: u8, buf: *const [compressedSize(d)]u8) Self { + const cs = comptime Poly.compressedSize(d); + var ret: Self = undefined; + inline for (0..K) |i| { + ret.ps[i] = Poly.decompress(d, buf[i * cs .. (i + 1) * cs]); + } + return ret; + } + + /// Serializes the key into a byte array. + fn toBytes(v: Self) [bytes_length]u8 { + var ret: [bytes_length]u8 = undefined; + inline for (0..K) |i| { + mem.copy( + u8, + ret[i * Poly.bytes_length .. (i + 1) * Poly.bytes_length], + &v.ps[i].toBytes(), + ); + } + return ret; + } + + /// Deserializes the key from a byte array. + fn fromBytes(buf: *const [bytes_length]u8) Self { + var ret: Self = undefined; + inline for (0..K) |i| { + ret.ps[i] = Poly.fromBytes( + buf[i * Poly.bytes_length .. (i + 1) * Poly.bytes_length], + ); + } + return ret; + } + }; +} + +// A matrix of K vectors +fn Mat(comptime K: u8) type { + return struct { + const Self = @This(); + vs: [K]Vec(K), + + fn uniform(seed: [32]u8, comptime transposed: bool) Self { + var ret: Self = undefined; + var i: u8 = 0; + while (i < K) : (i += 1) { + var j: u8 = 0; + while (j < K) : (j += 1) { + ret.vs[i].ps[j] = Poly.uniform( + seed, + if (transposed) i else j, + if (transposed) j else i, + ); + } + } + return ret; + } + + // Returns transpose of A + fn transpose(m: Self) Self { + var ret: Self = undefined; + for (0..K) |i| { + for (0..K) |j| { + ret.vs[i].ps[j] = m.vs[j].ps[i]; + } + } + return ret; + } + }; +} + +// Returns `true` if a ≠ b. +fn ctneq(comptime len: usize, a: [len]u8, b: [len]u8) u1 { + return 1 - @boolToInt(crypto.utils.timingSafeEql([len]u8, a, b)); +} + +// Copy src into dst given b = 1. +fn cmov(comptime len: usize, dst: *[len]u8, src: [len]u8, b: u1) void { + const mask = @as(u8, 0) -% b; + for (0..len) |i| { + dst[i] ^= mask & (dst[i] ^ src[i]); + } +} + +test "MulHat" { + var rnd = RndGen.init(0); + + for (0..100) |_| { + const a = Poly.randAbsLeqQ(&rnd); + const b = Poly.randAbsLeqQ(&rnd); + + const p2 = a.ntt().mulHat(b.ntt()).barrettReduce().invNTT().normalize(); + var p: Poly = undefined; + + mem.set(i16, &p.cs, 0); + + for (0..N) |i| { + for (0..N) |j| { + var v = montReduce(@as(i32, a.cs[i]) * @as(i32, b.cs[j])); + var k = i + j; + if (k >= N) { + // Recall Xᴺ = -1. + k -= N; + v = -v; + } + p.cs[k] = feBarrettReduce(v + p.cs[k]); + } + } + + p = p.toMont().normalize(); + + try testing.expectEqual(p, p2); + } +} + +test "NTT" { + var rnd = RndGen.init(0); + + for (0..1000) |_| { + var p = Poly.randAbsLeqQ(&rnd); + const q = p.toMont().normalize(); + p = p.ntt(); + + for (0..N) |i| { + try testing.expect(p.cs[i] <= 7 * Q and -7 * Q <= p.cs[i]); + } + + p = p.normalize().invNTT(); + for (0..N) |i| { + try testing.expect(p.cs[i] <= Q and -Q <= p.cs[i]); + } + + p = p.normalize(); + + try testing.expectEqual(p, q); + } +} + +test "Compression" { + var rnd = RndGen.init(0); + inline for (.{ 1, 4, 5, 10, 11 }) |d| { + for (0..1000) |_| { + const p = Poly.randNormalized(&rnd); + const pp = p.compress(d); + const pq = Poly.decompress(d, &pp).compress(d); + try testing.expectEqual(pp, pq); + } + } +} + +test "noise" { + var seed: [32]u8 = undefined; + for (&seed, 0..) |*s, i| { + s.* = @intCast(u8, i); + } + try testing.expectEqual(Poly.noise(3, 37, &seed).cs, .{ + 0, 0, 1, -1, 0, 2, 0, -1, -1, 3, 0, 1, -2, -2, 0, 1, -2, + 1, 0, -2, 3, 0, 0, 0, 1, 3, 1, 1, 2, 1, -1, -1, -1, 0, + 1, 0, 1, 0, 2, 0, 1, -2, 0, -1, -1, -2, 1, -1, -1, 2, -1, + 1, 1, 2, -3, -1, -1, 0, 0, 0, 0, 1, -1, -2, -2, 0, -2, 0, + 0, 0, 1, 0, -1, -1, 1, -2, 2, 0, 0, 2, -2, 0, 1, 0, 1, + 1, 1, 0, 1, -2, -1, -2, -1, 1, 0, 0, 0, 0, 0, 1, 0, -1, + -1, 0, -1, 1, 0, 1, 0, -1, -1, 0, -2, 2, 0, -2, 1, -1, 0, + 1, -1, -1, 2, 1, 0, 0, -2, -1, 2, 0, 0, 0, -1, -1, 3, 1, + 0, 1, 0, 1, 0, 2, 1, 0, 0, 1, 0, 1, 0, 0, -1, -1, -1, + 0, 1, 3, 1, 0, 1, 0, 1, -1, -1, -1, -1, 0, 0, -2, -1, -1, + 2, 0, 1, 0, 1, 0, 2, -2, 0, 1, 1, -3, -1, -2, -1, 0, 1, + 0, 1, -2, 2, 2, 1, 1, 0, -1, 0, -1, -1, 1, 0, -1, 2, 1, + -1, 1, 2, -2, 1, 2, 0, 1, 2, 1, 0, 0, 2, 1, 2, 1, 0, + 2, 1, 0, 0, -1, -1, 1, -1, 0, 1, -1, 2, 2, 0, 0, -1, 1, + 1, 1, 1, 0, 0, -2, 0, -1, 1, 2, 0, 0, 1, 1, -1, 1, 0, + 1, + }); + try testing.expectEqual(Poly.noise(2, 37, &seed).cs, .{ + 1, 0, 1, -1, -1, -2, -1, -1, 2, 0, -1, 0, 0, -1, + 1, 1, -1, 1, 0, 2, -2, 0, 1, 2, 0, 0, -1, 1, + 0, -1, 1, -1, 1, 2, 1, 1, 0, -1, 1, -1, -2, -1, + 1, -1, -1, -1, 2, -1, -1, 0, 0, 1, 1, -1, 1, 1, + 1, 1, -1, -2, 0, 1, 0, 0, 2, 1, -1, 2, 0, 0, + 1, 1, 0, -1, 0, 0, -1, -1, 2, 0, 1, -1, 2, -1, + -1, -1, -1, 0, -2, 0, 2, 1, 0, 0, 0, -1, 0, 0, + 0, -1, -1, 0, -1, -1, 0, -1, 0, 0, -2, 1, 1, 0, + 1, 0, 1, 0, 1, 1, -1, 2, 0, 1, -1, 1, 2, 0, + 0, 0, 0, -1, -1, -1, 0, 1, 0, -1, 2, 0, 0, 1, + 1, 1, 0, 1, -1, 1, 2, 1, 0, 2, -1, 1, -1, -2, + -1, -2, -1, 1, 0, -2, -2, -1, 1, 0, 0, 0, 0, 1, + 0, 0, 0, 2, 2, 0, 1, 0, -1, -1, 0, 2, 0, 0, + -2, 1, 0, 2, 1, -1, -2, 0, 0, -1, 1, 1, 0, 0, + 2, 0, 1, 1, -2, 1, -2, 1, 1, 0, 2, 0, -1, 0, + -1, 0, 1, 2, 0, 1, 0, -2, 1, -2, -2, 1, -1, 0, + -1, 1, 1, 0, 0, 0, 1, 0, -1, 1, 1, 0, 0, 0, + 0, 1, 0, 1, -1, 0, 1, -1, -1, 2, 0, 0, 1, -1, + 0, 1, -1, 0, + }); +} + +test "uniform sampling" { + var seed: [32]u8 = undefined; + for (&seed, 0..) |*s, i| { + s.* = @intCast(u8, i); + } + try testing.expectEqual(Poly.uniform(seed, 1, 0).cs, .{ + 797, 993, 161, 6, 2608, 2385, 2096, 2661, 1676, 247, 2440, + 342, 634, 194, 1570, 2848, 986, 684, 3148, 3208, 2018, 351, + 2288, 612, 1394, 170, 1521, 3119, 58, 596, 2093, 1549, 409, + 2156, 1934, 1730, 1324, 388, 446, 418, 1719, 2202, 1812, 98, + 1019, 2369, 214, 2699, 28, 1523, 2824, 273, 402, 2899, 246, + 210, 1288, 863, 2708, 177, 3076, 349, 44, 949, 854, 1371, + 957, 292, 2502, 1617, 1501, 254, 7, 1761, 2581, 2206, 2655, + 1211, 629, 1274, 2358, 816, 2766, 2115, 2985, 1006, 2433, 856, + 2596, 3192, 1, 1378, 2345, 707, 1891, 1669, 536, 1221, 710, + 2511, 120, 1176, 322, 1897, 2309, 595, 2950, 1171, 801, 1848, + 695, 2912, 1396, 1931, 1775, 2904, 893, 2507, 1810, 2873, 253, + 1529, 1047, 2615, 1687, 831, 1414, 965, 3169, 1887, 753, 3246, + 1937, 115, 2953, 586, 545, 1621, 1667, 3187, 1654, 1988, 1857, + 512, 1239, 1219, 898, 3106, 391, 1331, 2228, 3169, 586, 2412, + 845, 768, 156, 662, 478, 1693, 2632, 573, 2434, 1671, 173, + 969, 364, 1663, 2701, 2169, 813, 1000, 1471, 720, 2431, 2530, + 3161, 733, 1691, 527, 2634, 335, 26, 2377, 1707, 767, 3020, + 950, 502, 426, 1138, 3208, 2607, 2389, 44, 1358, 1392, 2334, + 875, 2097, 173, 1697, 2578, 942, 1817, 974, 1165, 2853, 1958, + 2973, 3282, 271, 1236, 1677, 2230, 673, 1554, 96, 242, 1729, + 2518, 1884, 2272, 71, 1382, 924, 1807, 1610, 456, 1148, 2479, + 2152, 238, 2208, 2329, 713, 1175, 1196, 757, 1078, 3190, 3169, + 708, 3117, 154, 1751, 3225, 1364, 154, 23, 2842, 1105, 1419, + 79, 5, 2013, + }); +} + +test "Polynomial packing" { + var rnd = RndGen.init(0); + + for (0..1000) |_| { + const p = Poly.randNormalized(&rnd); + try testing.expectEqual(Poly.fromBytes(&p.toBytes()), p); + } +} + +test "Test inner PKE" { + var seed: [32]u8 = undefined; + var pt: [32]u8 = undefined; + for (&seed, &pt, 0..) |*s, *p, i| { + s.* = @intCast(u8, i); + p.* = @intCast(u8, i + 32); + } + inline for (modes) |mode| { + for (0..100) |i| { + var pk: mode.InnerPk = undefined; + var sk: mode.InnerSk = undefined; + seed[0] = @intCast(u8, i); + mode.innerKeyFromSeed(seed, &pk, &sk); + for (0..10) |j| { + seed[1] = @intCast(u8, j); + try testing.expectEqual(sk.decrypt(&pk.encrypt(&pt, &seed)), pt); + } + } + } +} + +test "Test happy flow" { + var seed: [64]u8 = undefined; + for (&seed, 0..) |*s, i| { + s.* = @intCast(u8, i); + } + inline for (modes) |mode| { + for (0..100) |i| { + seed[0] = @intCast(u8, i); + const kp = try mode.KeyPair.create(seed); + const sk = try mode.SecretKey.fromBytes(&kp.secret_key.toBytes()); + try testing.expectEqual(sk, kp.secret_key); + const pk = try mode.PublicKey.fromBytes(&kp.public_key.toBytes()); + try testing.expectEqual(pk, kp.public_key); + for (0..10) |j| { + seed[1] = @intCast(u8, j); + const e = pk.encaps(seed[0..32].*); + try testing.expectEqual(e.shared_secret, try sk.decaps(&e.ciphertext)); + } + } + } +} + +// Code to test NIST Known Answer Tests (KAT), see PQCgenKAT.c. + +const sha2 = crypto.hash.sha2; + +test "NIST KAT test" { + inline for (.{ + .{ Kyber512, "e9c2bd37133fcb40772f81559f14b1f58dccd1c816701be9ba6214d43baf4547" }, + .{ Kyber1024, "89248f2f33f7f4f7051729111f3049c409a933ec904aedadf035f30fa5646cd5" }, + .{ Kyber768, "a1e122cad3c24bc51622e4c242d8b8acbcd3f618fee4220400605ca8f9ea02c2" }, + }) |modeHash| { + const mode = modeHash[0]; + var seed: [48]u8 = undefined; + for (&seed, 0..) |*s, i| { + s.* = @intCast(u8, i); + } + var f = sha2.Sha256.init(.{}); + const fw = f.writer(); + var g = NistDRBG.init(seed); + try std.fmt.format(fw, "# {s}\n\n", .{mode.name}); + for (0..100) |i| { + g.fill(&seed); + try std.fmt.format(fw, "count = {}\n", .{i}); + try std.fmt.format(fw, "seed = {s}\n", .{std.fmt.fmtSliceHexUpper(&seed)}); + var g2 = NistDRBG.init(seed); + + // This is not equivalent to g2.fill(kseed[:]). As the reference + // implementation calls randombytes twice generating the keypair, + // we have to do that as well. + var kseed: [64]u8 = undefined; + var eseed: [32]u8 = undefined; + g2.fill(kseed[0..32]); + g2.fill(kseed[32..64]); + g2.fill(&eseed); + const kp = try mode.KeyPair.create(kseed); + const e = kp.public_key.encaps(eseed); + const ss2 = try kp.secret_key.decaps(&e.ciphertext); + try testing.expectEqual(ss2, e.shared_secret); + try std.fmt.format(fw, "pk = {s}\n", .{std.fmt.fmtSliceHexUpper(&kp.public_key.toBytes())}); + try std.fmt.format(fw, "sk = {s}\n", .{std.fmt.fmtSliceHexUpper(&kp.secret_key.toBytes())}); + try std.fmt.format(fw, "ct = {s}\n", .{std.fmt.fmtSliceHexUpper(&e.ciphertext)}); + try std.fmt.format(fw, "ss = {s}\n\n", .{std.fmt.fmtSliceHexUpper(&e.shared_secret)}); + } + + var out: [32]u8 = undefined; + f.final(&out); + var outHex: [64]u8 = undefined; + _ = try std.fmt.bufPrint(&outHex, "{s}", .{std.fmt.fmtSliceHexLower(&out)}); + try testing.expectEqual(outHex, modeHash[1].*); + } +} + +const NistDRBG = struct { + key: [32]u8, + v: [16]u8, + + fn incV(g: *NistDRBG) void { + var j: usize = 15; + while (j >= 0) : (j -= 1) { + if (g.v[j] == 255) { + g.v[j] = 0; + } else { + g.v[j] += 1; + break; + } + } + } + + // AES256_CTR_DRBG_Update(pd, &g.key, &g.v). + fn update(g: *NistDRBG, pd: ?[48]u8) void { + var buf: [48]u8 = undefined; + const ctx = crypto.core.aes.Aes256.initEnc(g.key); + var i: usize = 0; + while (i < 3) : (i += 1) { + g.incV(); + var block: [16]u8 = undefined; + ctx.encrypt(&block, &g.v); + mem.copy(u8, buf[i * 16 .. (i + 1) * 16], &block); + } + if (pd) |p| { + for (&buf, p) |*b, x| { + b.* ^= x; + } + } + mem.copy(u8, &g.key, buf[0..32]); + mem.copy(u8, &g.v, buf[32..48]); + } + + // randombytes. + fn fill(g: *NistDRBG, out: []u8) void { + var block: [16]u8 = undefined; + var dst = out; + + const ctx = crypto.core.aes.Aes256.initEnc(g.key); + while (dst.len > 0) { + g.incV(); + ctx.encrypt(&block, &g.v); + if (dst.len < 16) { + mem.copy(u8, dst, block[0..dst.len]); + break; + } + mem.copy(u8, dst, &block); + dst = dst[16..dst.len]; + } + g.update(null); + } + + fn init(seed: [48]u8) NistDRBG { + var ret: NistDRBG = .{ .key = .{0} ** 32, .v = .{0} ** 16 }; + ret.update(seed); + return ret; + } +}; From 8be607348061674f7be766d6ee86eef3a2d28727 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Mon, 13 Mar 2023 18:41:14 -0400 Subject: [PATCH 124/294] tools: fix typo in lldb command --- tools/lldb_pretty_printers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/lldb_pretty_printers.py b/tools/lldb_pretty_printers.py index a1db03b33a..b72b6f9760 100644 --- a/tools/lldb_pretty_printers.py +++ b/tools/lldb_pretty_printers.py @@ -1,6 +1,6 @@ # pretty printing for the zig language, zig standard library, and zig stage 2 compiler. # put commands in ~/.lldbinit to run them automatically when starting lldb -# `command script /path/to/stage2_lldb_pretty_printers.py` to import this file +# `command script import /path/to/zig/tools/lldb_pretty_printers.py` to import this file # `type category enable zig` to enable pretty printing for the zig language # `type category enable zig.std` to enable pretty printing for the zig standard library # `type category enable zig.stage2` to enable pretty printing for the zig stage 2 compiler From 9a4e9215fcd91cc75e569195834ba1428a736fa0 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Mon, 13 Mar 2023 19:46:47 -0400 Subject: [PATCH 125/294] x86_64: fix error code paths to not have extra pops --- src/arch/x86_64/CodeGen.zig | 70 ++++++++++++++++++------------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 3b7dc0db57..a547b1222d 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -4410,16 +4410,16 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { // Capture the state of register and stack allocation state so that we can revert to it. const saved_state = try self.captureState(); - try self.branch_stack.append(.{}); - errdefer { - _ = self.branch_stack.pop(); - } + { + try self.branch_stack.append(.{}); + errdefer _ = self.branch_stack.pop(); - try self.ensureProcessDeathCapacity(liveness_condbr.then_deaths.len); - for (liveness_condbr.then_deaths) |operand| { - self.processDeath(operand); + try self.ensureProcessDeathCapacity(liveness_condbr.then_deaths.len); + for (liveness_condbr.then_deaths) |operand| { + self.processDeath(operand); + } + try self.genBody(then_body); } - try self.genBody(then_body); // Revert to the previous register and stack allocation state. @@ -4430,16 +4430,16 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { try self.performReloc(reloc); - try self.branch_stack.append(.{}); - errdefer { - _ = self.branch_stack.pop(); - } + { + try self.branch_stack.append(.{}); + errdefer _ = self.branch_stack.pop(); - try self.ensureProcessDeathCapacity(liveness_condbr.else_deaths.len); - for (liveness_condbr.else_deaths) |operand| { - self.processDeath(operand); + try self.ensureProcessDeathCapacity(liveness_condbr.else_deaths.len); + for (liveness_condbr.else_deaths) |operand| { + self.processDeath(operand); + } + try self.genBody(else_body); } - try self.genBody(else_body); var else_branch = self.branch_stack.pop(); defer else_branch.deinit(self.gpa); @@ -4850,17 +4850,17 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { // Capture the state of register and stack allocation state so that we can revert to it. const saved_state = try self.captureState(); - try self.branch_stack.append(.{}); - errdefer { - _ = self.branch_stack.pop(); - } + { + try self.branch_stack.append(.{}); + errdefer _ = self.branch_stack.pop(); - try self.ensureProcessDeathCapacity(liveness.deaths[case_i].len); - for (liveness.deaths[case_i]) |operand| { - self.processDeath(operand); - } + try self.ensureProcessDeathCapacity(liveness.deaths[case_i].len); + for (liveness.deaths[case_i]) |operand| { + self.processDeath(operand); + } - try self.genBody(case_body); + try self.genBody(case_body); + } branch_stack.appendAssumeCapacity(self.branch_stack.pop()); @@ -4878,18 +4878,18 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { // Capture the state of register and stack allocation state so that we can revert to it. const saved_state = try self.captureState(); - try self.branch_stack.append(.{}); - errdefer { - _ = self.branch_stack.pop(); - } + { + try self.branch_stack.append(.{}); + errdefer _ = self.branch_stack.pop(); - const else_deaths = liveness.deaths.len - 1; - try self.ensureProcessDeathCapacity(liveness.deaths[else_deaths].len); - for (liveness.deaths[else_deaths]) |operand| { - self.processDeath(operand); - } + const else_deaths = liveness.deaths.len - 1; + try self.ensureProcessDeathCapacity(liveness.deaths[else_deaths].len); + for (liveness.deaths[else_deaths]) |operand| { + self.processDeath(operand); + } - try self.genBody(else_body); + try self.genBody(else_body); + } branch_stack.appendAssumeCapacity(self.branch_stack.pop()); From 5ab426a3020614dcc8640a4019c8d93c6c917ea0 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Mon, 13 Mar 2023 20:25:13 -0400 Subject: [PATCH 126/294] x86_64: fix store of undefined --- src/arch/x86_64/CodeGen.zig | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index a547b1222d..12c5071462 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -2861,7 +2861,13 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type .unreach => unreachable, .eflags => unreachable, .undef => { - try self.genSetReg(value_ty, reg, value); + switch (abi_size) { + 1 => try self.store(ptr, .{ .immediate = 0xaa }, ptr_ty, value_ty), + 2 => try self.store(ptr, .{ .immediate = 0xaaaa }, ptr_ty, value_ty), + 4 => try self.store(ptr, .{ .immediate = 0xaaaaaaaa }, ptr_ty, value_ty), + 8 => try self.store(ptr, .{ .immediate = 0xaaaaaaaaaaaaaaaa }, ptr_ty, value_ty), + else => try self.genInlineMemset(ptr, .{ .immediate = 0xaa }, .{ .immediate = abi_size }, .{}), + } }, .immediate => |imm| { switch (abi_size) { From c51930b060cf66b21c78b97e53fd71b153a5e60c Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Mon, 13 Mar 2023 21:12:57 -0400 Subject: [PATCH 127/294] behavior: enable passing behavior tests on stage2_x86_64 --- test/behavior/align.zig | 1 - test/behavior/array.zig | 1 - test/behavior/bit_shifting.zig | 1 - test/behavior/bitreverse.zig | 1 - test/behavior/bugs/1076.zig | 1 - test/behavior/bugs/11046.zig | 1 - test/behavior/bugs/11787.zig | 1 - test/behavior/bugs/12000.zig | 1 - test/behavior/bugs/12119.zig | 1 - test/behavior/bugs/12169.zig | 1 - test/behavior/bugs/12890.zig | 1 - test/behavior/bugs/13113.zig | 1 - test/behavior/bugs/1421.zig | 1 - test/behavior/bugs/1607.zig | 1 - test/behavior/bugs/2622.zig | 1 - test/behavior/bugs/2727.zig | 1 - test/behavior/bugs/3742.zig | 1 - test/behavior/bugs/394.zig | 1 - test/behavior/bugs/421.zig | 1 - test/behavior/bugs/5398.zig | 1 - test/behavior/bugs/5487.zig | 1 - test/behavior/bugs/656.zig | 1 - test/behavior/bugs/6947.zig | 1 - test/behavior/byteswap.zig | 1 - test/behavior/byval_arg_var.zig | 1 - test/behavior/call.zig | 2 -- test/behavior/cast.zig | 9 --------- test/behavior/defer.zig | 1 - test/behavior/enum.zig | 2 -- test/behavior/error.zig | 10 ---------- test/behavior/eval.zig | 2 -- test/behavior/floatop.zig | 7 ------- test/behavior/fn.zig | 2 -- test/behavior/for.zig | 8 -------- test/behavior/generics.zig | 3 --- test/behavior/if.zig | 1 - test/behavior/member_func.zig | 2 -- test/behavior/null.zig | 3 --- test/behavior/optional.zig | 1 - test/behavior/pointers.zig | 2 -- test/behavior/ptrcast.zig | 1 - .../ref_var_in_if_after_if_2nd_switch_prong.zig | 1 - test/behavior/reflection.zig | 1 - test/behavior/sizeof_and_typeof.zig | 3 --- test/behavior/slice.zig | 9 --------- test/behavior/struct.zig | 5 ----- test/behavior/struct_contains_null_ptr_itself.zig | 1 - test/behavior/switch.zig | 2 -- test/behavior/threadlocal.zig | 2 -- test/behavior/translate_c_macros.zig | 1 - test/behavior/tuple.zig | 1 - test/behavior/type.zig | 4 ---- test/behavior/type_info.zig | 2 -- test/behavior/union.zig | 9 --------- test/behavior/vector.zig | 6 ------ test/behavior/void.zig | 1 - test/behavior/while.zig | 8 -------- test/behavior/widening.zig | 1 - 58 files changed, 138 deletions(-) diff --git a/test/behavior/align.zig b/test/behavior/align.zig index 901ea3697a..9d626dad66 100644 --- a/test/behavior/align.zig +++ b/test/behavior/align.zig @@ -568,7 +568,6 @@ fn overaligned_fn() align(0x1000) i32 { } test "comptime alloc alignment" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO diff --git a/test/behavior/array.zig b/test/behavior/array.zig index 3d711357f3..6c68d50fda 100644 --- a/test/behavior/array.zig +++ b/test/behavior/array.zig @@ -591,7 +591,6 @@ test "type coercion of anon struct literal to array" { test "type coercion of pointer to anon struct literal to pointer to array" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const S = struct { diff --git a/test/behavior/bit_shifting.zig b/test/behavior/bit_shifting.zig index 97186eb54a..8ad71400fe 100644 --- a/test/behavior/bit_shifting.zig +++ b/test/behavior/bit_shifting.zig @@ -63,7 +63,6 @@ fn ShardedTable(comptime Key: type, comptime mask_bit_count: comptime_int, compt test "sharded table" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO // realistic 16-way sharding diff --git a/test/behavior/bitreverse.zig b/test/behavior/bitreverse.zig index 80167b9a17..9a24090c0e 100644 --- a/test/behavior/bitreverse.zig +++ b/test/behavior/bitreverse.zig @@ -150,7 +150,6 @@ fn vector0() !void { test "bitReverse vectors u0" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; comptime try vector0(); try vector0(); diff --git a/test/behavior/bugs/1076.zig b/test/behavior/bugs/1076.zig index 6fe4fbd38f..ba2b61e3db 100644 --- a/test/behavior/bugs/1076.zig +++ b/test/behavior/bugs/1076.zig @@ -4,7 +4,6 @@ const mem = std.mem; const expect = std.testing.expect; test "comptime code should not modify constant data" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO diff --git a/test/behavior/bugs/11046.zig b/test/behavior/bugs/11046.zig index ba6c9d1a83..a13e02e45c 100644 --- a/test/behavior/bugs/11046.zig +++ b/test/behavior/bugs/11046.zig @@ -10,7 +10,6 @@ fn bar() !void { } test "fixed" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO diff --git a/test/behavior/bugs/11787.zig b/test/behavior/bugs/11787.zig index 8678f0789a..6d17730a47 100644 --- a/test/behavior/bugs/11787.zig +++ b/test/behavior/bugs/11787.zig @@ -4,7 +4,6 @@ const testing = std.testing; test "slicing zero length array field of struct" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO diff --git a/test/behavior/bugs/12000.zig b/test/behavior/bugs/12000.zig index c29fb84270..a823ce6a0a 100644 --- a/test/behavior/bugs/12000.zig +++ b/test/behavior/bugs/12000.zig @@ -7,7 +7,6 @@ const T = struct { test { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO diff --git a/test/behavior/bugs/12119.zig b/test/behavior/bugs/12119.zig index bb12e3565a..8c734ad6d6 100644 --- a/test/behavior/bugs/12119.zig +++ b/test/behavior/bugs/12119.zig @@ -6,7 +6,6 @@ const u32x8 = @Vector(8, u32); test { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO diff --git a/test/behavior/bugs/12169.zig b/test/behavior/bugs/12169.zig index 5dd3fdefa9..b3db56e20b 100644 --- a/test/behavior/bugs/12169.zig +++ b/test/behavior/bugs/12169.zig @@ -3,7 +3,6 @@ const builtin = @import("builtin"); test { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO diff --git a/test/behavior/bugs/12890.zig b/test/behavior/bugs/12890.zig index e6095ac33d..1316c2745e 100644 --- a/test/behavior/bugs/12890.zig +++ b/test/behavior/bugs/12890.zig @@ -10,7 +10,6 @@ fn a(b: []u3, c: u3) void { } test { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO var arr: [8]u3 = undefined; diff --git a/test/behavior/bugs/13113.zig b/test/behavior/bugs/13113.zig index cfbf7b6650..f9e0c8e7bb 100644 --- a/test/behavior/bugs/13113.zig +++ b/test/behavior/bugs/13113.zig @@ -8,7 +8,6 @@ const Foo = extern struct { test { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO diff --git a/test/behavior/bugs/1421.zig b/test/behavior/bugs/1421.zig index fbd05fb73c..1c85c3089e 100644 --- a/test/behavior/bugs/1421.zig +++ b/test/behavior/bugs/1421.zig @@ -9,7 +9,6 @@ const S = struct { }; test "functions with return type required to be comptime are generic" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; const ti = S.method(); try expect(@as(std.builtin.TypeId, ti) == std.builtin.TypeId.Struct); } diff --git a/test/behavior/bugs/1607.zig b/test/behavior/bugs/1607.zig index d9e97e37b7..a60a406b75 100644 --- a/test/behavior/bugs/1607.zig +++ b/test/behavior/bugs/1607.zig @@ -13,7 +13,6 @@ fn checkAddress(s: []const u8) !void { test "slices pointing at the same address as global array." { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO try checkAddress(&a); comptime try checkAddress(&a); diff --git a/test/behavior/bugs/2622.zig b/test/behavior/bugs/2622.zig index 8a0d1a06ba..89130a3974 100644 --- a/test/behavior/bugs/2622.zig +++ b/test/behavior/bugs/2622.zig @@ -4,7 +4,6 @@ var buf: []u8 = undefined; test "reslice of undefined global var slice" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO diff --git a/test/behavior/bugs/2727.zig b/test/behavior/bugs/2727.zig index 0478d41b63..9e0def70d4 100644 --- a/test/behavior/bugs/2727.zig +++ b/test/behavior/bugs/2727.zig @@ -6,7 +6,6 @@ fn t() bool { test { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO diff --git a/test/behavior/bugs/3742.zig b/test/behavior/bugs/3742.zig index a984f0d8e4..1ee88b8b64 100644 --- a/test/behavior/bugs/3742.zig +++ b/test/behavior/bugs/3742.zig @@ -37,7 +37,6 @@ pub const ArgSerializer = struct { test "fixed" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .aarch64 and builtin.os.tag == .windows) return error.SkipZigTest; diff --git a/test/behavior/bugs/394.zig b/test/behavior/bugs/394.zig index 02e90258bf..37864edbc5 100644 --- a/test/behavior/bugs/394.zig +++ b/test/behavior/bugs/394.zig @@ -11,7 +11,6 @@ const expect = @import("std").testing.expect; const builtin = @import("builtin"); test "fixed" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; const x = S{ .x = 3, .y = E{ .B = 1 }, diff --git a/test/behavior/bugs/421.zig b/test/behavior/bugs/421.zig index 500493e7d1..69ecbd2331 100644 --- a/test/behavior/bugs/421.zig +++ b/test/behavior/bugs/421.zig @@ -2,7 +2,6 @@ const builtin = @import("builtin"); const expect = @import("std").testing.expect; test "bitCast to array" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO diff --git a/test/behavior/bugs/5398.zig b/test/behavior/bugs/5398.zig index 78d31914d0..6f75bd9436 100644 --- a/test/behavior/bugs/5398.zig +++ b/test/behavior/bugs/5398.zig @@ -21,7 +21,6 @@ var renderable: Renderable = undefined; test "assignment of field with padding" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO renderable = Renderable{ diff --git a/test/behavior/bugs/5487.zig b/test/behavior/bugs/5487.zig index d901a692cd..3ea8cad220 100644 --- a/test/behavior/bugs/5487.zig +++ b/test/behavior/bugs/5487.zig @@ -12,7 +12,6 @@ pub fn writer() io.Writer(void, @typeInfo(@typeInfo(@TypeOf(write)).Fn.return_ty test "crash" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO _ = io.multiWriter(.{writer()}); } diff --git a/test/behavior/bugs/656.zig b/test/behavior/bugs/656.zig index fa9e3ecc1e..216c9d8e1c 100644 --- a/test/behavior/bugs/656.zig +++ b/test/behavior/bugs/656.zig @@ -13,7 +13,6 @@ const Value = struct { test "optional if after an if in a switch prong of a switch with 2 prongs in an else" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO try foo(false, true); } diff --git a/test/behavior/bugs/6947.zig b/test/behavior/bugs/6947.zig index 2e891ac5b3..c2b538c3fa 100644 --- a/test/behavior/bugs/6947.zig +++ b/test/behavior/bugs/6947.zig @@ -6,7 +6,6 @@ fn destroy(ptr: *void) void { test { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO diff --git a/test/behavior/byteswap.zig b/test/behavior/byteswap.zig index d173c13275..8bd6fec6e3 100644 --- a/test/behavior/byteswap.zig +++ b/test/behavior/byteswap.zig @@ -116,7 +116,6 @@ fn vector0() !void { test "@byteSwap vectors u0" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; comptime try vector0(); try vector0(); diff --git a/test/behavior/byval_arg_var.zig b/test/behavior/byval_arg_var.zig index 476d0d2e4e..01b5f90ef7 100644 --- a/test/behavior/byval_arg_var.zig +++ b/test/behavior/byval_arg_var.zig @@ -4,7 +4,6 @@ const builtin = @import("builtin"); var result: []const u8 = "wrong"; test "pass string literal byvalue to a generic var param" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO start(); diff --git a/test/behavior/call.zig b/test/behavior/call.zig index 9622aa3144..b51a459932 100644 --- a/test/behavior/call.zig +++ b/test/behavior/call.zig @@ -21,7 +21,6 @@ test "super basic invocations" { test "basic invocations" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -56,7 +55,6 @@ test "basic invocations" { test "tuple parameters" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const add = struct { diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index f179cbe525..275533d6ec 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -176,7 +176,6 @@ fn expectFloatToInt(comptime F: type, f: F, comptime I: type, i: I) !void { test "implicitly cast indirect pointer to maybe-indirect pointer" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const S = struct { @@ -318,7 +317,6 @@ test "peer result null and comptime_int" { test "*const ?[*]const T to [*c]const [*c]const T" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO var array = [_]u8{ 'o', 'k' }; @@ -588,7 +586,6 @@ fn testCastPtrOfArrayToSliceAndPtr() !void { test "cast *[1][*]const u8 to [*]const ?[*]const u8" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const window_name = [1][*]const u8{"window name"}; @@ -927,7 +924,6 @@ test "peer cast *[N:x]T to *[N]T" { test "peer cast [*:x]T to [*]T" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const S = struct { @@ -948,7 +944,6 @@ test "peer cast [*:x]T to [*]T" { test "peer cast [:x]T to [*:x]T" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const S = struct { @@ -989,7 +984,6 @@ test "peer type resolution implicit cast to return type" { test "peer type resolution implicit cast to variable type" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const S = struct { @@ -1112,8 +1106,6 @@ fn incrementVoidPtrArray(array: ?*anyopaque, len: usize) void { } test "compile time int to ptr of function" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - try foobar(FUNCTION_CONSTANT); } @@ -1505,7 +1497,6 @@ test "implicit cast from [:0]T to [*c]T" { test "bitcast packed struct with u0" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO const S = packed struct(u2) { a: u0, b: u2 }; const s = @bitCast(S, @as(u2, 2)); diff --git a/test/behavior/defer.zig b/test/behavior/defer.zig index 32f63b1a46..26c2adc271 100644 --- a/test/behavior/defer.zig +++ b/test/behavior/defer.zig @@ -50,7 +50,6 @@ fn testNestedFnErrDefer() anyerror!void { } test "return variable while defer expression in scope to modify it" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO diff --git a/test/behavior/enum.zig b/test/behavior/enum.zig index 095f3b740b..9076f9f9ac 100644 --- a/test/behavior/enum.zig +++ b/test/behavior/enum.zig @@ -937,7 +937,6 @@ test "enum literal casting to error union with payload enum" { } test "constant enum initialization with differing sizes" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -1137,7 +1136,6 @@ test "tag name functions are unique" { } test "size of enum with only one tag which has explicit integer tag type" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO diff --git a/test/behavior/error.zig b/test/behavior/error.zig index f30290eb91..9d4b154311 100644 --- a/test/behavior/error.zig +++ b/test/behavior/error.zig @@ -144,15 +144,11 @@ test "implicit cast to optional to error union to return result loc" { } test "fn returning empty error set can be passed as fn returning any error" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - entry(); comptime entry(); } test "fn returning empty error set can be passed as fn returning any error - pointer" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - entryPtr(); comptime entryPtr(); } @@ -219,7 +215,6 @@ fn testErrorSetType() !void { } test "explicit error set cast" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO try testExplicitErrorSetCast(Set1.A); @@ -238,7 +233,6 @@ fn testExplicitErrorSetCast(set1: Set1) !void { } test "comptime test error for empty error set" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO try testComptimeTestErrorEmptySet(1234); @@ -255,8 +249,6 @@ fn testComptimeTestErrorEmptySet(x: EmptyErrorSet!i32) !void { } test "comptime err to int of error set with only 1 possible value" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - testErrToIntWithOnePossibleValue(error.A, @errorToInt(error.A)); comptime testErrToIntWithOnePossibleValue(error.A, @errorToInt(error.A)); } @@ -426,7 +418,6 @@ test "nested error union function call in optional unwrap" { } test "return function call to error set from error union function" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -486,7 +477,6 @@ test "nested catch" { } test "function pointer with return type that is error union with payload which is pointer of parent struct" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const S = struct { diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index 8364196f94..52b30f9aed 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -138,7 +138,6 @@ test "pointer to type" { } test "a type constructed in a global expression" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -1515,7 +1514,6 @@ test "x or true is comptime-known true" { } test "non-optional and optional array elements concatenated" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO diff --git a/test/behavior/floatop.zig b/test/behavior/floatop.zig index f05901f7d9..a93949cd88 100644 --- a/test/behavior/floatop.zig +++ b/test/behavior/floatop.zig @@ -21,7 +21,6 @@ fn epsForType(comptime T: type) T { test "floating point comparisons" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO try testFloatComparisons(); @@ -91,7 +90,6 @@ fn testDifferentSizedFloatComparisons() !void { test "negative f128 floatToInt at compile-time" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -331,7 +329,6 @@ fn testExpWithVectors() !void { test "@exp2" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -500,7 +497,6 @@ fn testLog10WithVectors() !void { test "@fabs" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -627,7 +623,6 @@ test "a third @fabs test, surely there should not be three fabs tests" { test "@floor" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -720,7 +715,6 @@ fn testFloorLegacy(comptime T: type, x: T) !void { test "@ceil" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -813,7 +807,6 @@ fn testCeilLegacy(comptime T: type, x: T) !void { test "@trunc" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO diff --git a/test/behavior/fn.zig b/test/behavior/fn.zig index 9c37b9a8d9..5113e21452 100644 --- a/test/behavior/fn.zig +++ b/test/behavior/fn.zig @@ -275,7 +275,6 @@ test "implicit cast fn call result to optional in field result" { } test "void parameters" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; @@ -300,7 +299,6 @@ fn acceptsString(foo: []u8) void { } test "function pointers" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO diff --git a/test/behavior/for.zig b/test/behavior/for.zig index 67a20e4840..98ffff85a3 100644 --- a/test/behavior/for.zig +++ b/test/behavior/for.zig @@ -110,7 +110,6 @@ test "basic for loop" { } test "for with null and T peer types and inferred result location type" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -152,7 +151,6 @@ test "2 break statements and an else" { } test "for loop with pointer elem var" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -198,7 +196,6 @@ test "for copies its payload" { } test "for on slice with allowzero ptr" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -253,7 +250,6 @@ test "for loop with else branch" { test "count over fixed range" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO var sum: usize = 0; for (0..6) |i| { @@ -266,7 +262,6 @@ test "count over fixed range" { test "two counters" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO var sum: usize = 0; for (0..10, 10..20) |i, j| { @@ -280,7 +275,6 @@ test "two counters" { test "1-based counter and ptr to array" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO var ok: usize = 0; @@ -401,7 +395,6 @@ test "raw pointer and counter" { test "inline for with slice as the comptime-known" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO const comptime_slice = "hello"; @@ -432,7 +425,6 @@ test "inline for with slice as the comptime-known" { test "inline for with counter as the comptime-known" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO var runtime_slice = "hello"; diff --git a/test/behavior/generics.zig b/test/behavior/generics.zig index 205823430c..0e002b2016 100644 --- a/test/behavior/generics.zig +++ b/test/behavior/generics.zig @@ -56,7 +56,6 @@ fn sameButWithFloats(a: f64, b: f64) f64 { } test "fn with comptime args" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -67,7 +66,6 @@ test "fn with comptime args" { } test "anytype params" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -93,7 +91,6 @@ fn max_f64(a: f64, b: f64) f64 { } test "type constructed by comptime function call" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO diff --git a/test/behavior/if.zig b/test/behavior/if.zig index ac11a6585d..6632cdd5c2 100644 --- a/test/behavior/if.zig +++ b/test/behavior/if.zig @@ -78,7 +78,6 @@ test "const result loc, runtime if cond, else unreachable" { } test "if copies its payload" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO diff --git a/test/behavior/member_func.zig b/test/behavior/member_func.zig index a6229846d6..e7b19d5c01 100644 --- a/test/behavior/member_func.zig +++ b/test/behavior/member_func.zig @@ -27,7 +27,6 @@ const HasFuncs = struct { }; test "standard field calls" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -71,7 +70,6 @@ test "standard field calls" { } test "@field field calls" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO diff --git a/test/behavior/null.zig b/test/behavior/null.zig index 223be69084..c78a995833 100644 --- a/test/behavior/null.zig +++ b/test/behavior/null.zig @@ -50,7 +50,6 @@ test "rhs maybe unwrap return" { } test "maybe return" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -71,7 +70,6 @@ fn foo(x: ?i32) ?bool { } test "test null runtime" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -184,7 +182,6 @@ const SillyStruct = struct { const here_is_a_null_literal = SillyStruct{ .context = null }; test "unwrap optional which is field of global var" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO diff --git a/test/behavior/optional.zig b/test/behavior/optional.zig index 3a5b7b008b..bbcc5b3ce6 100644 --- a/test/behavior/optional.zig +++ b/test/behavior/optional.zig @@ -274,7 +274,6 @@ test "0-bit child type coerced to optional return ptr result location" { } test "0-bit child type coerced to optional" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const S = struct { diff --git a/test/behavior/pointers.zig b/test/behavior/pointers.zig index ec4ff332cf..e5ccfec543 100644 --- a/test/behavior/pointers.zig +++ b/test/behavior/pointers.zig @@ -190,7 +190,6 @@ test "compare equality of optional and non-optional pointer" { test "allowzero pointer and slice" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -510,7 +509,6 @@ test "ptrToInt on a generic function" { 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 - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO const S = struct { fn generic(i: anytype) @TypeOf(i) { diff --git a/test/behavior/ptrcast.zig b/test/behavior/ptrcast.zig index 9336d58641..599d13be1d 100644 --- a/test/behavior/ptrcast.zig +++ b/test/behavior/ptrcast.zig @@ -53,7 +53,6 @@ fn testReinterpretStructWrappedBytesAsInteger() !void { } test "reinterpret bytes of an array into an extern struct" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO diff --git a/test/behavior/ref_var_in_if_after_if_2nd_switch_prong.zig b/test/behavior/ref_var_in_if_after_if_2nd_switch_prong.zig index cd1f67dd11..bb6d5b1359 100644 --- a/test/behavior/ref_var_in_if_after_if_2nd_switch_prong.zig +++ b/test/behavior/ref_var_in_if_after_if_2nd_switch_prong.zig @@ -5,7 +5,6 @@ const mem = std.mem; var ok: bool = false; test "reference a variable in an if after an if in the 2nd switch prong" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO diff --git a/test/behavior/reflection.zig b/test/behavior/reflection.zig index 4c3f8ccad5..aea84bc45a 100644 --- a/test/behavior/reflection.zig +++ b/test/behavior/reflection.zig @@ -27,7 +27,6 @@ fn dummy(a: bool, b: i32, c: f32) i32 { test "reflection: @field" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO var f = Foo{ diff --git a/test/behavior/sizeof_and_typeof.zig b/test/behavior/sizeof_and_typeof.zig index cfe948ac02..940ceda107 100644 --- a/test/behavior/sizeof_and_typeof.zig +++ b/test/behavior/sizeof_and_typeof.zig @@ -18,7 +18,6 @@ test "@sizeOf on compile-time types" { } test "@TypeOf() with multiple arguments" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; { var var_1: u32 = undefined; var var_2: u8 = undefined; @@ -138,7 +137,6 @@ test "@sizeOf(T) == 0 doesn't force resolving struct size" { } test "@TypeOf() has no runtime side effects" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; const S = struct { fn foo(comptime T: type, ptr: *T) T { ptr.* += 1; @@ -153,7 +151,6 @@ test "@TypeOf() has no runtime side effects" { test "branching logic inside @TypeOf" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; const S = struct { var data: i32 = 0; fn foo() anyerror!i32 { diff --git a/test/behavior/slice.zig b/test/behavior/slice.zig index 6239de2d76..d749697ec5 100644 --- a/test/behavior/slice.zig +++ b/test/behavior/slice.zig @@ -119,7 +119,6 @@ test "slice of type" { } test "generic malloc free" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const a = memAlloc(u8, 10) catch unreachable; @@ -171,7 +170,6 @@ test "comptime pointer cast array and then slice" { test "slicing zero length array" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const s1 = ""[0..]; @@ -185,8 +183,6 @@ test "slicing zero length array" { const x = @intToPtr([*]i32, 0x1000)[0..0x500]; const y = x[0x100..]; test "compile time slice of pointer to hard coded address" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - try expect(@ptrToInt(x) == 0x1000); try expect(x.len == 0x500); @@ -342,7 +338,6 @@ test "@ptrCast slice to pointer" { } test "slice syntax resulting in pointer-to-array" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -477,7 +472,6 @@ test "slice syntax resulting in pointer-to-array" { } test "slice pointer-to-array null terminated" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -497,7 +491,6 @@ test "slice pointer-to-array null terminated" { } test "slice pointer-to-array zero length" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO comptime { @@ -530,7 +523,6 @@ test "slice pointer-to-array zero length" { } test "type coercion of pointer to anon struct literal to pointer to slice" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -730,7 +722,6 @@ test "slice with dereferenced value" { test "empty slice ptr is non null" { if (builtin.zig_backend == .stage2_aarch64 and builtin.os.tag == .macos) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64 and (builtin.os.tag == .macos or builtin.os.tag == .windows)) return error.SkipZigTest; // TODO const empty_slice: []u8 = &[_]u8{}; const p: [*]u8 = empty_slice.ptr + 0; diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index 348e269682..2a1acebc0f 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -342,7 +342,6 @@ fn testPassSliceOfEmptyStructToFn(slice: []const EmptyStruct2) usize { test "self-referencing struct via array member" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const T = struct { @@ -536,7 +535,6 @@ test "implicit cast packed struct field to const ptr" { } test "zero-bit field in packed struct" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const S = packed struct { @@ -719,7 +717,6 @@ test "pointer to packed struct member in a stack variable" { } test "packed struct with u0 field access" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const S = packed struct { @@ -1018,7 +1015,6 @@ test "type coercion of anon struct literal to struct" { } test "type coercion of pointer to anon struct literal to pointer to struct" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -1088,7 +1084,6 @@ test "packed struct with undefined initializers" { } test "for loop over pointers to struct, getting field from struct pointer" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO diff --git a/test/behavior/struct_contains_null_ptr_itself.zig b/test/behavior/struct_contains_null_ptr_itself.zig index d60e04a91a..7f0182af22 100644 --- a/test/behavior/struct_contains_null_ptr_itself.zig +++ b/test/behavior/struct_contains_null_ptr_itself.zig @@ -4,7 +4,6 @@ const builtin = @import("builtin"); test "struct contains null pointer which contains original struct" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO var x: ?*NodeLineComment = null; diff --git a/test/behavior/switch.zig b/test/behavior/switch.zig index b8c367eb44..9129b73f16 100644 --- a/test/behavior/switch.zig +++ b/test/behavior/switch.zig @@ -403,7 +403,6 @@ fn return_a_number() anyerror!i32 { } test "switch on integer with else capturing expr" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const S = struct { @@ -577,7 +576,6 @@ test "switch prongs with cases with identical payload types" { } test "switch on pointer type" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO diff --git a/test/behavior/threadlocal.zig b/test/behavior/threadlocal.zig index ebeb1177c2..1f1bc6bea4 100644 --- a/test/behavior/threadlocal.zig +++ b/test/behavior/threadlocal.zig @@ -4,7 +4,6 @@ const expect = std.testing.expect; test "thread local variable" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_llvm) switch (builtin.cpu.arch) { @@ -40,7 +39,6 @@ threadlocal var buffer: [11]u8 = undefined; test "reference a global threadlocal variable" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_llvm) switch (builtin.cpu.arch) { diff --git a/test/behavior/translate_c_macros.zig b/test/behavior/translate_c_macros.zig index 8143b1bddd..6d8d4eca6d 100644 --- a/test/behavior/translate_c_macros.zig +++ b/test/behavior/translate_c_macros.zig @@ -79,7 +79,6 @@ test "casting to union with a macro" { test "casting or calling a value with a paren-surrounded macro" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO diff --git a/test/behavior/tuple.zig b/test/behavior/tuple.zig index f7860be34e..79db21424e 100644 --- a/test/behavior/tuple.zig +++ b/test/behavior/tuple.zig @@ -126,7 +126,6 @@ test "tuple initializer for var" { } test "array-like initializer for tuple types" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO diff --git a/test/behavior/type.zig b/test/behavior/type.zig index 325bf0a8ed..7f44f350d1 100644 --- a/test/behavior/type.zig +++ b/test/behavior/type.zig @@ -449,8 +449,6 @@ test "Type.Union" { } test "Type.Union from Type.Enum" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - const Tag = @Type(.{ .Enum = .{ .tag_type = u0, @@ -475,8 +473,6 @@ test "Type.Union from Type.Enum" { } test "Type.Union from regular enum" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - const E = enum { working_as_expected }; const T = @Type(.{ .Union = .{ diff --git a/test/behavior/type_info.zig b/test/behavior/type_info.zig index 6f64c92006..495c1f3195 100644 --- a/test/behavior/type_info.zig +++ b/test/behavior/type_info.zig @@ -509,7 +509,6 @@ test "type info for async frames" { } test "Declarations are returned in declaration order" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const S = struct { @@ -532,7 +531,6 @@ test "Struct.is_tuple for anon list literal" { } test "Struct.is_tuple for anon struct literal" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const info = @typeInfo(@TypeOf(.{ .a = 0 })); diff --git a/test/behavior/union.zig b/test/behavior/union.zig index 87691cf3cb..03c47b09d6 100644 --- a/test/behavior/union.zig +++ b/test/behavior/union.zig @@ -11,7 +11,6 @@ const FooWithFloats = union { }; test "basic unions with floats" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -379,7 +378,6 @@ test "union with only 1 field which is void should be zero bits" { } test "tagged union initialization with runtime void" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -400,7 +398,6 @@ fn testTaggedUnionInit(x: anytype) bool { pub const UnionEnumNoPayloads = union(enum) { A, B }; test "tagged union with no payloads" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -466,7 +463,6 @@ pub const FooUnion = union(enum) { var glbl_array: [2]FooUnion = undefined; test "initialize global array of union" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -494,7 +490,6 @@ test "update the tag value for zero-sized unions" { test "union initializer generates padding only if needed" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -561,7 +556,6 @@ const FooNoVoid = union(enum) { const Baz = enum { A, B, C, D }; test "tagged union type" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const foo1 = TaggedFoo{ .One = 13 }; @@ -598,7 +592,6 @@ fn returnAnInt(x: i32) TaggedFoo { } test "tagged union with all void fields but a meaningful tag" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -777,7 +770,6 @@ fn Setter(comptime attr: Attribute) type { } test "return union init with void payload" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO @@ -913,7 +905,6 @@ test "extern union doesn't trigger field check at comptime" { test "anonymous union literal syntax" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const S = struct { diff --git a/test/behavior/vector.zig b/test/behavior/vector.zig index 1d9d517a96..562e9aba20 100644 --- a/test/behavior/vector.zig +++ b/test/behavior/vector.zig @@ -382,7 +382,6 @@ test "store vector elements via runtime index" { } test "initialize vector which is a struct field" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -794,7 +793,6 @@ test "vector reduce operation" { test "vector @reduce comptime" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -1069,7 +1067,6 @@ test "alignment of vectors" { test "loading the second vector from a slice of vectors" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -1150,7 +1147,6 @@ test "byte vector initialized in inline function" { test "zero divisor" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -1171,7 +1167,6 @@ test "zero divisor" { test "zero multiplicand" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -1234,7 +1229,6 @@ test "load packed vector element" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO var x: @Vector(2, u15) = .{ 1, 4 }; try expect((&x[0]).* == 1); diff --git a/test/behavior/void.zig b/test/behavior/void.zig index 9b6c05d07d..8c6269123d 100644 --- a/test/behavior/void.zig +++ b/test/behavior/void.zig @@ -33,7 +33,6 @@ fn times(n: usize) []const void { } test "void optional" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO diff --git a/test/behavior/while.zig b/test/behavior/while.zig index 6a97f96763..956aa30f7b 100644 --- a/test/behavior/while.zig +++ b/test/behavior/while.zig @@ -104,7 +104,6 @@ fn testBreakOuter() void { } test "while copies its payload" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -143,7 +142,6 @@ fn runContinueAndBreakTest() !void { } test "while with optional as condition" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -156,7 +154,6 @@ test "while with optional as condition" { } test "while with optional as condition with else" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -174,7 +171,6 @@ test "while with optional as condition with else" { } test "while with error union condition" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO numbers_left = 10; @@ -205,7 +201,6 @@ test "while on bool with else result follow break prong" { } test "while on optional with else result follow else prong" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -217,7 +212,6 @@ test "while on optional with else result follow else prong" { } test "while on optional with else result follow break prong" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -290,7 +284,6 @@ test "while bool 2 break statements and an else" { } test "while optional 2 break statements and an else" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -310,7 +303,6 @@ test "while optional 2 break statements and an else" { } test "while error 2 break statements and an else" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO diff --git a/test/behavior/widening.zig b/test/behavior/widening.zig index ddd438d5d3..0992943fc3 100644 --- a/test/behavior/widening.zig +++ b/test/behavior/widening.zig @@ -28,7 +28,6 @@ test "integer widening u0 to u8" { } test "implicit unsigned integer to signed integer" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO From bb6b9c19e06320d8617f8858dde1ae6a7cf7fc5e Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Tue, 14 Mar 2023 19:57:42 -0400 Subject: [PATCH 128/294] x86_64: fix lowering of non-pointer optional is null --- src/arch/x86_64/CodeGen.zig | 43 ++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 12c5071462..588782d838 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -208,7 +208,7 @@ const Branch = struct { }; const StackAllocation = struct { - inst: Air.Inst.Index, + inst: ?Air.Inst.Index, /// TODO do we need size? should be determined by inst.ty.abiSize(self.target.*) size: u32, }; @@ -1109,7 +1109,7 @@ fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void { try table.ensureUnusedCapacity(self.gpa, additional_count); } -fn allocMem(self: *Self, inst: Air.Inst.Index, abi_size: u32, abi_align: u32) !u32 { +fn allocMem(self: *Self, inst: ?Air.Inst.Index, abi_size: u32, abi_align: u32) !u32 { if (abi_align > self.stack_align) self.stack_align = abi_align; // TODO find a free slot instead of always appending @@ -1142,7 +1142,14 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { } fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { - const elem_ty = self.air.typeOfIndex(inst); + return self.allocRegOrMemAdvanced(self.air.typeOfIndex(inst), inst, reg_ok); +} + +fn allocTempRegOrMem(self: *Self, elem_ty: Type, reg_ok: bool) !MCValue { + return self.allocRegOrMemAdvanced(elem_ty, null, reg_ok); +} + +fn allocRegOrMemAdvanced(self: *Self, elem_ty: Type, inst: ?Air.Inst.Index, reg_ok: bool) !MCValue { const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) orelse { const mod = self.bin_file.options.module.?; return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)}); @@ -4571,18 +4578,16 @@ fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void { }; defer if (operand_ptr_lock) |lock| self.register_manager.unlockReg(lock); - const operand: MCValue = blk: { - if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { - // The MCValue that holds the pointer can be re-used as the value. - break :blk operand_ptr; - } else { - break :blk try self.allocRegOrMem(inst, true); - } - }; const ptr_ty = self.air.typeOf(un_op); + const elem_ty = ptr_ty.childType(); + const operand = if (elem_ty.isPtrLikeOptional() and self.reuseOperand(inst, un_op, 0, operand_ptr)) + // The MCValue that holds the pointer can be re-used as the value. + operand_ptr + else + try self.allocTempRegOrMem(elem_ty, true); try self.load(operand, operand_ptr, ptr_ty); - const result = try self.isNull(inst, ptr_ty.elemType(), operand); + const result = try self.isNull(inst, elem_ty, operand); return self.finishAir(inst, result, .{ un_op, .none, .none }); } @@ -4611,15 +4616,13 @@ fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void { }; defer if (operand_ptr_lock) |lock| self.register_manager.unlockReg(lock); - const operand: MCValue = blk: { - if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { - // The MCValue that holds the pointer can be re-used as the value. - break :blk operand_ptr; - } else { - break :blk try self.allocRegOrMem(inst, true); - } - }; const ptr_ty = self.air.typeOf(un_op); + const elem_ty = ptr_ty.childType(); + const operand = if (elem_ty.isPtrLikeOptional() and self.reuseOperand(inst, un_op, 0, operand_ptr)) + // The MCValue that holds the pointer can be re-used as the value. + operand_ptr + else + try self.allocTempRegOrMem(elem_ty, true); try self.load(operand, operand_ptr, ptr_ty); const result = try self.isNonNull(inst, ptr_ty.elemType(), operand); From d14a9e82feca6467176d188d2eccf6ff51606952 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Tue, 14 Mar 2023 22:13:52 -0400 Subject: [PATCH 129/294] x86_64: use new for loop syntax --- src/arch/x86_64/CodeGen.zig | 36 ++++++++++++++---------------------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 588782d838..37f20261d5 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -184,8 +184,7 @@ const Branch = struct { _ = options; comptime assert(unused_format_string.len == 0); try writer.writeAll("Branch {\n"); - for (ctx.insts, 0..) |inst, i| { - const mcv = ctx.mcvs[i]; + for (ctx.insts, ctx.mcvs) |inst, mcv| { try writer.print(" %{d} => {}\n", .{ inst, mcv }); } try writer.writeAll("}"); @@ -3982,10 +3981,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier }; defer if (ret_reg_lock) |lock| self.register_manager.unlockReg(lock); - for (args, 0..) |arg, arg_i| { - const mc_arg = info.args[arg_i]; + for (args, info.args) |arg, info_arg| { + const mc_arg = info_arg; const arg_ty = self.air.typeOf(arg); - const arg_mcv = try self.resolveInst(args[arg_i]); + const arg_mcv = try self.resolveInst(arg); // Here we do not use setRegOrMem even though the logic is similar, because // the function call will move the stack pointer, so the offsets are different. switch (mc_arg) { @@ -4851,9 +4850,9 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { var relocs = try self.gpa.alloc(u32, items.len); defer self.gpa.free(relocs); - for (items, 0..) |item, item_i| { + for (items, relocs) |item, *reloc| { const item_mcv = try self.resolveInst(item); - relocs[item_i] = try self.genCondSwitchMir(condition_ty, condition, item_mcv); + reloc.* = try self.genCondSwitchMir(condition_ty, condition, item_mcv); } // Capture the state of register and stack allocation state so that we can revert to it. @@ -4935,11 +4934,7 @@ fn canonicaliseBranches(self: *Self, parent_branch: *Branch, canon_branch: *Bran try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, target_branch.inst_table.count()); const target_slice = target_branch.inst_table.entries.slice(); - const target_keys = target_slice.items(.key); - const target_values = target_slice.items(.value); - - for (target_keys, 0..) |target_key, target_idx| { - const target_value = target_values[target_idx]; + for (target_slice.items(.key), target_slice.items(.value)) |target_key, target_value| { const canon_mcv = if (canon_branch.inst_table.fetchSwapRemove(target_key)) |canon_entry| blk: { // The instruction's MCValue is overridden in both branches. parent_branch.inst_table.putAssumeCapacity(target_key, canon_entry.value); @@ -4969,10 +4964,7 @@ fn canonicaliseBranches(self: *Self, parent_branch: *Branch, canon_branch: *Bran } try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, canon_branch.inst_table.count()); const canon_slice = canon_branch.inst_table.entries.slice(); - const canon_keys = canon_slice.items(.key); - const canon_values = canon_slice.items(.value); - for (canon_keys, 0..) |canon_key, canon_idx| { - const canon_value = canon_values[canon_idx]; + for (canon_slice.items(.key), canon_slice.items(.value)) |canon_key, canon_value| { // We already deleted the items from this table that matched the target_branch. // So these are all instructions that are only overridden in the canon branch. parent_branch.inst_table.putAssumeCapacity(canon_key, canon_value); @@ -6446,7 +6438,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { else => 0, }; - for (param_types, 0..) |ty, i| { + for (param_types, result.args, 0..) |ty, *arg, i| { assert(ty.hasRuntimeBits()); const classes: []const abi.Class = switch (self.target.os.tag) { @@ -6459,7 +6451,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { switch (classes[0]) { .integer => blk: { if (i >= abi.getCAbiIntParamRegs(self.target.*).len) break :blk; // fallthrough - result.args[i] = .{ .register = abi.getCAbiIntParamRegs(self.target.*)[i] }; + arg.* = .{ .register = abi.getCAbiIntParamRegs(self.target.*)[i] }; continue; }, .memory => {}, // fallthrough @@ -6471,7 +6463,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { const param_size = @intCast(u32, ty.abiSize(self.target.*)); const param_align = @intCast(u32, ty.abiAlignment(self.target.*)); const offset = mem.alignForwardGeneric(u32, next_stack_offset + param_size, param_align); - result.args[i] = .{ .stack_offset = @intCast(i32, offset) }; + arg.* = .{ .stack_offset = @intCast(i32, offset) }; next_stack_offset = offset; } @@ -6522,15 +6514,15 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { else => 0, }; - for (param_types, 0..) |ty, i| { + for (param_types, result.args) |ty, *arg| { if (!ty.hasRuntimeBits()) { - result.args[i] = .{ .none = {} }; + arg.* = .{ .none = {} }; continue; } const param_size = @intCast(u32, ty.abiSize(self.target.*)); const param_align = @intCast(u32, ty.abiAlignment(self.target.*)); const offset = mem.alignForwardGeneric(u32, next_stack_offset + param_size, param_align); - result.args[i] = .{ .stack_offset = @intCast(i32, offset) }; + arg.* = .{ .stack_offset = @intCast(i32, offset) }; next_stack_offset = offset; } From 238615984064f52b6ff31b5750824a9c764fdb15 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Tue, 14 Mar 2023 22:17:19 -0400 Subject: [PATCH 130/294] x86_64: use short union init --- src/arch/x86_64/CodeGen.zig | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 37f20261d5..1be93accc7 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -1037,7 +1037,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { fn processDeath(self: *Self, inst: Air.Inst.Index) void { const air_tags = self.air.instructions.items(.tag); if (air_tags[inst] == .constant) return; // Constants are immortal. - log.debug("%{d} => {}", .{ inst, MCValue{ .dead = {} } }); + log.debug("%{d} => {}", .{ inst, MCValue.dead }); // When editing this function, note that the logic must synchronize with `reuseOperand`. const prev_value = self.getResolvedInstValue(inst); const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; @@ -4729,7 +4729,7 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) !void { // break instruction will choose a MCValue for the block result and overwrite // this field. Following break instructions will use that MCValue to put their // block results. - .mcv = MCValue{ .none = {} }, + .mcv = .none, }); defer self.blocks.getPtr(inst).?.relocs.deinit(self.gpa); @@ -5145,9 +5145,9 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { const reg_name = output[2 .. output.len - 1]; const reg = parseRegName(reg_name) orelse return self.fail("unrecognized register: '{s}'", .{reg_name}); - break :result MCValue{ .register = reg }; + break :result .{ .register = reg }; } else { - break :result MCValue{ .none = {} }; + break :result .none; } }; @@ -6289,7 +6289,7 @@ fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { if (ref_int < Air.Inst.Ref.typed_value_map.len) { const tv = Air.Inst.Ref.typed_value_map[ref_int]; if (!tv.ty.hasRuntimeBitsIgnoreComptime() and !tv.ty.isError()) { - return MCValue{ .none = {} }; + return .none; } return self.genTypedValue(tv); } @@ -6297,7 +6297,7 @@ fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { // If the type has no codegen bits, no need to store it. const inst_ty = self.air.typeOf(inst); if (!inst_ty.hasRuntimeBitsIgnoreComptime() and !inst_ty.isError()) - return MCValue{ .none = {} }; + return .none; const inst_index = @intCast(Air.Inst.Index, ref_int - Air.Inst.Ref.typed_value_map.len); switch (self.air.instructions.items(.tag)[inst_index]) { @@ -6406,7 +6406,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { switch (cc) { .Naked => { assert(result.args.len == 0); - result.return_value = .{ .unreach = {} }; + result.return_value = .unreach; result.stack_byte_count = 0; result.stack_align = 1; return result; @@ -6414,10 +6414,10 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { .C => { // Return values if (ret_ty.zigTypeTag() == .NoReturn) { - result.return_value = .{ .unreach = {} }; + result.return_value = .unreach; } else if (!ret_ty.hasRuntimeBitsIgnoreComptime() and !ret_ty.isError()) { // TODO: is this even possible for C calling convention? - result.return_value = .{ .none = {} }; + result.return_value = .none; } else { const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*)); if (ret_ty_size == 0) { @@ -6489,9 +6489,9 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { .Unspecified => { // Return values if (ret_ty.zigTypeTag() == .NoReturn) { - result.return_value = .{ .unreach = {} }; + result.return_value = .unreach; } else if (!ret_ty.hasRuntimeBitsIgnoreComptime() and !ret_ty.isError()) { - result.return_value = .{ .none = {} }; + result.return_value = .none; } else { const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*)); if (ret_ty_size == 0) { @@ -6516,7 +6516,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { for (param_types, result.args) |ty, *arg| { if (!ty.hasRuntimeBits()) { - arg.* = .{ .none = {} }; + arg.* = .none; continue; } const param_size = @intCast(u32, ty.abiSize(self.target.*)); From 05b12e677939effa9da147008071908a2a93fe35 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Tue, 14 Mar 2023 22:32:00 -0400 Subject: [PATCH 131/294] x86_64: handle duplicate prong deaths --- src/arch/x86_64/CodeGen.zig | 13 ++++++------- test/behavior/inline_switch.zig | 1 - test/behavior/union.zig | 2 -- test/behavior/union_with_members.zig | 1 - 4 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 1be93accc7..bf1d9f0eb0 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -1037,9 +1037,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { fn processDeath(self: *Self, inst: Air.Inst.Index) void { const air_tags = self.air.instructions.items(.tag); if (air_tags[inst] == .constant) return; // Constants are immortal. + const prev_value = self.getResolvedInstValue(inst) orelse return; log.debug("%{d} => {}", .{ inst, MCValue.dead }); // When editing this function, note that the logic must synchronize with `reuseOperand`. - const prev_value = self.getResolvedInstValue(inst); const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; branch.inst_table.putAssumeCapacity(inst, .dead); switch (prev_value) { @@ -1225,7 +1225,7 @@ fn revertState(self: *Self, state: State) void { pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void { const stack_mcv = try self.allocRegOrMem(inst, false); log.debug("spilling %{d} to stack mcv {any}", .{ inst, stack_mcv }); - const reg_mcv = self.getResolvedInstValue(inst); + const reg_mcv = self.getResolvedInstValue(inst).?; switch (reg_mcv) { .register => |other| { assert(reg.to64() == other.to64()); @@ -1242,7 +1242,7 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void pub fn spillEflagsIfOccupied(self: *Self) !void { if (self.eflags_inst) |inst_to_save| { - const mcv = self.getResolvedInstValue(inst_to_save); + const mcv = self.getResolvedInstValue(inst_to_save).?; const new_mcv = switch (mcv) { .register_overflow => try self.allocRegOrMem(inst_to_save, false), .eflags => try self.allocRegOrMem(inst_to_save, true), @@ -6315,18 +6315,17 @@ fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { return gop.value_ptr.*; }, .const_ty => unreachable, - else => return self.getResolvedInstValue(inst_index), + else => return self.getResolvedInstValue(inst_index).?, } } -fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue { +fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) ?MCValue { // Treat each stack item as a "layer" on top of the previous one. var i: usize = self.branch_stack.items.len; while (true) { i -= 1; if (self.branch_stack.items[i].inst_table.get(inst)) |mcv| { - assert(mcv != .dead); - return mcv; + return if (mcv != .dead) mcv else null; } } } diff --git a/test/behavior/inline_switch.zig b/test/behavior/inline_switch.zig index 90e8b36284..dcd603c94f 100644 --- a/test/behavior/inline_switch.zig +++ b/test/behavior/inline_switch.zig @@ -46,7 +46,6 @@ const U = union(E) { a: void, b: u2, c: u3, d: u4 }; test "inline switch unions" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO var x: U = .a; diff --git a/test/behavior/union.zig b/test/behavior/union.zig index 03c47b09d6..9b49f8bf47 100644 --- a/test/behavior/union.zig +++ b/test/behavior/union.zig @@ -214,7 +214,6 @@ const Payload = union(Letter) { }; test "union with specified enum tag" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -224,7 +223,6 @@ test "union with specified enum tag" { } test "packed union generates correctly aligned type" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO diff --git a/test/behavior/union_with_members.zig b/test/behavior/union_with_members.zig index 0cb06a81ab..8e9c2db475 100644 --- a/test/behavior/union_with_members.zig +++ b/test/behavior/union_with_members.zig @@ -18,7 +18,6 @@ const ET = union(enum) { test "enum with members" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO From ba9d93dc9fd341f4dc08082f894fcbc1060cdcad Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Tue, 14 Mar 2023 23:49:24 -0400 Subject: [PATCH 132/294] x86_64: implement more binary immediate combinations --- src/arch/x86_64/CodeGen.zig | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index bf1d9f0eb0..3e89779ca7 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -3637,12 +3637,35 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu ), }, .immediate => |imm| { - // TODO - try self.asmRegisterImmediate( - mir_tag, - registerAlias(dst_reg, abi_size), - Immediate.u(@intCast(u32, imm)), - ); + switch (abi_size) { + 0 => unreachable, + 1...4 => { + try self.asmRegisterImmediate( + mir_tag, + registerAlias(dst_reg, abi_size), + Immediate.u(@intCast(u32, imm)), + ); + }, + 5...8 => { + if (math.cast(i32, @bitCast(i64, imm))) |small| { + try self.asmRegisterImmediate( + mir_tag, + registerAlias(dst_reg, abi_size), + Immediate.s(small), + ); + } else { + const tmp_reg = try self.register_manager.allocReg(null, gp); + const tmp_alias = registerAlias(tmp_reg, abi_size); + try self.asmRegisterImmediate(.mov, tmp_alias, Immediate.u(imm)); + try self.asmRegisterRegister( + mir_tag, + registerAlias(dst_reg, abi_size), + tmp_alias, + ); + } + }, + else => return self.fail("TODO getBinOpMir implement large immediate ABI", .{}), + } }, .memory, .linker_load, From d70955b0df92fdd0fdf3680f7f297a4cc676ee5a Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Tue, 14 Mar 2023 23:49:45 -0400 Subject: [PATCH 133/294] x86_64: turn packed struct crashes into compile errors --- src/arch/x86_64/CodeGen.zig | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 3e89779ca7..5dfce901f7 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -3053,6 +3053,9 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde const mcv = try self.resolveInst(operand); const ptr_ty = self.air.typeOf(operand); const struct_ty = ptr_ty.childType(); + if (struct_ty.zigTypeTag() == .Struct and struct_ty.containerLayout() == .Packed) { + return self.fail("TODO structFieldPtr implement packed structs", .{}); + } const struct_field_offset = @intCast(u32, struct_ty.structFieldOffset(index, self.target.*)); const dst_mcv: MCValue = result: { @@ -3116,6 +3119,9 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { const mcv = try self.resolveInst(operand); const struct_ty = self.air.typeOf(operand); + if (struct_ty.zigTypeTag() == .Struct and struct_ty.containerLayout() == .Packed) { + return self.fail("TODO airStructFieldVal implement packed structs", .{}); + } const struct_field_offset = struct_ty.structFieldOffset(index, self.target.*); const struct_field_ty = struct_ty.structFieldType(index); @@ -6242,6 +6248,9 @@ fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void { if (self.liveness.isUnused(inst)) break :res MCValue.dead; switch (result_ty.zigTypeTag()) { .Struct => { + if (result_ty.containerLayout() == .Packed) { + return self.fail("TODO airAggregateInit implement packed structs", .{}); + } const stack_offset = @intCast(i32, try self.allocMem(inst, abi_size, abi_align)); for (elements, 0..) |elem, elem_i| { if (result_ty.structFieldValueComptime(elem_i) != null) continue; // comptime elem From c6a895f6671d30e2db77ae4f4e0c2d9e40624787 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 13 Feb 2023 13:10:20 -0700 Subject: [PATCH 134/294] extract some logic from std.Build to build_runner.zig --- lib/build_runner.zig | 87 ++++++++++++++++++++++++++++++++------------ lib/std/Build.zig | 73 ++++++------------------------------- 2 files changed, 75 insertions(+), 85 deletions(-) diff --git a/lib/build_runner.zig b/lib/build_runner.zig index 64421d1031..f9a2bbd40a 100644 --- a/lib/build_runner.zig +++ b/lib/build_runner.zig @@ -92,23 +92,23 @@ pub fn main() !void { // before arg parsing, check for the NO_COLOR environment variable // if it exists, default the color setting to .off // explicit --color arguments will still override this setting. - builder.color = if (std.process.hasEnvVarConstant("NO_COLOR")) .off else .auto; + builder.color = if (process.hasEnvVarConstant("NO_COLOR")) .off else .auto; while (nextArg(args, &arg_idx)) |arg| { if (mem.startsWith(u8, arg, "-D")) { const option_contents = arg[2..]; if (option_contents.len == 0) { std.debug.print("Expected option name after '-D'\n\n", .{}); - return usageAndErr(builder, false, stderr_stream); + usageAndErr(builder, false, stderr_stream); } if (mem.indexOfScalar(u8, option_contents, '=')) |name_end| { const option_name = option_contents[0..name_end]; const option_value = option_contents[name_end + 1 ..]; if (try builder.addUserInputOption(option_name, option_value)) - return usageAndErr(builder, false, stderr_stream); + usageAndErr(builder, false, stderr_stream); } else { if (try builder.addUserInputFlag(option_contents)) - return usageAndErr(builder, false, stderr_stream); + usageAndErr(builder, false, stderr_stream); } } else if (mem.startsWith(u8, arg, "-")) { if (mem.eql(u8, arg, "--verbose")) { @@ -118,61 +118,61 @@ pub fn main() !void { } else if (mem.eql(u8, arg, "-p") or mem.eql(u8, arg, "--prefix")) { install_prefix = nextArg(args, &arg_idx) orelse { std.debug.print("Expected argument after {s}\n\n", .{arg}); - return usageAndErr(builder, false, stderr_stream); + usageAndErr(builder, false, stderr_stream); }; } else if (mem.eql(u8, arg, "-l") or mem.eql(u8, arg, "--list-steps")) { return steps(builder, false, stdout_stream); } else if (mem.eql(u8, arg, "--prefix-lib-dir")) { dir_list.lib_dir = nextArg(args, &arg_idx) orelse { std.debug.print("Expected argument after {s}\n\n", .{arg}); - return usageAndErr(builder, false, stderr_stream); + usageAndErr(builder, false, stderr_stream); }; } else if (mem.eql(u8, arg, "--prefix-exe-dir")) { dir_list.exe_dir = nextArg(args, &arg_idx) orelse { std.debug.print("Expected argument after {s}\n\n", .{arg}); - return usageAndErr(builder, false, stderr_stream); + usageAndErr(builder, false, stderr_stream); }; } else if (mem.eql(u8, arg, "--prefix-include-dir")) { dir_list.include_dir = nextArg(args, &arg_idx) orelse { std.debug.print("Expected argument after {s}\n\n", .{arg}); - return usageAndErr(builder, false, stderr_stream); + usageAndErr(builder, false, stderr_stream); }; } else if (mem.eql(u8, arg, "--sysroot")) { const sysroot = nextArg(args, &arg_idx) orelse { std.debug.print("Expected argument after --sysroot\n\n", .{}); - return usageAndErr(builder, false, stderr_stream); + usageAndErr(builder, false, stderr_stream); }; builder.sysroot = sysroot; } else if (mem.eql(u8, arg, "--search-prefix")) { const search_prefix = nextArg(args, &arg_idx) orelse { std.debug.print("Expected argument after --search-prefix\n\n", .{}); - return usageAndErr(builder, false, stderr_stream); + usageAndErr(builder, false, stderr_stream); }; builder.addSearchPrefix(search_prefix); } else if (mem.eql(u8, arg, "--libc")) { const libc_file = nextArg(args, &arg_idx) orelse { std.debug.print("Expected argument after --libc\n\n", .{}); - return usageAndErr(builder, false, stderr_stream); + usageAndErr(builder, false, stderr_stream); }; builder.libc_file = libc_file; } else if (mem.eql(u8, arg, "--color")) { const next_arg = nextArg(args, &arg_idx) orelse { std.debug.print("expected [auto|on|off] after --color", .{}); - return usageAndErr(builder, false, stderr_stream); + usageAndErr(builder, false, stderr_stream); }; builder.color = std.meta.stringToEnum(@TypeOf(builder.color), next_arg) orelse { std.debug.print("expected [auto|on|off] after --color, found '{s}'", .{next_arg}); - return usageAndErr(builder, false, stderr_stream); + usageAndErr(builder, false, stderr_stream); }; } else if (mem.eql(u8, arg, "--zig-lib-dir")) { builder.zig_lib_dir = nextArg(args, &arg_idx) orelse { std.debug.print("Expected argument after --zig-lib-dir\n\n", .{}); - return usageAndErr(builder, false, stderr_stream); + usageAndErr(builder, false, stderr_stream); }; } else if (mem.eql(u8, arg, "--debug-log")) { const next_arg = nextArg(args, &arg_idx) orelse { std.debug.print("Expected argument after {s}\n\n", .{arg}); - return usageAndErr(builder, false, stderr_stream); + usageAndErr(builder, false, stderr_stream); }; try debug_log_scopes.append(next_arg); } else if (mem.eql(u8, arg, "--debug-compile-errors")) { @@ -180,7 +180,7 @@ pub fn main() !void { } else if (mem.eql(u8, arg, "--glibc-runtimes")) { builder.glibc_runtimes_dir = nextArg(args, &arg_idx) orelse { std.debug.print("Expected argument after --glibc-runtimes\n\n", .{}); - return usageAndErr(builder, false, stderr_stream); + usageAndErr(builder, false, stderr_stream); }; } else if (mem.eql(u8, arg, "--verbose-link")) { builder.verbose_link = true; @@ -231,7 +231,7 @@ pub fn main() !void { break; } else { std.debug.print("Unrecognized argument: {s}\n\n", .{arg}); - return usageAndErr(builder, false, stderr_stream); + usageAndErr(builder, false, stderr_stream); } } else { try targets.append(arg); @@ -243,13 +243,10 @@ pub fn main() !void { try builder.runBuild(root); if (builder.validateUserInputDidItFail()) - return usageAndErr(builder, true, stderr_stream); + usageAndErr(builder, true, stderr_stream); - builder.make(targets.items) catch |err| { + make(builder, targets.items) catch |err| { switch (err) { - error.InvalidStepName => { - return usageAndErr(builder, true, stderr_stream); - }, error.UncleanExit => process.exit(1), // This error is intended to indicate that the step has already // logged an error message and so printing the error return trace @@ -261,6 +258,48 @@ pub fn main() !void { }; } +fn make(b: *std.Build, step_names: []const []const u8) !void { + var wanted_steps = ArrayList(*std.Build.Step).init(b.allocator); + defer wanted_steps.deinit(); + + if (step_names.len == 0) { + try wanted_steps.append(b.default_step); + } else { + for (step_names) |step_name| { + const s = b.top_level_steps.get(step_name) orelse { + std.debug.print("no step named '{s}'. Access the help menu with 'zig build -h'\n", .{step_name}); + process.exit(1); + }; + try wanted_steps.append(&s.step); + } + } + + for (wanted_steps.items) |s| { + try makeOneStep(b, s); + } +} + +fn makeOneStep(b: *std.Build, s: *std.Build.Step) anyerror!void { + if (s.loop_flag) { + std.debug.print("dependency loop detected:\n {s}\n", .{s.name}); + return error.DependencyLoopDetected; + } + s.loop_flag = true; + + for (s.dependencies.items) |dep| { + makeOneStep(b, dep) catch |err| { + if (err == error.DependencyLoopDetected) { + std.debug.print(" {s}\n", .{s.name}); + } + return err; + }; + } + + s.loop_flag = false; + + try s.make(); +} + fn steps(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !void { // run the build script to collect the options if (!already_ran_build) { @@ -269,7 +308,7 @@ fn steps(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi } const allocator = builder.allocator; - for (builder.top_level_steps.items) |top_level_step| { + for (builder.top_level_steps.values()) |top_level_step| { const name = if (&top_level_step.step == builder.default_step) try fmt.allocPrint(allocator, "{s} (default)", .{top_level_step.step.name}) else @@ -374,7 +413,7 @@ fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi ); } -fn usageAndErr(builder: *std.Build, already_ran_build: bool, out_stream: anytype) void { +fn usageAndErr(builder: *std.Build, already_ran_build: bool, out_stream: anytype) noreturn { usage(builder, already_ran_build, out_stream) catch {}; process.exit(1); } diff --git a/lib/std/Build.zig b/lib/std/Build.zig index 120196f972..168180e383 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -67,7 +67,7 @@ invalid_user_input: bool, zig_exe: []const u8, default_step: *Step, env_map: *EnvMap, -top_level_steps: ArrayList(*TopLevelStep), +top_level_steps: std.StringArrayHashMapUnmanaged(*TopLevelStep), install_prefix: []const u8, dest_dir: ?[]const u8, lib_dir: []const u8, @@ -217,7 +217,7 @@ pub fn create( .user_input_options = UserInputOptionsMap.init(allocator), .available_options_map = AvailableOptionsMap.init(allocator), .available_options_list = ArrayList(AvailableOption).init(allocator), - .top_level_steps = ArrayList(*TopLevelStep).init(allocator), + .top_level_steps = .{}, .default_step = undefined, .env_map = env_map, .search_prefixes = ArrayList([]const u8).init(allocator), @@ -241,8 +241,8 @@ pub fn create( .host = host, .modules = std.StringArrayHashMap(*Module).init(allocator), }; - try self.top_level_steps.append(&self.install_tls); - try self.top_level_steps.append(&self.uninstall_tls); + try self.top_level_steps.put(allocator, self.install_tls.step.name, &self.install_tls); + try self.top_level_steps.put(allocator, self.uninstall_tls.step.name, &self.uninstall_tls); self.default_step = &self.install_tls.step; return self; } @@ -288,7 +288,7 @@ fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: Cache.Direc .zig_exe = parent.zig_exe, .default_step = undefined, .env_map = parent.env_map, - .top_level_steps = ArrayList(*TopLevelStep).init(allocator), + .top_level_steps = .{}, .install_prefix = undefined, .dest_dir = parent.dest_dir, .lib_dir = parent.lib_dir, @@ -316,8 +316,8 @@ fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: Cache.Direc .dep_prefix = parent.fmt("{s}{s}.", .{ parent.dep_prefix, dep_name }), .modules = std.StringArrayHashMap(*Module).init(allocator), }; - try child.top_level_steps.append(&child.install_tls); - try child.top_level_steps.append(&child.uninstall_tls); + try child.top_level_steps.put(allocator, child.install_tls.step.name, &child.install_tls); + try child.top_level_steps.put(allocator, child.uninstall_tls.step.name, &child.uninstall_tls); child.default_step = &child.install_tls.step; return child; } @@ -389,10 +389,10 @@ fn applyArgs(b: *Build, args: anytype) !void { b.resolveInstallPrefix(install_prefix, .{}); } -pub fn destroy(self: *Build) void { - self.env_map.deinit(); - self.top_level_steps.deinit(); - self.allocator.destroy(self); +pub fn destroy(b: *Build) void { + b.env_map.deinit(); + b.top_level_steps.deinit(b.allocator); + b.allocator.destroy(b); } /// This function is intended to be called by lib/build_runner.zig, not a build.zig file. @@ -698,24 +698,6 @@ pub fn addTranslateC(self: *Build, options: TranslateCStep.Options) *TranslateCS return TranslateCStep.create(self, options); } -pub fn make(self: *Build, step_names: []const []const u8) !void { - var wanted_steps = ArrayList(*Step).init(self.allocator); - defer wanted_steps.deinit(); - - if (step_names.len == 0) { - try wanted_steps.append(self.default_step); - } else { - for (step_names) |step_name| { - const s = try self.getTopLevelStepByName(step_name); - try wanted_steps.append(s); - } - } - - for (wanted_steps.items) |s| { - try self.makeOneStep(s); - } -} - pub fn getInstallStep(self: *Build) *Step { return &self.install_tls.step; } @@ -739,37 +721,6 @@ fn makeUninstall(uninstall_step: *Step) anyerror!void { // TODO remove empty directories } -fn makeOneStep(self: *Build, s: *Step) anyerror!void { - if (s.loop_flag) { - log.err("Dependency loop detected:\n {s}", .{s.name}); - return error.DependencyLoopDetected; - } - s.loop_flag = true; - - for (s.dependencies.items) |dep| { - self.makeOneStep(dep) catch |err| { - if (err == error.DependencyLoopDetected) { - log.err(" {s}", .{s.name}); - } - return err; - }; - } - - s.loop_flag = false; - - try s.make(); -} - -fn getTopLevelStepByName(self: *Build, name: []const u8) !*Step { - for (self.top_level_steps.items) |top_level_step| { - if (mem.eql(u8, top_level_step.step.name, name)) { - return &top_level_step.step; - } - } - log.err("Cannot run step '{s}' because it does not exist", .{name}); - return error.InvalidStepName; -} - pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_raw: []const u8) ?T { const name = self.dupe(name_raw); const description = self.dupe(description_raw); @@ -910,7 +861,7 @@ pub fn step(self: *Build, name: []const u8, description: []const u8) *Step { .step = Step.initNoOp(.top_level, name, self.allocator), .description = self.dupe(description), }; - self.top_level_steps.append(step_info) catch @panic("OOM"); + self.top_level_steps.put(self.allocator, step_info.step.name, step_info) catch @panic("OOM"); return &step_info.step; } From 0b744d7d670d00fa865ebd17847cbdc1a909ba70 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 13 Feb 2023 13:28:18 -0700 Subject: [PATCH 135/294] build runner: untangle dependency loop checking from making --- lib/build_runner.zig | 23 ++++++++++++++++++----- lib/std/Build/Step.zig | 5 +++-- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/lib/build_runner.zig b/lib/build_runner.zig index f9a2bbd40a..cc5c9325ae 100644 --- a/lib/build_runner.zig +++ b/lib/build_runner.zig @@ -274,20 +274,27 @@ fn make(b: *std.Build, step_names: []const []const u8) !void { } } + for (wanted_steps.items) |s| { + checkForDependencyLoop(b, s) catch |err| switch (err) { + error.DependencyLoopDetected => return error.UncleanExit, + else => |e| return e, + }; + } + for (wanted_steps.items) |s| { try makeOneStep(b, s); } } -fn makeOneStep(b: *std.Build, s: *std.Build.Step) anyerror!void { - if (s.loop_flag) { +fn checkForDependencyLoop(b: *std.Build, s: *std.Build.Step) !void { + if (s.loop_tag == .started) { std.debug.print("dependency loop detected:\n {s}\n", .{s.name}); return error.DependencyLoopDetected; } - s.loop_flag = true; + s.loop_tag = .started; for (s.dependencies.items) |dep| { - makeOneStep(b, dep) catch |err| { + checkForDependencyLoop(b, dep) catch |err| { if (err == error.DependencyLoopDetected) { std.debug.print(" {s}\n", .{s.name}); } @@ -295,7 +302,13 @@ fn makeOneStep(b: *std.Build, s: *std.Build.Step) anyerror!void { }; } - s.loop_flag = false; + s.loop_tag = .done; +} + +fn makeOneStep(b: *std.Build, s: *std.Build.Step) anyerror!void { + for (s.dependencies.items) |dep| { + try makeOneStep(b, dep); + } try s.make(); } diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig index 82c39ac2cc..16b1640e70 100644 --- a/lib/std/Build/Step.zig +++ b/lib/std/Build/Step.zig @@ -2,7 +2,8 @@ id: Id, name: []const u8, makeFn: *const fn (self: *Step) anyerror!void, dependencies: std.ArrayList(*Step), -loop_flag: bool, +/// Used only during a pre-check for dependency loops. +loop_tag: enum { unstarted, started, done }, done_flag: bool, pub const Id = enum { @@ -60,7 +61,7 @@ pub fn init( .name = allocator.dupe(u8, name) catch @panic("OOM"), .makeFn = makeFn, .dependencies = std.ArrayList(*Step).init(allocator), - .loop_flag = false, + .loop_tag = .unstarted, .done_flag = false, }; } From 5b90fa05a4e5b155f25319713acfc67ad9516c69 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 13 Feb 2023 13:39:06 -0700 Subject: [PATCH 136/294] extract ThreadPool and WaitGroup from compiler to std lib --- CMakeLists.txt | 4 ++-- lib/std/Thread.zig | 2 ++ src/ThreadPool.zig => lib/std/Thread/Pool.zig | 16 ++++++++-------- {src => lib/std/Thread}/WaitGroup.zig | 0 src/Compilation.zig | 4 ++-- src/Package.zig | 4 ++-- src/link/MachO/CodeSignature.zig | 4 ++-- src/main.zig | 2 +- src/test.zig | 4 ++-- 9 files changed, 21 insertions(+), 19 deletions(-) rename src/ThreadPool.zig => lib/std/Thread/Pool.zig (90%) rename {src => lib/std/Thread}/WaitGroup.zig (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5afea9354e..c77c66add4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -506,7 +506,9 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/lib/std/Thread.zig" "${CMAKE_SOURCE_DIR}/lib/std/Thread/Futex.zig" "${CMAKE_SOURCE_DIR}/lib/std/Thread/Mutex.zig" + "${CMAKE_SOURCE_DIR}/lib/std/Thread/Pool.zig" "${CMAKE_SOURCE_DIR}/lib/std/Thread/ResetEvent.zig" + "${CMAKE_SOURCE_DIR}/lib/std/Thread/WaitGroup.zig" "${CMAKE_SOURCE_DIR}/lib/std/time.zig" "${CMAKE_SOURCE_DIR}/lib/std/treap.zig" "${CMAKE_SOURCE_DIR}/lib/std/unicode.zig" @@ -530,9 +532,7 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/src/Package.zig" "${CMAKE_SOURCE_DIR}/src/RangeSet.zig" "${CMAKE_SOURCE_DIR}/src/Sema.zig" - "${CMAKE_SOURCE_DIR}/src/ThreadPool.zig" "${CMAKE_SOURCE_DIR}/src/TypedValue.zig" - "${CMAKE_SOURCE_DIR}/src/WaitGroup.zig" "${CMAKE_SOURCE_DIR}/src/Zir.zig" "${CMAKE_SOURCE_DIR}/src/arch/aarch64/CodeGen.zig" "${CMAKE_SOURCE_DIR}/src/arch/aarch64/Emit.zig" diff --git a/lib/std/Thread.zig b/lib/std/Thread.zig index 27f7fa5030..e3345e4a42 100644 --- a/lib/std/Thread.zig +++ b/lib/std/Thread.zig @@ -16,6 +16,8 @@ pub const Mutex = @import("Thread/Mutex.zig"); pub const Semaphore = @import("Thread/Semaphore.zig"); pub const Condition = @import("Thread/Condition.zig"); pub const RwLock = @import("Thread/RwLock.zig"); +pub const Pool = @import("Thread/Pool.zig"); +pub const WaitGroup = @import("Thread/WaitGroup.zig"); pub const use_pthreads = target.os.tag != .windows and target.os.tag != .wasi and builtin.link_libc; const is_gnu = target.abi.isGnu(); diff --git a/src/ThreadPool.zig b/lib/std/Thread/Pool.zig similarity index 90% rename from src/ThreadPool.zig rename to lib/std/Thread/Pool.zig index fde5ed27db..930befbac5 100644 --- a/src/ThreadPool.zig +++ b/lib/std/Thread/Pool.zig @@ -1,6 +1,6 @@ const std = @import("std"); const builtin = @import("builtin"); -const ThreadPool = @This(); +const Pool = @This(); const WaitGroup = @import("WaitGroup.zig"); mutex: std.Thread.Mutex = .{}, @@ -17,7 +17,7 @@ const Runnable = struct { const RunProto = *const fn (*Runnable) void; -pub fn init(pool: *ThreadPool, allocator: std.mem.Allocator) !void { +pub fn init(pool: *Pool, allocator: std.mem.Allocator) !void { pool.* = .{ .allocator = allocator, .threads = &[_]std.Thread{}, @@ -41,12 +41,12 @@ pub fn init(pool: *ThreadPool, allocator: std.mem.Allocator) !void { } } -pub fn deinit(pool: *ThreadPool) void { +pub fn deinit(pool: *Pool) void { pool.join(pool.threads.len); // kill and join all threads. pool.* = undefined; } -fn join(pool: *ThreadPool, spawned: usize) void { +fn join(pool: *Pool, spawned: usize) void { if (builtin.single_threaded) { return; } @@ -69,7 +69,7 @@ fn join(pool: *ThreadPool, spawned: usize) void { pool.allocator.free(pool.threads); } -pub fn spawn(pool: *ThreadPool, comptime func: anytype, args: anytype) !void { +pub fn spawn(pool: *Pool, comptime func: anytype, args: anytype) !void { if (builtin.single_threaded) { @call(.auto, func, args); return; @@ -78,7 +78,7 @@ pub fn spawn(pool: *ThreadPool, comptime func: anytype, args: anytype) !void { const Args = @TypeOf(args); const Closure = struct { arguments: Args, - pool: *ThreadPool, + pool: *Pool, run_node: RunQueue.Node = .{ .data = .{ .runFn = runFn } }, fn runFn(runnable: *Runnable) void { @@ -112,7 +112,7 @@ pub fn spawn(pool: *ThreadPool, comptime func: anytype, args: anytype) !void { pool.cond.signal(); } -fn worker(pool: *ThreadPool) void { +fn worker(pool: *Pool) void { pool.mutex.lock(); defer pool.mutex.unlock(); @@ -135,7 +135,7 @@ fn worker(pool: *ThreadPool) void { } } -pub fn waitAndWork(pool: *ThreadPool, wait_group: *WaitGroup) void { +pub fn waitAndWork(pool: *Pool, wait_group: *WaitGroup) void { while (!wait_group.isDone()) { if (blk: { pool.mutex.lock(); diff --git a/src/WaitGroup.zig b/lib/std/Thread/WaitGroup.zig similarity index 100% rename from src/WaitGroup.zig rename to lib/std/Thread/WaitGroup.zig diff --git a/src/Compilation.zig b/src/Compilation.zig index de433a6800..63dd229ec5 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -7,6 +7,8 @@ const Allocator = std.mem.Allocator; const assert = std.debug.assert; const log = std.log.scoped(.compilation); const Target = std.Target; +const ThreadPool = std.Thread.Pool; +const WaitGroup = std.Thread.WaitGroup; const Value = @import("value.zig").Value; const Type = @import("type.zig").Type; @@ -30,8 +32,6 @@ const Cache = std.Build.Cache; const translate_c = @import("translate_c.zig"); const clang = @import("clang.zig"); const c_codegen = @import("codegen/c.zig"); -const ThreadPool = @import("ThreadPool.zig"); -const WaitGroup = @import("WaitGroup.zig"); const libtsan = @import("libtsan.zig"); const Zir = @import("Zir.zig"); const Autodoc = @import("Autodoc.zig"); diff --git a/src/Package.zig b/src/Package.zig index c238d3d567..87d52197bd 100644 --- a/src/Package.zig +++ b/src/Package.zig @@ -8,11 +8,11 @@ const Allocator = mem.Allocator; const assert = std.debug.assert; const log = std.log.scoped(.package); const main = @import("main.zig"); +const ThreadPool = std.Thread.Pool; +const WaitGroup = std.Thread.WaitGroup; const Compilation = @import("Compilation.zig"); const Module = @import("Module.zig"); -const ThreadPool = @import("ThreadPool.zig"); -const WaitGroup = @import("WaitGroup.zig"); const Cache = std.Build.Cache; const build_options = @import("build_options"); const Manifest = @import("Manifest.zig"); diff --git a/src/link/MachO/CodeSignature.zig b/src/link/MachO/CodeSignature.zig index 8bc00d9181..6d1cd7b536 100644 --- a/src/link/MachO/CodeSignature.zig +++ b/src/link/MachO/CodeSignature.zig @@ -7,12 +7,12 @@ const log = std.log.scoped(.link); const macho = std.macho; const mem = std.mem; const testing = std.testing; +const ThreadPool = std.Thread.Pool; +const WaitGroup = std.Thread.WaitGroup; const Allocator = mem.Allocator; const Compilation = @import("../../Compilation.zig"); const Sha256 = std.crypto.hash.sha2.Sha256; -const ThreadPool = @import("../../ThreadPool.zig"); -const WaitGroup = @import("../../WaitGroup.zig"); const hash_size = Sha256.digest_length; diff --git a/src/main.zig b/src/main.zig index 95cfca1463..dd0faa628c 100644 --- a/src/main.zig +++ b/src/main.zig @@ -9,6 +9,7 @@ const Allocator = mem.Allocator; const ArrayList = std.ArrayList; const Ast = std.zig.Ast; const warn = std.log.warn; +const ThreadPool = std.Thread.Pool; const tracy = @import("tracy.zig"); const Compilation = @import("Compilation.zig"); @@ -22,7 +23,6 @@ const translate_c = @import("translate_c.zig"); const clang = @import("clang.zig"); const Cache = std.Build.Cache; const target_util = @import("target.zig"); -const ThreadPool = @import("ThreadPool.zig"); const crash_report = @import("crash_report.zig"); pub const std_options = struct { diff --git a/src/test.zig b/src/test.zig index 61cdb705e3..ce87742606 100644 --- a/src/test.zig +++ b/src/test.zig @@ -4,14 +4,14 @@ const Allocator = std.mem.Allocator; const CrossTarget = std.zig.CrossTarget; const print = std.debug.print; const assert = std.debug.assert; +const ThreadPool = std.Thread.Pool; +const WaitGroup = std.Thread.WaitGroup; const link = @import("link.zig"); const Compilation = @import("Compilation.zig"); const Package = @import("Package.zig"); const introspect = @import("introspect.zig"); const build_options = @import("build_options"); -const ThreadPool = @import("ThreadPool.zig"); -const WaitGroup = @import("WaitGroup.zig"); const zig_h = link.File.C.zig_h; const enable_qemu: bool = build_options.enable_qemu; From 658de75500871f28015aa2ff14872eed0410dddf Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 13 Feb 2023 15:14:16 -0700 Subject: [PATCH 137/294] add std.heap.ThreadSafeAllocator This wraps any allocator and makes it thread-safe by using a mutex. --- lib/std/heap.zig | 1 + lib/std/heap/ThreadSafeAllocator.zig | 45 ++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 lib/std/heap/ThreadSafeAllocator.zig diff --git a/lib/std/heap.zig b/lib/std/heap.zig index c15e5d0ec2..e2d000f318 100644 --- a/lib/std/heap.zig +++ b/lib/std/heap.zig @@ -19,6 +19,7 @@ pub const GeneralPurposeAllocator = @import("heap/general_purpose_allocator.zig" pub const WasmAllocator = @import("heap/WasmAllocator.zig"); pub const WasmPageAllocator = @import("heap/WasmPageAllocator.zig"); pub const PageAllocator = @import("heap/PageAllocator.zig"); +pub const ThreadSafeAllocator = @import("heap/ThreadSafeAllocator.zig"); const memory_pool = @import("heap/memory_pool.zig"); pub const MemoryPool = memory_pool.MemoryPool; diff --git a/lib/std/heap/ThreadSafeAllocator.zig b/lib/std/heap/ThreadSafeAllocator.zig new file mode 100644 index 0000000000..fe10eb2fdb --- /dev/null +++ b/lib/std/heap/ThreadSafeAllocator.zig @@ -0,0 +1,45 @@ +//! Wraps a non-thread-safe allocator and makes it thread-safe. + +child_allocator: Allocator, +mutex: std.Thread.Mutex = .{}, + +pub fn allocator(self: *ThreadSafeAllocator) Allocator { + return .{ + .ptr = self, + .vtable = &.{ + .alloc = alloc, + .resize = resize, + .free = free, + }, + }; +} + +fn alloc(ctx: *anyopaque, n: usize, log2_ptr_align: u8, ra: usize) ?[*]u8 { + const self = @ptrCast(*ThreadSafeAllocator, @alignCast(@alignOf(ThreadSafeAllocator), ctx)); + self.mutex.lock(); + defer self.mutex.unlock(); + + return self.child_allocator.rawAlloc(n, log2_ptr_align, ra); +} + +fn resize(ctx: *anyopaque, buf: []u8, log2_buf_align: u8, new_len: usize, ret_addr: usize) bool { + const self = @ptrCast(*ThreadSafeAllocator, @alignCast(@alignOf(ThreadSafeAllocator), ctx)); + + self.mutex.lock(); + defer self.mutex.unlock(); + + return self.child_allocator.rawResize(buf, log2_buf_align, new_len, ret_addr); +} + +fn free(ctx: *anyopaque, buf: []u8, log2_buf_align: u8, ret_addr: usize) void { + const self = @ptrCast(*ThreadSafeAllocator, @alignCast(@alignOf(ThreadSafeAllocator), ctx)); + + self.mutex.lock(); + defer self.mutex.unlock(); + + return self.child_allocator.rawFree(buf, log2_buf_align, ret_addr); +} + +const std = @import("../std.zig"); +const ThreadSafeAllocator = @This(); +const Allocator = std.mem.Allocator; From cff86cf7a17e038db44fa1f72ee5919eea6a6cae Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 13 Feb 2023 15:14:49 -0700 Subject: [PATCH 138/294] build_runner now executes the step graph in parallel --- lib/build_runner.zig | 128 ++++++++++++++++++++++++++++++----------- lib/std/Build/Step.zig | 15 +++-- 2 files changed, 104 insertions(+), 39 deletions(-) diff --git a/lib/build_runner.zig b/lib/build_runner.zig index cc5c9325ae..c8f0b9493c 100644 --- a/lib/build_runner.zig +++ b/lib/build_runner.zig @@ -14,10 +14,14 @@ pub fn main() !void { // Here we use an ArenaAllocator backed by a DirectAllocator because a build is a short-lived, // one shot program. We don't need to waste time freeing memory and finding places to squish // bytes into. So we free everything all at once at the very end. - var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); - defer arena.deinit(); + var single_threaded_arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer single_threaded_arena.deinit(); + + var thread_safe_arena: std.heap.ThreadSafeAllocator = .{ + .child_allocator = single_threaded_arena.allocator(), + }; + const allocator = thread_safe_arena.allocator(); - const allocator = arena.allocator(); var args = try process.argsAlloc(allocator); defer process.argsFree(allocator, args); @@ -245,71 +249,127 @@ pub fn main() !void { if (builder.validateUserInputDidItFail()) usageAndErr(builder, true, stderr_stream); - make(builder, targets.items) catch |err| { + runStepNames(builder, targets.items) catch |err| { switch (err) { error.UncleanExit => process.exit(1), - // This error is intended to indicate that the step has already - // logged an error message and so printing the error return trace - // here would be unwanted extra information, unless the user opts - // into it with a debug flag. - error.StepFailed => process.exit(1), else => return err, } }; } -fn make(b: *std.Build, step_names: []const []const u8) !void { - var wanted_steps = ArrayList(*std.Build.Step).init(b.allocator); - defer wanted_steps.deinit(); +fn runStepNames(b: *std.Build, step_names: []const []const u8) !void { + var step_stack = ArrayList(*std.Build.Step).init(b.allocator); + defer step_stack.deinit(); if (step_names.len == 0) { - try wanted_steps.append(b.default_step); + try step_stack.append(b.default_step); } else { - for (step_names) |step_name| { + try step_stack.resize(step_names.len); + + for (step_names) |step_name, i| { const s = b.top_level_steps.get(step_name) orelse { std.debug.print("no step named '{s}'. Access the help menu with 'zig build -h'\n", .{step_name}); process.exit(1); }; - try wanted_steps.append(&s.step); + step_stack.items[step_names.len - i - 1] = &s.step; } } - for (wanted_steps.items) |s| { - checkForDependencyLoop(b, s) catch |err| switch (err) { + const starting_steps = step_stack.items; + for (starting_steps) |s| { + checkForDependencyLoop(b, s, &step_stack) catch |err| switch (err) { error.DependencyLoopDetected => return error.UncleanExit, else => |e| return e, }; } - for (wanted_steps.items) |s| { - try makeOneStep(b, s); + var thread_pool: std.Thread.Pool = undefined; + try thread_pool.init(b.allocator); + defer thread_pool.deinit(); + + { + var wait_group: std.Thread.WaitGroup = .{}; + defer wait_group.wait(); + var i = step_stack.items.len; + + while (i > 0) { + i -= 1; + const step = step_stack.items[i]; + + wait_group.start(); + thread_pool.spawn(workerMakeOneStep, .{ &wait_group, b, step }) catch + @panic("unhandled error"); + } + } + + var any_failed = false; + + for (step_stack.items) |s| { + switch (s.result) { + .not_done => unreachable, + .success => continue, + .failure => |f| { + any_failed = true; + std.debug.print("{s}: {s}\n", .{ + s.name, @errorName(f.err_code), + }); + }, + } + } + + if (any_failed) { + process.exit(1); } } -fn checkForDependencyLoop(b: *std.Build, s: *std.Build.Step) !void { - if (s.loop_tag == .started) { - std.debug.print("dependency loop detected:\n {s}\n", .{s.name}); - return error.DependencyLoopDetected; - } - s.loop_tag = .started; +fn checkForDependencyLoop( + b: *std.Build, + s: *std.Build.Step, + step_stack: *ArrayList(*std.Build.Step), +) !void { + switch (s.loop_tag) { + .started => { + std.debug.print("dependency loop detected:\n {s}\n", .{s.name}); + return error.DependencyLoopDetected; + }, + .unstarted => { + s.loop_tag = .started; - for (s.dependencies.items) |dep| { - checkForDependencyLoop(b, dep) catch |err| { - if (err == error.DependencyLoopDetected) { - std.debug.print(" {s}\n", .{s.name}); + try step_stack.append(s); + + for (s.dependencies.items) |dep| { + checkForDependencyLoop(b, dep, step_stack) catch |err| { + if (err == error.DependencyLoopDetected) { + std.debug.print(" {s}\n", .{s.name}); + } + return err; + }; } - return err; - }; - } - s.loop_tag = .done; + s.loop_tag = .done; + }, + .done => {}, + } +} + +fn workerMakeOneStep(wg: *std.Thread.WaitGroup, b: *std.Build, s: *std.Build.Step) void { + defer wg.finish(); + + _ = b; + + if (s.make()) |_| { + s.result = .success; + } else |err| { + s.result = .{ .failure = .{ + .err_code = err, + } }; + } } fn makeOneStep(b: *std.Build, s: *std.Build.Step) anyerror!void { for (s.dependencies.items) |dep| { try makeOneStep(b, dep); } - try s.make(); } diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig index 16b1640e70..a22ea04754 100644 --- a/lib/std/Build/Step.zig +++ b/lib/std/Build/Step.zig @@ -4,7 +4,13 @@ makeFn: *const fn (self: *Step) anyerror!void, dependencies: std.ArrayList(*Step), /// Used only during a pre-check for dependency loops. loop_tag: enum { unstarted, started, done }, -done_flag: bool, +result: union(enum) { + not_done, + success, + failure: struct { + err_code: anyerror, + }, +}, pub const Id = enum { top_level, @@ -62,7 +68,7 @@ pub fn init( .makeFn = makeFn, .dependencies = std.ArrayList(*Step).init(allocator), .loop_tag = .unstarted, - .done_flag = false, + .result = .not_done, }; } @@ -71,10 +77,8 @@ pub fn initNoOp(id: Id, name: []const u8, allocator: Allocator) Step { } pub fn make(self: *Step) !void { - if (self.done_flag) return; - + assert(self.result == .not_done); try self.makeFn(self); - self.done_flag = true; } pub fn dependOn(self: *Step, other: *Step) void { @@ -96,3 +100,4 @@ const Step = @This(); const std = @import("../std.zig"); const Build = std.Build; const Allocator = std.mem.Allocator; +const assert = std.debug.assert; From 1fa1484288dc7431f73facb8c423b71670d6914e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 14 Feb 2023 00:18:47 -0700 Subject: [PATCH 139/294] build runner: proper threaded dependency management After sorting the step stack so that dependencies can be popped before their dependants are popped, there is still a situation left to handle correctly: Example: A depends on: B C D depends on: E F They will be ordered like this: A B C D E F If there are 6+ cores, then all of them will be evaluated at once, incorrectly evaluating A and D before their dependencies. Starting evaluation of F and then E is correct, but waiting until they are done is not correct because it should start working on B and C as well. This commit solves the problem by computing dependants in the dependency loop checking logic, and then having workers queue up their dependants when they finish their own work. --- lib/build_runner.zig | 113 +++++++++++++++++++++++++++++------------ lib/std/Build/Step.zig | 31 +++++++---- 2 files changed, 100 insertions(+), 44 deletions(-) diff --git a/lib/build_runner.zig b/lib/build_runner.zig index c8f0b9493c..87a9226bf1 100644 --- a/lib/build_runner.zig +++ b/lib/build_runner.zig @@ -7,6 +7,7 @@ const mem = std.mem; const process = std.process; const ArrayList = std.ArrayList; const File = std.fs.File; +const Step = std.Build.Step; pub const dependencies = @import("@dependencies"); @@ -258,7 +259,7 @@ pub fn main() !void { } fn runStepNames(b: *std.Build, step_names: []const []const u8) !void { - var step_stack = ArrayList(*std.Build.Step).init(b.allocator); + var step_stack = ArrayList(*Step).init(b.allocator); defer step_stack.deinit(); if (step_names.len == 0) { @@ -290,28 +291,35 @@ fn runStepNames(b: *std.Build, step_names: []const []const u8) !void { { var wait_group: std.Thread.WaitGroup = .{}; defer wait_group.wait(); - var i = step_stack.items.len; + // Here we spawn the initial set of tasks with a nice heuristic - + // dependency order. Each worker when it finishes a step will then + // check whether it should run any dependants. + var i = step_stack.items.len; while (i > 0) { i -= 1; const step = step_stack.items[i]; wait_group.start(); - thread_pool.spawn(workerMakeOneStep, .{ &wait_group, b, step }) catch - @panic("unhandled error"); + thread_pool.spawn(workerMakeOneStep, .{ &wait_group, &thread_pool, b, step }) catch + @panic("OOM"); } } var any_failed = false; for (step_stack.items) |s| { - switch (s.result) { - .not_done => unreachable, + switch (s.state) { + .precheck_unstarted => unreachable, + .precheck_started => unreachable, + .precheck_done => unreachable, + .running => unreachable, + .dependency_failure => continue, .success => continue, - .failure => |f| { + .failure => { any_failed = true; std.debug.print("{s}: {s}\n", .{ - s.name, @errorName(f.err_code), + s.name, @errorName(s.result.err_code), }); }, } @@ -324,20 +332,20 @@ fn runStepNames(b: *std.Build, step_names: []const []const u8) !void { fn checkForDependencyLoop( b: *std.Build, - s: *std.Build.Step, - step_stack: *ArrayList(*std.Build.Step), + s: *Step, + step_stack: *ArrayList(*Step), ) !void { - switch (s.loop_tag) { - .started => { + switch (s.state) { + .precheck_started => { std.debug.print("dependency loop detected:\n {s}\n", .{s.name}); return error.DependencyLoopDetected; }, - .unstarted => { - s.loop_tag = .started; - - try step_stack.append(s); + .precheck_unstarted => { + s.state = .precheck_started; for (s.dependencies.items) |dep| { + try step_stack.append(dep); + try dep.dependants.append(b.allocator, s); checkForDependencyLoop(b, dep, step_stack) catch |err| { if (err == error.DependencyLoopDetected) { std.debug.print(" {s}\n", .{s.name}); @@ -346,31 +354,70 @@ fn checkForDependencyLoop( }; } - s.loop_tag = .done; + s.state = .precheck_done; }, - .done => {}, + .precheck_done => {}, + + // These don't happen until we actually run the step graph. + .dependency_failure => unreachable, + .running => unreachable, + .success => unreachable, + .failure => unreachable, } } -fn workerMakeOneStep(wg: *std.Thread.WaitGroup, b: *std.Build, s: *std.Build.Step) void { +fn workerMakeOneStep( + wg: *std.Thread.WaitGroup, + thread_pool: *std.Thread.Pool, + b: *std.Build, + s: *Step, +) void { defer wg.finish(); - _ = b; - - if (s.make()) |_| { - s.result = .success; - } else |err| { - s.result = .{ .failure = .{ - .err_code = err, - } }; - } -} - -fn makeOneStep(b: *std.Build, s: *std.Build.Step) anyerror!void { + // First, check the conditions for running this step. If they are not met, + // then we return without doing the step, relying on another worker to + // queue this step up again when dependencies are met. for (s.dependencies.items) |dep| { - try makeOneStep(b, dep); + switch (@atomicLoad(Step.State, &dep.state, .SeqCst)) { + .success => continue, + .failure, .dependency_failure => { + @atomicStore(Step.State, &s.state, .dependency_failure, .SeqCst); + return; + }, + .precheck_done, .running => { + // dependency is not finished yet. + return; + }, + .precheck_unstarted => unreachable, + .precheck_started => unreachable, + } + } + + // Avoid running steps twice. + if (@cmpxchgStrong(Step.State, &s.state, .precheck_done, .running, .SeqCst, .SeqCst) != null) { + // Another worker got the job. + return; + } + + // I suspect we will want to pass `b` to make() in a future modification. + // For example, CompileStep does some sus things with modifying the saved + // *Build object in install header steps that might be able to be removed + // by passing the *Build object through the make() functions. + s.make() catch |err| { + s.result = .{ + .err_code = err, + }; + @atomicStore(Step.State, &s.state, .failure, .SeqCst); + return; + }; + + @atomicStore(Step.State, &s.state, .success, .SeqCst); + + // Successful completion of a step, so we queue up its dependants as well. + for (s.dependants.items) |dep| { + wg.start(); + thread_pool.spawn(workerMakeOneStep, .{ wg, thread_pool, b, dep }) catch @panic("OOM"); } - try s.make(); } fn steps(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !void { diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig index a22ea04754..1226c78c73 100644 --- a/lib/std/Build/Step.zig +++ b/lib/std/Build/Step.zig @@ -2,16 +2,25 @@ id: Id, name: []const u8, makeFn: *const fn (self: *Step) anyerror!void, dependencies: std.ArrayList(*Step), -/// Used only during a pre-check for dependency loops. -loop_tag: enum { unstarted, started, done }, -result: union(enum) { - not_done, - success, - failure: struct { - err_code: anyerror, - }, +/// This field is empty during execution of the user's build script, and +/// then populated during dependency loop checking in the build runner. +dependants: std.ArrayListUnmanaged(*Step), +state: State, +/// Populated only if state is success. +result: struct { + err_code: anyerror, }, +pub const State = enum { + precheck_unstarted, + precheck_started, + precheck_done, + running, + dependency_failure, + success, + failure, +}; + pub const Id = enum { top_level, compile, @@ -67,8 +76,9 @@ pub fn init( .name = allocator.dupe(u8, name) catch @panic("OOM"), .makeFn = makeFn, .dependencies = std.ArrayList(*Step).init(allocator), - .loop_tag = .unstarted, - .result = .not_done, + .dependants = .{}, + .state = .precheck_unstarted, + .result = undefined, }; } @@ -77,7 +87,6 @@ pub fn initNoOp(id: Id, name: []const u8, allocator: Allocator) Step { } pub fn make(self: *Step) !void { - assert(self.result == .not_done); try self.makeFn(self); } From 9580fbcf3596a39ba4c7d7af2f3a1df0e0abb746 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 14 Feb 2023 02:53:05 -0700 Subject: [PATCH 140/294] build system: capture stderr and report it later Instead of dumping directly to stderr. This prevents processes running simultaneously from racing their stderr against each other. For now it only reports at the end, but an improvement would be to report as soon as a failed step occurs. --- build.zig | 5 +- lib/build_runner.zig | 8 +-- lib/std/Build.zig | 121 +++++++++++++++++++++++++++-------------- lib/std/Build/Step.zig | 6 +- 4 files changed, 90 insertions(+), 50 deletions(-) diff --git a/build.zig b/build.zig index 12e5d014e2..797c980739 100644 --- a/build.zig +++ b/build.zig @@ -676,10 +676,7 @@ fn addCxxKnownPath( ) !void { if (!std.process.can_spawn) return error.RequiredLibraryNotFound; - const path_padded = try b.exec(&[_][]const u8{ - ctx.cxx_compiler, - b.fmt("-print-file-name={s}", .{objname}), - }); + const path_padded = b.exec(&.{ ctx.cxx_compiler, b.fmt("-print-file-name={s}", .{objname}) }); var tokenizer = mem.tokenize(u8, path_padded, "\r\n"); const path_unpadded = tokenizer.next().?; if (mem.eql(u8, path_unpadded, objname)) { diff --git a/lib/build_runner.zig b/lib/build_runner.zig index 87a9226bf1..6ba583c59d 100644 --- a/lib/build_runner.zig +++ b/lib/build_runner.zig @@ -318,8 +318,8 @@ fn runStepNames(b: *std.Build, step_names: []const []const u8) !void { .success => continue, .failure => { any_failed = true; - std.debug.print("{s}: {s}\n", .{ - s.name, @errorName(s.result.err_code), + std.debug.print("{s}: {s}\n{s}", .{ + s.name, @errorName(s.result.err_code), s.result.stderr, }); }, } @@ -404,9 +404,7 @@ fn workerMakeOneStep( // *Build object in install header steps that might be able to be removed // by passing the *Build object through the make() functions. s.make() catch |err| { - s.result = .{ - .err_code = err, - }; + s.result.err_code = err; @atomicStore(Step.State, &s.state, .failure, .SeqCst); return; }; diff --git a/lib/std/Build.zig b/lib/std/Build.zig index 168180e383..225e4a58aa 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -1133,17 +1133,24 @@ pub fn spawnChild(self: *Build, argv: []const []const u8) !void { return self.spawnChildEnvMap(null, self.env_map, argv); } -fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void { - if (cwd) |yes_cwd| std.debug.print("cd {s} && ", .{yes_cwd}); +fn allocPrintCmd(ally: Allocator, opt_cwd: ?[]const u8, argv: []const []const u8) ![]u8 { + var buf = ArrayList(u8).init(ally); + if (opt_cwd) |cwd| try buf.writer().print("cd {s} && ", .{cwd}); for (argv) |arg| { - std.debug.print("{s} ", .{arg}); + try buf.writer().print("{s} ", .{arg}); } - std.debug.print("\n", .{}); + try buf.append('\n'); + return buf.toOwnedSlice(); +} + +fn printCmd(ally: Allocator, cwd: ?[]const u8, argv: []const []const u8) void { + const text = allocPrintCmd(ally, cwd, argv) catch @panic("OOM"); + std.debug.print("{s}", .{text}); } pub fn spawnChildEnvMap(self: *Build, cwd: ?[]const u8, env_map: *const EnvMap, argv: []const []const u8) !void { if (self.verbose) { - printCmd(cwd, argv); + printCmd(self.allocator, cwd, argv); } if (!std.process.can_spawn) @@ -1162,13 +1169,13 @@ pub fn spawnChildEnvMap(self: *Build, cwd: ?[]const u8, env_map: *const EnvMap, .Exited => |code| { if (code != 0) { log.err("The following command exited with error code {}:", .{code}); - printCmd(cwd, argv); + printCmd(self.allocator, cwd, argv); return error.UncleanExit; } }, else => { log.err("The following command terminated unexpectedly:", .{}); - printCmd(cwd, argv); + printCmd(self.allocator, cwd, argv); return error.UncleanExit; }, @@ -1381,57 +1388,91 @@ pub fn execAllowFail( } } -pub fn execFromStep(self: *Build, argv: []const []const u8, src_step: ?*Step) ![]u8 { +pub fn execFromStep(b: *Build, argv: []const []const u8, s: *Step) ![]u8 { assert(argv.len != 0); - if (self.verbose) { - printCmd(null, argv); + if (b.verbose) { + printCmd(b.allocator, null, argv); } if (!std.process.can_spawn) { - if (src_step) |s| log.err("{s}...", .{s.name}); - log.err("Unable to spawn the following command: cannot spawn child process", .{}); - printCmd(null, argv); - std.os.abort(); + s.result.stderr = b.fmt("Unable to spawn the following command: cannot spawn child processes\n{s}", .{ + try allocPrintCmd(b.allocator, null, argv), + }); + return error.CannotSpawnProcesses; } var code: u8 = undefined; - return self.execAllowFail(argv, &code, .Inherit) catch |err| switch (err) { - error.ExecNotSupported => { - if (src_step) |s| log.err("{s}...", .{s.name}); - log.err("Unable to spawn the following command: cannot spawn child process", .{}); - printCmd(null, argv); - std.os.abort(); - }, + const result = unwrapExecResult(&code, std.ChildProcess.exec(.{ + .allocator = b.allocator, + .argv = argv, + .env_map = b.env_map, + .max_output_bytes = 10 * 1024 * 1024, + })) catch |err| switch (err) { error.FileNotFound => { - if (src_step) |s| log.err("{s}...", .{s.name}); - log.err("Unable to spawn the following command: file not found", .{}); - printCmd(null, argv); - std.os.exit(@truncate(u8, code)); + s.result.stderr = b.fmt("unable to spawn the following command: file not found\n{s}", .{ + try allocPrintCmd(b.allocator, null, argv), + }); + return error.ExecFailed; }, error.ExitCodeFailure => { - if (src_step) |s| log.err("{s}...", .{s.name}); - if (self.prominent_compile_errors) { - log.err("The step exited with error code {d}", .{code}); - } else { - log.err("The following command exited with error code {d}:", .{code}); - printCmd(null, argv); - } - - std.os.exit(@truncate(u8, code)); + s.result.stderr = b.fmt("the following command exited with error code {d}:\n{s}", .{ + code, try allocPrintCmd(b.allocator, null, argv), + }); + return error.ExecFailed; }, error.ProcessTerminated => { - if (src_step) |s| log.err("{s}...", .{s.name}); - log.err("The following command terminated unexpectedly:", .{}); - printCmd(null, argv); - std.os.exit(@truncate(u8, code)); + s.result.stderr = b.fmt("the following command terminated unexpectedly:\n{s}", .{ + try allocPrintCmd(b.allocator, null, argv), + }); + return error.ExecFailed; }, else => |e| return e, }; + + s.result.stderr = result.stderr; + return result.stdout; } -pub fn exec(self: *Build, argv: []const []const u8) ![]u8 { - return self.execFromStep(argv, null); +fn unwrapExecResult( + code_ptr: *u8, + wrapped: std.ChildProcess.ExecError!std.ChildProcess.ExecResult, +) !std.ChildProcess.ExecResult { + const result = try wrapped; + switch (result.term) { + .Exited => |code| { + code_ptr.* = code; + if (code != 0) { + return error.ExitCodeFailure; + } + return result; + }, + .Signal, .Stopped, .Unknown => |code| { + _ = code; + return error.ProcessTerminated; + }, + } +} + +/// This is a helper function to be called from build.zig scripts, *not* from +/// inside step make() functions. If any errors occur, it fails the build with +/// a helpful message. +pub fn exec(b: *Build, argv: []const []const u8) []u8 { + if (!std.process.can_spawn) { + std.debug.print("unable to spawn the following command: cannot spawn child process\n{s}", .{ + try allocPrintCmd(b.allocator, null, argv), + }); + process.exit(1); + } + + var code: u8 = undefined; + return b.execAllowFail(argv, &code, .Inherit) catch |err| { + const printed_cmd = allocPrintCmd(b.allocator, null, argv) catch @panic("OOM"); + std.debug.print("unable to spawn the following command: {s}\n{s}", .{ + @errorName(err), printed_cmd, + }); + process.exit(1); + }; } pub fn addSearchPrefix(self: *Build, search_prefix: []const u8) void { diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig index 1226c78c73..44998f4a44 100644 --- a/lib/std/Build/Step.zig +++ b/lib/std/Build/Step.zig @@ -9,6 +9,7 @@ state: State, /// Populated only if state is success. result: struct { err_code: anyerror, + stderr: []u8, }, pub const State = enum { @@ -78,7 +79,10 @@ pub fn init( .dependencies = std.ArrayList(*Step).init(allocator), .dependants = .{}, .state = .precheck_unstarted, - .result = undefined, + .result = .{ + .err_code = undefined, + .stderr = &.{}, + }, }; } From 02381c037221937e941e03c5e7439383dde8a2a1 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 14 Feb 2023 12:47:45 -0700 Subject: [PATCH 141/294] std.Build: improve debugging of misconfigured steps * Step.init() now takes an options struct * Step.init() now captures a small stack trace and stores it in the Step so that it can be accessed when printing user-friendly debugging information, including the lines of code that created the step in question. --- lib/std/Build.zig | 88 ++++++++++++++++++++------- lib/std/Build/CheckFileStep.zig | 6 +- lib/std/Build/CheckObjectStep.zig | 6 +- lib/std/Build/CompileStep.zig | 6 +- lib/std/Build/ConfigHeaderStep.zig | 8 ++- lib/std/Build/EmulatableRunStep.zig | 6 +- lib/std/Build/FmtStep.zig | 6 +- lib/std/Build/InstallArtifactStep.zig | 6 +- lib/std/Build/InstallDirStep.zig | 8 ++- lib/std/Build/InstallFileStep.zig | 6 +- lib/std/Build/LogStep.zig | 6 +- lib/std/Build/ObjCopyStep.zig | 11 ++-- lib/std/Build/OptionsStep.zig | 6 +- lib/std/Build/RemoveDirStep.zig | 6 +- lib/std/Build/RunStep.zig | 8 ++- lib/std/Build/Step.zig | 48 +++++++++++---- lib/std/Build/TranslateCStep.zig | 6 +- lib/std/Build/WriteFileStep.zig | 6 +- test/link/macho/uuid/build.zig | 22 ++++--- test/tests.zig | 12 +++- 20 files changed, 209 insertions(+), 68 deletions(-) diff --git a/lib/std/Build.zig b/lib/std/Build.zig index 225e4a58aa..3ebdba4c70 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -227,12 +227,19 @@ pub fn create( .h_dir = undefined, .dest_dir = env_map.get("DESTDIR"), .installed_files = ArrayList(InstalledFile).init(allocator), - .install_tls = TopLevelStep{ - .step = Step.initNoOp(.top_level, "install", allocator), + .install_tls = .{ + .step = Step.init(allocator, .{ + .id = .top_level, + .name = "install", + }), .description = "Copy build artifacts to prefix path", }, - .uninstall_tls = TopLevelStep{ - .step = Step.init(.top_level, "uninstall", allocator, makeUninstall), + .uninstall_tls = .{ + .step = Step.init(allocator, .{ + .id = .top_level, + .name = "uninstall", + .makeFn = makeUninstall, + }), .description = "Remove build artifacts from prefix path", }, .zig_lib_dir = null, @@ -264,11 +271,18 @@ fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: Cache.Direc child.* = .{ .allocator = allocator, .install_tls = .{ - .step = Step.initNoOp(.top_level, "install", allocator), + .step = Step.init(allocator, .{ + .id = .top_level, + .name = "install", + }), .description = "Copy build artifacts to prefix path", }, .uninstall_tls = .{ - .step = Step.init(.top_level, "uninstall", allocator, makeUninstall), + .step = Step.init(allocator, .{ + .id = .top_level, + .name = "uninstall", + .makeFn = makeUninstall, + }), .description = "Remove build artifacts from prefix path", }, .user_input_options = UserInputOptionsMap.init(allocator), @@ -634,7 +648,11 @@ pub fn addConfigHeader( options: ConfigHeaderStep.Options, values: anytype, ) *ConfigHeaderStep { - const config_header_step = ConfigHeaderStep.create(b, options); + var options_copy = options; + if (options_copy.first_ret_addr == null) + options_copy.first_ret_addr = @returnAddress(); + + const config_header_step = ConfigHeaderStep.create(b, options_copy); config_header_step.addValues(values); return config_header_step; } @@ -858,7 +876,10 @@ pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_ pub fn step(self: *Build, name: []const u8, description: []const u8) *Step { const step_info = self.allocator.create(TopLevelStep) catch @panic("OOM"); step_info.* = TopLevelStep{ - .step = Step.initNoOp(.top_level, name, self.allocator), + .step = Step.init(self.allocator, .{ + .id = .top_level, + .name = name, + }), .description = self.dupe(description), }; self.top_level_steps.put(self.allocator, step_info.step.name, step_info) catch @panic("OOM"); @@ -1153,7 +1174,7 @@ pub fn spawnChildEnvMap(self: *Build, cwd: ?[]const u8, env_map: *const EnvMap, printCmd(self.allocator, cwd, argv); } - if (!std.process.can_spawn) + if (!process.can_spawn) return error.ExecNotSupported; var child = std.ChildProcess.init(argv, self.allocator); @@ -1355,7 +1376,7 @@ pub fn execAllowFail( ) ExecError![]u8 { assert(argv.len != 0); - if (!std.process.can_spawn) + if (!process.can_spawn) return error.ExecNotSupported; const max_output_size = 400 * 1024; @@ -1395,7 +1416,7 @@ pub fn execFromStep(b: *Build, argv: []const []const u8, s: *Step) ![]u8 { printCmd(b.allocator, null, argv); } - if (!std.process.can_spawn) { + if (!process.can_spawn) { s.result.stderr = b.fmt("Unable to spawn the following command: cannot spawn child processes\n{s}", .{ try allocPrintCmd(b.allocator, null, argv), }); @@ -1458,7 +1479,7 @@ fn unwrapExecResult( /// inside step make() functions. If any errors occur, it fails the build with /// a helpful message. pub fn exec(b: *Build, argv: []const []const u8) []u8 { - if (!std.process.can_spawn) { + if (!process.can_spawn) { std.debug.print("unable to spawn the following command: cannot spawn child process\n{s}", .{ try allocPrintCmd(b.allocator, null, argv), }); @@ -1539,7 +1560,7 @@ pub fn dependency(b: *Build, name: []const u8, args: anytype) *Dependency { const full_path = b.pathFromRoot("build.zig.zon"); std.debug.print("no dependency named '{s}' in '{s}'. All packages used in build.zig must be declared in this file.\n", .{ name, full_path }); - std.process.exit(1); + process.exit(1); } fn dependencyInner( @@ -1555,7 +1576,7 @@ fn dependencyInner( std.debug.print("unable to open '{s}': {s}\n", .{ build_root_string, @errorName(err), }); - std.process.exit(1); + process.exit(1); }, }; const sub_builder = b.createChild(name, build_root, args) catch @panic("unhandled error"); @@ -1599,7 +1620,7 @@ pub const GeneratedFile = struct { pub fn getPath(self: GeneratedFile) []const u8 { return self.path orelse std.debug.panic( - "getPath() was called on a GeneratedFile that wasn't build yet. Is there a missing Step dependency on step '{s}'?", + "getPath() was called on a GeneratedFile that wasn't built yet. Is there a missing Step dependency on step '{s}'?", .{self.step.name}, ); } @@ -1639,12 +1660,16 @@ pub const FileSource = union(enum) { } /// Should only be called during make(), returns a path relative to the build root or absolute. - pub fn getPath(self: FileSource, builder: *Build) []const u8 { - const path = switch (self) { - .path => |p| builder.pathFromRoot(p), - .generated => |gen| gen.getPath(), - }; - return path; + pub fn getPath(self: FileSource, src_builder: *Build) []const u8 { + switch (self) { + .path => |p| return src_builder.pathFromRoot(p), + .generated => |gen| return gen.path orelse { + std.debug.getStderrMutex().lock(); + const stderr = std.io.getStdErr(); + dumpBadGetPathHelp(gen.step, stderr, src_builder) catch {}; + @panic("unable to get path"); + }, + } } /// Duplicates the file source for a given builder. @@ -1656,6 +1681,27 @@ pub const FileSource = union(enum) { } }; +fn dumpBadGetPathHelp(s: *Step, stderr: fs.File, src_builder: *Build) anyerror!void { + try stderr.writer().print( + \\getPath() was called on a GeneratedFile that wasn't built yet. + \\ source package path: {s} + \\ Is there a missing Step dependency on step '{s}'? + \\ The step was created by this stack trace: + \\ + , .{ + src_builder.build_root.path orelse ".", + s.name, + }); + const debug_info = std.debug.getSelfDebugInfo() catch |err| { + try stderr.writer().print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{@errorName(err)}); + return; + }; + std.debug.writeStackTrace(s.getStackTrace(), stderr.writer(), debug_info.allocator, debug_info, std.debug.detectTTYConfig(stderr)) catch |err| { + try stderr.writer().print("Unable to dump stack trace: {s}\n", .{@errorName(err)}); + return; + }; +} + /// Allocates a new string for assigning a value to a named macro. /// If the value is omitted, it is set to 1. /// `name` and `value` need not live longer than the function call. diff --git a/lib/std/Build/CheckFileStep.zig b/lib/std/Build/CheckFileStep.zig index b08a797e84..5b2e5b4e5b 100644 --- a/lib/std/Build/CheckFileStep.zig +++ b/lib/std/Build/CheckFileStep.zig @@ -21,7 +21,11 @@ pub fn create( const self = builder.allocator.create(CheckFileStep) catch @panic("OOM"); self.* = CheckFileStep{ .builder = builder, - .step = Step.init(.check_file, "CheckFile", builder.allocator, make), + .step = Step.init(builder.allocator, .{ + .id = .check_file, + .name = "CheckFile", + .makeFn = make, + }), .source = source.dupe(builder), .expected_matches = builder.dupeStrings(expected_matches), }; diff --git a/lib/std/Build/CheckObjectStep.zig b/lib/std/Build/CheckObjectStep.zig index 5cb096581f..1775a5d39a 100644 --- a/lib/std/Build/CheckObjectStep.zig +++ b/lib/std/Build/CheckObjectStep.zig @@ -27,7 +27,11 @@ pub fn create(builder: *std.Build, source: std.Build.FileSource, obj_format: std const self = gpa.create(CheckObjectStep) catch @panic("OOM"); self.* = .{ .builder = builder, - .step = Step.init(.check_file, "CheckObject", gpa, make), + .step = Step.init(gpa, .{ + .id = .check_file, + .name = "CheckObject", + .makeFn = make, + }), .source = source.dupe(builder), .checks = std.ArrayList(Check).init(gpa), .obj_format = obj_format, diff --git a/lib/std/Build/CompileStep.zig b/lib/std/Build/CompileStep.zig index 49d2fae68d..ce0ede9510 100644 --- a/lib/std/Build/CompileStep.zig +++ b/lib/std/Build/CompileStep.zig @@ -326,7 +326,11 @@ pub fn create(builder: *std.Build, options: Options) *CompileStep { .root_src = root_src, .name = name, .frameworks = StringHashMap(FrameworkLinkInfo).init(builder.allocator), - .step = Step.init(base_id, name, builder.allocator, make), + .step = Step.init(builder.allocator, .{ + .id = base_id, + .name = name, + .makeFn = make, + }), .version = options.version, .out_filename = undefined, .out_h_filename = builder.fmt("{s}.h", .{name}), diff --git a/lib/std/Build/ConfigHeaderStep.zig b/lib/std/Build/ConfigHeaderStep.zig index 595c1018f7..fb8d92e4e2 100644 --- a/lib/std/Build/ConfigHeaderStep.zig +++ b/lib/std/Build/ConfigHeaderStep.zig @@ -46,6 +46,7 @@ pub const Options = struct { style: Style = .blank, max_bytes: usize = 2 * 1024 * 1024, include_path: ?[]const u8 = null, + first_ret_addr: ?usize = null, }; pub fn create(builder: *std.Build, options: Options) *ConfigHeaderStep { @@ -56,7 +57,12 @@ pub fn create(builder: *std.Build, options: Options) *ConfigHeaderStep { builder.fmt("configure {s} header", .{@tagName(options.style)}); self.* = .{ .builder = builder, - .step = Step.init(base_id, name, builder.allocator, make), + .step = Step.init(builder.allocator, .{ + .id = base_id, + .name = name, + .makeFn = make, + .first_ret_addr = options.first_ret_addr orelse @returnAddress(), + }), .style = options.style, .values = std.StringArrayHashMap(Value).init(builder.allocator), diff --git a/lib/std/Build/EmulatableRunStep.zig b/lib/std/Build/EmulatableRunStep.zig index d4b5238524..6556f4b8c0 100644 --- a/lib/std/Build/EmulatableRunStep.zig +++ b/lib/std/Build/EmulatableRunStep.zig @@ -56,7 +56,11 @@ pub fn create(builder: *std.Build, name: []const u8, artifact: *CompileStep) *Em self.* = .{ .builder = builder, - .step = Step.init(.emulatable_run, name, builder.allocator, make), + .step = Step.init(builder.allocator, .{ + .id = .emulatable_run, + .name = name, + .makeFn = make, + }), .exe = artifact, .env_map = null, .cwd = null, diff --git a/lib/std/Build/FmtStep.zig b/lib/std/Build/FmtStep.zig index 4a5efde2bd..04b8ccb3e4 100644 --- a/lib/std/Build/FmtStep.zig +++ b/lib/std/Build/FmtStep.zig @@ -12,7 +12,11 @@ pub fn create(builder: *std.Build, paths: []const []const u8) *FmtStep { const self = builder.allocator.create(FmtStep) catch @panic("OOM"); const name = "zig fmt"; self.* = FmtStep{ - .step = Step.init(.fmt, name, builder.allocator, make), + .step = Step.init(builder.allocator, .{ + .id = .fmt, + .name = name, + .makeFn = make, + }), .builder = builder, .argv = builder.allocator.alloc([]u8, paths.len + 2) catch @panic("OOM"), }; diff --git a/lib/std/Build/InstallArtifactStep.zig b/lib/std/Build/InstallArtifactStep.zig index c419c85fdf..43dcffede8 100644 --- a/lib/std/Build/InstallArtifactStep.zig +++ b/lib/std/Build/InstallArtifactStep.zig @@ -19,7 +19,11 @@ pub fn create(builder: *std.Build, artifact: *CompileStep) *InstallArtifactStep const self = builder.allocator.create(InstallArtifactStep) catch @panic("OOM"); self.* = InstallArtifactStep{ .builder = builder, - .step = Step.init(.install_artifact, builder.fmt("install {s}", .{artifact.step.name}), builder.allocator, make), + .step = Step.init(builder.allocator, .{ + .id = base_id, + .name = builder.fmt("install {s}", .{artifact.step.name}), + .makeFn = make, + }), .artifact = artifact, .dest_dir = artifact.override_dest_dir orelse switch (artifact.kind) { .obj => @panic("Cannot install a .obj build artifact."), diff --git a/lib/std/Build/InstallDirStep.zig b/lib/std/Build/InstallDirStep.zig index 41dbb3e35a..54a37af1d4 100644 --- a/lib/std/Build/InstallDirStep.zig +++ b/lib/std/Build/InstallDirStep.zig @@ -45,9 +45,13 @@ pub fn init( options: Options, ) InstallDirStep { builder.pushInstalledFile(options.install_dir, options.install_subdir); - return InstallDirStep{ + return .{ .builder = builder, - .step = Step.init(.install_dir, builder.fmt("install {s}/", .{options.source_dir}), builder.allocator, make), + .step = Step.init(builder.allocator, .{ + .id = .install_dir, + .name = builder.fmt("install {s}/", .{options.source_dir}), + .makeFn = make, + }), .options = options.dupe(builder), }; } diff --git a/lib/std/Build/InstallFileStep.zig b/lib/std/Build/InstallFileStep.zig index 8c8d8ad2d4..70dee5d45d 100644 --- a/lib/std/Build/InstallFileStep.zig +++ b/lib/std/Build/InstallFileStep.zig @@ -24,7 +24,11 @@ pub fn init( builder.pushInstalledFile(dir, dest_rel_path); return InstallFileStep{ .builder = builder, - .step = Step.init(.install_file, builder.fmt("install {s} to {s}", .{ source.getDisplayName(), dest_rel_path }), builder.allocator, make), + .step = Step.init(builder.allocator, .{ + .id = .install_file, + .name = builder.fmt("install {s} to {s}", .{ source.getDisplayName(), dest_rel_path }), + .makeFn = make, + }), .source = source.dupe(builder), .dir = dir.dupe(builder), .dest_rel_path = builder.dupePath(dest_rel_path), diff --git a/lib/std/Build/LogStep.zig b/lib/std/Build/LogStep.zig index 6d51df8cbd..008ed6e8bb 100644 --- a/lib/std/Build/LogStep.zig +++ b/lib/std/Build/LogStep.zig @@ -12,7 +12,11 @@ data: []const u8, pub fn init(builder: *std.Build, data: []const u8) LogStep { return LogStep{ .builder = builder, - .step = Step.init(.log, builder.fmt("log {s}", .{data}), builder.allocator, make), + .step = Step.init(builder.allocator, .{ + .id = .log, + .name = builder.fmt("log {s}", .{data}), + .makeFn = make, + }), .data = builder.dupe(data), }; } diff --git a/lib/std/Build/ObjCopyStep.zig b/lib/std/Build/ObjCopyStep.zig index aea5b8975c..549bb1ed00 100644 --- a/lib/std/Build/ObjCopyStep.zig +++ b/lib/std/Build/ObjCopyStep.zig @@ -44,12 +44,11 @@ pub fn create( ) *ObjCopyStep { const self = builder.allocator.create(ObjCopyStep) catch @panic("OOM"); self.* = ObjCopyStep{ - .step = Step.init( - base_id, - builder.fmt("objcopy {s}", .{file_source.getDisplayName()}), - builder.allocator, - make, - ), + .step = Step.init(builder.allocator, .{ + .id = base_id, + .name = builder.fmt("objcopy {s}", .{file_source.getDisplayName()}), + .makeFn = make, + }), .builder = builder, .file_source = file_source, .basename = options.basename orelse file_source.getDisplayName(), diff --git a/lib/std/Build/OptionsStep.zig b/lib/std/Build/OptionsStep.zig index e5c3e23821..8e86578e30 100644 --- a/lib/std/Build/OptionsStep.zig +++ b/lib/std/Build/OptionsStep.zig @@ -22,7 +22,11 @@ pub fn create(builder: *std.Build) *OptionsStep { const self = builder.allocator.create(OptionsStep) catch @panic("OOM"); self.* = .{ .builder = builder, - .step = Step.init(.options, "options", builder.allocator, make), + .step = Step.init(builder.allocator, .{ + .id = base_id, + .name = "options", + .makeFn = make, + }), .generated_file = undefined, .contents = std.ArrayList(u8).init(builder.allocator), .artifact_args = std.ArrayList(OptionArtifactArg).init(builder.allocator), diff --git a/lib/std/Build/RemoveDirStep.zig b/lib/std/Build/RemoveDirStep.zig index f3b71dcec1..acde60a745 100644 --- a/lib/std/Build/RemoveDirStep.zig +++ b/lib/std/Build/RemoveDirStep.zig @@ -13,7 +13,11 @@ dir_path: []const u8, pub fn init(builder: *std.Build, dir_path: []const u8) RemoveDirStep { return RemoveDirStep{ .builder = builder, - .step = Step.init(.remove_dir, builder.fmt("RemoveDir {s}", .{dir_path}), builder.allocator, make), + .step = Step.init(builder.allocator, .{ + .id = .remove_dir, + .name = builder.fmt("RemoveDir {s}", .{dir_path}), + .makeFn = make, + }), .dir_path = builder.dupePath(dir_path), }; } diff --git a/lib/std/Build/RunStep.zig b/lib/std/Build/RunStep.zig index 1aae37d2f3..815916f380 100644 --- a/lib/std/Build/RunStep.zig +++ b/lib/std/Build/RunStep.zig @@ -69,9 +69,13 @@ pub const Arg = union(enum) { pub fn create(builder: *std.Build, name: []const u8) *RunStep { const self = builder.allocator.create(RunStep) catch @panic("OOM"); - self.* = RunStep{ + self.* = .{ .builder = builder, - .step = Step.init(base_id, name, builder.allocator, make), + .step = Step.init(builder.allocator, .{ + .id = base_id, + .name = name, + .makeFn = make, + }), .argv = ArrayList(Arg).init(builder.allocator), .cwd = null, .env_map = null, diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig index 44998f4a44..7bab2d9ae4 100644 --- a/lib/std/Build/Step.zig +++ b/lib/std/Build/Step.zig @@ -11,6 +11,11 @@ result: struct { err_code: anyerror, stderr: []u8, }, +/// The return addresss associated with creation of this step that can be useful +/// to print along with debugging messages. +debug_stack_trace: [n_debug_stack_frames]usize, + +const n_debug_stack_frames = 4; pub const State = enum { precheck_unstarted, @@ -66,16 +71,26 @@ pub const Id = enum { } }; -pub fn init( +pub const Options = struct { id: Id, name: []const u8, - allocator: Allocator, - makeFn: *const fn (self: *Step) anyerror!void, -) Step { - return Step{ - .id = id, - .name = allocator.dupe(u8, name) catch @panic("OOM"), - .makeFn = makeFn, + makeFn: *const fn (self: *Step) anyerror!void = makeNoOp, + first_ret_addr: ?usize = null, +}; + +pub fn init(allocator: Allocator, options: Options) Step { + var addresses = [1]usize{0} ** n_debug_stack_frames; + const first_ret_addr = options.first_ret_addr orelse @returnAddress(); + var stack_trace = std.builtin.StackTrace{ + .instruction_addresses = &addresses, + .index = 0, + }; + std.debug.captureStackTrace(first_ret_addr, &stack_trace); + + return .{ + .id = options.id, + .name = allocator.dupe(u8, options.name) catch @panic("OOM"), + .makeFn = options.makeFn, .dependencies = std.ArrayList(*Step).init(allocator), .dependants = .{}, .state = .precheck_unstarted, @@ -83,13 +98,10 @@ pub fn init( .err_code = undefined, .stderr = &.{}, }, + .debug_stack_trace = addresses, }; } -pub fn initNoOp(id: Id, name: []const u8, allocator: Allocator) Step { - return init(id, name, allocator, makeNoOp); -} - pub fn make(self: *Step) !void { try self.makeFn(self); } @@ -98,6 +110,18 @@ pub fn dependOn(self: *Step, other: *Step) void { self.dependencies.append(other) catch @panic("OOM"); } +pub fn getStackTrace(s: *Step) std.builtin.StackTrace { + const stack_addresses = &s.debug_stack_trace; + var len: usize = 0; + while (len < n_debug_stack_frames and stack_addresses[len] != 0) { + len += 1; + } + return .{ + .instruction_addresses = stack_addresses, + .index = len, + }; +} + fn makeNoOp(self: *Step) anyerror!void { _ = self; } diff --git a/lib/std/Build/TranslateCStep.zig b/lib/std/Build/TranslateCStep.zig index fb0adfd0ae..8c3a254d6a 100644 --- a/lib/std/Build/TranslateCStep.zig +++ b/lib/std/Build/TranslateCStep.zig @@ -30,7 +30,11 @@ pub fn create(builder: *std.Build, options: Options) *TranslateCStep { const self = builder.allocator.create(TranslateCStep) catch @panic("OOM"); const source = options.source_file.dupe(builder); self.* = TranslateCStep{ - .step = Step.init(.translate_c, "translate-c", builder.allocator, make), + .step = Step.init(builder.allocator, .{ + .id = .translate_c, + .name = "translate-c", + .makeFn = make, + }), .builder = builder, .source = source, .include_dirs = std.ArrayList([]const u8).init(builder.allocator), diff --git a/lib/std/Build/WriteFileStep.zig b/lib/std/Build/WriteFileStep.zig index 3a30aba190..4f5e768237 100644 --- a/lib/std/Build/WriteFileStep.zig +++ b/lib/std/Build/WriteFileStep.zig @@ -37,7 +37,11 @@ pub const Contents = union(enum) { pub fn init(builder: *std.Build) WriteFileStep { return .{ .builder = builder, - .step = Step.init(.write_file, "writefile", builder.allocator, make), + .step = Step.init(builder.allocator, .{ + .id = .write_file, + .name = "writefile", + .makeFn = make, + }), .files = .{}, .output_source_files = .{}, }; diff --git a/test/link/macho/uuid/build.zig b/test/link/macho/uuid/build.zig index 5a8c14ae37..ec7664cd72 100644 --- a/test/link/macho/uuid/build.zig +++ b/test/link/macho/uuid/build.zig @@ -90,10 +90,13 @@ const InstallWithRename = struct { const self = builder.allocator.create(InstallWithRename) catch @panic("OOM"); self.* = InstallWithRename{ .builder = builder, - .step = Step.init(.custom, builder.fmt("install and rename: {s} -> {s}", .{ - source.getDisplayName(), - name, - }), builder.allocator, make), + .step = Step.init(builder.allocator, .{ + .id = .custom, + .name = builder.fmt("install and rename: {s} -> {s}", .{ + source.getDisplayName(), name, + }), + .makeFn = make, + }), .source = source, .name = builder.dupe(name), }; @@ -123,15 +126,14 @@ const CompareUuid = struct { const self = builder.allocator.create(CompareUuid) catch @panic("OOM"); self.* = CompareUuid{ .builder = builder, - .step = Step.init( - .custom, - builder.fmt("compare uuid: {s} and {s}", .{ + .step = Step.init(builder.allocator, .{ + .id = .custom, + .name = builder.fmt("compare uuid: {s} and {s}", .{ lhs, rhs, }), - builder.allocator, - make, - ), + .makeFn = make, + }), .lhs = lhs, .rhs = rhs, }; diff --git a/test/tests.zig b/test/tests.zig index 035311372f..e470a4f251 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -858,7 +858,11 @@ pub const StackTracesContext = struct { const allocator = context.b.allocator; const ptr = allocator.create(RunAndCompareStep) catch unreachable; ptr.* = RunAndCompareStep{ - .step = Step.init(.custom, "StackTraceCompareOutputStep", allocator, make), + .step = Step.init(allocator, .{ + .id = .custom, + .name = "StackTraceCompareOutputStep", + .makeFn = make, + }), .context = context, .exe = exe, .name = name, @@ -1194,7 +1198,11 @@ pub const GenHContext = struct { const allocator = context.b.allocator; const ptr = allocator.create(GenHCmpOutputStep) catch unreachable; ptr.* = GenHCmpOutputStep{ - .step = Step.init(.Custom, "ParseCCmpOutput", allocator, make), + .step = Step.init(allocator, .{ + .id = .custom, + .name = "ParseCCmpOutput", + .makeFn = make, + }), .context = context, .obj = obj, .name = name, From 8d384722937edcfdcb3c461d316ed97405e6e2ee Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 14 Feb 2023 13:12:47 -0700 Subject: [PATCH 142/294] std.Build: further enhance debug message for bad getPath() Now it also shows the step stack trace of the step whose make function is being run. --- lib/std/Build.zig | 48 ++++++++++++++++++++++++++----- lib/std/Build/InstallFileStep.zig | 2 +- 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/lib/std/Build.zig b/lib/std/Build.zig index 3ebdba4c70..75fa7ec1dd 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -1661,13 +1661,20 @@ pub const FileSource = union(enum) { /// Should only be called during make(), returns a path relative to the build root or absolute. pub fn getPath(self: FileSource, src_builder: *Build) []const u8 { + return getPath2(self, src_builder, null); + } + + /// Should only be called during make(), returns a path relative to the build root or absolute. + /// asking_step is only used for debugging purposes; it's the step being run that is asking for + /// the path. + pub fn getPath2(self: FileSource, src_builder: *Build, asking_step: ?*Step) []const u8 { switch (self) { .path => |p| return src_builder.pathFromRoot(p), .generated => |gen| return gen.path orelse { std.debug.getStderrMutex().lock(); const stderr = std.io.getStdErr(); - dumpBadGetPathHelp(gen.step, stderr, src_builder) catch {}; - @panic("unable to get path"); + dumpBadGetPathHelp(gen.step, stderr, src_builder, asking_step) catch {}; + @panic("misconfigured build script"); }, } } @@ -1681,25 +1688,52 @@ pub const FileSource = union(enum) { } }; -fn dumpBadGetPathHelp(s: *Step, stderr: fs.File, src_builder: *Build) anyerror!void { - try stderr.writer().print( +/// In this function the stderr mutex has already been locked. +fn dumpBadGetPathHelp( + s: *Step, + stderr: fs.File, + src_builder: *Build, + asking_step: ?*Step, +) anyerror!void { + const w = stderr.writer(); + try w.print( \\getPath() was called on a GeneratedFile that wasn't built yet. \\ source package path: {s} \\ Is there a missing Step dependency on step '{s}'? - \\ The step was created by this stack trace: \\ , .{ src_builder.build_root.path orelse ".", s.name, }); + + const tty_config = std.debug.detectTTYConfig(stderr); + tty_config.setColor(w, .Red) catch {}; + try stderr.writeAll(" The step was created by this stack trace:\n"); + tty_config.setColor(w, .Reset) catch {}; + const debug_info = std.debug.getSelfDebugInfo() catch |err| { - try stderr.writer().print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{@errorName(err)}); + try w.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{@errorName(err)}); return; }; - std.debug.writeStackTrace(s.getStackTrace(), stderr.writer(), debug_info.allocator, debug_info, std.debug.detectTTYConfig(stderr)) catch |err| { + const ally = debug_info.allocator; + std.debug.writeStackTrace(s.getStackTrace(), w, ally, debug_info, tty_config) catch |err| { try stderr.writer().print("Unable to dump stack trace: {s}\n", .{@errorName(err)}); return; }; + if (asking_step) |as| { + tty_config.setColor(w, .Red) catch {}; + try stderr.writeAll(" The step that is missing a dependency on the above step was created by this stack trace:\n"); + tty_config.setColor(w, .Reset) catch {}; + + std.debug.writeStackTrace(as.getStackTrace(), w, ally, debug_info, tty_config) catch |err| { + try stderr.writer().print("Unable to dump stack trace: {s}\n", .{@errorName(err)}); + return; + }; + } + + tty_config.setColor(w, .Red) catch {}; + try stderr.writeAll(" Hope that helps. Proceeding to panic.\n"); + tty_config.setColor(w, .Reset) catch {}; } /// Allocates a new string for assigning a value to a named macro. diff --git a/lib/std/Build/InstallFileStep.zig b/lib/std/Build/InstallFileStep.zig index 70dee5d45d..e6c57a8050 100644 --- a/lib/std/Build/InstallFileStep.zig +++ b/lib/std/Build/InstallFileStep.zig @@ -38,7 +38,7 @@ pub fn init( fn make(step: *Step) !void { const self = @fieldParentPtr(InstallFileStep, "step", step); const src_builder = self.override_source_builder orelse self.builder; - const full_src_path = self.source.getPath(src_builder); + const full_src_path = self.source.getPath2(src_builder, step); const full_dest_path = self.builder.getInstallPath(self.dir, self.dest_rel_path); try self.builder.updateFile(full_src_path, full_dest_path); } From 7ebaa05bb138ea397859edf8ec96a9209482ae8e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 16 Feb 2023 15:03:05 -0700 Subject: [PATCH 143/294] std.Progress: add lock_stderr and unlock_stderr API users can take advantage of these to freely write to the terminal which has an ongoing progress display, similar to what Ninja does when compiling C/C++ objects and a warning or error message is printed. --- lib/std/Progress.zig | 65 +++++++++++++++++++++++++++++++------------- 1 file changed, 46 insertions(+), 19 deletions(-) diff --git a/lib/std/Progress.zig b/lib/std/Progress.zig index f0b0e2dbd5..64084e761f 100644 --- a/lib/std/Progress.zig +++ b/lib/std/Progress.zig @@ -192,32 +192,28 @@ pub fn refresh(self: *Progress) void { return self.refreshWithHeldLock(); } -fn refreshWithHeldLock(self: *Progress) void { - const is_dumb = !self.supports_ansi_escape_codes and !self.is_windows_terminal; - if (is_dumb and self.dont_print_on_dumb) return; - - const file = self.terminal orelse return; - - var end: usize = 0; - if (self.columns_written > 0) { +fn clearWithHeldLock(p: *Progress, end_ptr: *usize) void { + const file = p.terminal orelse return; + var end = end_ptr.*; + if (p.columns_written > 0) { // restore the cursor position by moving the cursor // `columns_written` cells to the left, then clear the rest of the // line - if (self.supports_ansi_escape_codes) { - end += (std.fmt.bufPrint(self.output_buffer[end..], "\x1b[{d}D", .{self.columns_written}) catch unreachable).len; - end += (std.fmt.bufPrint(self.output_buffer[end..], "\x1b[0K", .{}) catch unreachable).len; + if (p.supports_ansi_escape_codes) { + end += (std.fmt.bufPrint(p.output_buffer[end..], "\x1b[{d}D", .{p.columns_written}) catch unreachable).len; + end += (std.fmt.bufPrint(p.output_buffer[end..], "\x1b[0K", .{}) catch unreachable).len; } else if (builtin.os.tag == .windows) winapi: { - std.debug.assert(self.is_windows_terminal); + std.debug.assert(p.is_windows_terminal); var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined; if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) != windows.TRUE) { // stop trying to write to this file - self.terminal = null; + p.terminal = null; break :winapi; } var cursor_pos = windows.COORD{ - .X = info.dwCursorPosition.X - @intCast(windows.SHORT, self.columns_written), + .X = info.dwCursorPosition.X - @intCast(windows.SHORT, p.columns_written), .Y = info.dwCursorPosition.Y, }; @@ -235,7 +231,7 @@ fn refreshWithHeldLock(self: *Progress) void { &written, ) != windows.TRUE) { // stop trying to write to this file - self.terminal = null; + p.terminal = null; break :winapi; } if (windows.kernel32.FillConsoleOutputCharacterW( @@ -246,22 +242,33 @@ fn refreshWithHeldLock(self: *Progress) void { &written, ) != windows.TRUE) { // stop trying to write to this file - self.terminal = null; + p.terminal = null; break :winapi; } if (windows.kernel32.SetConsoleCursorPosition(file.handle, cursor_pos) != windows.TRUE) { // stop trying to write to this file - self.terminal = null; + p.terminal = null; break :winapi; } } else { // we are in a "dumb" terminal like in acme or writing to a file - self.output_buffer[end] = '\n'; + p.output_buffer[end] = '\n'; end += 1; } - self.columns_written = 0; + p.columns_written = 0; } + end_ptr.* = end; +} + +fn refreshWithHeldLock(self: *Progress) void { + const is_dumb = !self.supports_ansi_escape_codes and !self.is_windows_terminal; + if (is_dumb and self.dont_print_on_dumb) return; + + const file = self.terminal orelse return; + + var end: usize = 0; + clearWithHeldLock(self, &end); if (!self.done) { var need_ellipse = false; @@ -318,6 +325,26 @@ pub fn log(self: *Progress, comptime format: []const u8, args: anytype) void { self.columns_written = 0; } +/// Allows the caller to freely write to stderr until unlock_stderr() is called. +/// During the lock, the progress information is cleared from the terminal. +pub fn lock_stderr(p: *Progress) void { + p.update_mutex.lock(); + if (p.terminal) |file| { + var end: usize = 0; + clearWithHeldLock(p, &end); + _ = file.write(p.output_buffer[0..end]) catch { + // stop trying to write to this file + p.terminal = null; + }; + } + std.debug.getStderrMutex().lock(); +} + +pub fn unlock_stderr(p: *Progress) void { + std.debug.getStderrMutex().unlock(); + p.update_mutex.unlock(); +} + fn bufWrite(self: *Progress, end: *usize, comptime format: []const u8, args: anytype) void { if (std.fmt.bufPrint(self.output_buffer[end.*..], format, args)) |written| { const amt = written.len; From c5edd8b7f87f8432bbf058b3456393793118906e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 16 Feb 2023 15:04:10 -0700 Subject: [PATCH 144/294] std.Build: better handling of stderr of child processes With this commit, the build runner now communicates progress towards completion of the step graph to the terminal, while also printing the stderr of child processes as soon as possible, without clobbering each other, and without clobbering the CLI progress output. --- lib/build_runner.zig | 54 ++++++++++++++++++++++++++++++++++-------- lib/std/Build.zig | 52 ++++++++++++++-------------------------- lib/std/Build/Step.zig | 26 ++++++++++++++++++-- 3 files changed, 86 insertions(+), 46 deletions(-) diff --git a/lib/build_runner.zig b/lib/build_runner.zig index 6ba583c59d..c1af3798ff 100644 --- a/lib/build_runner.zig +++ b/lib/build_runner.zig @@ -243,14 +243,22 @@ pub fn main() !void { } } + var progress: std.Progress = .{}; + const main_progress_node = progress.start("", 0); + defer main_progress_node.end(); + builder.debug_log_scopes = debug_log_scopes.items; builder.resolveInstallPrefix(install_prefix, dir_list); - try builder.runBuild(root); + { + var prog_node = main_progress_node.start("user build.zig logic", 0); + defer prog_node.end(); + try builder.runBuild(root); + } if (builder.validateUserInputDidItFail()) usageAndErr(builder, true, stderr_stream); - runStepNames(builder, targets.items) catch |err| { + runStepNames(builder, targets.items, main_progress_node) catch |err| { switch (err) { error.UncleanExit => process.exit(1), else => return err, @@ -258,7 +266,11 @@ pub fn main() !void { }; } -fn runStepNames(b: *std.Build, step_names: []const []const u8) !void { +fn runStepNames( + b: *std.Build, + step_names: []const []const u8, + parent_prog_node: *std.Progress.Node, +) !void { var step_stack = ArrayList(*Step).init(b.allocator); defer step_stack.deinit(); @@ -289,6 +301,9 @@ fn runStepNames(b: *std.Build, step_names: []const []const u8) !void { defer thread_pool.deinit(); { + var step_prog = parent_prog_node.start("run steps", step_stack.items.len); + defer step_prog.end(); + var wait_group: std.Thread.WaitGroup = .{}; defer wait_group.wait(); @@ -301,8 +316,9 @@ fn runStepNames(b: *std.Build, step_names: []const []const u8) !void { const step = step_stack.items[i]; wait_group.start(); - thread_pool.spawn(workerMakeOneStep, .{ &wait_group, &thread_pool, b, step }) catch - @panic("OOM"); + thread_pool.spawn(workerMakeOneStep, .{ + &wait_group, &thread_pool, b, step, &step_prog, + }) catch @panic("OOM"); } } @@ -312,14 +328,18 @@ fn runStepNames(b: *std.Build, step_names: []const []const u8) !void { switch (s.state) { .precheck_unstarted => unreachable, .precheck_started => unreachable, - .precheck_done => unreachable, .running => unreachable, - .dependency_failure => continue, + // precheck_done is equivalent to dependency_failure in the case of + // transitive dependencies. For example: + // A -> B -> C (failure) + // B will be marked as dependency_failure, while A may never be queued, and thus + // remain in the initial state of precheck_done. + .dependency_failure, .precheck_done => continue, .success => continue, .failure => { any_failed = true; - std.debug.print("{s}: {s}\n{s}", .{ - s.name, @errorName(s.result.err_code), s.result.stderr, + std.debug.print("{s}: {s}\n", .{ + s.name, @errorName(s.result.err_code), }); }, } @@ -371,6 +391,7 @@ fn workerMakeOneStep( thread_pool: *std.Thread.Pool, b: *std.Build, s: *Step, + prog_node: *std.Progress.Node, ) void { defer wg.finish(); @@ -399,6 +420,10 @@ fn workerMakeOneStep( return; } + var sub_prog_node = prog_node.start(s.name, 0); + sub_prog_node.activate(); + defer sub_prog_node.end(); + // I suspect we will want to pass `b` to make() in a future modification. // For example, CompileStep does some sus things with modifying the saved // *Build object in install header steps that might be able to be removed @@ -406,6 +431,13 @@ fn workerMakeOneStep( s.make() catch |err| { s.result.err_code = err; @atomicStore(Step.State, &s.state, .failure, .SeqCst); + + sub_prog_node.context.lock_stderr(); + defer sub_prog_node.context.unlock_stderr(); + + for (s.result.error_msgs.items) |msg| { + std.io.getStdErr().writeAll(msg) catch return; + } return; }; @@ -414,7 +446,9 @@ fn workerMakeOneStep( // Successful completion of a step, so we queue up its dependants as well. for (s.dependants.items) |dep| { wg.start(); - thread_pool.spawn(workerMakeOneStep, .{ wg, thread_pool, b, dep }) catch @panic("OOM"); + thread_pool.spawn(workerMakeOneStep, .{ + wg, thread_pool, b, dep, prog_node, + }) catch @panic("OOM"); } } diff --git a/lib/std/Build.zig b/lib/std/Build.zig index 75fa7ec1dd..92ace4e60b 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -1417,59 +1417,43 @@ pub fn execFromStep(b: *Build, argv: []const []const u8, s: *Step) ![]u8 { } if (!process.can_spawn) { - s.result.stderr = b.fmt("Unable to spawn the following command: cannot spawn child processes\n{s}", .{ + try s.result.error_msgs.append(b.allocator, b.fmt("Unable to spawn the following command: cannot spawn child processes\n{s}", .{ try allocPrintCmd(b.allocator, null, argv), - }); + })); return error.CannotSpawnProcesses; } - var code: u8 = undefined; - const result = unwrapExecResult(&code, std.ChildProcess.exec(.{ + const result = std.ChildProcess.exec(.{ .allocator = b.allocator, .argv = argv, .env_map = b.env_map, .max_output_bytes = 10 * 1024 * 1024, - })) catch |err| switch (err) { - error.FileNotFound => { - s.result.stderr = b.fmt("unable to spawn the following command: file not found\n{s}", .{ - try allocPrintCmd(b.allocator, null, argv), - }); - return error.ExecFailed; - }, - error.ExitCodeFailure => { - s.result.stderr = b.fmt("the following command exited with error code {d}:\n{s}", .{ - code, try allocPrintCmd(b.allocator, null, argv), - }); - return error.ExecFailed; - }, - error.ProcessTerminated => { - s.result.stderr = b.fmt("the following command terminated unexpectedly:\n{s}", .{ - try allocPrintCmd(b.allocator, null, argv), - }); - return error.ExecFailed; - }, - else => |e| return e, + }) catch |err| { + try s.result.error_msgs.append(b.allocator, b.fmt("unable to spawn the following command: {s}\n{s}", .{ + @errorName(err), try allocPrintCmd(b.allocator, null, argv), + })); + return error.ExecFailed; }; - s.result.stderr = result.stderr; - return result.stdout; -} + if (result.stderr.len != 0) { + try s.result.error_msgs.append(b.allocator, result.stderr); + } -fn unwrapExecResult( - code_ptr: *u8, - wrapped: std.ChildProcess.ExecError!std.ChildProcess.ExecResult, -) !std.ChildProcess.ExecResult { - const result = try wrapped; switch (result.term) { .Exited => |code| { - code_ptr.* = code; if (code != 0) { + try s.result.error_msgs.append(b.allocator, b.fmt("the following command exited with error code {d}:\n{s}", .{ + code, try allocPrintCmd(b.allocator, null, argv), + })); return error.ExitCodeFailure; } - return result; + return result.stdout; }, .Signal, .Stopped, .Unknown => |code| { _ = code; + try s.result.error_msgs.append(b.allocator, b.fmt("the following command terminated unexpectedly:\n{s}", .{ + try allocPrintCmd(b.allocator, null, argv), + })); return error.ProcessTerminated; }, } diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig index 7bab2d9ae4..42698f2190 100644 --- a/lib/std/Build/Step.zig +++ b/lib/std/Build/Step.zig @@ -9,7 +9,7 @@ state: State, /// Populated only if state is success. result: struct { err_code: anyerror, - stderr: []u8, + error_msgs: std.ArrayListUnmanaged([]const u8), }, /// The return addresss associated with creation of this step that can be useful /// to print along with debugging messages. @@ -96,7 +96,7 @@ pub fn init(allocator: Allocator, options: Options) Step { .state = .precheck_unstarted, .result = .{ .err_code = undefined, - .stderr = &.{}, + .error_msgs = .{}, }, .debug_stack_trace = addresses, }; @@ -133,6 +133,28 @@ pub fn cast(step: *Step, comptime T: type) ?*T { return null; } +/// For debugging purposes, prints identifying information about this Step. +pub fn dump(step: *Step) void { + std.debug.getStderrMutex().lock(); + defer std.debug.getStderrMutex().unlock(); + + const stderr = std.io.getStdErr(); + const w = stderr.writer(); + const tty_config = std.debug.detectTTYConfig(stderr); + const debug_info = std.debug.getSelfDebugInfo() catch |err| { + w.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{ + @errorName(err), + }) catch {}; + return; + }; + const ally = debug_info.allocator; + w.print("name: '{s}'. creation stack trace:\n", .{step.name}) catch {}; + std.debug.writeStackTrace(step.getStackTrace(), w, ally, debug_info, tty_config) catch |err| { + stderr.writer().print("Unable to dump stack trace: {s}\n", .{@errorName(err)}) catch {}; + return; + }; +} + const Step = @This(); const std = @import("../std.zig"); const Build = std.Build; From 26486c7f235596ffdcbfcd44283a787b049561b8 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 16 Feb 2023 15:08:37 -0700 Subject: [PATCH 145/294] build runner: show stderr even on successful steps run --- lib/build_runner.zig | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/build_runner.zig b/lib/build_runner.zig index c1af3798ff..b2d89ba79c 100644 --- a/lib/build_runner.zig +++ b/lib/build_runner.zig @@ -428,16 +428,21 @@ fn workerMakeOneStep( // For example, CompileStep does some sus things with modifying the saved // *Build object in install header steps that might be able to be removed // by passing the *Build object through the make() functions. - s.make() catch |err| { - s.result.err_code = err; - @atomicStore(Step.State, &s.state, .failure, .SeqCst); + const make_result = s.make(); + // No matter the result, we want to display error/warning messages. + if (s.result.error_msgs.items.len > 0) { sub_prog_node.context.lock_stderr(); defer sub_prog_node.context.unlock_stderr(); for (s.result.error_msgs.items) |msg| { - std.io.getStdErr().writeAll(msg) catch return; + std.io.getStdErr().writeAll(msg) catch break; } + } + + make_result catch |err| { + s.result.err_code = err; + @atomicStore(Step.State, &s.state, .failure, .SeqCst); return; }; From 96d798db8b704a2fd367e58a19d6fe853a8a76a8 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 19 Feb 2023 10:14:59 -0700 Subject: [PATCH 146/294] update to new for loop syntax --- lib/build_runner.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/build_runner.zig b/lib/build_runner.zig index b2d89ba79c..24fd3440a3 100644 --- a/lib/build_runner.zig +++ b/lib/build_runner.zig @@ -279,7 +279,7 @@ fn runStepNames( } else { try step_stack.resize(step_names.len); - for (step_names) |step_name, i| { + for (step_names, 0..) |step_name, i| { const s = b.top_level_steps.get(step_name) orelse { std.debug.print("no step named '{s}'. Access the help menu with 'zig build -h'\n", .{step_name}); process.exit(1); From cb094700631ea1ae238ea678c192ce4f85fbecc0 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 19 Feb 2023 16:21:36 -0700 Subject: [PATCH 147/294] zig build: add a -j option for limiting concurrency --- lib/build_runner.zig | 20 ++++++++++++++++++-- lib/std/Thread/Pool.zig | 11 +++++++++-- src/main.zig | 4 ++-- 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/lib/build_runner.zig b/lib/build_runner.zig index 24fd3440a3..e12d46f03d 100644 --- a/lib/build_runner.zig +++ b/lib/build_runner.zig @@ -87,6 +87,7 @@ pub fn main() !void { var targets = ArrayList([]const u8).init(allocator); var debug_log_scopes = ArrayList([]const u8).init(allocator); + var thread_pool_options: std.Thread.Pool.Options = .{ .allocator = allocator }; const stderr_stream = io.getStdErr().writer(); const stdout_stream = io.getStdOut().writer(); @@ -231,6 +232,19 @@ pub fn main() !void { }; } else if (mem.eql(u8, arg, "-fno-reference-trace")) { builder.reference_trace = null; + } else if (mem.startsWith(u8, arg, "-j")) { + const num = arg["-j".len..]; + const n_jobs = std.fmt.parseUnsigned(u32, num, 10) catch |err| { + std.debug.print("unable to parse jobs count '{s}': {s}", .{ + num, @errorName(err), + }); + process.exit(1); + }; + if (n_jobs < 1) { + std.debug.print("number of jobs must be at least 1\n", .{}); + process.exit(1); + } + thread_pool_options.n_jobs = n_jobs; } else if (mem.eql(u8, arg, "--")) { builder.args = argsRest(args, arg_idx); break; @@ -258,7 +272,7 @@ pub fn main() !void { if (builder.validateUserInputDidItFail()) usageAndErr(builder, true, stderr_stream); - runStepNames(builder, targets.items, main_progress_node) catch |err| { + runStepNames(builder, targets.items, main_progress_node, thread_pool_options) catch |err| { switch (err) { error.UncleanExit => process.exit(1), else => return err, @@ -270,6 +284,7 @@ fn runStepNames( b: *std.Build, step_names: []const []const u8, parent_prog_node: *std.Progress.Node, + thread_pool_options: std.Thread.Pool.Options, ) !void { var step_stack = ArrayList(*Step).init(b.allocator); defer step_stack.deinit(); @@ -297,7 +312,7 @@ fn runStepNames( } var thread_pool: std.Thread.Pool = undefined; - try thread_pool.init(b.allocator); + try thread_pool.init(thread_pool_options); defer thread_pool.deinit(); { @@ -523,6 +538,7 @@ fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi \\ --verbose Print commands before executing them \\ --color [auto|off|on] Enable or disable colored error messages \\ --prominent-compile-errors Output compile errors formatted for a human to read + \\ -j Limit concurrent jobs (default is to use all CPU cores) \\ \\Project-Specific Options: \\ diff --git a/lib/std/Thread/Pool.zig b/lib/std/Thread/Pool.zig index 930befbac5..ed1a4dc052 100644 --- a/lib/std/Thread/Pool.zig +++ b/lib/std/Thread/Pool.zig @@ -17,7 +17,14 @@ const Runnable = struct { const RunProto = *const fn (*Runnable) void; -pub fn init(pool: *Pool, allocator: std.mem.Allocator) !void { +pub const Options = struct { + allocator: std.mem.Allocator, + n_jobs: ?u32 = null, +}; + +pub fn init(pool: *Pool, options: Options) !void { + const allocator = options.allocator; + pool.* = .{ .allocator = allocator, .threads = &[_]std.Thread{}, @@ -27,7 +34,7 @@ pub fn init(pool: *Pool, allocator: std.mem.Allocator) !void { return; } - const thread_count = std.math.max(1, std.Thread.getCpuCount() catch 1); + const thread_count = options.n_jobs orelse @max(1, std.Thread.getCpuCount() catch 1); pool.threads = try allocator.alloc(std.Thread, thread_count); errdefer allocator.free(pool.threads); diff --git a/src/main.zig b/src/main.zig index dd0faa628c..3a06d0272f 100644 --- a/src/main.zig +++ b/src/main.zig @@ -2999,7 +2999,7 @@ fn buildOutputType( defer zig_lib_directory.handle.close(); var thread_pool: ThreadPool = undefined; - try thread_pool.init(gpa); + try thread_pool.init(.{ .allocator = gpa }); defer thread_pool.deinit(); var libc_installation: ?LibCInstallation = null; @@ -4201,7 +4201,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi .basename = exe_basename, }; var thread_pool: ThreadPool = undefined; - try thread_pool.init(gpa); + try thread_pool.init(.{ .allocator = gpa }); defer thread_pool.deinit(); var cleanup_build_runner_dir: ?fs.Dir = null; From c641af3cba8f0e8e6bba0ba26dce18bd225a832e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 19 Feb 2023 16:23:59 -0700 Subject: [PATCH 148/294] CI: use -j1 like a coward Zig's build script has several race conditions preventing proper concurrent builds from working. By using -j1 for now, finishing this branch (concurrent zig builds) is untangled from the separate problem of correcting concurrency issues with zig's own build script. In other words, let's solve one problem at a time. --- ci/aarch64-linux-debug.sh | 1 + ci/aarch64-linux-release.sh | 1 + ci/aarch64-macos.sh | 1 + ci/x86_64-linux-debug.sh | 1 + ci/x86_64-linux-release.sh | 1 + ci/x86_64-macos-debug.sh | 1 + ci/x86_64-macos-release.sh | 1 + test/tests.zig | 4 ++++ 8 files changed, 11 insertions(+) diff --git a/ci/aarch64-linux-debug.sh b/ci/aarch64-linux-debug.sh index 94f40c557b..90ed8bbc35 100644 --- a/ci/aarch64-linux-debug.sh +++ b/ci/aarch64-linux-debug.sh @@ -60,6 +60,7 @@ stage3-debug/bin/zig build -Dtarget=arm-linux-musleabihf # TODO: add -fqemu back to this line stage3-debug/bin/zig build test docs \ + -j1 \ -fwasmtime \ -Dstatic-llvm \ -Dtarget=native-native-musl \ diff --git a/ci/aarch64-linux-release.sh b/ci/aarch64-linux-release.sh index 65d6063f25..e99e3e1c08 100644 --- a/ci/aarch64-linux-release.sh +++ b/ci/aarch64-linux-release.sh @@ -60,6 +60,7 @@ stage3-release/bin/zig build -Dtarget=arm-linux-musleabihf # TODO: add -fqemu back to this line stage3-release/bin/zig build test docs \ + -j1 \ -fwasmtime \ -Dstatic-llvm \ -Dtarget=native-native-musl \ diff --git a/ci/aarch64-macos.sh b/ci/aarch64-macos.sh index b4533e149f..634a60b600 100755 --- a/ci/aarch64-macos.sh +++ b/ci/aarch64-macos.sh @@ -44,6 +44,7 @@ PATH="$HOME/local/bin:$PATH" cmake .. \ $HOME/local/bin/ninja install stage3-release/bin/zig build test docs \ + -j1 \ --zig-lib-dir "$(pwd)/../lib" \ -Denable-macos-sdk \ -Dstatic-llvm \ diff --git a/ci/x86_64-linux-debug.sh b/ci/x86_64-linux-debug.sh index 7f2382f04a..1267a2d753 100755 --- a/ci/x86_64-linux-debug.sh +++ b/ci/x86_64-linux-debug.sh @@ -58,6 +58,7 @@ stage3-debug/bin/zig fmt --check .. \ stage3-debug/bin/zig build -Dtarget=arm-linux-musleabihf stage3-debug/bin/zig build test docs \ + -j1 \ -fqemu \ -fwasmtime \ -Dstatic-llvm \ diff --git a/ci/x86_64-linux-release.sh b/ci/x86_64-linux-release.sh index cdb24e4a6f..3305f5e951 100755 --- a/ci/x86_64-linux-release.sh +++ b/ci/x86_64-linux-release.sh @@ -59,6 +59,7 @@ stage3-release/bin/zig fmt --check .. \ stage3-release/bin/zig build -Dtarget=arm-linux-musleabihf stage3-release/bin/zig build test docs \ + -j1 \ -fqemu \ -fwasmtime \ -Dstatic-llvm \ diff --git a/ci/x86_64-macos-debug.sh b/ci/x86_64-macos-debug.sh index c24ff5d295..edd35970b8 100755 --- a/ci/x86_64-macos-debug.sh +++ b/ci/x86_64-macos-debug.sh @@ -48,6 +48,7 @@ cmake .. \ make $JOBS install stage3/bin/zig build test docs \ + -j1 \ --zig-lib-dir "$(pwd)/../lib" \ -Denable-macos-sdk \ -Dstatic-llvm \ diff --git a/ci/x86_64-macos-release.sh b/ci/x86_64-macos-release.sh index a4dedb4446..65dea3afcb 100755 --- a/ci/x86_64-macos-release.sh +++ b/ci/x86_64-macos-release.sh @@ -48,6 +48,7 @@ cmake .. \ make $JOBS install stage3/bin/zig build test docs \ + -j1 \ --zig-lib-dir "$(pwd)/../lib" \ -Denable-macos-sdk \ -Dstatic-llvm \ diff --git a/test/tests.zig b/test/tests.zig index e470a4f251..fe2efbc06e 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -1067,6 +1067,10 @@ pub const StandaloneContext = struct { zig_args.append(rel_zig_exe) catch unreachable; zig_args.append("build") catch unreachable; + // TODO: fix the various non-concurrency-safe issues in zig's standalone tests, + // and then remove this! + zig_args.append("-j1") catch @panic("OOM"); + zig_args.append("--build-file") catch unreachable; zig_args.append(b.pathFromRoot(build_file)) catch unreachable; From ee693bfe04522efe556cb416050326187f168aea Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 14 Jan 2022 18:03:59 -0700 Subject: [PATCH 149/294] std.os.linux: add ptrace --- lib/std/os/linux.zig | 54 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index b151e5f235..218ed71ddb 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -1820,6 +1820,23 @@ pub fn seccomp(operation: u32, flags: u32, args: ?*const anyopaque) usize { return syscall3(.seccomp, operation, flags, @ptrToInt(args)); } +pub fn ptrace( + req: u32, + pid: pid_t, + addr: usize, + data: usize, + addr2: usize, +) usize { + return syscall5( + .ptrace, + req, + @bitCast(usize, @as(isize, pid)), + addr, + data, + addr2, + ); +} + pub const E = switch (native_arch) { .mips, .mipsel => @import("linux/errno/mips.zig").E, .sparc, .sparcel, .sparc64 => @import("linux/errno/sparc.zig").E, @@ -5721,3 +5738,40 @@ pub const AUDIT = struct { } }; }; + +pub const PTRACE = struct { + pub const TRACEME = 0; + pub const PEEKTEXT = 1; + pub const PEEKDATA = 2; + pub const PEEKUSER = 3; + pub const POKETEXT = 4; + pub const POKEDATA = 5; + pub const POKEUSER = 6; + pub const CONT = 7; + pub const KILL = 8; + pub const SINGLESTEP = 9; + pub const GETREGS = 12; + pub const SETREGS = 13; + pub const GETFPREGS = 14; + pub const SETFPREGS = 15; + pub const ATTACH = 16; + pub const DETACH = 17; + pub const GETFPXREGS = 18; + pub const SETFPXREGS = 19; + pub const SYSCALL = 24; + pub const SETOPTIONS = 0x4200; + pub const GETEVENTMSG = 0x4201; + pub const GETSIGINFO = 0x4202; + pub const SETSIGINFO = 0x4203; + pub const GETREGSET = 0x4204; + pub const SETREGSET = 0x4205; + pub const SEIZE = 0x4206; + pub const INTERRUPT = 0x4207; + pub const LISTEN = 0x4208; + pub const PEEKSIGINFO = 0x4209; + pub const GETSIGMASK = 0x420a; + pub const SETSIGMASK = 0x420b; + pub const SECCOMP_GET_FILTER = 0x420c; + pub const SECCOMP_GET_METADATA = 0x420d; + pub const GET_SYSCALL_INFO = 0x420e; +}; From ae8e7c8f5a6065d12087aa547971001a399667b9 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 14 Jan 2022 17:04:03 -0700 Subject: [PATCH 150/294] stage2: hot code swapping PoC * CLI supports --listen to accept commands on a socket * make it able to produce an updated executable while it is running --- lib/std/child_process.zig | 1 + src/Compilation.zig | 7 ++ src/link.zig | 13 +++ src/main.zig | 187 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 208 insertions(+) diff --git a/lib/std/child_process.zig b/lib/std/child_process.zig index 3bdef3177a..f8cba85874 100644 --- a/lib/std/child_process.zig +++ b/lib/std/child_process.zig @@ -185,6 +185,7 @@ pub const ChildProcess = struct { } /// Blocks until child process terminates and then cleans up all resources. + /// TODO: set the pid to undefined in this function. pub fn wait(self: *ChildProcess) !Term { const term = if (builtin.os.tag == .windows) try self.waitWindows() diff --git a/src/Compilation.zig b/src/Compilation.zig index 63dd229ec5..83e0f0d5d9 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -5663,3 +5663,10 @@ pub fn compilerRtStrip(comp: Compilation) bool { return true; } } + +pub fn hotCodeSwap(comp: *Compilation, pid: std.os.pid_t) !void { + comp.bin_file.child_pid = pid; + try comp.makeBinFileWritable(); + try comp.update(); + try comp.makeBinFileExecutable(); +} diff --git a/src/link.zig b/src/link.zig index 1ecbeadd7f..34a16a1a1b 100644 --- a/src/link.zig +++ b/src/link.zig @@ -264,6 +264,8 @@ pub const File = struct { /// of this linking operation. lock: ?Cache.Lock = null, + child_pid: ?std.os.pid_t = null, + /// Attempts incremental linking, if the file already exists. If /// incremental linking fails, falls back to truncating the file and /// rewriting it. A malicious file is detected as incremental link failure @@ -376,6 +378,17 @@ pub const File = struct { if (build_options.only_c) unreachable; if (base.file != null) return; const emit = base.options.emit orelse return; + if (base.child_pid != null) { + // If we try to open the output file in write mode while it is running, + // it will return ETXTBSY. So instead, we copy the file, atomically rename it + // over top of the exe path, and then proceed normally. This changes the inode, + // avoiding the error. + const tmp_sub_path = try std.fmt.allocPrint(base.allocator, "{s}-{x}", .{ + emit.sub_path, std.crypto.random.int(u32), + }); + try emit.directory.handle.copyFile(emit.sub_path, emit.directory.handle, tmp_sub_path, .{}); + try emit.directory.handle.rename(tmp_sub_path, emit.sub_path); + } base.file = try emit.directory.handle.createFile(emit.sub_path, .{ .truncate = false, .read = true, diff --git a/src/main.zig b/src/main.zig index 3a06d0272f..a485fdb9e2 100644 --- a/src/main.zig +++ b/src/main.zig @@ -687,6 +687,7 @@ fn buildOutputType( var function_sections = false; var no_builtin = false; var watch = false; + var listen_addr: ?std.net.Ip4Address = null; var debug_compile_errors = false; var verbose_link = (builtin.os.tag != .wasi or builtin.link_libc) and std.process.hasEnvVarConstant("ZIG_VERBOSE_LINK"); var verbose_cc = (builtin.os.tag != .wasi or builtin.link_libc) and std.process.hasEnvVarConstant("ZIG_VERBOSE_CC"); @@ -1144,6 +1145,17 @@ fn buildOutputType( } else { try log_scopes.append(gpa, args_iter.nextOrFatal()); } + } else if (mem.eql(u8, arg, "--listen")) { + const next_arg = args_iter.nextOrFatal(); + // example: --listen 127.0.0.1:9000 + var it = std.mem.split(u8, next_arg, ":"); + const host = it.next().?; + const port_text = it.next() orelse "14735"; + const port = std.fmt.parseInt(u16, port_text, 10) catch |err| + fatal("invalid port number: '{s}': {s}", .{ port_text, @errorName(err) }); + listen_addr = std.net.Ip4Address.parse(host, port) catch |err| + fatal("invalid host: '{s}': {s}", .{ host, @errorName(err) }); + watch = true; } else if (mem.eql(u8, arg, "--debug-link-snapshot")) { if (!build_options.enable_link_snapshots) { std.log.warn("Zig was compiled without linker snapshots enabled (-Dlink-snapshot). --debug-link-snapshot has no effect.", .{}); @@ -3353,6 +3365,125 @@ fn buildOutputType( var last_cmd: ReplCmd = .help; + if (listen_addr) |ip4_addr| { + var server = std.net.StreamServer.init(.{ + .reuse_address = true, + }); + defer server.deinit(); + + try server.listen(.{ .in = ip4_addr }); + + while (true) { + const conn = try server.accept(); + defer conn.stream.close(); + + var buf: [100]u8 = undefined; + var child_pid: ?i32 = null; + + while (true) { + try comp.makeBinFileExecutable(); + + const amt = try conn.stream.read(&buf); + const line = buf[0..amt]; + const actual_line = mem.trimRight(u8, line, "\r\n "); + + const cmd: ReplCmd = blk: { + if (mem.eql(u8, actual_line, "update")) { + break :blk .update; + } else if (mem.eql(u8, actual_line, "exit")) { + break; + } else if (mem.eql(u8, actual_line, "help")) { + break :blk .help; + } else if (mem.eql(u8, actual_line, "run")) { + break :blk .run; + } else if (mem.eql(u8, actual_line, "update-and-run")) { + break :blk .update_and_run; + } else if (actual_line.len == 0) { + break :blk last_cmd; + } else { + try stderr.print("unknown command: {s}\n", .{actual_line}); + continue; + } + }; + last_cmd = cmd; + switch (cmd) { + .update => { + tracy.frameMark(); + if (output_mode == .Exe) { + try comp.makeBinFileWritable(); + } + updateModule(gpa, comp, hook) catch |err| switch (err) { + error.SemanticAnalyzeFail => continue, + else => |e| return e, + }; + }, + .help => { + try stderr.writeAll(repl_help); + }, + .run => { + tracy.frameMark(); + try runOrTest( + comp, + gpa, + arena, + test_exec_args.items, + self_exe_path.?, + arg_mode, + target_info, + watch, + &comp_destroyed, + all_args, + runtime_args_start, + link_libc, + ); + }, + .update_and_run => { + tracy.frameMark(); + if (child_pid) |pid| { + try conn.stream.writer().print("hot code swap requested for pid {d}", .{pid}); + try comp.hotCodeSwap(pid); + + var errors = try comp.getAllErrorsAlloc(); + defer errors.deinit(comp.gpa); + + if (errors.list.len != 0) { + const ttyconf: std.debug.TTY.Config = switch (comp.color) { + .auto => std.debug.detectTTYConfig(std.io.getStdErr()), + .on => .escape_codes, + .off => .no_color, + }; + for (errors.list) |full_err_msg| { + try full_err_msg.renderToWriter(ttyconf, conn.stream.writer(), "error:", .Red, 0); + } + continue; + } + } else { + if (output_mode == .Exe) { + try comp.makeBinFileWritable(); + } + updateModule(gpa, comp, hook) catch |err| switch (err) { + error.SemanticAnalyzeFail => continue, + else => |e| return e, + }; + try comp.makeBinFileExecutable(); + + child_pid = try runOrTestHotSwap( + comp, + gpa, + arena, + test_exec_args.items, + self_exe_path.?, + arg_mode, + all_args, + runtime_args_start, + ); + } + }, + } + } + } + } + while (watch) { try stderr.print("(zig) ", .{}); try comp.makeBinFileExecutable(); @@ -3631,6 +3762,62 @@ fn runOrTest( } } +fn runOrTestHotSwap( + comp: *Compilation, + gpa: Allocator, + arena: Allocator, + test_exec_args: []const ?[]const u8, + self_exe_path: []const u8, + arg_mode: ArgMode, + all_args: []const []const u8, + runtime_args_start: ?usize, +) !i32 { + const exe_emit = comp.bin_file.options.emit.?; + // A naive `directory.join` here will indeed get the correct path to the binary, + // however, in the case of cwd, we actually want `./foo` so that the path can be executed. + const exe_path = try fs.path.join(arena, &[_][]const u8{ + exe_emit.directory.path orelse ".", exe_emit.sub_path, + }); + + var argv = std.ArrayList([]const u8).init(gpa); + defer argv.deinit(); + + if (test_exec_args.len == 0) { + // when testing pass the zig_exe_path to argv + if (arg_mode == .zig_test) + try argv.appendSlice(&[_][]const u8{ + exe_path, self_exe_path, + }) + // when running just pass the current exe + else + try argv.appendSlice(&[_][]const u8{ + exe_path, + }); + } else { + for (test_exec_args) |arg| { + if (arg) |a| { + try argv.append(a); + } else { + try argv.appendSlice(&[_][]const u8{ + exe_path, self_exe_path, + }); + } + } + } + if (runtime_args_start) |i| { + try argv.appendSlice(all_args[i..]); + } + var child = std.ChildProcess.init(argv.items, arena); + + child.stdin_behavior = .Inherit; + child.stdout_behavior = .Inherit; + child.stderr_behavior = .Inherit; + + try child.spawn(); + + return child.pid; +} + const AfterUpdateHook = union(enum) { none, print_emit_bin_dir_path, From 50a2bb58d23a686f52b389c062f0299cc2a5f8ed Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 14 Jan 2022 18:04:11 -0700 Subject: [PATCH 151/294] link: PTRACE_ATTACH/PTRACE_DETACH --- src/link.zig | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/link.zig b/src/link.zig index 34a16a1a1b..a2f40eed65 100644 --- a/src/link.zig +++ b/src/link.zig @@ -378,7 +378,7 @@ pub const File = struct { if (build_options.only_c) unreachable; if (base.file != null) return; const emit = base.options.emit orelse return; - if (base.child_pid != null) { + if (base.child_pid) |pid| { // If we try to open the output file in write mode while it is running, // it will return ETXTBSY. So instead, we copy the file, atomically rename it // over top of the exe path, and then proceed normally. This changes the inode, @@ -388,6 +388,11 @@ pub const File = struct { }); try emit.directory.handle.copyFile(emit.sub_path, emit.directory.handle, tmp_sub_path, .{}); try emit.directory.handle.rename(tmp_sub_path, emit.sub_path); + + switch (std.os.errno(std.os.linux.ptrace(std.os.linux.PTRACE.ATTACH, pid, 0, 0, 0))) { + .SUCCESS => {}, + else => |errno| log.warn("ptrace failure: {s}", .{@tagName(errno)}), + } } base.file = try emit.directory.handle.createFile(emit.sub_path, .{ .truncate = false, @@ -437,6 +442,13 @@ pub const File = struct { } f.close(); base.file = null; + + if (base.child_pid) |pid| { + switch (std.os.errno(std.os.linux.ptrace(std.os.linux.PTRACE.DETACH, pid, 0, 0, 0))) { + .SUCCESS => {}, + else => |errno| log.warn("ptrace failure: {s}", .{@tagName(errno)}), + } + } }, .c, .spirv, .nvptx => {}, } From 4f4ddf5ef2f2e2ee2774e0a5311b099ea19d9ce2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 21 Jan 2022 18:47:29 -0700 Subject: [PATCH 152/294] hot code swapping PoC working - improve fn prototypes of process_vm_writev - make the memory writable in the ELF file - force the linker to always append the function - write updates with process_vm_writev --- lib/std/os/linux.zig | 20 ++++++++++---------- src/link/Elf.zig | 41 ++++++++++++++++++++++++++++++++++++++--- 2 files changed, 48 insertions(+), 13 deletions(-) diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index 218ed71ddb..15b0dbc17b 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -1716,26 +1716,26 @@ pub fn pidfd_send_signal(pidfd: fd_t, sig: i32, info: ?*siginfo_t, flags: u32) u ); } -pub fn process_vm_readv(pid: pid_t, local: [*]const iovec, local_count: usize, remote: [*]const iovec, remote_count: usize, flags: usize) usize { +pub fn process_vm_readv(pid: pid_t, local: []iovec, remote: []const iovec_const, flags: usize) usize { return syscall6( .process_vm_readv, @bitCast(usize, @as(isize, pid)), - @ptrToInt(local), - local_count, - @ptrToInt(remote), - remote_count, + @ptrToInt(local.ptr), + local.len, + @ptrToInt(remote.ptr), + remote.len, flags, ); } -pub fn process_vm_writev(pid: pid_t, local: [*]const iovec, local_count: usize, remote: [*]const iovec, remote_count: usize, flags: usize) usize { +pub fn process_vm_writev(pid: pid_t, local: []const iovec_const, remote: []const iovec_const, flags: usize) usize { return syscall6( .process_vm_writev, @bitCast(usize, @as(isize, pid)), - @ptrToInt(local), - local_count, - @ptrToInt(remote), - remote_count, + @ptrToInt(local.ptr), + local.len, + @ptrToInt(remote.ptr), + remote.len, flags, ); } diff --git a/src/link/Elf.zig b/src/link/Elf.zig index a91722d072..e69b937867 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -467,7 +467,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { .p_paddr = entry_addr, .p_memsz = file_size, .p_align = p_align, - .p_flags = elf.PF_X | elf.PF_R, + .p_flags = elf.PF_X | elf.PF_R | elf.PF_W, }); self.entry_addr = null; self.phdr_table_dirty = true; @@ -493,7 +493,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { .p_paddr = got_addr, .p_memsz = file_size, .p_align = p_align, - .p_flags = elf.PF_R, + .p_flags = elf.PF_R | elf.PF_W, }); self.phdr_table_dirty = true; } @@ -516,7 +516,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { .p_paddr = rodata_addr, .p_memsz = file_size, .p_align = p_align, - .p_flags = elf.PF_R, + .p_flags = elf.PF_R | elf.PF_W, }); self.phdr_table_dirty = true; } @@ -2451,6 +2451,23 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s const phdr_index = self.sections.items(.phdr_index)[shdr_index]; const section_offset = local_sym.st_value - self.program_headers.items[phdr_index].p_vaddr; const file_offset = self.sections.items(.shdr)[shdr_index].sh_offset + section_offset; + + if (self.base.child_pid) |pid| { + var code_vec: [1]std.os.iovec_const = .{.{ + .iov_base = code.ptr, + .iov_len = code.len, + }}; + var remote_vec: [1]std.os.iovec_const = .{.{ + .iov_base = @intToPtr([*]u8, local_sym.st_value), + .iov_len = code.len, + }}; + const rc = std.os.linux.process_vm_writev(pid, &code_vec, &remote_vec, 0); + switch (std.os.errno(rc)) { + .SUCCESS => assert(rc == code.len), + else => |errno| log.warn("process_vm_writev failure: {s}", .{@tagName(errno)}), + } + } + try self.base.file.?.pwriteAll(code, file_offset); return local_sym; @@ -2820,6 +2837,8 @@ fn writeOffsetTableEntry(self: *Elf, index: usize) !void { const endian = self.base.options.target.cpu.arch.endian(); const shdr = &self.sections.items(.shdr)[self.got_section_index.?]; const off = shdr.sh_offset + @as(u64, entry_size) * index; + const phdr = &self.program_headers.items[self.phdr_got_index.?]; + const vaddr = phdr.p_vaddr + @as(u64, entry_size) * index; switch (entry_size) { 2 => { var buf: [2]u8 = undefined; @@ -2835,6 +2854,22 @@ fn writeOffsetTableEntry(self: *Elf, index: usize) !void { var buf: [8]u8 = undefined; mem.writeInt(u64, &buf, self.offset_table.items[index], endian); try self.base.file.?.pwriteAll(&buf, off); + + if (self.base.child_pid) |pid| { + var local_vec: [1]std.os.iovec_const = .{.{ + .iov_base = &buf, + .iov_len = buf.len, + }}; + var remote_vec: [1]std.os.iovec_const = .{.{ + .iov_base = @intToPtr([*]u8, vaddr), + .iov_len = buf.len, + }}; + const rc = std.os.linux.process_vm_writev(pid, &local_vec, &remote_vec, 0); + switch (std.os.errno(rc)) { + .SUCCESS => assert(rc == buf.len), + else => |errno| log.warn("process_vm_writev failure: {s}", .{@tagName(errno)}), + } + } }, else => unreachable, } From c911de825b3b9932e84345f6cec910553b21e203 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 21 Feb 2023 11:44:03 -0700 Subject: [PATCH 153/294] link.Elf: keep the logic for updates but condition on hcs --- src/link/Elf.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index e69b937867..b849dcc9d2 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -2166,7 +2166,7 @@ fn allocateAtom(self: *Elf, atom_index: Atom.Index, new_block_size: u64, alignme // First we look for an appropriately sized free list node. // The list is unordered. We'll just take the first thing that works. const vaddr = blk: { - var i: usize = 0; + var i: usize = if (self.base.child_pid == null) 0 else free_list.items.len; while (i < free_list.items.len) { const big_atom_index = free_list.items[i]; const big_atom = self.getAtom(big_atom_index); @@ -2397,7 +2397,7 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s const atom = self.getAtom(atom_index); const shdr_index = decl_metadata.shdr; - if (atom.getSymbol(self).st_size != 0) { + if (atom.getSymbol(self).st_size != 0 and self.base.child_pid == null) { const local_sym = atom.getSymbolPtr(self); local_sym.st_name = try self.shstrtab.insert(gpa, decl_name); local_sym.st_info = (elf.STB_LOCAL << 4) | stt_bits; From 4db5bc7b2132d8794d98077a67fc410be9dc98bd Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 23 Feb 2023 16:18:15 -0700 Subject: [PATCH 154/294] std.mem.copy: update to new for loop syntax --- lib/std/mem.zig | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/lib/std/mem.zig b/lib/std/mem.zig index b9b5fb1004..a486713e1c 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -196,13 +196,8 @@ test "Allocator.resize" { /// dest.len must be >= source.len. /// If the slices overlap, dest.ptr must be <= src.ptr. pub fn copy(comptime T: type, dest: []T, source: []const T) void { - // TODO instead of manually doing this check for the whole array - // and turning off runtime safety, the compiler should detect loops like - // this and automatically omit safety checks for loops - @setRuntimeSafety(false); - assert(dest.len >= source.len); - for (source, 0..) |s, i| - dest[i] = s; + for (dest[0..source.len], source) |*d, s| + d.* = s; } /// Copy all of source into dest at position 0. @@ -611,8 +606,8 @@ test "lessThan" { pub fn eql(comptime T: type, a: []const T, b: []const T) bool { if (a.len != b.len) return false; if (a.ptr == b.ptr) return true; - for (a, 0..) |item, index| { - if (b[index] != item) return false; + for (a, b) |a_elem, b_elem| { + if (a_elem != b_elem) return false; } return true; } From 572cb24d1a4f70c662ddf17df72d27dec44bc4fc Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 23 Feb 2023 16:18:43 -0700 Subject: [PATCH 155/294] progress towards semantic error serialization Introduces std.zig.ErrorBundle which is a trivially serializeable set of compilation errors. This is in the standard library so that both the compiler and the build runner can use it. The idea is they will use it to communicate compilation errors over a binary protocol. The binary encoding of ErrorBundle is a bit problematic - I got a little too aggressive with compaction. I need to change it in a follow-up commit to use some indirection in the error message list, otherwise iteration is too unergonomic. In fact it's so problematic right now that the logic getAllErrorsAlloc() actually fails to produce a viable ErrorBundle because it puts SourceLocation data in between the root level ErrorMessage data. This commit has a simplification - redundant logic for rendering AST errors to stderr has been removed in favor of moving the logic for lowering AST errors into AstGen. So even if we get parse errors, the errors will get lowered into ZIR before being reported. I believe this will be useful when working on --autofix. Either way, some redundant brittle logic was happily deleted. In Compilation, updateSubCompilation() is improved to properly perform error reporting when a sub-compilation object fails. It no longer dumps directly to stderr; instead it populates an ErrorBundle object, which gets added to the parent one during getAllErrorsAlloc(). In package fetching code, instead of dumping directly to stderr, it now populates an ErrorBundle object, and gets properly reported at the CLI layer of abstraction. --- lib/std/zig.zig | 1 + lib/std/zig/ErrorBundle.zig | 419 ++++++++++++++++ src/AstGen.zig | 96 +++- src/Compilation.zig | 947 +++++++++++++++--------------------- src/Module.zig | 60 +-- src/Package.zig | 108 ++-- src/Sema.zig | 25 +- src/glibc.zig | 10 +- src/libcxx.zig | 4 +- src/libtsan.zig | 2 +- src/libunwind.zig | 2 +- src/main.zig | 263 ++++------ src/mingw.zig | 12 +- src/musl.zig | 14 +- src/wasi_libc.zig | 14 +- 15 files changed, 1068 insertions(+), 909 deletions(-) create mode 100644 lib/std/zig/ErrorBundle.zig diff --git a/lib/std/zig.zig b/lib/std/zig.zig index f85cf75e60..ecff8e99bd 100644 --- a/lib/std/zig.zig +++ b/lib/std/zig.zig @@ -3,6 +3,7 @@ const tokenizer = @import("zig/tokenizer.zig"); const fmt = @import("zig/fmt.zig"); const assert = std.debug.assert; +pub const ErrorBundle = @import("zig/ErrorBundle.zig"); pub const Token = tokenizer.Token; pub const Tokenizer = tokenizer.Tokenizer; pub const fmtId = fmt.fmtId; diff --git a/lib/std/zig/ErrorBundle.zig b/lib/std/zig/ErrorBundle.zig new file mode 100644 index 0000000000..dee19818a8 --- /dev/null +++ b/lib/std/zig/ErrorBundle.zig @@ -0,0 +1,419 @@ +//! To support incremental compilation, errors are stored in various places +//! so that they can be created and destroyed appropriately. This structure +//! is used to collect all the errors from the various places into one +//! convenient place for API users to consume. + +string_bytes: std.ArrayListUnmanaged(u8), +/// The first thing in this array is a ErrorMessageListIndex. +extra: std.ArrayListUnmanaged(u32), + +// An index into `extra` pointing at an `ErrorMessage`. +pub const MessageIndex = enum(u32) { + _, +}; + +/// After the header is: +/// * string_bytes +/// * extra (little endian) +pub const Header = struct { + string_bytes_len: u32, + extra_len: u32, +}; + +/// Trailing: ErrorMessage for each len +pub const ErrorMessageList = struct { + len: u32, + start: u32, +}; + +/// Trailing: +/// * ReferenceTrace for each reference_trace_len +pub const SourceLocation = struct { + /// null terminated string index + src_path: u32, + line: u32, + column: u32, + /// byte offset of starting token + span_start: u32, + /// byte offset of main error location + span_main: u32, + /// byte offset of end of last token + span_end: u32, + /// null terminated string index, possibly null. + /// Does not include the trailing newline. + source_line: u32 = 0, + reference_trace_len: u32 = 0, +}; + +/// Trailing: +/// * ErrorMessage for each notes_len. +pub const ErrorMessage = struct { + /// null terminated string index + msg: u32, + /// Usually one, but incremented for redundant messages. + count: u32 = 1, + /// 0 or the index into extra of a SourceLocation + src_loc: u32 = 0, + notes_len: u32 = 0, +}; + +pub const ReferenceTrace = struct { + /// null terminated string index + /// Except for the sentinel ReferenceTrace element, in which case: + /// * 0 means remaining references hidden + /// * >0 means N references hidden + decl_name: u32, + /// Index into extra of a SourceLocation + /// If this is 0, this is the sentinel ReferenceTrace element. + src_loc: u32, +}; + +pub fn init(eb: *ErrorBundle, gpa: Allocator) !void { + eb.* = .{ + .string_bytes = .{}, + .extra = .{}, + }; + + // So that 0 can be used to indicate a null string. + try eb.string_bytes.append(gpa, 0); + + _ = try addExtra(eb, gpa, ErrorMessageList{ + .len = 0, + .start = 0, + }); +} + +pub fn deinit(eb: *ErrorBundle, gpa: Allocator) void { + eb.string_bytes.deinit(gpa); + eb.extra.deinit(gpa); + eb.* = undefined; +} + +pub fn addString(eb: *ErrorBundle, gpa: Allocator, s: []const u8) !u32 { + const index = @intCast(u32, eb.string_bytes.items.len); + try eb.string_bytes.ensureUnusedCapacity(gpa, s.len + 1); + eb.string_bytes.appendSliceAssumeCapacity(s); + eb.string_bytes.appendAssumeCapacity(0); + return index; +} + +pub fn printString(eb: *ErrorBundle, gpa: Allocator, comptime fmt: []const u8, args: anytype) !u32 { + const index = @intCast(u32, eb.string_bytes.items.len); + try eb.string_bytes.writer(gpa).print(fmt, args); + try eb.string_bytes.append(gpa, 0); + return index; +} + +pub fn addErrorMessage(eb: *ErrorBundle, gpa: Allocator, em: ErrorMessage) !void { + if (eb.errorMessageCount() == 0) { + eb.setStartIndex(@intCast(u32, eb.extra.items.len)); + } + _ = try addExtra(eb, gpa, em); +} + +pub fn addSourceLocation(eb: *ErrorBundle, gpa: Allocator, sl: SourceLocation) !u32 { + return addExtra(eb, gpa, sl); +} + +pub fn addReferenceTrace(eb: *ErrorBundle, gpa: Allocator, rt: ReferenceTrace) !void { + _ = try addExtra(eb, gpa, rt); +} + +pub fn addBundle(eb: *ErrorBundle, gpa: Allocator, other: ErrorBundle) !void { + // Skip over the initial ErrorMessageList len field. + const root_fields_len = @typeInfo(ErrorMessageList).Struct.fields.len; + const other_list = other.extraData(ErrorMessageList, 0).data; + const other_extra = other.extra.items[root_fields_len..]; + + try eb.string_bytes.ensureUnusedCapacity(gpa, other.string_bytes.items.len); + try eb.extra.ensureUnusedCapacity(gpa, other_extra.len); + + const new_string_base = @intCast(u32, eb.string_bytes.items.len); + const new_data_base = @intCast(u32, eb.extra.items.len - root_fields_len); + + eb.string_bytes.appendSliceAssumeCapacity(other.string_bytes.items); + eb.extra.appendSliceAssumeCapacity(other_extra); + + // Now we must offset the string indexes and extra indexes of the newly + // added extra. + var index = new_data_base + other_list.start; + for (0..other_list.len) |_| { + index = try patchMessage(eb, index, new_string_base, new_data_base); + } +} + +fn patchMessage(eb: *ErrorBundle, msg_idx: usize, new_string_base: u32, new_data_base: u32) !u32 { + var msg = eb.extraData(ErrorMessage, msg_idx); + if (msg.data.msg != 0) msg.data.msg += new_string_base; + if (msg.data.src_loc != 0) msg.data.src_loc += new_data_base; + eb.setExtra(msg_idx, msg.data); + + try patchSrcLoc(eb, msg.data.src_loc, new_string_base, new_data_base); + + var index = @intCast(u32, msg.end); + for (0..msg.data.notes_len) |_| { + index = try patchMessage(eb, index, new_string_base, new_data_base); + } + return index; +} + +fn patchSrcLoc(eb: *ErrorBundle, idx: usize, new_string_base: u32, new_data_base: u32) !void { + if (idx == 0) return; + + var src_loc = eb.extraData(SourceLocation, idx); + if (src_loc.data.src_path != 0) src_loc.data.src_path += new_string_base; + if (src_loc.data.source_line != 0) src_loc.data.source_line += new_string_base; + eb.setExtra(idx, src_loc.data); + + var index = src_loc.end; + for (0..src_loc.data.reference_trace_len) |_| { + var ref_trace = eb.extraData(ReferenceTrace, index); + if (ref_trace.data.decl_name != 0) ref_trace.data.decl_name += new_string_base; + if (ref_trace.data.src_loc != 0) ref_trace.data.src_loc += new_data_base; + eb.setExtra(index, ref_trace.data); + try patchSrcLoc(eb, ref_trace.data.src_loc, new_string_base, new_data_base); + index = ref_trace.end; + } +} + +fn addExtra(eb: *ErrorBundle, gpa: Allocator, extra: anytype) Allocator.Error!u32 { + const fields = @typeInfo(@TypeOf(extra)).Struct.fields; + try eb.extra.ensureUnusedCapacity(gpa, fields.len); + return addExtraAssumeCapacity(eb, extra); +} + +fn addExtraAssumeCapacity(eb: *ErrorBundle, extra: anytype) u32 { + const fields = @typeInfo(@TypeOf(extra)).Struct.fields; + const result = @intCast(u32, eb.extra.items.len); + eb.extra.items.len += fields.len; + setExtra(eb, result, extra); + return result; +} + +fn setExtra(eb: *ErrorBundle, index: usize, extra: anytype) void { + const fields = @typeInfo(@TypeOf(extra)).Struct.fields; + var i = index; + inline for (fields) |field| { + eb.extra.items[i] = switch (field.type) { + u32 => @field(extra, field.name), + else => @compileError("bad field type"), + }; + i += 1; + } +} + +pub fn errorMessageCount(eb: ErrorBundle) u32 { + return eb.extra.items[0]; +} + +pub fn setErrorMessageCount(eb: *ErrorBundle, count: u32) void { + eb.extra.items[0] = count; +} + +pub fn incrementCount(eb: *ErrorBundle, delta: u32) void { + eb.extra.items[0] += delta; +} + +pub fn getStartIndex(eb: ErrorBundle) u32 { + return eb.extra.items[1]; +} + +pub fn setStartIndex(eb: *ErrorBundle, index: u32) void { + eb.extra.items[1] = index; +} + +pub fn getErrorMessage(eb: ErrorBundle, index: MessageIndex) ErrorMessage { + return eb.extraData(ErrorMessage, @enumToInt(index)).data; +} + +pub fn getSourceLocation(eb: ErrorBundle, index: u32) SourceLocation { + assert(index != 0); + return eb.extraData(SourceLocation, index).data; +} + +/// Returns the requested data, as well as the new index which is at the start of the +/// trailers for the object. +fn extraData(eb: ErrorBundle, comptime T: type, index: usize) struct { data: T, end: usize } { + const fields = @typeInfo(T).Struct.fields; + var i: usize = index; + var result: T = undefined; + inline for (fields) |field| { + @field(result, field.name) = switch (field.type) { + u32 => eb.extra.items[i], + else => @compileError("bad field type"), + }; + i += 1; + } + return .{ + .data = result, + .end = i, + }; +} + +/// Given an index into `string_bytes` returns the null-terminated string found there. +pub fn nullTerminatedString(eb: ErrorBundle, index: usize) [:0]const u8 { + const string_bytes = eb.string_bytes.items; + var end: usize = index; + while (string_bytes[end] != 0) { + end += 1; + } + return string_bytes[index..end :0]; +} + +pub fn renderToStdErr(eb: ErrorBundle, ttyconf: std.debug.TTY.Config) void { + std.debug.getStderrMutex().lock(); + defer std.debug.getStderrMutex().unlock(); + const stderr = std.io.getStdErr(); + return renderToWriter(eb, ttyconf, stderr.writer()) catch return; +} + +pub fn renderToWriter( + eb: ErrorBundle, + ttyconf: std.debug.TTY.Config, + writer: anytype, +) anyerror!void { + const list = eb.extraData(ErrorMessageList, 0).data; + var index: usize = list.start; + for (0..list.len) |_| { + const err_msg = eb.extraData(ErrorMessage, index); + index = try renderErrorMessageToWriter(eb, err_msg.data, err_msg.end, ttyconf, writer, "error", .Red, 0); + } +} + +fn renderErrorMessageToWriter( + eb: ErrorBundle, + err_msg: ErrorMessage, + end_index: usize, + ttyconf: std.debug.TTY.Config, + stderr: anytype, + kind: []const u8, + color: std.debug.TTY.Color, + indent: usize, +) anyerror!usize { + var counting_writer = std.io.countingWriter(stderr); + const counting_stderr = counting_writer.writer(); + if (err_msg.src_loc != 0) { + const src = eb.extraData(SourceLocation, err_msg.src_loc); + try counting_stderr.writeByteNTimes(' ', indent); + try ttyconf.setColor(stderr, .Bold); + try counting_stderr.print("{s}:{d}:{d}: ", .{ + eb.nullTerminatedString(src.data.src_path), + src.data.line + 1, + src.data.column + 1, + }); + try ttyconf.setColor(stderr, color); + try counting_stderr.writeAll(kind); + try counting_stderr.writeAll(": "); + // This is the length of the part before the error message: + // e.g. "file.zig:4:5: error: " + const prefix_len = @intCast(usize, counting_stderr.context.bytes_written); + try ttyconf.setColor(stderr, .Reset); + try ttyconf.setColor(stderr, .Bold); + if (err_msg.count == 1) { + try writeMsg(eb, err_msg, stderr, prefix_len); + try stderr.writeByte('\n'); + } else { + try writeMsg(eb, err_msg, stderr, prefix_len); + try ttyconf.setColor(stderr, .Dim); + try stderr.print(" ({d} times)\n", .{err_msg.count}); + } + try ttyconf.setColor(stderr, .Reset); + if (src.data.source_line != 0) { + const line = eb.nullTerminatedString(src.data.source_line); + for (line) |b| switch (b) { + '\t' => try stderr.writeByte(' '), + else => try stderr.writeByte(b), + }; + try stderr.writeByte('\n'); + // TODO basic unicode code point monospace width + const before_caret = src.data.span_main - src.data.span_start; + // -1 since span.main includes the caret + const after_caret = src.data.span_end - src.data.span_main -| 1; + try stderr.writeByteNTimes(' ', src.data.column - before_caret); + try ttyconf.setColor(stderr, .Green); + try stderr.writeByteNTimes('~', before_caret); + try stderr.writeByte('^'); + try stderr.writeByteNTimes('~', after_caret); + try stderr.writeByte('\n'); + try ttyconf.setColor(stderr, .Reset); + } + var index = end_index; + for (0..err_msg.notes_len) |_| { + const note = eb.extraData(ErrorMessage, index); + index = try renderErrorMessageToWriter(eb, note.data, note.end, ttyconf, stderr, "note", .Cyan, indent); + } + if (src.data.reference_trace_len > 0) { + try ttyconf.setColor(stderr, .Reset); + try ttyconf.setColor(stderr, .Dim); + try stderr.print("referenced by:\n", .{}); + var ref_index = src.end; + for (0..src.data.reference_trace_len) |_| { + const ref_trace = eb.extraData(ReferenceTrace, ref_index); + ref_index = ref_trace.end; + if (ref_trace.data.src_loc != 0) { + const ref_src = eb.getSourceLocation(ref_trace.data.src_loc); + try stderr.print(" {s}: {s}:{d}:{d}\n", .{ + eb.nullTerminatedString(ref_trace.data.decl_name), + eb.nullTerminatedString(ref_src.src_path), + ref_src.line + 1, + ref_src.column + 1, + }); + } else if (ref_trace.data.decl_name != 0) { + const count = ref_trace.data.decl_name; + try stderr.print( + " {d} reference(s) hidden; use '-freference-trace={d}' to see all references\n", + .{ count, count + src.data.reference_trace_len - 1 }, + ); + } else { + try stderr.print( + " remaining reference traces hidden; use '-freference-trace' to see all reference traces\n", + .{}, + ); + } + } + try stderr.writeByte('\n'); + try ttyconf.setColor(stderr, .Reset); + } + return index; + } else { + try ttyconf.setColor(stderr, color); + try stderr.writeByteNTimes(' ', indent); + try stderr.writeAll(kind); + try stderr.writeAll(": "); + try ttyconf.setColor(stderr, .Reset); + const msg = eb.nullTerminatedString(err_msg.msg); + if (err_msg.count == 1) { + try stderr.print("{s}\n", .{msg}); + } else { + try stderr.print("{s}", .{msg}); + try ttyconf.setColor(stderr, .Dim); + try stderr.print(" ({d} times)\n", .{err_msg.count}); + } + try ttyconf.setColor(stderr, .Reset); + var index = end_index; + for (0..err_msg.notes_len) |_| { + const note = eb.extraData(ErrorMessage, index); + index = try renderErrorMessageToWriter(eb, note.data, note.end, ttyconf, stderr, "note", .Cyan, indent + 4); + } + return index; + } +} + +/// Splits the error message up into lines to properly indent them +/// to allow for long, good-looking error messages. +/// +/// This is used to split the message in `@compileError("hello\nworld")` for example. +fn writeMsg(eb: ErrorBundle, err_msg: ErrorMessage, stderr: anytype, indent: usize) !void { + var lines = std.mem.split(u8, eb.nullTerminatedString(err_msg.msg), "\n"); + while (lines.next()) |line| { + try stderr.writeAll(line); + if (lines.index == null) break; + try stderr.writeByte('\n'); + try stderr.writeByteNTimes(' ', indent); + } +} + +const std = @import("std"); +const ErrorBundle = @This(); +const Allocator = std.mem.Allocator; +const assert = std.debug.assert; diff --git a/src/AstGen.zig b/src/AstGen.zig index 7b2138a535..8ac67a7107 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -133,6 +133,8 @@ pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir { try astgen.extra.ensureTotalCapacity(gpa, tree.nodes.len + reserved_count); astgen.extra.items.len += reserved_count; + try lowerAstErrors(&astgen); + var top_scope: Scope.Top = .{}; var gz_instructions: std.ArrayListUnmanaged(Zir.Inst.Index) = .{}; @@ -10401,27 +10403,11 @@ fn appendErrorTokNotes( args: anytype, notes: []const u32, ) !void { - @setCold(true); - const string_bytes = &astgen.string_bytes; - const msg = @intCast(u32, string_bytes.items.len); - try string_bytes.writer(astgen.gpa).print(format ++ "\x00", args); - const notes_index: u32 = if (notes.len != 0) blk: { - const notes_start = astgen.extra.items.len; - try astgen.extra.ensureTotalCapacity(astgen.gpa, notes_start + 1 + notes.len); - astgen.extra.appendAssumeCapacity(@intCast(u32, notes.len)); - astgen.extra.appendSliceAssumeCapacity(notes); - break :blk @intCast(u32, notes_start); - } else 0; - try astgen.compile_errors.append(astgen.gpa, .{ - .msg = msg, - .node = 0, - .token = token, - .byte_offset = 0, - .notes = notes_index, - }); + return appendErrorTokNotesOff(astgen, token, 0, format, args, notes); } -/// Same as `fail`, except given an absolute byte offset. +/// Same as `fail`, except given a token plus an offset from its starting byte +/// offset. fn failOff( astgen: *AstGen, token: Ast.TokenIndex, @@ -10429,27 +10415,36 @@ fn failOff( comptime format: []const u8, args: anytype, ) InnerError { - try appendErrorOff(astgen, token, byte_offset, format, args); + try appendErrorTokNotesOff(astgen, token, byte_offset, format, args, &.{}); return error.AnalysisFail; } -fn appendErrorOff( +fn appendErrorTokNotesOff( astgen: *AstGen, token: Ast.TokenIndex, byte_offset: u32, comptime format: []const u8, args: anytype, -) Allocator.Error!void { + notes: []const u32, +) !void { @setCold(true); + const gpa = astgen.gpa; const string_bytes = &astgen.string_bytes; const msg = @intCast(u32, string_bytes.items.len); - try string_bytes.writer(astgen.gpa).print(format ++ "\x00", args); - try astgen.compile_errors.append(astgen.gpa, .{ + try string_bytes.writer(gpa).print(format ++ "\x00", args); + const notes_index: u32 = if (notes.len != 0) blk: { + const notes_start = astgen.extra.items.len; + try astgen.extra.ensureTotalCapacity(gpa, notes_start + 1 + notes.len); + astgen.extra.appendAssumeCapacity(@intCast(u32, notes.len)); + astgen.extra.appendSliceAssumeCapacity(notes); + break :blk @intCast(u32, notes_start); + } else 0; + try astgen.compile_errors.append(gpa, .{ .msg = msg, .node = 0, .token = token, .byte_offset = byte_offset, - .notes = 0, + .notes = notes_index, }); } @@ -10458,6 +10453,16 @@ fn errNoteTok( token: Ast.TokenIndex, comptime format: []const u8, args: anytype, +) Allocator.Error!u32 { + return errNoteTokOff(astgen, token, 0, format, args); +} + +fn errNoteTokOff( + astgen: *AstGen, + token: Ast.TokenIndex, + byte_offset: u32, + comptime format: []const u8, + args: anytype, ) Allocator.Error!u32 { @setCold(true); const string_bytes = &astgen.string_bytes; @@ -10467,7 +10472,7 @@ fn errNoteTok( .msg = msg, .node = 0, .token = token, - .byte_offset = 0, + .byte_offset = byte_offset, .notes = 0, }); } @@ -12634,3 +12639,42 @@ fn emitDbgStmt(gz: *GenZir, line: u32, column: u32) !void { }, } }); } + +fn lowerAstErrors(astgen: *AstGen) !void { + const tree = astgen.tree; + if (tree.errors.len == 0) return; + + const gpa = astgen.gpa; + const parse_err = tree.errors[0]; + + var msg: std.ArrayListUnmanaged(u8) = .{}; + defer msg.deinit(gpa); + + const token_starts = tree.tokens.items(.start); + const token_tags = tree.tokens.items(.tag); + + var notes: std.ArrayListUnmanaged(u32) = .{}; + defer notes.deinit(gpa); + + if (token_tags[parse_err.token + @boolToInt(parse_err.token_is_prev)] == .invalid) { + const tok = parse_err.token + @boolToInt(parse_err.token_is_prev); + const bad_off = @intCast(u32, tree.tokenSlice(parse_err.token + @boolToInt(parse_err.token_is_prev)).len); + const byte_abs = token_starts[parse_err.token + @boolToInt(parse_err.token_is_prev)] + bad_off; + try notes.append(gpa, try astgen.errNoteTokOff(tok, bad_off, "invalid byte: '{'}'", .{ + std.zig.fmtEscapes(tree.source[byte_abs..][0..1]), + })); + } + + for (tree.errors[1..]) |note| { + if (!note.is_note) break; + + msg.clearRetainingCapacity(); + try tree.renderError(note, msg.writer(gpa)); + try notes.append(gpa, try astgen.errNoteTok(note.token, "{s}", .{msg.items})); + } + + const extra_offset = tree.errorOffset(parse_err); + msg.clearRetainingCapacity(); + try tree.renderError(parse_err, msg.writer(gpa)); + try astgen.appendErrorTokNotesOff(parse_err.token, extra_offset, "{s}", .{msg.items}, notes.items); +} diff --git a/src/Compilation.zig b/src/Compilation.zig index 83e0f0d5d9..f5a92c66ea 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -9,6 +9,7 @@ const log = std.log.scoped(.compilation); const Target = std.Target; const ThreadPool = std.Thread.Pool; const WaitGroup = std.Thread.WaitGroup; +const ErrorBundle = std.zig.ErrorBundle; const Value = @import("value.zig").Value; const Type = @import("type.zig").Type; @@ -334,12 +335,41 @@ pub const MiscTask = enum { libssp, zig_libc, analyze_pkg, + + @"musl crti.o", + @"musl crtn.o", + @"musl crt1.o", + @"musl rcrt1.o", + @"musl Scrt1.o", + @"musl libc.a", + @"musl libc.so", + + @"wasi crt1-reactor.o", + @"wasi crt1-command.o", + @"wasi libc.a", + @"libwasi-emulated-process-clocks.a", + @"libwasi-emulated-getpid.a", + @"libwasi-emulated-mman.a", + @"libwasi-emulated-signal.a", + + @"glibc crti.o", + @"glibc crtn.o", + @"glibc Scrt1.o", + @"glibc libc_nonshared.a", + @"glibc shared object", + + @"mingw-w64 crt2.o", + @"mingw-w64 dllcrt2.o", + @"mingw-w64 mingw32.lib", + @"mingw-w64 msvcrt-os.lib", + @"mingw-w64 mingwex.lib", + @"mingw-w64 uuid.lib", }; pub const MiscError = struct { /// Allocated with gpa. msg: []u8, - children: ?AllErrors = null, + children: ?ErrorBundle = null, pub fn deinit(misc_err: *MiscError, gpa: Allocator) void { gpa.free(misc_err.msg); @@ -365,448 +395,6 @@ pub const LldError = struct { } }; -/// To support incremental compilation, errors are stored in various places -/// so that they can be created and destroyed appropriately. This structure -/// is used to collect all the errors from the various places into one -/// convenient place for API users to consume. It is allocated into 1 arena -/// and freed all at once. -pub const AllErrors = struct { - arena: std.heap.ArenaAllocator.State, - list: []const Message, - - pub const Message = union(enum) { - src: struct { - msg: []const u8, - src_path: []const u8, - line: u32, - column: u32, - span: Module.SrcLoc.Span, - /// Usually one, but incremented for redundant messages. - count: u32 = 1, - /// Does not include the trailing newline. - source_line: ?[]const u8, - notes: []const Message = &.{}, - reference_trace: []Message = &.{}, - - /// Splits the error message up into lines to properly indent them - /// to allow for long, good-looking error messages. - /// - /// This is used to split the message in `@compileError("hello\nworld")` for example. - fn writeMsg(src: @This(), stderr: anytype, indent: usize) !void { - var lines = mem.split(u8, src.msg, "\n"); - while (lines.next()) |line| { - try stderr.writeAll(line); - if (lines.index == null) break; - try stderr.writeByte('\n'); - try stderr.writeByteNTimes(' ', indent); - } - } - }, - plain: struct { - msg: []const u8, - notes: []Message = &.{}, - /// Usually one, but incremented for redundant messages. - count: u32 = 1, - }, - - pub fn incrementCount(msg: *Message) void { - switch (msg.*) { - .src => |*src| { - src.count += 1; - }, - .plain => |*plain| { - plain.count += 1; - }, - } - } - - pub fn renderToStdErr(msg: Message, ttyconf: std.debug.TTY.Config) void { - std.debug.getStderrMutex().lock(); - defer std.debug.getStderrMutex().unlock(); - const stderr = std.io.getStdErr(); - return msg.renderToWriter(ttyconf, stderr.writer(), "error", .Red, 0) catch return; - } - - pub fn renderToWriter( - msg: Message, - ttyconf: std.debug.TTY.Config, - stderr: anytype, - kind: []const u8, - color: std.debug.TTY.Color, - indent: usize, - ) anyerror!void { - var counting_writer = std.io.countingWriter(stderr); - const counting_stderr = counting_writer.writer(); - switch (msg) { - .src => |src| { - try counting_stderr.writeByteNTimes(' ', indent); - try ttyconf.setColor(stderr, .Bold); - try counting_stderr.print("{s}:{d}:{d}: ", .{ - src.src_path, - src.line + 1, - src.column + 1, - }); - try ttyconf.setColor(stderr, color); - try counting_stderr.writeAll(kind); - try counting_stderr.writeAll(": "); - // This is the length of the part before the error message: - // e.g. "file.zig:4:5: error: " - const prefix_len = @intCast(usize, counting_stderr.context.bytes_written); - try ttyconf.setColor(stderr, .Reset); - try ttyconf.setColor(stderr, .Bold); - if (src.count == 1) { - try src.writeMsg(stderr, prefix_len); - try stderr.writeByte('\n'); - } else { - try src.writeMsg(stderr, prefix_len); - try ttyconf.setColor(stderr, .Dim); - try stderr.print(" ({d} times)\n", .{src.count}); - } - try ttyconf.setColor(stderr, .Reset); - if (src.source_line) |line| { - for (line) |b| switch (b) { - '\t' => try stderr.writeByte(' '), - else => try stderr.writeByte(b), - }; - try stderr.writeByte('\n'); - // TODO basic unicode code point monospace width - const before_caret = src.span.main - src.span.start; - // -1 since span.main includes the caret - const after_caret = src.span.end - src.span.main -| 1; - try stderr.writeByteNTimes(' ', src.column - before_caret); - try ttyconf.setColor(stderr, .Green); - try stderr.writeByteNTimes('~', before_caret); - try stderr.writeByte('^'); - try stderr.writeByteNTimes('~', after_caret); - try stderr.writeByte('\n'); - try ttyconf.setColor(stderr, .Reset); - } - for (src.notes) |note| { - try note.renderToWriter(ttyconf, stderr, "note", .Cyan, indent); - } - if (src.reference_trace.len != 0) { - try ttyconf.setColor(stderr, .Reset); - try ttyconf.setColor(stderr, .Dim); - try stderr.print("referenced by:\n", .{}); - for (src.reference_trace) |reference| { - switch (reference) { - .src => |ref_src| try stderr.print(" {s}: {s}:{d}:{d}\n", .{ - ref_src.msg, - ref_src.src_path, - ref_src.line + 1, - ref_src.column + 1, - }), - .plain => |plain| if (plain.count != 0) { - try stderr.print( - " {d} reference(s) hidden; use '-freference-trace={d}' to see all references\n", - .{ plain.count, plain.count + src.reference_trace.len - 1 }, - ); - } else { - try stderr.print( - " remaining reference traces hidden; use '-freference-trace' to see all reference traces\n", - .{}, - ); - }, - } - } - try stderr.writeByte('\n'); - try ttyconf.setColor(stderr, .Reset); - } - }, - .plain => |plain| { - try ttyconf.setColor(stderr, color); - try stderr.writeByteNTimes(' ', indent); - try stderr.writeAll(kind); - try stderr.writeAll(": "); - try ttyconf.setColor(stderr, .Reset); - if (plain.count == 1) { - try stderr.print("{s}\n", .{plain.msg}); - } else { - try stderr.print("{s}", .{plain.msg}); - try ttyconf.setColor(stderr, .Dim); - try stderr.print(" ({d} times)\n", .{plain.count}); - } - try ttyconf.setColor(stderr, .Reset); - for (plain.notes) |note| { - try note.renderToWriter(ttyconf, stderr, "note", .Cyan, indent + 4); - } - }, - } - } - - pub const HashContext = struct { - pub fn hash(ctx: HashContext, key: *Message) u64 { - _ = ctx; - var hasher = std.hash.Wyhash.init(0); - - switch (key.*) { - .src => |src| { - hasher.update(src.msg); - hasher.update(src.src_path); - std.hash.autoHash(&hasher, src.line); - std.hash.autoHash(&hasher, src.column); - std.hash.autoHash(&hasher, src.span.main); - }, - .plain => |plain| { - hasher.update(plain.msg); - }, - } - - return hasher.final(); - } - - pub fn eql(ctx: HashContext, a: *Message, b: *Message) bool { - _ = ctx; - switch (a.*) { - .src => |a_src| switch (b.*) { - .src => |b_src| { - return mem.eql(u8, a_src.msg, b_src.msg) and - mem.eql(u8, a_src.src_path, b_src.src_path) and - a_src.line == b_src.line and - a_src.column == b_src.column and - a_src.span.main == b_src.span.main; - }, - .plain => return false, - }, - .plain => |a_plain| switch (b.*) { - .src => return false, - .plain => |b_plain| { - return mem.eql(u8, a_plain.msg, b_plain.msg); - }, - }, - } - } - }; - }; - - pub fn deinit(self: *AllErrors, gpa: Allocator) void { - self.arena.promote(gpa).deinit(); - } - - pub fn add( - module: *Module, - arena: *std.heap.ArenaAllocator, - errors: *std.ArrayList(Message), - module_err_msg: Module.ErrorMsg, - ) !void { - const allocator = arena.allocator(); - - const notes_buf = try allocator.alloc(Message, module_err_msg.notes.len); - var note_i: usize = 0; - - // De-duplicate error notes. The main use case in mind for this is - // too many "note: called from here" notes when eval branch quota is reached. - var seen_notes = std.HashMap( - *Message, - void, - Message.HashContext, - std.hash_map.default_max_load_percentage, - ).init(allocator); - const err_source = module_err_msg.src_loc.file_scope.getSource(module.gpa) catch |err| { - const file_path = try module_err_msg.src_loc.file_scope.fullPath(allocator); - try errors.append(.{ - .plain = .{ - .msg = try std.fmt.allocPrint(allocator, "unable to load '{s}': {s}", .{ - file_path, @errorName(err), - }), - }, - }); - return; - }; - const err_span = try module_err_msg.src_loc.span(module.gpa); - const err_loc = std.zig.findLineColumn(err_source.bytes, err_span.main); - - for (module_err_msg.notes) |module_note| { - const source = try module_note.src_loc.file_scope.getSource(module.gpa); - const span = try module_note.src_loc.span(module.gpa); - const loc = std.zig.findLineColumn(source.bytes, span.main); - const file_path = try module_note.src_loc.file_scope.fullPath(allocator); - const note = ¬es_buf[note_i]; - note.* = .{ - .src = .{ - .src_path = file_path, - .msg = try allocator.dupe(u8, module_note.msg), - .span = span, - .line = @intCast(u32, loc.line), - .column = @intCast(u32, loc.column), - .source_line = if (err_loc.eql(loc)) null else try allocator.dupe(u8, loc.source_line), - }, - }; - const gop = try seen_notes.getOrPut(note); - if (gop.found_existing) { - gop.key_ptr.*.incrementCount(); - } else { - note_i += 1; - } - } - - const reference_trace = try allocator.alloc(Message, module_err_msg.reference_trace.len); - for (reference_trace, 0..) |*reference, i| { - const module_reference = module_err_msg.reference_trace[i]; - if (module_reference.hidden != 0) { - reference.* = .{ .plain = .{ .msg = undefined, .count = module_reference.hidden } }; - break; - } else if (module_reference.decl == null) { - reference.* = .{ .plain = .{ .msg = undefined, .count = 0 } }; - break; - } - const source = try module_reference.src_loc.file_scope.getSource(module.gpa); - const span = try module_reference.src_loc.span(module.gpa); - const loc = std.zig.findLineColumn(source.bytes, span.main); - const file_path = try module_reference.src_loc.file_scope.fullPath(allocator); - reference.* = .{ - .src = .{ - .src_path = file_path, - .msg = try allocator.dupe(u8, std.mem.sliceTo(module_reference.decl.?, 0)), - .span = span, - .line = @intCast(u32, loc.line), - .column = @intCast(u32, loc.column), - .source_line = null, - }, - }; - } - const file_path = try module_err_msg.src_loc.file_scope.fullPath(allocator); - try errors.append(.{ - .src = .{ - .src_path = file_path, - .msg = try allocator.dupe(u8, module_err_msg.msg), - .span = err_span, - .line = @intCast(u32, err_loc.line), - .column = @intCast(u32, err_loc.column), - .notes = notes_buf[0..note_i], - .reference_trace = reference_trace, - .source_line = if (module_err_msg.src_loc.lazy == .entire_file) null else try allocator.dupe(u8, err_loc.source_line), - }, - }); - } - - pub fn addZir( - arena: Allocator, - errors: *std.ArrayList(Message), - file: *Module.File, - ) !void { - assert(file.zir_loaded); - assert(file.tree_loaded); - assert(file.source_loaded); - const payload_index = file.zir.extra[@enumToInt(Zir.ExtraIndex.compile_errors)]; - assert(payload_index != 0); - - const header = file.zir.extraData(Zir.Inst.CompileErrors, payload_index); - const items_len = header.data.items_len; - var extra_index = header.end; - var item_i: usize = 0; - while (item_i < items_len) : (item_i += 1) { - const item = file.zir.extraData(Zir.Inst.CompileErrors.Item, extra_index); - extra_index = item.end; - const err_span = blk: { - if (item.data.node != 0) { - break :blk Module.SrcLoc.nodeToSpan(&file.tree, item.data.node); - } - const token_starts = file.tree.tokens.items(.start); - const start = token_starts[item.data.token] + item.data.byte_offset; - const end = start + @intCast(u32, file.tree.tokenSlice(item.data.token).len) - item.data.byte_offset; - break :blk Module.SrcLoc.Span{ .start = start, .end = end, .main = start }; - }; - const err_loc = std.zig.findLineColumn(file.source, err_span.main); - - var notes: []Message = &[0]Message{}; - if (item.data.notes != 0) { - const block = file.zir.extraData(Zir.Inst.Block, item.data.notes); - const body = file.zir.extra[block.end..][0..block.data.body_len]; - notes = try arena.alloc(Message, body.len); - for (notes, 0..) |*note, i| { - const note_item = file.zir.extraData(Zir.Inst.CompileErrors.Item, body[i]); - const msg = file.zir.nullTerminatedString(note_item.data.msg); - const span = blk: { - if (note_item.data.node != 0) { - break :blk Module.SrcLoc.nodeToSpan(&file.tree, note_item.data.node); - } - const token_starts = file.tree.tokens.items(.start); - const start = token_starts[note_item.data.token] + note_item.data.byte_offset; - const end = start + @intCast(u32, file.tree.tokenSlice(note_item.data.token).len) - item.data.byte_offset; - break :blk Module.SrcLoc.Span{ .start = start, .end = end, .main = start }; - }; - const loc = std.zig.findLineColumn(file.source, span.main); - - note.* = .{ - .src = .{ - .src_path = try file.fullPath(arena), - .msg = try arena.dupe(u8, msg), - .span = span, - .line = @intCast(u32, loc.line), - .column = @intCast(u32, loc.column), - .notes = &.{}, // TODO rework this function to be recursive - .source_line = if (loc.eql(err_loc)) null else try arena.dupe(u8, loc.source_line), - }, - }; - } - } - - const msg = file.zir.nullTerminatedString(item.data.msg); - try errors.append(.{ - .src = .{ - .src_path = try file.fullPath(arena), - .msg = try arena.dupe(u8, msg), - .span = err_span, - .line = @intCast(u32, err_loc.line), - .column = @intCast(u32, err_loc.column), - .notes = notes, - .source_line = try arena.dupe(u8, err_loc.source_line), - }, - }); - } - } - - fn addPlain( - arena: *std.heap.ArenaAllocator, - errors: *std.ArrayList(Message), - msg: []const u8, - ) !void { - _ = arena; - try errors.append(.{ .plain = .{ .msg = msg } }); - } - - fn addPlainWithChildren( - arena: *std.heap.ArenaAllocator, - errors: *std.ArrayList(Message), - msg: []const u8, - optional_children: ?AllErrors, - ) !void { - const allocator = arena.allocator(); - const duped_msg = try allocator.dupe(u8, msg); - if (optional_children) |*children| { - try errors.append(.{ .plain = .{ - .msg = duped_msg, - .notes = try dupeList(children.list, allocator), - } }); - } else { - try errors.append(.{ .plain = .{ .msg = duped_msg } }); - } - } - - fn dupeList(list: []const Message, arena: Allocator) Allocator.Error![]Message { - const duped_list = try arena.alloc(Message, list.len); - for (list, 0..) |item, i| { - duped_list[i] = switch (item) { - .src => |src| .{ .src = .{ - .msg = try arena.dupe(u8, src.msg), - .src_path = try arena.dupe(u8, src.src_path), - .line = src.line, - .column = src.column, - .span = src.span, - .source_line = if (src.source_line) |s| try arena.dupe(u8, s) else null, - .notes = try dupeList(src.notes, arena), - } }, - .plain => |plain| .{ .plain = .{ - .msg = try arena.dupe(u8, plain.msg), - .notes = try dupeList(plain.notes, arena), - } }, - }; - } - return duped_list; - } -}; - pub const Directory = Cache.Directory; pub const EmitLoc = struct { @@ -2891,7 +2479,7 @@ pub fn makeBinFileWritable(self: *Compilation) !void { } /// This function is temporally single-threaded. -pub fn totalErrorCount(self: *Compilation) usize { +pub fn totalErrorCount(self: *Compilation) u32 { var total: usize = self.failed_c_objects.count() + self.misc_failures.count() + @boolToInt(self.alloc_failure_occurred) + self.lld_errors.items.len; @@ -2951,17 +2539,16 @@ pub fn totalErrorCount(self: *Compilation) usize { } } - return total; + return @intCast(u32, total); } /// This function is temporally single-threaded. -pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { - var arena = std.heap.ArenaAllocator.init(self.gpa); - errdefer arena.deinit(); - const arena_allocator = arena.allocator(); +pub fn getAllErrorsAlloc(self: *Compilation) !ErrorBundle { + const gpa = self.gpa; - var errors = std.ArrayList(AllErrors.Message).init(self.gpa); - defer errors.deinit(); + var bundle: ErrorBundle = undefined; + try bundle.init(gpa); + errdefer bundle.deinit(gpa); { var it = self.failed_c_objects.iterator(); @@ -2970,53 +2557,63 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { const err_msg = entry.value_ptr.*; // TODO these fields will need to be adjusted when we have proper // C error reporting bubbling up. - try errors.append(.{ - .src = .{ - .src_path = try arena_allocator.dupe(u8, c_object.src.src_path), - .msg = try std.fmt.allocPrint(arena_allocator, "unable to build C object: {s}", .{ - err_msg.msg, - }), - .span = .{ .start = 0, .end = 1, .main = 0 }, + try bundle.addErrorMessage(gpa, .{ + .msg = try bundle.printString(gpa, "unable to build C object: {s}", .{ + err_msg.msg, + }), + .src_loc = try bundle.addSourceLocation(gpa, .{ + .src_path = try bundle.addString(gpa, c_object.src.src_path), + .span_start = 0, + .span_main = 0, + .span_end = 1, .line = err_msg.line, .column = err_msg.column, - .source_line = null, // TODO - }, + .source_line = 0, // TODO + }), + }); + bundle.incrementCount(1); + } + } + + for (self.lld_errors.items) |lld_error| { + try bundle.addErrorMessage(gpa, .{ + .msg = try bundle.addString(gpa, lld_error.msg), + .notes_len = @intCast(u32, lld_error.context_lines.len), + }); + bundle.incrementCount(1); + + for (lld_error.context_lines) |context_line| { + try bundle.addErrorMessage(gpa, .{ + .msg = try bundle.addString(gpa, context_line), }); } } - for (self.lld_errors.items) |lld_error| { - const notes = try arena_allocator.alloc(AllErrors.Message, lld_error.context_lines.len); - for (lld_error.context_lines, 0..) |context_line, i| { - notes[i] = .{ .plain = .{ - .msg = try arena_allocator.dupe(u8, context_line), - } }; - } - - try errors.append(.{ - .plain = .{ - .msg = try arena_allocator.dupe(u8, lld_error.msg), - .notes = notes, - }, - }); - } for (self.misc_failures.values()) |*value| { - try AllErrors.addPlainWithChildren(&arena, &errors, value.msg, value.children); + try bundle.addErrorMessage(gpa, .{ + .msg = try bundle.addString(gpa, value.msg), + .notes_len = if (value.children) |b| b.errorMessageCount() else 0, + }); + if (value.children) |b| try bundle.addBundle(gpa, b); + bundle.incrementCount(1); } if (self.alloc_failure_occurred) { - try AllErrors.addPlain(&arena, &errors, "memory allocation failure"); + try bundle.addErrorMessage(gpa, .{ + .msg = try bundle.addString(gpa, "memory allocation failure"), + }); + bundle.incrementCount(1); } if (self.bin_file.options.module) |module| { { var it = module.failed_files.iterator(); while (it.next()) |entry| { if (entry.value_ptr.*) |msg| { - try AllErrors.add(module, &arena, &errors, msg.*); + try addModuleErrorMsg(gpa, &bundle, msg.*); } else { // Must be ZIR errors. In order for ZIR errors to exist, the parsing // must have completed successfully. const tree = try entry.key_ptr.*.getTree(module.gpa); assert(tree.errors.len == 0); - try AllErrors.addZir(arena_allocator, &errors, entry.key_ptr.*); + try addZirErrorMessages(gpa, &bundle, entry.key_ptr.*); } } } @@ -3024,7 +2621,7 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { var it = module.failed_embed_files.iterator(); while (it.next()) |entry| { const msg = entry.value_ptr.*; - try AllErrors.add(module, &arena, &errors, msg.*); + try addModuleErrorMsg(gpa, &bundle, msg.*); } } { @@ -3034,23 +2631,21 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { // Skip errors for Decls within files that had a parse failure. // We'll try again once parsing succeeds. if (decl.getFileScope().okToReportErrors()) { - try AllErrors.add(module, &arena, &errors, entry.value_ptr.*.*); + try addModuleErrorMsg(gpa, &bundle, entry.value_ptr.*.*); if (module.cimport_errors.get(entry.key_ptr.*)) |cimport_errors| for (cimport_errors) |c_error| { - if (c_error.path) |some| - try errors.append(.{ - .src = .{ - .src_path = try arena_allocator.dupe(u8, std.mem.span(some)), - .span = .{ .start = c_error.offset, .end = c_error.offset + 1, .main = c_error.offset }, - .msg = try arena_allocator.dupe(u8, std.mem.span(c_error.msg)), - .line = c_error.line, - .column = c_error.column, - .source_line = if (c_error.source_line) |line| try arena_allocator.dupe(u8, std.mem.span(line)) else null, - }, - }) - else - try errors.append(.{ - .plain = .{ .msg = try arena_allocator.dupe(u8, std.mem.span(c_error.msg)) }, - }); + try bundle.addErrorMessage(gpa, .{ + .msg = try bundle.addString(gpa, std.mem.span(c_error.msg)), + .src_loc = if (c_error.path) |some| try bundle.addSourceLocation(gpa, .{ + .src_path = try bundle.addString(gpa, std.mem.span(some)), + .span_start = c_error.offset, + .span_main = c_error.offset, + .span_end = c_error.offset + 1, + .line = c_error.line, + .column = c_error.column, + .source_line = if (c_error.source_line) |line| try bundle.addString(gpa, std.mem.span(line)) else 0, + }) else 0, + }); + bundle.incrementCount(1); }; } } @@ -3062,45 +2657,40 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { // Skip errors for Decls within files that had a parse failure. // We'll try again once parsing succeeds. if (decl.getFileScope().okToReportErrors()) { - try AllErrors.add(module, &arena, &errors, entry.value_ptr.*.*); + try addModuleErrorMsg(gpa, &bundle, entry.value_ptr.*.*); } } } for (module.failed_exports.values()) |value| { - try AllErrors.add(module, &arena, &errors, value.*); + try addModuleErrorMsg(gpa, &bundle, value.*); } } - if (errors.items.len == 0) { + if (bundle.errorMessageCount() == 0) { if (self.link_error_flags.no_entry_point_found) { - try errors.append(.{ - .plain = .{ - .msg = try std.fmt.allocPrint(arena_allocator, "no entry point found", .{}), - }, + try bundle.addErrorMessage(gpa, .{ + .msg = try bundle.addString(gpa, "no entry point found"), }); + bundle.incrementCount(1); } } if (self.link_error_flags.missing_libc) { - const notes = try arena_allocator.create([2]AllErrors.Message); - notes.* = .{ - .{ .plain = .{ - .msg = try arena_allocator.dupe(u8, "run 'zig libc -h' to learn about libc installations"), - } }, - .{ .plain = .{ - .msg = try arena_allocator.dupe(u8, "run 'zig targets' to see the targets for which zig can always provide libc"), - } }, - }; - try errors.append(.{ - .plain = .{ - .msg = try std.fmt.allocPrint(arena_allocator, "libc not available", .{}), - .notes = notes, - }, + try bundle.addErrorMessage(gpa, .{ + .msg = try bundle.addString(gpa, "libc not available"), + .notes_len = 2, }); + try bundle.addErrorMessage(gpa, .{ + .msg = try bundle.addString(gpa, "run 'zig libc -h' to learn about libc installations"), + }); + try bundle.addErrorMessage(gpa, .{ + .msg = try bundle.addString(gpa, "run 'zig targets' to see the targets for which zig can always provide libc"), + }); + bundle.incrementCount(1); } if (self.bin_file.options.module) |module| { - if (errors.items.len == 0 and module.compile_log_decls.count() != 0) { + if (bundle.errorMessageCount() == 0 and module.compile_log_decls.count() != 0) { const keys = module.compile_log_decls.keys(); const values = module.compile_log_decls.values(); // First one will be the error; subsequent ones will be notes. @@ -3121,16 +2711,259 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { }; } - try AllErrors.add(module, &arena, &errors, err_msg); + try addModuleErrorMsg(gpa, &bundle, err_msg); } } - assert(errors.items.len == self.totalErrorCount()); + assert(self.totalErrorCount() == bundle.errorMessageCount()); - return AllErrors{ - .list = try arena_allocator.dupe(AllErrors.Message, errors.items), - .arena = arena.state, + return bundle; +} + +pub const ErrorNoteHashContext = struct { + eb: *const ErrorBundle, + + pub fn hash(ctx: ErrorNoteHashContext, key: ErrorBundle.ErrorMessage) u32 { + var hasher = std.hash.Wyhash.init(0); + + hasher.update(ctx.eb.nullTerminatedString(key.msg)); + if (key.src_loc != 0) { + const src = ctx.eb.getSourceLocation(key.src_loc); + hasher.update(ctx.eb.nullTerminatedString(src.src_path)); + std.hash.autoHash(&hasher, src.line); + std.hash.autoHash(&hasher, src.column); + std.hash.autoHash(&hasher, src.span_main); + } + + return @truncate(u32, hasher.final()); + } + + pub fn eql( + ctx: ErrorNoteHashContext, + a: ErrorBundle.ErrorMessage, + b: ErrorBundle.ErrorMessage, + b_index: usize, + ) bool { + _ = b_index; + const msg_a = ctx.eb.nullTerminatedString(a.msg); + const msg_b = ctx.eb.nullTerminatedString(b.msg); + if (!std.mem.eql(u8, msg_a, msg_b)) return false; + + if (a.src_loc == 0 and b.src_loc == 0) return true; + if (a.src_loc == 0 or b.src_loc == 0) return false; + const src_a = ctx.eb.getSourceLocation(a.src_loc); + const src_b = ctx.eb.getSourceLocation(b.src_loc); + + const src_path_a = ctx.eb.nullTerminatedString(src_a.src_path); + const src_path_b = ctx.eb.nullTerminatedString(src_b.src_path); + + return std.mem.eql(u8, src_path_a, src_path_b) and + src_a.line == src_b.line and + src_a.column == src_b.column and + src_a.span_main == src_b.span_main; + } +}; + +pub fn addModuleErrorMsg(gpa: Allocator, eb: *ErrorBundle, module_err_msg: Module.ErrorMsg) !void { + const err_source = module_err_msg.src_loc.file_scope.getSource(gpa) catch |err| { + const file_path = try module_err_msg.src_loc.file_scope.fullPath(gpa); + defer gpa.free(file_path); + try eb.addErrorMessage(gpa, .{ + .msg = try eb.printString(gpa, "unable to load '{s}': {s}", .{ + file_path, @errorName(err), + }), + }); + eb.incrementCount(1); + return; }; + const err_span = try module_err_msg.src_loc.span(gpa); + const err_loc = std.zig.findLineColumn(err_source.bytes, err_span.main); + const file_path = try module_err_msg.src_loc.file_scope.fullPath(gpa); + defer gpa.free(file_path); + + var ref_traces: std.ArrayListUnmanaged(ErrorBundle.ReferenceTrace) = .{}; + defer ref_traces.deinit(gpa); + + for (module_err_msg.reference_trace) |module_reference| { + if (module_reference.hidden != 0) { + try ref_traces.append(gpa, .{ + .decl_name = module_reference.hidden, + .src_loc = 0, + }); + break; + } else if (module_reference.decl == null) { + try ref_traces.append(gpa, .{ + .decl_name = 0, + .src_loc = 0, + }); + break; + } + const source = try module_reference.src_loc.file_scope.getSource(gpa); + const span = try module_reference.src_loc.span(gpa); + const loc = std.zig.findLineColumn(source.bytes, span.main); + const rt_file_path = try module_reference.src_loc.file_scope.fullPath(gpa); + defer gpa.free(rt_file_path); + try ref_traces.append(gpa, .{ + .decl_name = try eb.addString(gpa, std.mem.sliceTo(module_reference.decl.?, 0)), + .src_loc = try eb.addSourceLocation(gpa, .{ + .src_path = try eb.addString(gpa, rt_file_path), + .span_start = span.start, + .span_main = span.main, + .span_end = span.end, + .line = @intCast(u32, loc.line), + .column = @intCast(u32, loc.column), + .source_line = 0, + }), + }); + } + + const src_loc = try eb.addSourceLocation(gpa, .{ + .src_path = try eb.addString(gpa, file_path), + .span_start = err_span.start, + .span_main = err_span.main, + .span_end = err_span.end, + .line = @intCast(u32, err_loc.line), + .column = @intCast(u32, err_loc.column), + .source_line = if (module_err_msg.src_loc.lazy == .entire_file) + 0 + else + try eb.addString(gpa, err_loc.source_line), + .reference_trace_len = @intCast(u32, ref_traces.items.len), + }); + + for (ref_traces.items) |rt| { + try eb.addReferenceTrace(gpa, rt); + } + + // De-duplicate error notes. The main use case in mind for this is + // too many "note: called from here" notes when eval branch quota is reached. + var notes: std.ArrayHashMapUnmanaged(ErrorBundle.ErrorMessage, void, ErrorNoteHashContext, true) = .{}; + defer notes.deinit(gpa); + + for (module_err_msg.notes) |module_note| { + const source = try module_note.src_loc.file_scope.getSource(gpa); + const span = try module_note.src_loc.span(gpa); + const loc = std.zig.findLineColumn(source.bytes, span.main); + const note_file_path = try module_note.src_loc.file_scope.fullPath(gpa); + defer gpa.free(note_file_path); + + const gop = try notes.getOrPutContext(gpa, .{ + .msg = try eb.addString(gpa, module_note.msg), + .src_loc = try eb.addSourceLocation(gpa, .{ + .src_path = try eb.addString(gpa, note_file_path), + .span_start = span.start, + .span_main = span.main, + .span_end = span.end, + .line = @intCast(u32, loc.line), + .column = @intCast(u32, loc.column), + .source_line = if (err_loc.eql(loc)) 0 else try eb.addString(gpa, loc.source_line), + }), + }, .{ .eb = eb }); + if (gop.found_existing) { + gop.key_ptr.count += 1; + } + } + + try eb.addErrorMessage(gpa, .{ + .msg = try eb.addString(gpa, module_err_msg.msg), + .src_loc = src_loc, + .notes_len = @intCast(u32, notes.entries.len), + }); + eb.incrementCount(1); + + for (notes.keys()) |note| { + try eb.addErrorMessage(gpa, note); + } +} + +pub fn addZirErrorMessages(gpa: Allocator, eb: *ErrorBundle, file: *Module.File) !void { + assert(file.zir_loaded); + assert(file.tree_loaded); + assert(file.source_loaded); + const payload_index = file.zir.extra[@enumToInt(Zir.ExtraIndex.compile_errors)]; + assert(payload_index != 0); + + const header = file.zir.extraData(Zir.Inst.CompileErrors, payload_index); + const items_len = header.data.items_len; + var extra_index = header.end; + for (0..items_len) |_| { + const item = file.zir.extraData(Zir.Inst.CompileErrors.Item, extra_index); + extra_index = item.end; + const err_span = blk: { + if (item.data.node != 0) { + break :blk Module.SrcLoc.nodeToSpan(&file.tree, item.data.node); + } + const token_starts = file.tree.tokens.items(.start); + const start = token_starts[item.data.token] + item.data.byte_offset; + const end = start + @intCast(u32, file.tree.tokenSlice(item.data.token).len) - item.data.byte_offset; + break :blk Module.SrcLoc.Span{ .start = start, .end = end, .main = start }; + }; + const err_loc = std.zig.findLineColumn(file.source, err_span.main); + + var notes: []ErrorBundle.ErrorMessage = &.{}; + defer gpa.free(notes); + + if (item.data.notes != 0) { + const block = file.zir.extraData(Zir.Inst.Block, item.data.notes); + const body = file.zir.extra[block.end..][0..block.data.body_len]; + notes = try gpa.alloc(ErrorBundle.ErrorMessage, body.len); + for (notes, body) |*note, body_elem| { + const note_item = file.zir.extraData(Zir.Inst.CompileErrors.Item, body_elem); + const msg = file.zir.nullTerminatedString(note_item.data.msg); + const span = blk: { + if (note_item.data.node != 0) { + break :blk Module.SrcLoc.nodeToSpan(&file.tree, note_item.data.node); + } + const token_starts = file.tree.tokens.items(.start); + const start = token_starts[note_item.data.token] + note_item.data.byte_offset; + const end = start + @intCast(u32, file.tree.tokenSlice(note_item.data.token).len) - item.data.byte_offset; + break :blk Module.SrcLoc.Span{ .start = start, .end = end, .main = start }; + }; + const loc = std.zig.findLineColumn(file.source, span.main); + const src_path = try file.fullPath(gpa); + defer gpa.free(src_path); + + note.* = .{ + .msg = try eb.addString(gpa, msg), + .src_loc = try eb.addSourceLocation(gpa, .{ + .src_path = try eb.addString(gpa, src_path), + .span_start = span.start, + .span_main = span.main, + .span_end = span.end, + .line = @intCast(u32, loc.line), + .column = @intCast(u32, loc.column), + .source_line = if (loc.eql(err_loc)) + 0 + else + try eb.addString(gpa, loc.source_line), + }), + .notes_len = 0, // TODO rework this function to be recursive + }; + } + } + + const msg = file.zir.nullTerminatedString(item.data.msg); + const src_path = try file.fullPath(gpa); + defer gpa.free(src_path); + try eb.addErrorMessage(gpa, .{ + .msg = try eb.addString(gpa, msg), + .src_loc = try eb.addSourceLocation(gpa, .{ + .src_path = try eb.addString(gpa, src_path), + .span_start = err_span.start, + .span_main = err_span.main, + .span_end = err_span.end, + .line = @intCast(u32, err_loc.line), + .column = @intCast(u32, err_loc.column), + .source_line = try eb.addString(gpa, err_loc.source_line), + }), + .notes_len = @intCast(u32, notes.len), + }); + + for (notes) |note| { + try eb.addErrorMessage(gpa, note); + } + } + eb.incrementCount(items_len); } pub fn getCompileLogOutput(self: *Compilation) []const u8 { @@ -5417,34 +5250,29 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: Allocator) Alloca return buffer.toOwnedSliceSentinel(0); } -pub fn updateSubCompilation(sub_compilation: *Compilation) !void { - try sub_compilation.update(); +pub fn updateSubCompilation( + parent_comp: *Compilation, + sub_comp: *Compilation, + misc_task: MiscTask, +) !void { + try sub_comp.update(); - // Look for compilation errors in this sub_compilation - // TODO instead of logging these errors, handle them in the callsites - // of updateSubCompilation and attach them as sub-errors, properly - // surfacing the errors. You can see an example of this already - // done inside buildOutputFromZig. - var errors = try sub_compilation.getAllErrorsAlloc(); - defer errors.deinit(sub_compilation.gpa); + // Look for compilation errors in this sub compilation + const gpa = parent_comp.gpa; + var keep_errors = false; + var errors = try sub_comp.getAllErrorsAlloc(); + defer if (!keep_errors) errors.deinit(gpa); - if (errors.list.len != 0) { - for (errors.list) |full_err_msg| { - switch (full_err_msg) { - .src => |src| { - log.err("{s}:{d}:{d}: {s}", .{ - src.src_path, - src.line + 1, - src.column + 1, - src.msg, - }); - }, - .plain => |plain| { - log.err("{s}", .{plain.msg}); - }, - } - } - return error.BuildingLibCObjectFailed; + if (errors.errorMessageCount() > 0) { + try parent_comp.misc_failures.ensureUnusedCapacity(gpa, 1); + parent_comp.misc_failures.putAssumeCapacityNoClobber(misc_task, .{ + .msg = try std.fmt.allocPrint(gpa, "sub-compilation of {s} failed", .{ + @tagName(misc_task), + }), + .children = errors, + }); + keep_errors = true; + return error.SubCompilationFailed; } } @@ -5520,23 +5348,7 @@ fn buildOutputFromZig( }); defer sub_compilation.destroy(); - try sub_compilation.update(); - // Look for compilation errors in this sub_compilation. - var keep_errors = false; - var errors = try sub_compilation.getAllErrorsAlloc(); - defer if (!keep_errors) errors.deinit(sub_compilation.gpa); - - if (errors.list.len != 0) { - try comp.misc_failures.ensureUnusedCapacity(comp.gpa, 1); - comp.misc_failures.putAssumeCapacityNoClobber(misc_task_tag, .{ - .msg = try std.fmt.allocPrint(comp.gpa, "sub-compilation of {s} failed", .{ - @tagName(misc_task_tag), - }), - .children = errors, - }); - keep_errors = true; - return error.SubCompilationFailed; - } + try comp.updateSubCompilation(sub_compilation, misc_task_tag); assert(out.* == null); out.* = Compilation.CRTFile{ @@ -5551,6 +5363,7 @@ pub fn build_crt_file( comp: *Compilation, root_name: []const u8, output_mode: std.builtin.OutputMode, + misc_task_tag: MiscTask, c_source_files: []const Compilation.CSourceFile, ) !void { const tracy_trace = trace(@src()); @@ -5611,7 +5424,7 @@ pub fn build_crt_file( }); defer sub_compilation.destroy(); - try sub_compilation.updateSubCompilation(); + try comp.updateSubCompilation(sub_compilation, misc_task_tag); try comp.crt_files.ensureUnusedCapacity(comp.gpa, 1); diff --git a/src/Module.zig b/src/Module.zig index 8c52176edd..1520a7d1b2 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -3756,67 +3756,9 @@ pub fn astGenFile(mod: *Module, file: *File) !void { file.source_loaded = true; file.tree = try Ast.parse(gpa, source, .zig); - defer if (!file.tree_loaded) file.tree.deinit(gpa); - - if (file.tree.errors.len != 0) { - const parse_err = file.tree.errors[0]; - - var msg = std.ArrayList(u8).init(gpa); - defer msg.deinit(); - - const token_starts = file.tree.tokens.items(.start); - const token_tags = file.tree.tokens.items(.tag); - - const extra_offset = file.tree.errorOffset(parse_err); - try file.tree.renderError(parse_err, msg.writer()); - const err_msg = try gpa.create(ErrorMsg); - err_msg.* = .{ - .src_loc = .{ - .file_scope = file, - .parent_decl_node = 0, - .lazy = if (extra_offset == 0) .{ - .token_abs = parse_err.token, - } else .{ - .byte_abs = token_starts[parse_err.token] + extra_offset, - }, - }, - .msg = try msg.toOwnedSlice(), - }; - if (token_tags[parse_err.token + @boolToInt(parse_err.token_is_prev)] == .invalid) { - const bad_off = @intCast(u32, file.tree.tokenSlice(parse_err.token + @boolToInt(parse_err.token_is_prev)).len); - const byte_abs = token_starts[parse_err.token + @boolToInt(parse_err.token_is_prev)] + bad_off; - try mod.errNoteNonLazy(.{ - .file_scope = file, - .parent_decl_node = 0, - .lazy = .{ .byte_abs = byte_abs }, - }, err_msg, "invalid byte: '{'}'", .{std.zig.fmtEscapes(source[byte_abs..][0..1])}); - } - - for (file.tree.errors[1..]) |note| { - if (!note.is_note) break; - - try file.tree.renderError(note, msg.writer()); - err_msg.notes = try mod.gpa.realloc(err_msg.notes, err_msg.notes.len + 1); - err_msg.notes[err_msg.notes.len - 1] = .{ - .src_loc = .{ - .file_scope = file, - .parent_decl_node = 0, - .lazy = .{ .token_abs = note.token }, - }, - .msg = try msg.toOwnedSlice(), - }; - } - - { - comp.mutex.lock(); - defer comp.mutex.unlock(); - try mod.failed_files.putNoClobber(gpa, file, err_msg); - } - file.status = .parse_failure; - return error.AnalysisFail; - } file.tree_loaded = true; + // Any potential AST errors are converted to ZIR errors here. file.zir = try AstGen.generate(gpa, file.tree); file.zir_loaded = true; file.status = .success_zir; diff --git a/src/Package.zig b/src/Package.zig index 87d52197bd..febcc51788 100644 --- a/src/Package.zig +++ b/src/Package.zig @@ -225,7 +225,7 @@ pub fn fetchAndAddDependencies( dependencies_source: *std.ArrayList(u8), build_roots_source: *std.ArrayList(u8), name_prefix: []const u8, - color: main.Color, + error_bundle: *std.zig.ErrorBundle, all_modules: *AllModules, ) !void { const max_bytes = 10 * 1024 * 1024; @@ -250,7 +250,7 @@ pub fn fetchAndAddDependencies( if (ast.errors.len > 0) { const file_path = try directory.join(arena, &.{Manifest.basename}); - try main.printErrsMsgToStdErr(gpa, arena, ast, file_path, color); + try main.putAstErrorsIntoBundle(gpa, ast, file_path, error_bundle); return error.PackageFetchFailed; } @@ -258,23 +258,18 @@ pub fn fetchAndAddDependencies( defer manifest.deinit(gpa); if (manifest.errors.len > 0) { - const ttyconf: std.debug.TTY.Config = switch (color) { - .auto => std.debug.detectTTYConfig(std.io.getStdErr()), - .on => .escape_codes, - .off => .no_color, - }; const file_path = try directory.join(arena, &.{Manifest.basename}); for (manifest.errors) |msg| { - Report.renderErrorMessage(ast, file_path, ttyconf, msg, &.{}); + try Report.addErrorMessage(gpa, ast, file_path, error_bundle, 0, msg); } return error.PackageFetchFailed; } const report: Report = .{ + .gpa = gpa, .ast = &ast, .directory = directory, - .color = color, - .arena = arena, + .error_bundle = error_bundle, }; var any_error = false; @@ -307,7 +302,7 @@ pub fn fetchAndAddDependencies( dependencies_source, build_roots_source, sub_prefix, - color, + error_bundle, all_modules, ); @@ -348,10 +343,10 @@ pub fn createFilePkg( } const Report = struct { + gpa: Allocator, ast: *const std.zig.Ast, directory: Compilation.Directory, - color: main.Color, - arena: Allocator, + error_bundle: *std.zig.ErrorBundle, fn fail( report: Report, @@ -359,52 +354,48 @@ const Report = struct { comptime fmt_string: []const u8, fmt_args: anytype, ) error{ PackageFetchFailed, OutOfMemory } { - return failWithNotes(report, &.{}, tok, fmt_string, fmt_args); - } + const gpa = report.gpa; - fn failWithNotes( - report: Report, - notes: []const Compilation.AllErrors.Message, - tok: std.zig.Ast.TokenIndex, - comptime fmt_string: []const u8, - fmt_args: anytype, - ) error{ PackageFetchFailed, OutOfMemory } { - const ttyconf: std.debug.TTY.Config = switch (report.color) { - .auto => std.debug.detectTTYConfig(std.io.getStdErr()), - .on => .escape_codes, - .off => .no_color, - }; - const file_path = try report.directory.join(report.arena, &.{Manifest.basename}); - renderErrorMessage(report.ast.*, file_path, ttyconf, .{ + const file_path = try report.directory.join(gpa, &.{Manifest.basename}); + defer gpa.free(file_path); + + const msg = try std.fmt.allocPrint(gpa, fmt_string, fmt_args); + defer gpa.free(msg); + + try addErrorMessage(report.gpa, report.ast.*, file_path, report.error_bundle, 0, .{ .tok = tok, .off = 0, - .msg = try std.fmt.allocPrint(report.arena, fmt_string, fmt_args), - }, notes); + .msg = msg, + }); + return error.PackageFetchFailed; } - fn renderErrorMessage( + fn addErrorMessage( + gpa: Allocator, ast: std.zig.Ast, file_path: []const u8, - ttyconf: std.debug.TTY.Config, + eb: *std.zig.ErrorBundle, + notes_len: u32, msg: Manifest.ErrorMessage, - notes: []const Compilation.AllErrors.Message, - ) void { + ) error{OutOfMemory}!void { const token_starts = ast.tokens.items(.start); const start_loc = ast.tokenLocation(0, msg.tok); - Compilation.AllErrors.Message.renderToStdErr(.{ .src = .{ - .msg = msg.msg, - .src_path = file_path, - .line = @intCast(u32, start_loc.line), - .column = @intCast(u32, start_loc.column), - .span = .{ - .start = token_starts[msg.tok], - .end = @intCast(u32, token_starts[msg.tok] + ast.tokenSlice(msg.tok).len), - .main = token_starts[msg.tok] + msg.off, - }, - .source_line = ast.source[start_loc.line_start..start_loc.line_end], - .notes = notes, - } }, ttyconf); + + try eb.addErrorMessage(gpa, .{ + .msg = try eb.addString(gpa, msg.msg), + .src_loc = try eb.addSourceLocation(gpa, .{ + .src_path = try eb.addString(gpa, file_path), + .span_start = token_starts[msg.tok], + .span_end = @intCast(u32, token_starts[msg.tok] + ast.tokenSlice(msg.tok).len), + .span_main = token_starts[msg.tok] + msg.off, + .line = @intCast(u32, start_loc.line), + .column = @intCast(u32, start_loc.column), + .source_line = try eb.addString(gpa, ast.source[start_loc.line_start..start_loc.line_end]), + }), + .notes_len = notes_len, + }); + eb.incrementCount(1); } }; @@ -504,9 +495,7 @@ fn fetchAndUnpack( // by default, so the same logic applies for buffering the reader as for gzip. try unpackTarball(gpa, &req, tmp_directory.handle, std.compress.xz); } else { - return report.fail(dep.url_tok, "unknown file extension for path '{s}'", .{ - uri.path, - }); + return report.fail(dep.url_tok, "unknown file extension for path '{s}'", .{uri.path}); } // TODO: delete files not included in the package prior to computing the package hash. @@ -533,10 +522,19 @@ fn fetchAndUnpack( }); } } else { - const notes: [1]Compilation.AllErrors.Message = .{.{ .plain = .{ - .msg = try std.fmt.allocPrint(report.arena, "expected .hash = \"{s}\",", .{&actual_hex}), - } }}; - return report.failWithNotes(¬es, dep.url_tok, "url field is missing corresponding hash field", .{}); + const file_path = try report.directory.join(gpa, &.{Manifest.basename}); + defer gpa.free(file_path); + + const eb = report.error_bundle; + try Report.addErrorMessage(gpa, report.ast.*, file_path, eb, 1, .{ + .tok = dep.url_tok, + .off = 0, + .msg = "url field is missing corresponding hash field", + }); + try eb.addErrorMessage(gpa, .{ + .msg = try eb.printString(gpa, "expected .hash = \"{s}\",", .{&actual_hex}), + }); + return error.PackageFetchFailed; } const build_root = try global_cache_directory.join(gpa, &.{pkg_dir_sub_path}); diff --git a/src/Sema.zig b/src/Sema.zig index 8b6c269246..237936547e 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -2211,29 +2211,26 @@ pub fn fail( fn failWithOwnedErrorMsg(sema: *Sema, err_msg: *Module.ErrorMsg) CompileError { @setCold(true); + const gpa = sema.gpa; if (crash_report.is_enabled and sema.mod.comp.debug_compile_errors) { if (err_msg.src_loc.lazy == .unneeded) return error.NeededSourceLocation; - var arena = std.heap.ArenaAllocator.init(sema.gpa); - errdefer arena.deinit(); - var errors = std.ArrayList(Compilation.AllErrors.Message).init(sema.gpa); - defer errors.deinit(); - - Compilation.AllErrors.add(sema.mod, &arena, &errors, err_msg.*) catch unreachable; - + var errors: std.zig.ErrorBundle = undefined; + errors.init(gpa) catch unreachable; + Compilation.addModuleErrorMsg(gpa, &errors, err_msg.*) catch unreachable; std.debug.print("compile error during Sema:\n", .{}); - Compilation.AllErrors.Message.renderToStdErr(errors.items[0], .no_color); + errors.renderToStdErr(.no_color); crash_report.compilerPanic("unexpected compile error occurred", null, null); } const mod = sema.mod; ref: { - errdefer err_msg.destroy(mod.gpa); + errdefer err_msg.destroy(gpa); if (err_msg.src_loc.lazy == .unneeded) { return error.NeededSourceLocation; } - try mod.failed_decls.ensureUnusedCapacity(mod.gpa, 1); - try mod.failed_files.ensureUnusedCapacity(mod.gpa, 1); + try mod.failed_decls.ensureUnusedCapacity(gpa, 1); + try mod.failed_files.ensureUnusedCapacity(gpa, 1); const max_references = blk: { if (sema.mod.comp.reference_trace) |num| break :blk num; @@ -2243,11 +2240,11 @@ fn failWithOwnedErrorMsg(sema: *Sema, err_msg: *Module.ErrorMsg) CompileError { }; var referenced_by = if (sema.func) |some| some.owner_decl else sema.owner_decl_index; - var reference_stack = std.ArrayList(Module.ErrorMsg.Trace).init(sema.gpa); + var reference_stack = std.ArrayList(Module.ErrorMsg.Trace).init(gpa); defer reference_stack.deinit(); // Avoid infinite loops. - var seen = std.AutoHashMap(Module.Decl.Index, void).init(sema.gpa); + var seen = std.AutoHashMap(Module.Decl.Index, void).init(gpa); defer seen.deinit(); var cur_reference_trace: u32 = 0; @@ -2288,7 +2285,7 @@ fn failWithOwnedErrorMsg(sema: *Sema, err_msg: *Module.ErrorMsg) CompileError { if (gop.found_existing) { // If there are multiple errors for the same Decl, prefer the first one added. sema.err = null; - err_msg.destroy(mod.gpa); + err_msg.destroy(gpa); } else { sema.err = err_msg; gop.value_ptr.* = err_msg; diff --git a/src/glibc.zig b/src/glibc.zig index 3021e7c7ba..530f35531a 100644 --- a/src/glibc.zig +++ b/src/glibc.zig @@ -196,7 +196,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { "-DASSEMBLER", "-Wa,--noexecstack", }); - return comp.build_crt_file("crti", .Obj, &[1]Compilation.CSourceFile{ + return comp.build_crt_file("crti", .Obj, .@"glibc crti.o", &[1]Compilation.CSourceFile{ .{ .src_path = try start_asm_path(comp, arena, "crti.S"), .cache_exempt_flags = args.items, @@ -215,7 +215,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { "-DASSEMBLER", "-Wa,--noexecstack", }); - return comp.build_crt_file("crtn", .Obj, &[1]Compilation.CSourceFile{ + return comp.build_crt_file("crtn", .Obj, .@"glibc crtn.o", &[1]Compilation.CSourceFile{ .{ .src_path = try start_asm_path(comp, arena, "crtn.S"), .cache_exempt_flags = args.items, @@ -265,7 +265,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { .cache_exempt_flags = args.items, }; }; - return comp.build_crt_file("Scrt1", .Obj, &[_]Compilation.CSourceFile{ start_o, abi_note_o }); + return comp.build_crt_file("Scrt1", .Obj, .@"glibc Scrt1.o", &[_]Compilation.CSourceFile{ start_o, abi_note_o }); }, .libc_nonshared_a => { const s = path.sep_str; @@ -366,7 +366,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { files_index += 1; } const files = files_buf[0..files_index]; - return comp.build_crt_file("c_nonshared", .Lib, files); + return comp.build_crt_file("c_nonshared", .Lib, .@"glibc libc_nonshared.a", files); }, } } @@ -1105,7 +1105,7 @@ fn buildSharedLib( }); defer sub_compilation.destroy(); - try sub_compilation.updateSubCompilation(); + try comp.updateSubCompilation(sub_compilation, .@"glibc shared object"); } // Return true if glibc has crti/crtn sources for that architecture. diff --git a/src/libcxx.zig b/src/libcxx.zig index 7ca405cf15..c17352c562 100644 --- a/src/libcxx.zig +++ b/src/libcxx.zig @@ -258,7 +258,7 @@ pub fn buildLibCXX(comp: *Compilation) !void { }); defer sub_compilation.destroy(); - try sub_compilation.updateSubCompilation(); + try comp.updateSubCompilation(sub_compilation, .libcxx); assert(comp.libcxx_static_lib == null); comp.libcxx_static_lib = Compilation.CRTFile{ @@ -418,7 +418,7 @@ pub fn buildLibCXXABI(comp: *Compilation) !void { }); defer sub_compilation.destroy(); - try sub_compilation.updateSubCompilation(); + try comp.updateSubCompilation(sub_compilation, .libcxxabi); assert(comp.libcxxabi_static_lib == null); comp.libcxxabi_static_lib = Compilation.CRTFile{ diff --git a/src/libtsan.zig b/src/libtsan.zig index 16e40c16f8..1399b6b76c 100644 --- a/src/libtsan.zig +++ b/src/libtsan.zig @@ -235,7 +235,7 @@ pub fn buildTsan(comp: *Compilation) !void { }); defer sub_compilation.destroy(); - try sub_compilation.updateSubCompilation(); + try comp.updateSubCompilation(sub_compilation, .libtsan); assert(comp.tsan_static_lib == null); comp.tsan_static_lib = Compilation.CRTFile{ diff --git a/src/libunwind.zig b/src/libunwind.zig index a20b5e81f7..667195a369 100644 --- a/src/libunwind.zig +++ b/src/libunwind.zig @@ -130,7 +130,7 @@ pub fn buildStaticLib(comp: *Compilation) !void { }); defer sub_compilation.destroy(); - try sub_compilation.updateSubCompilation(); + try comp.updateSubCompilation(sub_compilation, .libunwind); assert(comp.libunwind_static_lib == null); diff --git a/src/main.zig b/src/main.zig index a485fdb9e2..14b63017e5 100644 --- a/src/main.zig +++ b/src/main.zig @@ -24,6 +24,8 @@ const clang = @import("clang.zig"); const Cache = std.Build.Cache; const target_util = @import("target.zig"); const crash_report = @import("crash_report.zig"); +const Module = @import("Module.zig"); +const AstGen = @import("AstGen.zig"); pub const std_options = struct { pub const wasiCwd = wasi_cwd; @@ -3446,15 +3448,13 @@ fn buildOutputType( var errors = try comp.getAllErrorsAlloc(); defer errors.deinit(comp.gpa); - if (errors.list.len != 0) { + if (errors.errorMessageCount() > 0) { const ttyconf: std.debug.TTY.Config = switch (comp.color) { .auto => std.debug.detectTTYConfig(std.io.getStdErr()), .on => .escape_codes, .off => .no_color, }; - for (errors.list) |full_err_msg| { - try full_err_msg.renderToWriter(ttyconf, conn.stream.writer(), "error:", .Red, 0); - } + try errors.renderToWriter(ttyconf, conn.stream.writer()); continue; } } else { @@ -3830,15 +3830,13 @@ fn updateModule(gpa: Allocator, comp: *Compilation, hook: AfterUpdateHook) !void var errors = try comp.getAllErrorsAlloc(); defer errors.deinit(comp.gpa); - if (errors.list.len != 0) { + if (errors.errorMessageCount() > 0) { const ttyconf: std.debug.TTY.Config = switch (comp.color) { .auto => std.debug.detectTTYConfig(std.io.getStdErr()), .on => .escape_codes, .off => .no_color, }; - for (errors.list) |full_err_msg| { - full_err_msg.renderToStdErr(ttyconf); - } + errors.renderToStdErr(ttyconf); const log_text = comp.getCompileLogOutput(); if (log_text.len != 0) { std.debug.print("\nCompile Log Output:\n{s}", .{log_text}); @@ -4438,9 +4436,13 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi var all_modules: Package.AllModules = .{}; defer all_modules.deinit(gpa); + var errors: std.zig.ErrorBundle = undefined; + try errors.init(gpa); + defer errors.deinit(gpa); + // Here we borrow main package's table and will replace it with a fresh // one after this process completes. - build_pkg.fetchAndAddDependencies( + const fetch_result = build_pkg.fetchAndAddDependencies( &main_pkg, arena, &thread_pool, @@ -4451,12 +4453,19 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi &dependencies_source, &build_roots_source, "", - color, + &errors, &all_modules, - ) catch |err| switch (err) { - error.PackageFetchFailed => process.exit(1), - else => |e| return e, - }; + ); + if (errors.errorMessageCount() > 0) { + const ttyconf: std.debug.TTY.Config = switch (color) { + .auto => std.debug.detectTTYConfig(std.io.getStdErr()), + .on => .escape_codes, + .off => .no_color, + }; + errors.renderToStdErr(ttyconf); + process.exit(1); + } + try fetch_result; try dependencies_source.appendSlice("};\npub const build_root = struct {\n"); try dependencies_source.appendSlice(build_roots_source.items); @@ -4543,7 +4552,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi } fn readSourceFileToEndAlloc( - allocator: mem.Allocator, + allocator: Allocator, input: *const fs.File, size_hint: ?usize, ) ![:0]u8 { @@ -4687,12 +4696,9 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void }; defer tree.deinit(gpa); - try printErrsMsgToStdErr(gpa, arena, tree, "", color); + try printAstErrorsToStderr(gpa, tree, "", color); var has_ast_error = false; if (check_ast_flag) { - const Module = @import("Module.zig"); - const AstGen = @import("AstGen.zig"); - var file: Module.File = .{ .status = .never_loaded, .source_loaded = true, @@ -4715,20 +4721,16 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void defer file.zir.deinit(gpa); if (file.zir.hasCompileErrors()) { - var arena_instance = std.heap.ArenaAllocator.init(gpa); - defer arena_instance.deinit(); - var errors = std.ArrayList(Compilation.AllErrors.Message).init(gpa); - defer errors.deinit(); - - try Compilation.AllErrors.addZir(arena_instance.allocator(), &errors, &file); + var errors: std.zig.ErrorBundle = undefined; + try errors.init(gpa); + defer errors.deinit(gpa); + try Compilation.addZirErrorMessages(gpa, &errors, &file); const ttyconf: std.debug.TTY.Config = switch (color) { .auto => std.debug.detectTTYConfig(std.io.getStdErr()), .on => .escape_codes, .off => .no_color, }; - for (errors.items) |full_err_msg| { - full_err_msg.renderToStdErr(ttyconf); - } + errors.renderToStdErr(ttyconf); has_ast_error = true; } } @@ -4875,12 +4877,13 @@ fn fmtPathFile( if (stat.kind == .Directory) return error.IsDir; + const gpa = fmt.gpa; const source_code = try readSourceFileToEndAlloc( - fmt.gpa, + gpa, &source_file, std.math.cast(usize, stat.size) orelse return error.FileTooBig, ); - defer fmt.gpa.free(source_code); + defer gpa.free(source_code); source_file.close(); file_closed = true; @@ -4888,19 +4891,16 @@ fn fmtPathFile( // Add to set after no longer possible to get error.IsDir. if (try fmt.seen.fetchPut(stat.inode, {})) |_| return; - var tree = try Ast.parse(fmt.gpa, source_code, .zig); - defer tree.deinit(fmt.gpa); + var tree = try Ast.parse(gpa, source_code, .zig); + defer tree.deinit(gpa); - try printErrsMsgToStdErr(fmt.gpa, fmt.arena, tree, file_path, fmt.color); + try printAstErrorsToStderr(gpa, tree, file_path, fmt.color); if (tree.errors.len != 0) { fmt.any_error = true; return; } if (fmt.check_ast) { - const Module = @import("Module.zig"); - const AstGen = @import("AstGen.zig"); - var file: Module.File = .{ .status = .never_loaded, .source_loaded = true, @@ -4919,31 +4919,27 @@ fn fmtPathFile( .root_decl = .none, }; - file.pkg = try Package.create(fmt.gpa, null, file.sub_file_path); - defer file.pkg.destroy(fmt.gpa); + file.pkg = try Package.create(gpa, null, file.sub_file_path); + defer file.pkg.destroy(gpa); if (stat.size > max_src_size) return error.FileTooBig; - file.zir = try AstGen.generate(fmt.gpa, file.tree); + file.zir = try AstGen.generate(gpa, file.tree); file.zir_loaded = true; - defer file.zir.deinit(fmt.gpa); + defer file.zir.deinit(gpa); if (file.zir.hasCompileErrors()) { - var arena_instance = std.heap.ArenaAllocator.init(fmt.gpa); - defer arena_instance.deinit(); - var errors = std.ArrayList(Compilation.AllErrors.Message).init(fmt.gpa); - defer errors.deinit(); - - try Compilation.AllErrors.addZir(arena_instance.allocator(), &errors, &file); + var errors: std.zig.ErrorBundle = undefined; + try errors.init(gpa); + defer errors.deinit(gpa); + try Compilation.addZirErrorMessages(gpa, &errors, &file); const ttyconf: std.debug.TTY.Config = switch (fmt.color) { .auto => std.debug.detectTTYConfig(std.io.getStdErr()), .on => .escape_codes, .off => .no_color, }; - for (errors.items) |full_err_msg| { - full_err_msg.renderToStdErr(ttyconf); - } + errors.renderToStdErr(ttyconf); fmt.any_error = true; } } @@ -4971,100 +4967,53 @@ fn fmtPathFile( } } -pub fn printErrsMsgToStdErr( - gpa: mem.Allocator, - arena: mem.Allocator, +fn printAstErrorsToStderr(gpa: Allocator, tree: Ast, path: []const u8, color: Color) !void { + var error_bundle: std.zig.ErrorBundle = undefined; + try error_bundle.init(gpa); + defer error_bundle.deinit(gpa); + + try putAstErrorsIntoBundle(gpa, tree, path, &error_bundle); + + const ttyconf: std.debug.TTY.Config = switch (color) { + .auto => std.debug.detectTTYConfig(std.io.getStdErr()), + .on => .escape_codes, + .off => .no_color, + }; + error_bundle.renderToStdErr(ttyconf); +} + +pub fn putAstErrorsIntoBundle( + gpa: Allocator, tree: Ast, path: []const u8, - color: Color, + error_bundle: *std.zig.ErrorBundle, ) !void { - const parse_errors: []const Ast.Error = tree.errors; - var i: usize = 0; - while (i < parse_errors.len) : (i += 1) { - const parse_error = parse_errors[i]; - const lok_token = parse_error.token; - const token_tags = tree.tokens.items(.tag); - const start_loc = tree.tokenLocation(0, lok_token); - const source_line = tree.source[start_loc.line_start..start_loc.line_end]; + var file: Module.File = .{ + .status = .never_loaded, + .source_loaded = true, + .zir_loaded = false, + .sub_file_path = path, + .source = tree.source, + .stat = .{ + .size = 0, + .inode = 0, + .mtime = 0, + }, + .tree = tree, + .tree_loaded = true, + .zir = undefined, + .pkg = undefined, + .root_decl = .none, + }; - var text_buf = std.ArrayList(u8).init(gpa); - defer text_buf.deinit(); - const writer = text_buf.writer(); - try tree.renderError(parse_error, writer); - const text = try arena.dupe(u8, text_buf.items); + file.pkg = try Package.create(gpa, null, path); + defer file.pkg.destroy(gpa); - var notes_buffer: [2]Compilation.AllErrors.Message = undefined; - var notes_len: usize = 0; + file.zir = try AstGen.generate(gpa, file.tree); + file.zir_loaded = true; + defer file.zir.deinit(gpa); - if (token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)] == .invalid) { - const bad_off = @intCast(u32, tree.tokenSlice(parse_error.token + @boolToInt(parse_error.token_is_prev)).len); - const byte_offset = @intCast(u32, start_loc.line_start) + @intCast(u32, start_loc.column) + bad_off; - notes_buffer[notes_len] = .{ - .src = .{ - .src_path = path, - .msg = try std.fmt.allocPrint(arena, "invalid byte: '{'}'", .{ - std.zig.fmtEscapes(tree.source[byte_offset..][0..1]), - }), - .span = .{ .start = byte_offset, .end = byte_offset + 1, .main = byte_offset }, - .line = @intCast(u32, start_loc.line), - .column = @intCast(u32, start_loc.column) + bad_off, - .source_line = source_line, - }, - }; - notes_len += 1; - } - - for (parse_errors[i + 1 ..]) |note| { - if (!note.is_note) break; - - text_buf.items.len = 0; - try tree.renderError(note, writer); - const note_loc = tree.tokenLocation(0, note.token); - const byte_offset = @intCast(u32, note_loc.line_start); - notes_buffer[notes_len] = .{ - .src = .{ - .src_path = path, - .msg = try arena.dupe(u8, text_buf.items), - .span = .{ - .start = byte_offset, - .end = byte_offset + @intCast(u32, tree.tokenSlice(note.token).len), - .main = byte_offset, - }, - .line = @intCast(u32, note_loc.line), - .column = @intCast(u32, note_loc.column), - .source_line = tree.source[note_loc.line_start..note_loc.line_end], - }, - }; - i += 1; - notes_len += 1; - } - - const extra_offset = tree.errorOffset(parse_error); - const byte_offset = @intCast(u32, start_loc.line_start) + extra_offset; - const message: Compilation.AllErrors.Message = .{ - .src = .{ - .src_path = path, - .msg = text, - .span = .{ - .start = byte_offset, - .end = byte_offset + @intCast(u32, tree.tokenSlice(lok_token).len), - .main = byte_offset, - }, - .line = @intCast(u32, start_loc.line), - .column = @intCast(u32, start_loc.column) + extra_offset, - .source_line = source_line, - .notes = notes_buffer[0..notes_len], - }, - }; - - const ttyconf: std.debug.TTY.Config = switch (color) { - .auto => std.debug.detectTTYConfig(std.io.getStdErr()), - .on => .escape_codes, - .off => .no_color, - }; - - message.renderToStdErr(ttyconf); - } + try Compilation.addZirErrorMessages(gpa, error_bundle, &file); } pub const info_zen = @@ -5547,8 +5496,6 @@ pub fn cmdAstCheck( arena: Allocator, args: []const []const u8, ) !void { - const Module = @import("Module.zig"); - const AstGen = @import("AstGen.zig"); const Zir = @import("Zir.zig"); var color: Color = .auto; @@ -5638,7 +5585,7 @@ pub fn cmdAstCheck( file.tree_loaded = true; defer file.tree.deinit(gpa); - try printErrsMsgToStdErr(gpa, arena, file.tree, file.sub_file_path, color); + try printAstErrorsToStderr(gpa, file.tree, file.sub_file_path, color); if (file.tree.errors.len != 0) { process.exit(1); } @@ -5648,16 +5595,16 @@ pub fn cmdAstCheck( defer file.zir.deinit(gpa); if (file.zir.hasCompileErrors()) { - var errors = std.ArrayList(Compilation.AllErrors.Message).init(arena); - try Compilation.AllErrors.addZir(arena, &errors, &file); + var errors: std.zig.ErrorBundle = undefined; + try errors.init(gpa); + defer errors.deinit(gpa); + try Compilation.addZirErrorMessages(gpa, &errors, &file); const ttyconf: std.debug.TTY.Config = switch (color) { .auto => std.debug.detectTTYConfig(std.io.getStdErr()), .on => .escape_codes, .off => .no_color, }; - for (errors.items) |full_err_msg| { - full_err_msg.renderToStdErr(ttyconf); - } + errors.renderToStdErr(ttyconf); process.exit(1); } @@ -5715,8 +5662,6 @@ pub fn cmdChangelist( arena: Allocator, args: []const []const u8, ) !void { - const Module = @import("Module.zig"); - const AstGen = @import("AstGen.zig"); const Zir = @import("Zir.zig"); const old_source_file = args[0]; @@ -5764,7 +5709,7 @@ pub fn cmdChangelist( file.tree_loaded = true; defer file.tree.deinit(gpa); - try printErrsMsgToStdErr(gpa, arena, file.tree, old_source_file, .auto); + try printAstErrorsToStderr(gpa, file.tree, old_source_file, .auto); if (file.tree.errors.len != 0) { process.exit(1); } @@ -5774,12 +5719,12 @@ pub fn cmdChangelist( defer file.zir.deinit(gpa); if (file.zir.hasCompileErrors()) { - var errors = std.ArrayList(Compilation.AllErrors.Message).init(arena); - try Compilation.AllErrors.addZir(arena, &errors, &file); + var errors: std.zig.ErrorBundle = undefined; + try errors.init(gpa); + defer errors.deinit(gpa); + try Compilation.addZirErrorMessages(gpa, &errors, &file); const ttyconf = std.debug.detectTTYConfig(std.io.getStdErr()); - for (errors.items) |full_err_msg| { - full_err_msg.renderToStdErr(ttyconf); - } + errors.renderToStdErr(ttyconf); process.exit(1); } @@ -5801,7 +5746,7 @@ pub fn cmdChangelist( var new_tree = try Ast.parse(gpa, new_source, .zig); defer new_tree.deinit(gpa); - try printErrsMsgToStdErr(gpa, arena, new_tree, new_source_file, .auto); + try printAstErrorsToStderr(gpa, new_tree, new_source_file, .auto); if (new_tree.errors.len != 0) { process.exit(1); } @@ -5813,12 +5758,12 @@ pub fn cmdChangelist( file.zir_loaded = true; if (file.zir.hasCompileErrors()) { - var errors = std.ArrayList(Compilation.AllErrors.Message).init(arena); - try Compilation.AllErrors.addZir(arena, &errors, &file); + var errors: std.zig.ErrorBundle = undefined; + try errors.init(gpa); + defer errors.deinit(gpa); + try Compilation.addZirErrorMessages(gpa, &errors, &file); const ttyconf = std.debug.detectTTYConfig(std.io.getStdErr()); - for (errors.items) |full_err_msg| { - full_err_msg.renderToStdErr(ttyconf); - } + errors.renderToStdErr(ttyconf); process.exit(1); } diff --git a/src/mingw.zig b/src/mingw.zig index 9e9e180945..d5c42c3ccc 100644 --- a/src/mingw.zig +++ b/src/mingw.zig @@ -41,7 +41,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { //"-D_UNICODE", //"-DWPRFLAG=1", }); - return comp.build_crt_file("crt2", .Obj, &[1]Compilation.CSourceFile{ + return comp.build_crt_file("crt2", .Obj, .@"mingw-w64 crt2.o", &[1]Compilation.CSourceFile{ .{ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "mingw", "crt", "crtexe.c", @@ -60,7 +60,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { "-U__CRTDLL__", "-D__MSVCRT__", }); - return comp.build_crt_file("dllcrt2", .Obj, &[1]Compilation.CSourceFile{ + return comp.build_crt_file("dllcrt2", .Obj, .@"mingw-w64 dllcrt2.o", &[1]Compilation.CSourceFile{ .{ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "mingw", "crt", "crtdll.c", @@ -100,7 +100,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { .extra_flags = args.items, }; } - return comp.build_crt_file("mingw32", .Lib, &c_source_files); + return comp.build_crt_file("mingw32", .Lib, .@"mingw-w64 mingw32.lib", &c_source_files); }, .msvcrt_os_lib => { @@ -148,7 +148,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { }; } } - return comp.build_crt_file("msvcrt-os", .Lib, c_source_files.items); + return comp.build_crt_file("msvcrt-os", .Lib, .@"mingw-w64 msvcrt-os.lib", c_source_files.items); }, .mingwex_lib => { @@ -211,7 +211,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { } else { @panic("unsupported arch"); } - return comp.build_crt_file("mingwex", .Lib, c_source_files.items); + return comp.build_crt_file("mingwex", .Lib, .@"mingw-w64 mingwex.lib", c_source_files.items); }, .uuid_lib => { @@ -244,7 +244,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { .extra_flags = extra_flags, }; } - return comp.build_crt_file("uuid", .Lib, &c_source_files); + return comp.build_crt_file("uuid", .Lib, .@"mingw-w64 uuid.lib", &c_source_files); }, } } diff --git a/src/musl.zig b/src/musl.zig index 18e618df8f..7dd224604f 100644 --- a/src/musl.zig +++ b/src/musl.zig @@ -33,7 +33,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { try args.appendSlice(&[_][]const u8{ "-Qunused-arguments", }); - return comp.build_crt_file("crti", .Obj, &[1]Compilation.CSourceFile{ + return comp.build_crt_file("crti", .Obj, .@"musl crti.o", &[1]Compilation.CSourceFile{ .{ .src_path = try start_asm_path(comp, arena, "crti.s"), .extra_flags = args.items, @@ -46,7 +46,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { try args.appendSlice(&[_][]const u8{ "-Qunused-arguments", }); - return comp.build_crt_file("crtn", .Obj, &[1]Compilation.CSourceFile{ + return comp.build_crt_file("crtn", .Obj, .@"musl crtn.o", &[1]Compilation.CSourceFile{ .{ .src_path = try start_asm_path(comp, arena, "crtn.s"), .extra_flags = args.items, @@ -60,7 +60,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { "-fno-stack-protector", "-DCRT", }); - return comp.build_crt_file("crt1", .Obj, &[1]Compilation.CSourceFile{ + return comp.build_crt_file("crt1", .Obj, .@"musl crt1.o", &[1]Compilation.CSourceFile{ .{ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "musl", "crt", "crt1.c", @@ -77,7 +77,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { "-fno-stack-protector", "-DCRT", }); - return comp.build_crt_file("rcrt1", .Obj, &[1]Compilation.CSourceFile{ + return comp.build_crt_file("rcrt1", .Obj, .@"musl rcrt1.o", &[1]Compilation.CSourceFile{ .{ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "musl", "crt", "rcrt1.c", @@ -94,7 +94,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { "-fno-stack-protector", "-DCRT", }); - return comp.build_crt_file("Scrt1", .Obj, &[1]Compilation.CSourceFile{ + return comp.build_crt_file("Scrt1", .Obj, .@"musl Scrt1.o", &[1]Compilation.CSourceFile{ .{ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "musl", "crt", "Scrt1.c", @@ -187,7 +187,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { .extra_flags = args.items, }; } - return comp.build_crt_file("c", .Lib, c_source_files.items); + return comp.build_crt_file("c", .Lib, .@"musl libc.a", c_source_files.items); }, .libc_so => { const target = comp.getTarget(); @@ -241,7 +241,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { }); defer sub_compilation.destroy(); - try sub_compilation.updateSubCompilation(); + try comp.updateSubCompilation(sub_compilation, .@"musl libc.so"); try comp.crt_files.ensureUnusedCapacity(comp.gpa, 1); diff --git a/src/wasi_libc.zig b/src/wasi_libc.zig index fc8c81d5af..c4a4cbc4a5 100644 --- a/src/wasi_libc.zig +++ b/src/wasi_libc.zig @@ -74,7 +74,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { var args = std.ArrayList([]const u8).init(arena); try addCCArgs(comp, arena, &args, false); try addLibcBottomHalfIncludes(comp, arena, &args); - return comp.build_crt_file("crt1-reactor", .Obj, &[1]Compilation.CSourceFile{ + return comp.build_crt_file("crt1-reactor", .Obj, .@"wasi crt1-reactor.o", &[1]Compilation.CSourceFile{ .{ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", try sanitize(arena, crt1_reactor_src_file), @@ -87,7 +87,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { var args = std.ArrayList([]const u8).init(arena); try addCCArgs(comp, arena, &args, false); try addLibcBottomHalfIncludes(comp, arena, &args); - return comp.build_crt_file("crt1-command", .Obj, &[1]Compilation.CSourceFile{ + return comp.build_crt_file("crt1-command", .Obj, .@"wasi crt1-command.o", &[1]Compilation.CSourceFile{ .{ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", try sanitize(arena, crt1_command_src_file), @@ -145,7 +145,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { } } - try comp.build_crt_file("c", .Lib, libc_sources.items); + try comp.build_crt_file("c", .Lib, .@"wasi libc.a", libc_sources.items); }, .libwasi_emulated_process_clocks_a => { var args = std.ArrayList([]const u8).init(arena); @@ -161,7 +161,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { .extra_flags = args.items, }); } - try comp.build_crt_file("wasi-emulated-process-clocks", .Lib, emu_clocks_sources.items); + try comp.build_crt_file("wasi-emulated-process-clocks", .Lib, .@"libwasi-emulated-process-clocks.a", emu_clocks_sources.items); }, .libwasi_emulated_getpid_a => { var args = std.ArrayList([]const u8).init(arena); @@ -177,7 +177,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { .extra_flags = args.items, }); } - try comp.build_crt_file("wasi-emulated-getpid", .Lib, emu_getpid_sources.items); + try comp.build_crt_file("wasi-emulated-getpid", .Lib, .@"libwasi-emulated-getpid.a", emu_getpid_sources.items); }, .libwasi_emulated_mman_a => { var args = std.ArrayList([]const u8).init(arena); @@ -193,7 +193,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { .extra_flags = args.items, }); } - try comp.build_crt_file("wasi-emulated-mman", .Lib, emu_mman_sources.items); + try comp.build_crt_file("wasi-emulated-mman", .Lib, .@"libwasi-emulated-mman.a", emu_mman_sources.items); }, .libwasi_emulated_signal_a => { var emu_signal_sources = std.ArrayList(Compilation.CSourceFile).init(arena); @@ -228,7 +228,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { } } - try comp.build_crt_file("wasi-emulated-signal", .Lib, emu_signal_sources.items); + try comp.build_crt_file("wasi-emulated-signal", .Lib, .@"libwasi-emulated-signal.a", emu_signal_sources.items); }, } } From 6f717b18f05ba02439603e0987e9c9551fbadedb Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 23 Feb 2023 19:21:44 -0700 Subject: [PATCH 156/294] std.zig.ErrorBundle: rework binary encoding * Separate into a "WIP" struct and a "finished" struct. * Use a bit of indirection for error notes to simplify ergonomics of this data structure. --- lib/std/zig/ErrorBundle.zig | 410 ++++++++++++++++++++---------------- src/Compilation.zig | 257 +++++++++++----------- src/Package.zig | 36 ++-- src/Sema.zig | 9 +- src/main.zig | 86 ++++---- src/test.zig | 21 +- 6 files changed, 427 insertions(+), 392 deletions(-) diff --git a/lib/std/zig/ErrorBundle.zig b/lib/std/zig/ErrorBundle.zig index dee19818a8..1d2eca9703 100644 --- a/lib/std/zig/ErrorBundle.zig +++ b/lib/std/zig/ErrorBundle.zig @@ -3,24 +3,22 @@ //! is used to collect all the errors from the various places into one //! convenient place for API users to consume. -string_bytes: std.ArrayListUnmanaged(u8), -/// The first thing in this array is a ErrorMessageListIndex. -extra: std.ArrayListUnmanaged(u32), +string_bytes: []const u8, +/// The first thing in this array is an `ErrorMessageList`. +extra: []const u32, // An index into `extra` pointing at an `ErrorMessage`. pub const MessageIndex = enum(u32) { _, }; -/// After the header is: -/// * string_bytes -/// * extra (little endian) -pub const Header = struct { - string_bytes_len: u32, - extra_len: u32, +// An index into `extra` pointing at an `SourceLocation`. +pub const SourceLocationIndex = enum(u32) { + none = 0, + _, }; -/// Trailing: ErrorMessage for each len +/// There will be a MessageIndex for each len at start. pub const ErrorMessageList = struct { len: u32, start: u32, @@ -46,14 +44,13 @@ pub const SourceLocation = struct { }; /// Trailing: -/// * ErrorMessage for each notes_len. +/// * MessageIndex for each notes_len. pub const ErrorMessage = struct { /// null terminated string index msg: u32, /// Usually one, but incremented for redundant messages. count: u32 = 1, - /// 0 or the index into extra of a SourceLocation - src_loc: u32 = 0, + src_loc: SourceLocationIndex = .none, notes_len: u32 = 0, }; @@ -65,170 +62,41 @@ pub const ReferenceTrace = struct { decl_name: u32, /// Index into extra of a SourceLocation /// If this is 0, this is the sentinel ReferenceTrace element. - src_loc: u32, + src_loc: SourceLocationIndex, }; -pub fn init(eb: *ErrorBundle, gpa: Allocator) !void { - eb.* = .{ - .string_bytes = .{}, - .extra = .{}, - }; - - // So that 0 can be used to indicate a null string. - try eb.string_bytes.append(gpa, 0); - - _ = try addExtra(eb, gpa, ErrorMessageList{ - .len = 0, - .start = 0, - }); -} - pub fn deinit(eb: *ErrorBundle, gpa: Allocator) void { - eb.string_bytes.deinit(gpa); - eb.extra.deinit(gpa); + gpa.free(eb.string_bytes); + gpa.free(eb.extra); eb.* = undefined; } -pub fn addString(eb: *ErrorBundle, gpa: Allocator, s: []const u8) !u32 { - const index = @intCast(u32, eb.string_bytes.items.len); - try eb.string_bytes.ensureUnusedCapacity(gpa, s.len + 1); - eb.string_bytes.appendSliceAssumeCapacity(s); - eb.string_bytes.appendAssumeCapacity(0); - return index; -} - -pub fn printString(eb: *ErrorBundle, gpa: Allocator, comptime fmt: []const u8, args: anytype) !u32 { - const index = @intCast(u32, eb.string_bytes.items.len); - try eb.string_bytes.writer(gpa).print(fmt, args); - try eb.string_bytes.append(gpa, 0); - return index; -} - -pub fn addErrorMessage(eb: *ErrorBundle, gpa: Allocator, em: ErrorMessage) !void { - if (eb.errorMessageCount() == 0) { - eb.setStartIndex(@intCast(u32, eb.extra.items.len)); - } - _ = try addExtra(eb, gpa, em); -} - -pub fn addSourceLocation(eb: *ErrorBundle, gpa: Allocator, sl: SourceLocation) !u32 { - return addExtra(eb, gpa, sl); -} - -pub fn addReferenceTrace(eb: *ErrorBundle, gpa: Allocator, rt: ReferenceTrace) !void { - _ = try addExtra(eb, gpa, rt); -} - -pub fn addBundle(eb: *ErrorBundle, gpa: Allocator, other: ErrorBundle) !void { - // Skip over the initial ErrorMessageList len field. - const root_fields_len = @typeInfo(ErrorMessageList).Struct.fields.len; - const other_list = other.extraData(ErrorMessageList, 0).data; - const other_extra = other.extra.items[root_fields_len..]; - - try eb.string_bytes.ensureUnusedCapacity(gpa, other.string_bytes.items.len); - try eb.extra.ensureUnusedCapacity(gpa, other_extra.len); - - const new_string_base = @intCast(u32, eb.string_bytes.items.len); - const new_data_base = @intCast(u32, eb.extra.items.len - root_fields_len); - - eb.string_bytes.appendSliceAssumeCapacity(other.string_bytes.items); - eb.extra.appendSliceAssumeCapacity(other_extra); - - // Now we must offset the string indexes and extra indexes of the newly - // added extra. - var index = new_data_base + other_list.start; - for (0..other_list.len) |_| { - index = try patchMessage(eb, index, new_string_base, new_data_base); - } -} - -fn patchMessage(eb: *ErrorBundle, msg_idx: usize, new_string_base: u32, new_data_base: u32) !u32 { - var msg = eb.extraData(ErrorMessage, msg_idx); - if (msg.data.msg != 0) msg.data.msg += new_string_base; - if (msg.data.src_loc != 0) msg.data.src_loc += new_data_base; - eb.setExtra(msg_idx, msg.data); - - try patchSrcLoc(eb, msg.data.src_loc, new_string_base, new_data_base); - - var index = @intCast(u32, msg.end); - for (0..msg.data.notes_len) |_| { - index = try patchMessage(eb, index, new_string_base, new_data_base); - } - return index; -} - -fn patchSrcLoc(eb: *ErrorBundle, idx: usize, new_string_base: u32, new_data_base: u32) !void { - if (idx == 0) return; - - var src_loc = eb.extraData(SourceLocation, idx); - if (src_loc.data.src_path != 0) src_loc.data.src_path += new_string_base; - if (src_loc.data.source_line != 0) src_loc.data.source_line += new_string_base; - eb.setExtra(idx, src_loc.data); - - var index = src_loc.end; - for (0..src_loc.data.reference_trace_len) |_| { - var ref_trace = eb.extraData(ReferenceTrace, index); - if (ref_trace.data.decl_name != 0) ref_trace.data.decl_name += new_string_base; - if (ref_trace.data.src_loc != 0) ref_trace.data.src_loc += new_data_base; - eb.setExtra(index, ref_trace.data); - try patchSrcLoc(eb, ref_trace.data.src_loc, new_string_base, new_data_base); - index = ref_trace.end; - } -} - -fn addExtra(eb: *ErrorBundle, gpa: Allocator, extra: anytype) Allocator.Error!u32 { - const fields = @typeInfo(@TypeOf(extra)).Struct.fields; - try eb.extra.ensureUnusedCapacity(gpa, fields.len); - return addExtraAssumeCapacity(eb, extra); -} - -fn addExtraAssumeCapacity(eb: *ErrorBundle, extra: anytype) u32 { - const fields = @typeInfo(@TypeOf(extra)).Struct.fields; - const result = @intCast(u32, eb.extra.items.len); - eb.extra.items.len += fields.len; - setExtra(eb, result, extra); - return result; -} - -fn setExtra(eb: *ErrorBundle, index: usize, extra: anytype) void { - const fields = @typeInfo(@TypeOf(extra)).Struct.fields; - var i = index; - inline for (fields) |field| { - eb.extra.items[i] = switch (field.type) { - u32 => @field(extra, field.name), - else => @compileError("bad field type"), - }; - i += 1; - } -} - pub fn errorMessageCount(eb: ErrorBundle) u32 { - return eb.extra.items[0]; + return eb.getErrorMessageList().len; } -pub fn setErrorMessageCount(eb: *ErrorBundle, count: u32) void { - eb.extra.items[0] = count; +pub fn getErrorMessageList(eb: ErrorBundle) ErrorMessageList { + return eb.extraData(ErrorMessageList, 0).data; } -pub fn incrementCount(eb: *ErrorBundle, delta: u32) void { - eb.extra.items[0] += delta; -} - -pub fn getStartIndex(eb: ErrorBundle) u32 { - return eb.extra.items[1]; -} - -pub fn setStartIndex(eb: *ErrorBundle, index: u32) void { - eb.extra.items[1] = index; +pub fn getMessages(eb: ErrorBundle) []const MessageIndex { + const list = eb.getErrorMessageList(); + return @ptrCast([]const MessageIndex, eb.extra[list.start..][0..list.len]); } pub fn getErrorMessage(eb: ErrorBundle, index: MessageIndex) ErrorMessage { return eb.extraData(ErrorMessage, @enumToInt(index)).data; } -pub fn getSourceLocation(eb: ErrorBundle, index: u32) SourceLocation { - assert(index != 0); - return eb.extraData(SourceLocation, index).data; +pub fn getSourceLocation(eb: ErrorBundle, index: SourceLocationIndex) SourceLocation { + assert(index != .none); + return eb.extraData(SourceLocation, @enumToInt(index)).data; +} + +pub fn getNotes(eb: ErrorBundle, index: MessageIndex) []const MessageIndex { + const notes_len = eb.getErrorMessage(index).notes_len; + const start = @enumToInt(index) + @typeInfo(ErrorMessage).Struct.fields.len; + return @ptrCast([]const MessageIndex, eb.extra[start..][0..notes_len]); } /// Returns the requested data, as well as the new index which is at the start of the @@ -239,7 +107,9 @@ fn extraData(eb: ErrorBundle, comptime T: type, index: usize) struct { data: T, var result: T = undefined; inline for (fields) |field| { @field(result, field.name) = switch (field.type) { - u32 => eb.extra.items[i], + u32 => eb.extra[i], + MessageIndex => @intToEnum(MessageIndex, eb.extra[i]), + SourceLocationIndex => @intToEnum(SourceLocationIndex, eb.extra[i]), else => @compileError("bad field type"), }; i += 1; @@ -252,7 +122,7 @@ fn extraData(eb: ErrorBundle, comptime T: type, index: usize) struct { data: T, /// Given an index into `string_bytes` returns the null-terminated string found there. pub fn nullTerminatedString(eb: ErrorBundle, index: usize) [:0]const u8 { - const string_bytes = eb.string_bytes.items; + const string_bytes = eb.string_bytes; var end: usize = index; while (string_bytes[end] != 0) { end += 1; @@ -272,28 +142,25 @@ pub fn renderToWriter( ttyconf: std.debug.TTY.Config, writer: anytype, ) anyerror!void { - const list = eb.extraData(ErrorMessageList, 0).data; - var index: usize = list.start; - for (0..list.len) |_| { - const err_msg = eb.extraData(ErrorMessage, index); - index = try renderErrorMessageToWriter(eb, err_msg.data, err_msg.end, ttyconf, writer, "error", .Red, 0); + for (eb.getMessages()) |err_msg| { + try renderErrorMessageToWriter(eb, err_msg, ttyconf, writer, "error", .Red, 0); } } fn renderErrorMessageToWriter( eb: ErrorBundle, - err_msg: ErrorMessage, - end_index: usize, + err_msg_index: MessageIndex, ttyconf: std.debug.TTY.Config, stderr: anytype, kind: []const u8, color: std.debug.TTY.Color, indent: usize, -) anyerror!usize { +) anyerror!void { var counting_writer = std.io.countingWriter(stderr); const counting_stderr = counting_writer.writer(); - if (err_msg.src_loc != 0) { - const src = eb.extraData(SourceLocation, err_msg.src_loc); + const err_msg = eb.getErrorMessage(err_msg_index); + if (err_msg.src_loc != .none) { + const src = eb.extraData(SourceLocation, @enumToInt(err_msg.src_loc)); try counting_stderr.writeByteNTimes(' ', indent); try ttyconf.setColor(stderr, .Bold); try counting_stderr.print("{s}:{d}:{d}: ", .{ @@ -337,10 +204,8 @@ fn renderErrorMessageToWriter( try stderr.writeByte('\n'); try ttyconf.setColor(stderr, .Reset); } - var index = end_index; - for (0..err_msg.notes_len) |_| { - const note = eb.extraData(ErrorMessage, index); - index = try renderErrorMessageToWriter(eb, note.data, note.end, ttyconf, stderr, "note", .Cyan, indent); + for (eb.getNotes(err_msg_index)) |note| { + try renderErrorMessageToWriter(eb, note, ttyconf, stderr, "note", .Cyan, indent); } if (src.data.reference_trace_len > 0) { try ttyconf.setColor(stderr, .Reset); @@ -350,7 +215,7 @@ fn renderErrorMessageToWriter( for (0..src.data.reference_trace_len) |_| { const ref_trace = eb.extraData(ReferenceTrace, ref_index); ref_index = ref_trace.end; - if (ref_trace.data.src_loc != 0) { + if (ref_trace.data.src_loc != .none) { const ref_src = eb.getSourceLocation(ref_trace.data.src_loc); try stderr.print(" {s}: {s}:{d}:{d}\n", .{ eb.nullTerminatedString(ref_trace.data.decl_name), @@ -374,7 +239,6 @@ fn renderErrorMessageToWriter( try stderr.writeByte('\n'); try ttyconf.setColor(stderr, .Reset); } - return index; } else { try ttyconf.setColor(stderr, color); try stderr.writeByteNTimes(' ', indent); @@ -390,12 +254,9 @@ fn renderErrorMessageToWriter( try stderr.print(" ({d} times)\n", .{err_msg.count}); } try ttyconf.setColor(stderr, .Reset); - var index = end_index; - for (0..err_msg.notes_len) |_| { - const note = eb.extraData(ErrorMessage, index); - index = try renderErrorMessageToWriter(eb, note.data, note.end, ttyconf, stderr, "note", .Cyan, indent + 4); + for (eb.getNotes(err_msg_index)) |note| { + try renderErrorMessageToWriter(eb, note, ttyconf, stderr, "note", .Cyan, indent + 4); } - return index; } } @@ -417,3 +278,186 @@ const std = @import("std"); const ErrorBundle = @This(); const Allocator = std.mem.Allocator; const assert = std.debug.assert; + +pub const Wip = struct { + gpa: Allocator, + string_bytes: std.ArrayListUnmanaged(u8), + /// The first thing in this array is a ErrorMessageList. + extra: std.ArrayListUnmanaged(u32), + root_list: std.ArrayListUnmanaged(MessageIndex), + + pub fn init(wip: *Wip, gpa: Allocator) !void { + wip.* = .{ + .gpa = gpa, + .string_bytes = .{}, + .extra = .{}, + .root_list = .{}, + }; + + // So that 0 can be used to indicate a null string. + try wip.string_bytes.append(gpa, 0); + + assert(0 == try addExtra(wip, ErrorMessageList{ + .len = 0, + .start = 0, + })); + } + + pub fn deinit(wip: *Wip) void { + const gpa = wip.gpa; + wip.root_list.deinit(gpa); + wip.string_bytes.deinit(gpa); + wip.extra.deinit(gpa); + wip.* = undefined; + } + + pub fn toOwnedBundle(wip: *Wip) !ErrorBundle { + const gpa = wip.gpa; + wip.setExtra(0, ErrorMessageList{ + .len = @intCast(u32, wip.root_list.items.len), + .start = @intCast(u32, wip.extra.items.len), + }); + try wip.extra.appendSlice(gpa, @ptrCast([]const u32, wip.root_list.items)); + wip.root_list.clearAndFree(gpa); + return .{ + .string_bytes = try wip.string_bytes.toOwnedSlice(gpa), + .extra = try wip.extra.toOwnedSlice(gpa), + }; + } + + pub fn tmpBundle(wip: Wip) ErrorBundle { + return .{ + .string_bytes = wip.string_bytes.items, + .extra = wip.extra.items, + }; + } + + pub fn addString(wip: *Wip, s: []const u8) !u32 { + const gpa = wip.gpa; + const index = @intCast(u32, wip.string_bytes.items.len); + try wip.string_bytes.ensureUnusedCapacity(gpa, s.len + 1); + wip.string_bytes.appendSliceAssumeCapacity(s); + wip.string_bytes.appendAssumeCapacity(0); + return index; + } + + pub fn printString(wip: *Wip, comptime fmt: []const u8, args: anytype) !u32 { + const gpa = wip.gpa; + const index = @intCast(u32, wip.string_bytes.items.len); + try wip.string_bytes.writer(gpa).print(fmt, args); + try wip.string_bytes.append(gpa, 0); + return index; + } + + pub fn addRootErrorMessage(wip: *Wip, em: ErrorMessage) !void { + try wip.root_list.ensureUnusedCapacity(wip.gpa, 1); + wip.root_list.appendAssumeCapacity(try addErrorMessage(wip, em)); + } + + pub fn addErrorMessage(wip: *Wip, em: ErrorMessage) !MessageIndex { + return @intToEnum(MessageIndex, try addExtra(wip, em)); + } + + pub fn addErrorMessageAssumeCapacity(wip: *Wip, em: ErrorMessage) MessageIndex { + return @intToEnum(MessageIndex, addExtraAssumeCapacity(wip, em)); + } + + pub fn addSourceLocation(wip: *Wip, sl: SourceLocation) !SourceLocationIndex { + return @intToEnum(SourceLocationIndex, try addExtra(wip, sl)); + } + + pub fn addReferenceTrace(wip: *Wip, rt: ReferenceTrace) !void { + _ = try addExtra(wip, rt); + } + + pub fn addBundle(wip: *Wip, other: ErrorBundle) !void { + const gpa = wip.gpa; + + try wip.string_bytes.ensureUnusedCapacity(gpa, other.string_bytes.len); + try wip.extra.ensureUnusedCapacity(gpa, other.extra.len); + + const other_list = other.getMessages(); + + // The ensureUnusedCapacity call above guarantees this. + const notes_start = wip.reserveNotes(@intCast(u32, other_list.len)) catch unreachable; + for (notes_start.., other_list) |note, message| { + wip.extra.items[note] = @enumToInt(wip.addOtherMessage(other, message) catch unreachable); + } + } + + pub fn reserveNotes(wip: *Wip, notes_len: u32) !u32 { + try wip.extra.ensureUnusedCapacity(wip.gpa, notes_len + + notes_len * @typeInfo(ErrorBundle.ErrorMessage).Struct.fields.len); + wip.extra.items.len += notes_len; + return @intCast(u32, wip.extra.items.len - notes_len); + } + + fn addOtherMessage(wip: *Wip, other: ErrorBundle, msg_index: MessageIndex) !MessageIndex { + const other_msg = other.getErrorMessage(msg_index); + const src_loc = try wip.addOtherSourceLocation(other, other_msg.src_loc); + const msg = try wip.addErrorMessage(.{ + .msg = try wip.addString(other.nullTerminatedString(other_msg.msg)), + .count = other_msg.count, + .src_loc = src_loc, + .notes_len = other_msg.notes_len, + }); + const notes_start = try wip.reserveNotes(other_msg.notes_len); + for (notes_start.., other.getNotes(msg_index)) |note, other_note| { + wip.extra.items[note] = @enumToInt(try wip.addOtherMessage(other, other_note)); + } + return msg; + } + + fn addOtherSourceLocation( + wip: *Wip, + other: ErrorBundle, + index: SourceLocationIndex, + ) !SourceLocationIndex { + if (index == .none) return .none; + const other_sl = other.getSourceLocation(index); + + const src_loc = try wip.addSourceLocation(.{ + .src_path = try wip.addString(other.nullTerminatedString(other_sl.src_path)), + .line = other_sl.line, + .column = other_sl.column, + .span_start = other_sl.span_start, + .span_main = other_sl.span_main, + .span_end = other_sl.span_end, + .source_line = try wip.addString(other.nullTerminatedString(other_sl.source_line)), + .reference_trace_len = other_sl.reference_trace_len, + }); + + // TODO: also add the reference trace + + return src_loc; + } + + fn addExtra(wip: *Wip, extra: anytype) Allocator.Error!u32 { + const gpa = wip.gpa; + const fields = @typeInfo(@TypeOf(extra)).Struct.fields; + try wip.extra.ensureUnusedCapacity(gpa, fields.len); + return addExtraAssumeCapacity(wip, extra); + } + + fn addExtraAssumeCapacity(wip: *Wip, extra: anytype) u32 { + const fields = @typeInfo(@TypeOf(extra)).Struct.fields; + const result = @intCast(u32, wip.extra.items.len); + wip.extra.items.len += fields.len; + setExtra(wip, result, extra); + return result; + } + + fn setExtra(wip: *Wip, index: usize, extra: anytype) void { + const fields = @typeInfo(@TypeOf(extra)).Struct.fields; + var i = index; + inline for (fields) |field| { + wip.extra.items[i] = switch (field.type) { + u32 => @field(extra, field.name), + MessageIndex => @enumToInt(@field(extra, field.name)), + SourceLocationIndex => @enumToInt(@field(extra, field.name)), + else => @compileError("bad field type"), + }; + i += 1; + } + } +}; diff --git a/src/Compilation.zig b/src/Compilation.zig index f5a92c66ea..e19dc59445 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -2546,9 +2546,9 @@ pub fn totalErrorCount(self: *Compilation) u32 { pub fn getAllErrorsAlloc(self: *Compilation) !ErrorBundle { const gpa = self.gpa; - var bundle: ErrorBundle = undefined; + var bundle: ErrorBundle.Wip = undefined; try bundle.init(gpa); - errdefer bundle.deinit(gpa); + defer bundle.deinit(); { var it = self.failed_c_objects.iterator(); @@ -2557,12 +2557,10 @@ pub fn getAllErrorsAlloc(self: *Compilation) !ErrorBundle { const err_msg = entry.value_ptr.*; // TODO these fields will need to be adjusted when we have proper // C error reporting bubbling up. - try bundle.addErrorMessage(gpa, .{ - .msg = try bundle.printString(gpa, "unable to build C object: {s}", .{ - err_msg.msg, - }), - .src_loc = try bundle.addSourceLocation(gpa, .{ - .src_path = try bundle.addString(gpa, c_object.src.src_path), + try bundle.addRootErrorMessage(.{ + .msg = try bundle.printString("unable to build C object: {s}", .{err_msg.msg}), + .src_loc = try bundle.addSourceLocation(.{ + .src_path = try bundle.addString(c_object.src.src_path), .span_start = 0, .span_main = 0, .span_end = 1, @@ -2571,49 +2569,46 @@ pub fn getAllErrorsAlloc(self: *Compilation) !ErrorBundle { .source_line = 0, // TODO }), }); - bundle.incrementCount(1); } } for (self.lld_errors.items) |lld_error| { - try bundle.addErrorMessage(gpa, .{ - .msg = try bundle.addString(gpa, lld_error.msg), - .notes_len = @intCast(u32, lld_error.context_lines.len), - }); - bundle.incrementCount(1); + const notes_len = @intCast(u32, lld_error.context_lines.len); - for (lld_error.context_lines) |context_line| { - try bundle.addErrorMessage(gpa, .{ - .msg = try bundle.addString(gpa, context_line), - }); + try bundle.addRootErrorMessage(.{ + .msg = try bundle.addString(lld_error.msg), + .notes_len = notes_len, + }); + const notes_start = try bundle.reserveNotes(notes_len); + for (notes_start.., lld_error.context_lines) |note, context_line| { + bundle.extra.items[note] = @enumToInt(bundle.addErrorMessageAssumeCapacity(.{ + .msg = try bundle.addString(context_line), + })); } } for (self.misc_failures.values()) |*value| { - try bundle.addErrorMessage(gpa, .{ - .msg = try bundle.addString(gpa, value.msg), + try bundle.addRootErrorMessage(.{ + .msg = try bundle.addString(value.msg), .notes_len = if (value.children) |b| b.errorMessageCount() else 0, }); - if (value.children) |b| try bundle.addBundle(gpa, b); - bundle.incrementCount(1); + if (value.children) |b| try bundle.addBundle(b); } if (self.alloc_failure_occurred) { - try bundle.addErrorMessage(gpa, .{ - .msg = try bundle.addString(gpa, "memory allocation failure"), + try bundle.addRootErrorMessage(.{ + .msg = try bundle.addString("memory allocation failure"), }); - bundle.incrementCount(1); } if (self.bin_file.options.module) |module| { { var it = module.failed_files.iterator(); while (it.next()) |entry| { if (entry.value_ptr.*) |msg| { - try addModuleErrorMsg(gpa, &bundle, msg.*); + try addModuleErrorMsg(&bundle, msg.*); } else { - // Must be ZIR errors. In order for ZIR errors to exist, the parsing - // must have completed successfully. - const tree = try entry.key_ptr.*.getTree(module.gpa); - assert(tree.errors.len == 0); - try addZirErrorMessages(gpa, &bundle, entry.key_ptr.*); + // Must be ZIR errors. Note that this may include AST errors. + // addZirErrorMessages asserts that the tree is loaded. + _ = try entry.key_ptr.*.getTree(gpa); + try addZirErrorMessages(&bundle, entry.key_ptr.*); } } } @@ -2621,7 +2616,7 @@ pub fn getAllErrorsAlloc(self: *Compilation) !ErrorBundle { var it = module.failed_embed_files.iterator(); while (it.next()) |entry| { const msg = entry.value_ptr.*; - try addModuleErrorMsg(gpa, &bundle, msg.*); + try addModuleErrorMsg(&bundle, msg.*); } } { @@ -2631,21 +2626,20 @@ pub fn getAllErrorsAlloc(self: *Compilation) !ErrorBundle { // Skip errors for Decls within files that had a parse failure. // We'll try again once parsing succeeds. if (decl.getFileScope().okToReportErrors()) { - try addModuleErrorMsg(gpa, &bundle, entry.value_ptr.*.*); + try addModuleErrorMsg(&bundle, entry.value_ptr.*.*); if (module.cimport_errors.get(entry.key_ptr.*)) |cimport_errors| for (cimport_errors) |c_error| { - try bundle.addErrorMessage(gpa, .{ - .msg = try bundle.addString(gpa, std.mem.span(c_error.msg)), - .src_loc = if (c_error.path) |some| try bundle.addSourceLocation(gpa, .{ - .src_path = try bundle.addString(gpa, std.mem.span(some)), + try bundle.addRootErrorMessage(.{ + .msg = try bundle.addString(std.mem.span(c_error.msg)), + .src_loc = if (c_error.path) |some| try bundle.addSourceLocation(.{ + .src_path = try bundle.addString(std.mem.span(some)), .span_start = c_error.offset, .span_main = c_error.offset, .span_end = c_error.offset + 1, .line = c_error.line, .column = c_error.column, - .source_line = if (c_error.source_line) |line| try bundle.addString(gpa, std.mem.span(line)) else 0, - }) else 0, + .source_line = if (c_error.source_line) |line| try bundle.addString(std.mem.span(line)) else 0, + }) else .none, }); - bundle.incrementCount(1); }; } } @@ -2657,40 +2651,39 @@ pub fn getAllErrorsAlloc(self: *Compilation) !ErrorBundle { // Skip errors for Decls within files that had a parse failure. // We'll try again once parsing succeeds. if (decl.getFileScope().okToReportErrors()) { - try addModuleErrorMsg(gpa, &bundle, entry.value_ptr.*.*); + try addModuleErrorMsg(&bundle, entry.value_ptr.*.*); } } } for (module.failed_exports.values()) |value| { - try addModuleErrorMsg(gpa, &bundle, value.*); + try addModuleErrorMsg(&bundle, value.*); } } - if (bundle.errorMessageCount() == 0) { + if (bundle.root_list.items.len == 0) { if (self.link_error_flags.no_entry_point_found) { - try bundle.addErrorMessage(gpa, .{ - .msg = try bundle.addString(gpa, "no entry point found"), + try bundle.addRootErrorMessage(.{ + .msg = try bundle.addString("no entry point found"), }); - bundle.incrementCount(1); } } if (self.link_error_flags.missing_libc) { - try bundle.addErrorMessage(gpa, .{ - .msg = try bundle.addString(gpa, "libc not available"), + try bundle.addRootErrorMessage(.{ + .msg = try bundle.addString("libc not available"), .notes_len = 2, }); - try bundle.addErrorMessage(gpa, .{ - .msg = try bundle.addString(gpa, "run 'zig libc -h' to learn about libc installations"), - }); - try bundle.addErrorMessage(gpa, .{ - .msg = try bundle.addString(gpa, "run 'zig targets' to see the targets for which zig can always provide libc"), - }); - bundle.incrementCount(1); + const notes_start = try bundle.reserveNotes(2); + bundle.extra.items[notes_start + 0] = @enumToInt(try bundle.addErrorMessage(.{ + .msg = try bundle.addString("run 'zig libc -h' to learn about libc installations"), + })); + bundle.extra.items[notes_start + 1] = @enumToInt(try bundle.addErrorMessage(.{ + .msg = try bundle.addString("run 'zig targets' to see the targets for which zig can always provide libc"), + })); } if (self.bin_file.options.module) |module| { - if (bundle.errorMessageCount() == 0 and module.compile_log_decls.count() != 0) { + if (bundle.root_list.items.len == 0 and module.compile_log_decls.count() != 0) { const keys = module.compile_log_decls.keys(); const values = module.compile_log_decls.values(); // First one will be the error; subsequent ones will be notes. @@ -2699,9 +2692,9 @@ pub fn getAllErrorsAlloc(self: *Compilation) !ErrorBundle { const err_msg = Module.ErrorMsg{ .src_loc = src_loc, .msg = "found compile log statement", - .notes = try self.gpa.alloc(Module.ErrorMsg, module.compile_log_decls.count() - 1), + .notes = try gpa.alloc(Module.ErrorMsg, module.compile_log_decls.count() - 1), }; - defer self.gpa.free(err_msg.notes); + defer gpa.free(err_msg.notes); for (keys[1..], 0..) |key, i| { const note_decl = module.declPtr(key); @@ -2711,25 +2704,26 @@ pub fn getAllErrorsAlloc(self: *Compilation) !ErrorBundle { }; } - try addModuleErrorMsg(gpa, &bundle, err_msg); + try addModuleErrorMsg(&bundle, err_msg); } } - assert(self.totalErrorCount() == bundle.errorMessageCount()); + assert(self.totalErrorCount() == bundle.root_list.items.len); - return bundle; + return bundle.toOwnedBundle(); } pub const ErrorNoteHashContext = struct { - eb: *const ErrorBundle, + eb: *const ErrorBundle.Wip, pub fn hash(ctx: ErrorNoteHashContext, key: ErrorBundle.ErrorMessage) u32 { var hasher = std.hash.Wyhash.init(0); + const eb = ctx.eb.tmpBundle(); - hasher.update(ctx.eb.nullTerminatedString(key.msg)); - if (key.src_loc != 0) { - const src = ctx.eb.getSourceLocation(key.src_loc); - hasher.update(ctx.eb.nullTerminatedString(src.src_path)); + hasher.update(eb.nullTerminatedString(key.msg)); + if (key.src_loc != .none) { + const src = eb.getSourceLocation(key.src_loc); + hasher.update(eb.nullTerminatedString(src.src_path)); std.hash.autoHash(&hasher, src.line); std.hash.autoHash(&hasher, src.column); std.hash.autoHash(&hasher, src.span_main); @@ -2745,17 +2739,18 @@ pub const ErrorNoteHashContext = struct { b_index: usize, ) bool { _ = b_index; - const msg_a = ctx.eb.nullTerminatedString(a.msg); - const msg_b = ctx.eb.nullTerminatedString(b.msg); + const eb = ctx.eb.tmpBundle(); + const msg_a = eb.nullTerminatedString(a.msg); + const msg_b = eb.nullTerminatedString(b.msg); if (!std.mem.eql(u8, msg_a, msg_b)) return false; - if (a.src_loc == 0 and b.src_loc == 0) return true; - if (a.src_loc == 0 or b.src_loc == 0) return false; - const src_a = ctx.eb.getSourceLocation(a.src_loc); - const src_b = ctx.eb.getSourceLocation(b.src_loc); + if (a.src_loc == .none and b.src_loc == .none) return true; + if (a.src_loc == .none or b.src_loc == .none) return false; + const src_a = eb.getSourceLocation(a.src_loc); + const src_b = eb.getSourceLocation(b.src_loc); - const src_path_a = ctx.eb.nullTerminatedString(src_a.src_path); - const src_path_b = ctx.eb.nullTerminatedString(src_b.src_path); + const src_path_a = eb.nullTerminatedString(src_a.src_path); + const src_path_b = eb.nullTerminatedString(src_b.src_path); return std.mem.eql(u8, src_path_a, src_path_b) and src_a.line == src_b.line and @@ -2764,16 +2759,16 @@ pub const ErrorNoteHashContext = struct { } }; -pub fn addModuleErrorMsg(gpa: Allocator, eb: *ErrorBundle, module_err_msg: Module.ErrorMsg) !void { +pub fn addModuleErrorMsg(eb: *ErrorBundle.Wip, module_err_msg: Module.ErrorMsg) !void { + const gpa = eb.gpa; const err_source = module_err_msg.src_loc.file_scope.getSource(gpa) catch |err| { const file_path = try module_err_msg.src_loc.file_scope.fullPath(gpa); defer gpa.free(file_path); - try eb.addErrorMessage(gpa, .{ - .msg = try eb.printString(gpa, "unable to load '{s}': {s}", .{ + try eb.addRootErrorMessage(.{ + .msg = try eb.printString("unable to load '{s}': {s}", .{ file_path, @errorName(err), }), }); - eb.incrementCount(1); return; }; const err_span = try module_err_msg.src_loc.span(gpa); @@ -2788,13 +2783,13 @@ pub fn addModuleErrorMsg(gpa: Allocator, eb: *ErrorBundle, module_err_msg: Modul if (module_reference.hidden != 0) { try ref_traces.append(gpa, .{ .decl_name = module_reference.hidden, - .src_loc = 0, + .src_loc = .none, }); break; } else if (module_reference.decl == null) { try ref_traces.append(gpa, .{ .decl_name = 0, - .src_loc = 0, + .src_loc = .none, }); break; } @@ -2804,9 +2799,9 @@ pub fn addModuleErrorMsg(gpa: Allocator, eb: *ErrorBundle, module_err_msg: Modul const rt_file_path = try module_reference.src_loc.file_scope.fullPath(gpa); defer gpa.free(rt_file_path); try ref_traces.append(gpa, .{ - .decl_name = try eb.addString(gpa, std.mem.sliceTo(module_reference.decl.?, 0)), - .src_loc = try eb.addSourceLocation(gpa, .{ - .src_path = try eb.addString(gpa, rt_file_path), + .decl_name = try eb.addString(std.mem.sliceTo(module_reference.decl.?, 0)), + .src_loc = try eb.addSourceLocation(.{ + .src_path = try eb.addString(rt_file_path), .span_start = span.start, .span_main = span.main, .span_end = span.end, @@ -2817,8 +2812,8 @@ pub fn addModuleErrorMsg(gpa: Allocator, eb: *ErrorBundle, module_err_msg: Modul }); } - const src_loc = try eb.addSourceLocation(gpa, .{ - .src_path = try eb.addString(gpa, file_path), + const src_loc = try eb.addSourceLocation(.{ + .src_path = try eb.addString(file_path), .span_start = err_span.start, .span_main = err_span.main, .span_end = err_span.end, @@ -2827,12 +2822,12 @@ pub fn addModuleErrorMsg(gpa: Allocator, eb: *ErrorBundle, module_err_msg: Modul .source_line = if (module_err_msg.src_loc.lazy == .entire_file) 0 else - try eb.addString(gpa, err_loc.source_line), + try eb.addString(err_loc.source_line), .reference_trace_len = @intCast(u32, ref_traces.items.len), }); for (ref_traces.items) |rt| { - try eb.addReferenceTrace(gpa, rt); + try eb.addReferenceTrace(rt); } // De-duplicate error notes. The main use case in mind for this is @@ -2848,15 +2843,15 @@ pub fn addModuleErrorMsg(gpa: Allocator, eb: *ErrorBundle, module_err_msg: Modul defer gpa.free(note_file_path); const gop = try notes.getOrPutContext(gpa, .{ - .msg = try eb.addString(gpa, module_note.msg), - .src_loc = try eb.addSourceLocation(gpa, .{ - .src_path = try eb.addString(gpa, note_file_path), + .msg = try eb.addString(module_note.msg), + .src_loc = try eb.addSourceLocation(.{ + .src_path = try eb.addString(note_file_path), .span_start = span.start, .span_main = span.main, .span_end = span.end, .line = @intCast(u32, loc.line), .column = @intCast(u32, loc.column), - .source_line = if (err_loc.eql(loc)) 0 else try eb.addString(gpa, loc.source_line), + .source_line = if (err_loc.eql(loc)) 0 else try eb.addString(loc.source_line), }), }, .{ .eb = eb }); if (gop.found_existing) { @@ -2864,24 +2859,28 @@ pub fn addModuleErrorMsg(gpa: Allocator, eb: *ErrorBundle, module_err_msg: Modul } } - try eb.addErrorMessage(gpa, .{ - .msg = try eb.addString(gpa, module_err_msg.msg), - .src_loc = src_loc, - .notes_len = @intCast(u32, notes.entries.len), - }); - eb.incrementCount(1); + const notes_len = @intCast(u32, notes.entries.len); - for (notes.keys()) |note| { - try eb.addErrorMessage(gpa, note); + try eb.addRootErrorMessage(.{ + .msg = try eb.addString(module_err_msg.msg), + .src_loc = src_loc, + .notes_len = notes_len, + }); + + const notes_start = try eb.reserveNotes(notes_len); + + for (notes_start.., notes.keys()) |i, note| { + eb.extra.items[i] = @enumToInt(try eb.addErrorMessage(note)); } } -pub fn addZirErrorMessages(gpa: Allocator, eb: *ErrorBundle, file: *Module.File) !void { +pub fn addZirErrorMessages(eb: *ErrorBundle.Wip, file: *Module.File) !void { assert(file.zir_loaded); assert(file.tree_loaded); assert(file.source_loaded); const payload_index = file.zir.extra[@enumToInt(Zir.ExtraIndex.compile_errors)]; assert(payload_index != 0); + const gpa = eb.gpa; const header = file.zir.extraData(Zir.Inst.CompileErrors, payload_index); const items_len = header.data.items_len; @@ -2900,14 +2899,30 @@ pub fn addZirErrorMessages(gpa: Allocator, eb: *ErrorBundle, file: *Module.File) }; const err_loc = std.zig.findLineColumn(file.source, err_span.main); - var notes: []ErrorBundle.ErrorMessage = &.{}; - defer gpa.free(notes); + { + const msg = file.zir.nullTerminatedString(item.data.msg); + const src_path = try file.fullPath(gpa); + defer gpa.free(src_path); + try eb.addRootErrorMessage(.{ + .msg = try eb.addString(msg), + .src_loc = try eb.addSourceLocation(.{ + .src_path = try eb.addString(src_path), + .span_start = err_span.start, + .span_main = err_span.main, + .span_end = err_span.end, + .line = @intCast(u32, err_loc.line), + .column = @intCast(u32, err_loc.column), + .source_line = try eb.addString(err_loc.source_line), + }), + .notes_len = item.data.notes, + }); + } if (item.data.notes != 0) { + const notes_start = try eb.reserveNotes(item.data.notes); const block = file.zir.extraData(Zir.Inst.Block, item.data.notes); const body = file.zir.extra[block.end..][0..block.data.body_len]; - notes = try gpa.alloc(ErrorBundle.ErrorMessage, body.len); - for (notes, body) |*note, body_elem| { + for (notes_start.., body) |note_i, body_elem| { const note_item = file.zir.extraData(Zir.Inst.CompileErrors.Item, body_elem); const msg = file.zir.nullTerminatedString(note_item.data.msg); const span = blk: { @@ -2923,10 +2938,10 @@ pub fn addZirErrorMessages(gpa: Allocator, eb: *ErrorBundle, file: *Module.File) const src_path = try file.fullPath(gpa); defer gpa.free(src_path); - note.* = .{ - .msg = try eb.addString(gpa, msg), - .src_loc = try eb.addSourceLocation(gpa, .{ - .src_path = try eb.addString(gpa, src_path), + eb.extra.items[note_i] = @enumToInt(try eb.addErrorMessage(.{ + .msg = try eb.addString(msg), + .src_loc = try eb.addSourceLocation(.{ + .src_path = try eb.addString(src_path), .span_start = span.start, .span_main = span.main, .span_end = span.end, @@ -2935,35 +2950,13 @@ pub fn addZirErrorMessages(gpa: Allocator, eb: *ErrorBundle, file: *Module.File) .source_line = if (loc.eql(err_loc)) 0 else - try eb.addString(gpa, loc.source_line), + try eb.addString(loc.source_line), }), .notes_len = 0, // TODO rework this function to be recursive - }; + })); } } - - const msg = file.zir.nullTerminatedString(item.data.msg); - const src_path = try file.fullPath(gpa); - defer gpa.free(src_path); - try eb.addErrorMessage(gpa, .{ - .msg = try eb.addString(gpa, msg), - .src_loc = try eb.addSourceLocation(gpa, .{ - .src_path = try eb.addString(gpa, src_path), - .span_start = err_span.start, - .span_main = err_span.main, - .span_end = err_span.end, - .line = @intCast(u32, err_loc.line), - .column = @intCast(u32, err_loc.column), - .source_line = try eb.addString(gpa, err_loc.source_line), - }), - .notes_len = @intCast(u32, notes.len), - }); - - for (notes) |note| { - try eb.addErrorMessage(gpa, note); - } } - eb.incrementCount(items_len); } pub fn getCompileLogOutput(self: *Compilation) []const u8 { diff --git a/src/Package.zig b/src/Package.zig index febcc51788..d26daf5a0c 100644 --- a/src/Package.zig +++ b/src/Package.zig @@ -225,7 +225,7 @@ pub fn fetchAndAddDependencies( dependencies_source: *std.ArrayList(u8), build_roots_source: *std.ArrayList(u8), name_prefix: []const u8, - error_bundle: *std.zig.ErrorBundle, + error_bundle: *std.zig.ErrorBundle.Wip, all_modules: *AllModules, ) !void { const max_bytes = 10 * 1024 * 1024; @@ -260,13 +260,12 @@ pub fn fetchAndAddDependencies( if (manifest.errors.len > 0) { const file_path = try directory.join(arena, &.{Manifest.basename}); for (manifest.errors) |msg| { - try Report.addErrorMessage(gpa, ast, file_path, error_bundle, 0, msg); + try Report.addErrorMessage(ast, file_path, error_bundle, 0, msg); } return error.PackageFetchFailed; } const report: Report = .{ - .gpa = gpa, .ast = &ast, .directory = directory, .error_bundle = error_bundle, @@ -343,10 +342,9 @@ pub fn createFilePkg( } const Report = struct { - gpa: Allocator, ast: *const std.zig.Ast, directory: Compilation.Directory, - error_bundle: *std.zig.ErrorBundle, + error_bundle: *std.zig.ErrorBundle.Wip, fn fail( report: Report, @@ -354,7 +352,7 @@ const Report = struct { comptime fmt_string: []const u8, fmt_args: anytype, ) error{ PackageFetchFailed, OutOfMemory } { - const gpa = report.gpa; + const gpa = report.error_bundle.gpa; const file_path = try report.directory.join(gpa, &.{Manifest.basename}); defer gpa.free(file_path); @@ -362,7 +360,7 @@ const Report = struct { const msg = try std.fmt.allocPrint(gpa, fmt_string, fmt_args); defer gpa.free(msg); - try addErrorMessage(report.gpa, report.ast.*, file_path, report.error_bundle, 0, .{ + try addErrorMessage(report.ast.*, file_path, report.error_bundle, 0, .{ .tok = tok, .off = 0, .msg = msg, @@ -372,30 +370,28 @@ const Report = struct { } fn addErrorMessage( - gpa: Allocator, ast: std.zig.Ast, file_path: []const u8, - eb: *std.zig.ErrorBundle, + eb: *std.zig.ErrorBundle.Wip, notes_len: u32, msg: Manifest.ErrorMessage, ) error{OutOfMemory}!void { const token_starts = ast.tokens.items(.start); const start_loc = ast.tokenLocation(0, msg.tok); - try eb.addErrorMessage(gpa, .{ - .msg = try eb.addString(gpa, msg.msg), - .src_loc = try eb.addSourceLocation(gpa, .{ - .src_path = try eb.addString(gpa, file_path), + try eb.addRootErrorMessage(.{ + .msg = try eb.addString(msg.msg), + .src_loc = try eb.addSourceLocation(.{ + .src_path = try eb.addString(file_path), .span_start = token_starts[msg.tok], .span_end = @intCast(u32, token_starts[msg.tok] + ast.tokenSlice(msg.tok).len), .span_main = token_starts[msg.tok] + msg.off, .line = @intCast(u32, start_loc.line), .column = @intCast(u32, start_loc.column), - .source_line = try eb.addString(gpa, ast.source[start_loc.line_start..start_loc.line_end]), + .source_line = try eb.addString(ast.source[start_loc.line_start..start_loc.line_end]), }), .notes_len = notes_len, }); - eb.incrementCount(1); } }; @@ -526,14 +522,16 @@ fn fetchAndUnpack( defer gpa.free(file_path); const eb = report.error_bundle; - try Report.addErrorMessage(gpa, report.ast.*, file_path, eb, 1, .{ + const notes_len = 1; + try Report.addErrorMessage(report.ast.*, file_path, eb, notes_len, .{ .tok = dep.url_tok, .off = 0, .msg = "url field is missing corresponding hash field", }); - try eb.addErrorMessage(gpa, .{ - .msg = try eb.printString(gpa, "expected .hash = \"{s}\",", .{&actual_hex}), - }); + const notes_start = try eb.reserveNotes(notes_len); + eb.extra.items[notes_start] = @enumToInt(try eb.addErrorMessage(.{ + .msg = try eb.printString("expected .hash = \"{s}\",", .{&actual_hex}), + })); return error.PackageFetchFailed; } diff --git a/src/Sema.zig b/src/Sema.zig index 237936547e..03ebbbdbac 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -2215,11 +2215,12 @@ fn failWithOwnedErrorMsg(sema: *Sema, err_msg: *Module.ErrorMsg) CompileError { if (crash_report.is_enabled and sema.mod.comp.debug_compile_errors) { if (err_msg.src_loc.lazy == .unneeded) return error.NeededSourceLocation; - var errors: std.zig.ErrorBundle = undefined; - errors.init(gpa) catch unreachable; - Compilation.addModuleErrorMsg(gpa, &errors, err_msg.*) catch unreachable; + var wip_errors: std.zig.ErrorBundle.Wip = undefined; + wip_errors.init(gpa) catch unreachable; + Compilation.addModuleErrorMsg(&wip_errors, err_msg.*) catch unreachable; std.debug.print("compile error during Sema:\n", .{}); - errors.renderToStdErr(.no_color); + var error_bundle = wip_errors.toOwnedBundle() catch unreachable; + error_bundle.renderToStdErr(.no_color); crash_report.compilerPanic("unexpected compile error occurred", null, null); } diff --git a/src/main.zig b/src/main.zig index 14b63017e5..0a16aa5a46 100644 --- a/src/main.zig +++ b/src/main.zig @@ -4436,9 +4436,9 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi var all_modules: Package.AllModules = .{}; defer all_modules.deinit(gpa); - var errors: std.zig.ErrorBundle = undefined; - try errors.init(gpa); - defer errors.deinit(gpa); + var wip_errors: std.zig.ErrorBundle.Wip = undefined; + try wip_errors.init(gpa); + defer wip_errors.deinit(); // Here we borrow main package's table and will replace it with a fresh // one after this process completes. @@ -4453,15 +4453,17 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi &dependencies_source, &build_roots_source, "", - &errors, + &wip_errors, &all_modules, ); - if (errors.errorMessageCount() > 0) { + if (wip_errors.root_list.items.len > 0) { const ttyconf: std.debug.TTY.Config = switch (color) { .auto => std.debug.detectTTYConfig(std.io.getStdErr()), .on => .escape_codes, .off => .no_color, }; + var errors = try wip_errors.toOwnedBundle(); + defer errors.deinit(gpa); errors.renderToStdErr(ttyconf); process.exit(1); } @@ -4721,16 +4723,18 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void defer file.zir.deinit(gpa); if (file.zir.hasCompileErrors()) { - var errors: std.zig.ErrorBundle = undefined; - try errors.init(gpa); - defer errors.deinit(gpa); - try Compilation.addZirErrorMessages(gpa, &errors, &file); + var wip_errors: std.zig.ErrorBundle.Wip = undefined; + try wip_errors.init(gpa); + defer wip_errors.deinit(); + try Compilation.addZirErrorMessages(&wip_errors, &file); const ttyconf: std.debug.TTY.Config = switch (color) { .auto => std.debug.detectTTYConfig(std.io.getStdErr()), .on => .escape_codes, .off => .no_color, }; - errors.renderToStdErr(ttyconf); + var error_bundle = try wip_errors.toOwnedBundle(); + defer error_bundle.deinit(gpa); + error_bundle.renderToStdErr(ttyconf); has_ast_error = true; } } @@ -4930,16 +4934,18 @@ fn fmtPathFile( defer file.zir.deinit(gpa); if (file.zir.hasCompileErrors()) { - var errors: std.zig.ErrorBundle = undefined; - try errors.init(gpa); - defer errors.deinit(gpa); - try Compilation.addZirErrorMessages(gpa, &errors, &file); + var wip_errors: std.zig.ErrorBundle.Wip = undefined; + try wip_errors.init(gpa); + defer wip_errors.deinit(); + try Compilation.addZirErrorMessages(&wip_errors, &file); const ttyconf: std.debug.TTY.Config = switch (fmt.color) { .auto => std.debug.detectTTYConfig(std.io.getStdErr()), .on => .escape_codes, .off => .no_color, }; - errors.renderToStdErr(ttyconf); + var error_bundle = try wip_errors.toOwnedBundle(); + defer error_bundle.deinit(gpa); + error_bundle.renderToStdErr(ttyconf); fmt.any_error = true; } } @@ -4968,17 +4974,19 @@ fn fmtPathFile( } fn printAstErrorsToStderr(gpa: Allocator, tree: Ast, path: []const u8, color: Color) !void { - var error_bundle: std.zig.ErrorBundle = undefined; - try error_bundle.init(gpa); - defer error_bundle.deinit(gpa); + var wip_errors: std.zig.ErrorBundle.Wip = undefined; + try wip_errors.init(gpa); + defer wip_errors.deinit(); - try putAstErrorsIntoBundle(gpa, tree, path, &error_bundle); + try putAstErrorsIntoBundle(gpa, tree, path, &wip_errors); const ttyconf: std.debug.TTY.Config = switch (color) { .auto => std.debug.detectTTYConfig(std.io.getStdErr()), .on => .escape_codes, .off => .no_color, }; + var error_bundle = try wip_errors.toOwnedBundle(); + defer error_bundle.deinit(gpa); error_bundle.renderToStdErr(ttyconf); } @@ -4986,7 +4994,7 @@ pub fn putAstErrorsIntoBundle( gpa: Allocator, tree: Ast, path: []const u8, - error_bundle: *std.zig.ErrorBundle, + wip_errors: *std.zig.ErrorBundle.Wip, ) !void { var file: Module.File = .{ .status = .never_loaded, @@ -5013,7 +5021,7 @@ pub fn putAstErrorsIntoBundle( file.zir_loaded = true; defer file.zir.deinit(gpa); - try Compilation.addZirErrorMessages(gpa, error_bundle, &file); + try Compilation.addZirErrorMessages(wip_errors, &file); } pub const info_zen = @@ -5595,16 +5603,18 @@ pub fn cmdAstCheck( defer file.zir.deinit(gpa); if (file.zir.hasCompileErrors()) { - var errors: std.zig.ErrorBundle = undefined; - try errors.init(gpa); - defer errors.deinit(gpa); - try Compilation.addZirErrorMessages(gpa, &errors, &file); + var wip_errors: std.zig.ErrorBundle.Wip = undefined; + try wip_errors.init(gpa); + defer wip_errors.deinit(); + try Compilation.addZirErrorMessages(&wip_errors, &file); const ttyconf: std.debug.TTY.Config = switch (color) { .auto => std.debug.detectTTYConfig(std.io.getStdErr()), .on => .escape_codes, .off => .no_color, }; - errors.renderToStdErr(ttyconf); + var error_bundle = try wip_errors.toOwnedBundle(); + defer error_bundle.deinit(gpa); + error_bundle.renderToStdErr(ttyconf); process.exit(1); } @@ -5719,12 +5729,14 @@ pub fn cmdChangelist( defer file.zir.deinit(gpa); if (file.zir.hasCompileErrors()) { - var errors: std.zig.ErrorBundle = undefined; - try errors.init(gpa); - defer errors.deinit(gpa); - try Compilation.addZirErrorMessages(gpa, &errors, &file); + var wip_errors: std.zig.ErrorBundle.Wip = undefined; + try wip_errors.init(gpa); + defer wip_errors.deinit(); + try Compilation.addZirErrorMessages(&wip_errors, &file); const ttyconf = std.debug.detectTTYConfig(std.io.getStdErr()); - errors.renderToStdErr(ttyconf); + var error_bundle = try wip_errors.toOwnedBundle(); + defer error_bundle.deinit(gpa); + error_bundle.renderToStdErr(ttyconf); process.exit(1); } @@ -5758,12 +5770,14 @@ pub fn cmdChangelist( file.zir_loaded = true; if (file.zir.hasCompileErrors()) { - var errors: std.zig.ErrorBundle = undefined; - try errors.init(gpa); - defer errors.deinit(gpa); - try Compilation.addZirErrorMessages(gpa, &errors, &file); + var wip_errors: std.zig.ErrorBundle.Wip = undefined; + try wip_errors.init(gpa); + defer wip_errors.deinit(); + try Compilation.addZirErrorMessages(&wip_errors, &file); const ttyconf = std.debug.detectTTYConfig(std.io.getStdErr()); - errors.renderToStdErr(ttyconf); + var error_bundle = try wip_errors.toOwnedBundle(); + defer error_bundle.deinit(gpa); + error_bundle.renderToStdErr(ttyconf); process.exit(1); } diff --git a/src/test.zig b/src/test.zig index ce87742606..663c4f1aff 100644 --- a/src/test.zig +++ b/src/test.zig @@ -1242,7 +1242,7 @@ pub const TestContext = struct { defer self.gpa.free(zig_lib_directory.path.?); var aux_thread_pool: ThreadPool = undefined; - try aux_thread_pool.init(self.gpa); + try aux_thread_pool.init(.{ .allocator = self.gpa }); defer aux_thread_pool.deinit(); // Use the same global cache dir for all the tests, such that we for example don't have to @@ -1614,23 +1614,8 @@ pub const TestContext = struct { if (update.case != .Error) { var all_errors = try comp.getAllErrorsAlloc(); defer all_errors.deinit(allocator); - if (all_errors.list.len != 0) { - print( - "\nCase '{s}': unexpected errors at update_index={d}:\n{s}\n", - .{ case.name, update_index, hr }, - ); - for (all_errors.list) |err_msg| { - switch (err_msg) { - .src => |src| { - print("{s}:{d}:{d}: error: {s}\n{s}\n", .{ - src.src_path, src.line + 1, src.column + 1, src.msg, hr, - }); - }, - .plain => |plain| { - print("error: {s}\n{s}\n", .{ plain.msg, hr }); - }, - } - } + if (all_errors.errorMessageCount() > 0) { + all_errors.renderToStdErr(std.debug.detectTTYConfig(std.io.getStdErr())); // TODO print generated C code return error.UnexpectedCompileErrors; } From 6d88c3e935357e73493a0d10cd85dd2676f6b85b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 28 Feb 2023 16:27:49 -0700 Subject: [PATCH 157/294] add builtin.zig_version_string sometimes this is more useful than SemanticVersion --- src/Compilation.zig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index e19dc59445..3a75b44861 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -5056,7 +5056,8 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: Allocator) Alloca \\const std = @import("std"); \\/// Zig version. When writing code that supports multiple versions of Zig, prefer \\/// feature detection (i.e. with `@hasDecl` or `@hasField`) over version checks. - \\pub const zig_version = std.SemanticVersion.parse("{s}") catch unreachable; + \\pub const zig_version = std.SemanticVersion.parse(zig_version_string) catch unreachable; + \\pub const zig_version_string = "{s}"; \\pub const zig_backend = std.builtin.CompilerBackend.{}; \\ \\pub const output_mode = std.builtin.OutputMode.{}; From a42888e145ced152e6437c3241b799fd39c0da6a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 28 Feb 2023 16:36:09 -0700 Subject: [PATCH 158/294] std.zig.ErrorBundle: add special representation for empty This allows the common case of no compilation errors to be represented without any allocations. --- lib/std/zig/ErrorBundle.zig | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/lib/std/zig/ErrorBundle.zig b/lib/std/zig/ErrorBundle.zig index 1d2eca9703..94f80797a8 100644 --- a/lib/std/zig/ErrorBundle.zig +++ b/lib/std/zig/ErrorBundle.zig @@ -2,11 +2,21 @@ //! so that they can be created and destroyed appropriately. This structure //! is used to collect all the errors from the various places into one //! convenient place for API users to consume. +//! +//! There is one special encoding for this data structure. If both arrays are +//! empty, it means there are no errors. This special encoding exists so that +//! heap allocation is not needed in the common case of no errors. string_bytes: []const u8, /// The first thing in this array is an `ErrorMessageList`. extra: []const u32, +/// Special encoding when there are no errors. +pub const empty: ErrorBundle = .{ + .string_bytes = &.{}, + .extra = &.{}, +}; + // An index into `extra` pointing at an `ErrorMessage`. pub const MessageIndex = enum(u32) { _, @@ -72,6 +82,7 @@ pub fn deinit(eb: *ErrorBundle, gpa: Allocator) void { } pub fn errorMessageCount(eb: ErrorBundle) u32 { + if (eb.extra.len == 0) return 0; return eb.getErrorMessageList().len; } @@ -313,6 +324,17 @@ pub const Wip = struct { pub fn toOwnedBundle(wip: *Wip) !ErrorBundle { const gpa = wip.gpa; + if (wip.root_list.items.len == 0) { + // Special encoding when there are no errors. + wip.deinit(); + wip.* = .{ + .gpa = gpa, + .string_bytes = .{}, + .extra = .{}, + .root_list = .{}, + }; + return empty; + } wip.setExtra(0, ErrorMessageList{ .len = @intCast(u32, wip.root_list.items.len), .start = @intCast(u32, wip.extra.items.len), From c583d140135fe5a57d055d9c0b8bdf59698f29e1 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 28 Feb 2023 16:49:44 -0700 Subject: [PATCH 159/294] std.fifo: add toOwnedSlice method --- lib/std/fifo.zig | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lib/std/fifo.zig b/lib/std/fifo.zig index b7c8f761d3..ad929cde8a 100644 --- a/lib/std/fifo.zig +++ b/lib/std/fifo.zig @@ -383,6 +383,21 @@ pub fn LinearFifo( self.discard(try dest_writer.write(self.readableSlice(0))); } } + + pub fn toOwnedSlice(self: *Self) Allocator.Error![]T { + assert(self.head == 0); + assert(self.count <= self.buf.len); + const allocator = self.allocator; + if (allocator.resize(self.buf, self.count)) { + const result = self.buf[0..self.count]; + self.* = Self.init(allocator); + return result; + } + const new_memory = try allocator.dupe(T, self.buf[0..self.count]); + allocator.free(self.buf); + self.* = Self.init(allocator); + return new_memory; + } }; } From 986a30e373f6b2f0da2de64570013c83cacc17b6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 28 Feb 2023 16:52:35 -0700 Subject: [PATCH 160/294] integrate the build runner and the compiler server The compiler now provides a server protocol for an interactive session with another process. The build runner uses this protocol to communicate compilation errors semantically from zig compiler subprocesses to the build runner. The protocol is exposed via stdin/stdout, or on a network socket, depending on whether the CLI flag `--listen=-` or e.g. `--listen=127.0.0.1:1337` is used. Additionally: * add the zig version string to the build runner cache prefix * remove --prominent-compile-errors CLI flag because it no longer does anything. Compilation errors are now unconditionally displayed at the bottom of the build summary output when using the terminal-based build runner. * Remove the color field from std.Build. The build steps are no longer supposed to interact with stderr directly. Instead they communicate semantically back to the build runner, which has its own logic about TTY configuration. * Use the cleanExit() pattern in the build runner. * Build steps can now use error.MakeFailed when they have already properly reported an error, or they can fail with any other error code in which case the build runner will create a simple message based on this error code. --- lib/build_runner.zig | 113 +++++++--- lib/std/Build.zig | 147 ++++++++++--- lib/std/Build/CompileStep.zig | 6 +- lib/std/Build/RunStep.zig | 8 +- lib/std/Build/Step.zig | 29 ++- lib/std/zig.zig | 2 + lib/std/zig/Client.zig | 32 +++ lib/std/zig/Server.zig | 28 +++ src/main.zig | 380 ++++++++++++++++++++++------------ 9 files changed, 524 insertions(+), 221 deletions(-) create mode 100644 lib/std/zig/Client.zig create mode 100644 lib/std/zig/Server.zig diff --git a/lib/build_runner.zig b/lib/build_runner.zig index e12d46f03d..b95c36a196 100644 --- a/lib/build_runner.zig +++ b/lib/build_runner.zig @@ -1,6 +1,7 @@ const root = @import("@build"); const std = @import("std"); const builtin = @import("builtin"); +const assert = std.debug.assert; const io = std.io; const fmt = std.fmt; const mem = std.mem; @@ -71,8 +72,7 @@ pub fn main() !void { cache.addPrefix(build_root_directory); cache.addPrefix(local_cache_directory); cache.addPrefix(global_cache_directory); - - //cache.hash.addBytes(builtin.zig_version); + cache.hash.addBytes(builtin.zig_version_string); const builder = try std.Build.create( allocator, @@ -95,10 +95,8 @@ pub fn main() !void { var install_prefix: ?[]const u8 = null; var dir_list = std.Build.DirList{}; - // before arg parsing, check for the NO_COLOR environment variable - // if it exists, default the color setting to .off - // explicit --color arguments will still override this setting. - builder.color = if (process.hasEnvVarConstant("NO_COLOR")) .off else .auto; + const Color = enum { auto, off, on }; + var color: Color = .auto; while (nextArg(args, &arg_idx)) |arg| { if (mem.startsWith(u8, arg, "-D")) { @@ -166,7 +164,7 @@ pub fn main() !void { std.debug.print("expected [auto|on|off] after --color", .{}); usageAndErr(builder, false, stderr_stream); }; - builder.color = std.meta.stringToEnum(@TypeOf(builder.color), next_arg) orelse { + color = std.meta.stringToEnum(Color, next_arg) orelse { std.debug.print("expected [auto|on|off] after --color, found '{s}'", .{next_arg}); usageAndErr(builder, false, stderr_stream); }; @@ -200,8 +198,6 @@ pub fn main() !void { builder.verbose_cc = true; } else if (mem.eql(u8, arg, "--verbose-llvm-cpu-features")) { builder.verbose_llvm_cpu_features = true; - } else if (mem.eql(u8, arg, "--prominent-compile-errors")) { - builder.prominent_compile_errors = true; } else if (mem.eql(u8, arg, "-fwine")) { builder.enable_wine = true; } else if (mem.eql(u8, arg, "-fno-wine")) { @@ -257,6 +253,12 @@ pub fn main() !void { } } + const ttyconf: std.debug.TTY.Config = switch (color) { + .auto => std.debug.detectTTYConfig(std.io.getStdErr()), + .on => .escape_codes, + .off => .no_color, + }; + var progress: std.Progress = .{}; const main_progress_node = progress.start("", 0); defer main_progress_node.end(); @@ -272,11 +274,15 @@ pub fn main() !void { if (builder.validateUserInputDidItFail()) usageAndErr(builder, true, stderr_stream); - runStepNames(builder, targets.items, main_progress_node, thread_pool_options) catch |err| { - switch (err) { - error.UncleanExit => process.exit(1), - else => return err, - } + runStepNames( + builder, + targets.items, + main_progress_node, + thread_pool_options, + ttyconf, + ) catch |err| switch (err) { + error.UncleanExit => process.exit(1), + else => return err, }; } @@ -285,6 +291,7 @@ fn runStepNames( step_names: []const []const u8, parent_prog_node: *std.Progress.Node, thread_pool_options: std.Thread.Pool.Options, + ttyconf: std.debug.TTY.Config, ) !void { var step_stack = ArrayList(*Step).init(b.allocator); defer step_stack.deinit(); @@ -332,12 +339,14 @@ fn runStepNames( wait_group.start(); thread_pool.spawn(workerMakeOneStep, .{ - &wait_group, &thread_pool, b, step, &step_prog, + &wait_group, &thread_pool, b, step, &step_prog, ttyconf, }) catch @panic("OOM"); } } - var any_failed = false; + var success_count: usize = 0; + var failure_count: usize = 0; + var pending_count: usize = 0; for (step_stack.items) |s| { switch (s.state) { @@ -349,20 +358,42 @@ fn runStepNames( // A -> B -> C (failure) // B will be marked as dependency_failure, while A may never be queued, and thus // remain in the initial state of precheck_done. - .dependency_failure, .precheck_done => continue, - .success => continue, - .failure => { - any_failed = true; - std.debug.print("{s}: {s}\n", .{ - s.name, @errorName(s.result.err_code), - }); - }, + .dependency_failure, .precheck_done => pending_count += 1, + .success => success_count += 1, + .failure => failure_count += 1, } } - if (any_failed) { - process.exit(1); - } + const stderr = std.io.getStdErr(); + + const total_count = success_count + failure_count + pending_count; + stderr.writer().print("build summary: {d}/{d} steps succeeded; {d} failed\n", .{ + success_count, total_count, failure_count, + }) catch {}; + if (failure_count == 0) return cleanExit(); + + for (step_stack.items) |s| switch (s.state) { + .failure => { + // TODO print the dep prefix too + ttyconf.setColor(stderr, .Bold) catch break; + stderr.writeAll(s.name) catch break; + ttyconf.setColor(stderr, .Reset) catch break; + + if (s.result_error_bundle.errorMessageCount() > 0) { + stderr.writer().print(": {d} compilation errors:\n", .{ + s.result_error_bundle.errorMessageCount(), + }) catch break; + s.result_error_bundle.renderToStdErr(ttyconf); + } else { + stderr.writer().print(": {d} error messages (printed above)\n", .{ + s.result_error_msgs.items.len, + }) catch break; + } + }, + else => continue, + }; + + process.exit(1); } fn checkForDependencyLoop( @@ -407,6 +438,7 @@ fn workerMakeOneStep( b: *std.Build, s: *Step, prog_node: *std.Progress.Node, + ttyconf: std.debug.TTY.Config, ) void { defer wg.finish(); @@ -446,17 +478,26 @@ fn workerMakeOneStep( const make_result = s.make(); // No matter the result, we want to display error/warning messages. - if (s.result.error_msgs.items.len > 0) { + if (s.result_error_msgs.items.len > 0) { sub_prog_node.context.lock_stderr(); defer sub_prog_node.context.unlock_stderr(); - for (s.result.error_msgs.items) |msg| { - std.io.getStdErr().writeAll(msg) catch break; + const stderr = std.io.getStdErr(); + + for (s.result_error_msgs.items) |msg| { + // TODO print the dep prefix too + ttyconf.setColor(stderr, .Bold) catch break; + stderr.writeAll(s.name) catch break; + stderr.writeAll(": ") catch break; + ttyconf.setColor(stderr, .Red) catch break; + stderr.writeAll("error: ") catch break; + ttyconf.setColor(stderr, .Reset) catch break; + stderr.writeAll(msg) catch break; } } make_result catch |err| { - s.result.err_code = err; + assert(err == error.MakeFailed); @atomicStore(Step.State, &s.state, .failure, .SeqCst); return; }; @@ -467,7 +508,7 @@ fn workerMakeOneStep( for (s.dependants.items) |dep| { wg.start(); thread_pool.spawn(workerMakeOneStep, .{ - wg, thread_pool, b, dep, prog_node, + wg, thread_pool, b, dep, prog_node, ttyconf, }) catch @panic("OOM"); } } @@ -601,3 +642,11 @@ fn argsRest(args: [][]const u8, idx: usize) ?[][]const u8 { if (idx >= args.len) return null; return args[idx..]; } + +fn cleanExit() void { + if (builtin.mode == .Debug) { + return; + } else { + process.exit(0); + } +} diff --git a/lib/std/Build.zig b/lib/std/Build.zig index 92ace4e60b..fc8ec26723 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -59,9 +59,6 @@ verbose_air: bool, verbose_llvm_ir: bool, verbose_cimport: bool, verbose_llvm_cpu_features: bool, -/// The purpose of executing the command is for a human to read compile errors from the terminal -prominent_compile_errors: bool, -color: enum { auto, on, off } = .auto, reference_trace: ?u32 = null, invalid_user_input: bool, zig_exe: []const u8, @@ -211,7 +208,6 @@ pub fn create( .verbose_llvm_ir = false, .verbose_cimport = false, .verbose_llvm_cpu_features = false, - .prominent_compile_errors = false, .invalid_user_input = false, .allocator = allocator, .user_input_options = UserInputOptionsMap.init(allocator), @@ -295,8 +291,6 @@ fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: Cache.Direc .verbose_llvm_ir = parent.verbose_llvm_ir, .verbose_cimport = parent.verbose_cimport, .verbose_llvm_cpu_features = parent.verbose_llvm_cpu_features, - .prominent_compile_errors = parent.prominent_compile_errors, - .color = parent.color, .reference_trace = parent.reference_trace, .invalid_user_input = false, .zig_exe = parent.zig_exe, @@ -1409,54 +1403,149 @@ pub fn execAllowFail( } } -pub fn execFromStep(b: *Build, argv: []const []const u8, s: *Step) ![]u8 { +/// This function is used exclusively for spawning and communicating with the zig compiler. +/// TODO: move to build_runner.zig +pub fn execFromStep(b: *Build, argv: []const []const u8, s: *Step) ![]const u8 { assert(argv.len != 0); if (b.verbose) { - printCmd(b.allocator, null, argv); + const text = try allocPrintCmd(b.allocator, null, argv); + try s.result_error_msgs.append(b.allocator, text); } if (!process.can_spawn) { - try s.result.error_msgs.append(b.allocator, b.fmt("Unable to spawn the following command: cannot spawn child processes\n{s}", .{ + try s.result_error_msgs.append(b.allocator, b.fmt("Unable to spawn the following command: cannot spawn child processes\n{s}", .{ try allocPrintCmd(b.allocator, null, argv), })); - return error.CannotSpawnProcesses; + return error.MakeFailed; } - const result = std.ChildProcess.exec(.{ - .allocator = b.allocator, - .argv = argv, - .env_map = b.env_map, - .max_output_bytes = 10 * 1024 * 1024, - }) catch |err| { - try s.result.error_msgs.append(b.allocator, b.fmt("unable to spawn the following command: {s}\n{s}", .{ - @errorName(err), try allocPrintCmd(b.allocator, null, argv), - })); - return error.ExecFailed; - }; + var child = std.ChildProcess.init(argv, b.allocator); + child.env_map = b.env_map; + child.stdin_behavior = .Pipe; + child.stdout_behavior = .Pipe; + child.stderr_behavior = .Pipe; - if (result.stderr.len != 0) { - try s.result.error_msgs.append(b.allocator, result.stderr); + try child.spawn(); + + var poller = std.io.poll(b.allocator, enum { stdout, stderr }, .{ + .stdout = child.stdout.?, + .stderr = child.stderr.?, + }); + defer poller.deinit(); + + try sendMessage(child.stdin.?, .update); + try sendMessage(child.stdin.?, .exit); + + const Header = std.zig.Server.Message.Header; + var result: ?[]const u8 = null; + + while (try poller.poll()) { + const stdout = poller.fifo(.stdout); + const buf = stdout.readableSlice(0); + assert(stdout.readableLength() == buf.len); + if (buf.len >= @sizeOf(Header)) { + const header = @ptrCast(*align(1) const Header, buf[0..@sizeOf(Header)]); + const header_and_msg_len = header.bytes_len + @sizeOf(Header); + if (buf.len >= header_and_msg_len) { + const body = buf[@sizeOf(Header)..]; + switch (header.tag) { + .zig_version => { + if (!mem.eql(u8, builtin.zig_version_string, body)) { + try s.result_error_msgs.append( + b.allocator, + b.fmt("zig version mismatch build runner vs compiler: '{s}' vs '{s}'", .{ + builtin.zig_version_string, body, + }), + ); + return error.MakeFailed; + } + }, + .error_bundle => { + const EbHdr = std.zig.Server.Message.ErrorBundle; + const eb_hdr = @ptrCast(*align(1) const EbHdr, body); + const extra_bytes = + body[@sizeOf(EbHdr)..][0 .. @sizeOf(u32) * eb_hdr.extra_len]; + const string_bytes = + body[@sizeOf(EbHdr) + extra_bytes.len ..][0..eb_hdr.string_bytes_len]; + // TODO: use @ptrCast when the compiler supports it + const unaligned_extra = mem.bytesAsSlice(u32, extra_bytes); + const extra_array = try b.allocator.alloc(u32, unaligned_extra.len); + // TODO: use @memcpy when it supports slices + for (extra_array, unaligned_extra) |*dst, src| dst.* = src; + s.result_error_bundle = .{ + .string_bytes = try b.allocator.dupe(u8, string_bytes), + .extra = extra_array, + }; + }, + .progress => { + @panic("TODO handle progress message"); + }, + .emit_bin_path => { + @panic("TODO handle emit_bin_path message"); + }, + _ => { + // Unrecognized message. + }, + } + stdout.discard(header_and_msg_len); + } + } } - switch (result.term) { + const stderr = poller.fifo(.stderr); + if (stderr.readableLength() > 0) { + try s.result_error_msgs.append(b.allocator, try stderr.toOwnedSlice()); + } + + // Send EOF to stdin. + child.stdin.?.close(); + child.stdin = null; + + const term = try child.wait(); + switch (term) { .Exited => |code| { if (code != 0) { - try s.result.error_msgs.append(b.allocator, b.fmt("the following command exited with error code {d}:\n{s}", .{ + try s.result_error_msgs.append(b.allocator, b.fmt("the following command exited with error code {d}:\n{s}", .{ code, try allocPrintCmd(b.allocator, null, argv), })); - return error.ExitCodeFailure; + return error.MakeFailed; } - return result.stdout; }, .Signal, .Stopped, .Unknown => |code| { _ = code; - try s.result.error_msgs.append(b.allocator, b.fmt("the following command terminated unexpectedly:\n{s}", .{ + try s.result_error_msgs.append(b.allocator, b.fmt("the following command terminated unexpectedly:\n{s}", .{ try allocPrintCmd(b.allocator, null, argv), })); - return error.ProcessTerminated; + return error.MakeFailed; }, } + + if (s.result_error_bundle.errorMessageCount() > 0) { + try s.result_error_msgs.append( + b.allocator, + b.fmt("the following command failed with {d} compilation errors:\n{s}", .{ + s.result_error_bundle.errorMessageCount(), + try allocPrintCmd(b.allocator, null, argv), + }), + ); + return error.MakeFailed; + } + + return result orelse { + try s.result_error_msgs.append(b.allocator, b.fmt("the following command failed to communicate the compilation result:\n{s}", .{ + try allocPrintCmd(b.allocator, null, argv), + })); + return error.MakeFailed; + }; +} + +fn sendMessage(file: fs.File, tag: std.zig.Client.Message.Tag) !void { + const header: std.zig.Client.Message.Header = .{ + .tag = tag, + .bytes_len = 0, + }; + try file.writeAll(std.mem.asBytes(&header)); } /// This is a helper function to be called from build.zig scripts, *not* from diff --git a/lib/std/Build/CompileStep.zig b/lib/std/Build/CompileStep.zig index ce0ede9510..c5c2b2a440 100644 --- a/lib/std/Build/CompileStep.zig +++ b/lib/std/Build/CompileStep.zig @@ -1177,11 +1177,6 @@ fn make(step: *Step) !void { }; try zig_args.append(cmd); - if (builder.color != .auto) { - try zig_args.append("--color"); - try zig_args.append(@tagName(builder.color)); - } - if (builder.reference_trace) |some| { try zig_args.append(try std.fmt.allocPrint(builder.allocator, "-freference-trace={d}", .{some})); } @@ -1834,6 +1829,7 @@ fn make(step: *Step) !void { } try zig_args.append("--enable-cache"); + try zig_args.append("--listen=-"); // Windows has an argument length limit of 32,766 characters, macOS 262,144 and Linux // 2,097,152. If our args exceed 30 KiB, we instead write them to a "response file" and diff --git a/lib/std/Build/RunStep.zig b/lib/std/Build/RunStep.zig index 815916f380..904ef0935f 100644 --- a/lib/std/Build/RunStep.zig +++ b/lib/std/Build/RunStep.zig @@ -419,12 +419,8 @@ pub fn runCommand( }; if (!termMatches(expected_term, term)) { - if (builder.prominent_compile_errors) { - std.debug.print("Run step {} (expected {})\n", .{ fmtTerm(term), fmtTerm(expected_term) }); - } else { - std.debug.print("The following command {} (expected {}):\n", .{ fmtTerm(term), fmtTerm(expected_term) }); - printCmd(cwd, argv); - } + std.debug.print("The following command {} (expected {}):\n", .{ fmtTerm(term), fmtTerm(expected_term) }); + printCmd(cwd, argv); return error.UnexpectedExit; } diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig index 42698f2190..4edece8038 100644 --- a/lib/std/Build/Step.zig +++ b/lib/std/Build/Step.zig @@ -6,15 +6,13 @@ dependencies: std.ArrayList(*Step), /// then populated during dependency loop checking in the build runner. dependants: std.ArrayListUnmanaged(*Step), state: State, -/// Populated only if state is success. -result: struct { - err_code: anyerror, - error_msgs: std.ArrayListUnmanaged([]const u8), -}, /// The return addresss associated with creation of this step that can be useful /// to print along with debugging messages. debug_stack_trace: [n_debug_stack_frames]usize, +result_error_msgs: std.ArrayListUnmanaged([]const u8), +result_error_bundle: std.zig.ErrorBundle, + const n_debug_stack_frames = 4; pub const State = enum { @@ -94,16 +92,25 @@ pub fn init(allocator: Allocator, options: Options) Step { .dependencies = std.ArrayList(*Step).init(allocator), .dependants = .{}, .state = .precheck_unstarted, - .result = .{ - .err_code = undefined, - .error_msgs = .{}, - }, .debug_stack_trace = addresses, + .result_error_msgs = .{}, + .result_error_bundle = std.zig.ErrorBundle.empty, }; } -pub fn make(self: *Step) !void { - try self.makeFn(self); +/// If the Step's `make` function reports `error.MakeFailed`, it indicates they +/// have already reported the error. Otherwise, we add a simple error report +/// here. +pub fn make(s: *Step) error{MakeFailed}!void { + return s.makeFn(s) catch |err| { + if (err != error.MakeFailed) { + const gpa = s.dependencies.allocator; + s.result_error_msgs.append(gpa, std.fmt.allocPrint(gpa, "{s} failed: {s}", .{ + s.name, @errorName(err), + }) catch @panic("OOM")) catch @panic("OOM"); + } + return error.MakeFailed; + }; } pub fn dependOn(self: *Step, other: *Step) void { diff --git a/lib/std/zig.zig b/lib/std/zig.zig index ecff8e99bd..98edeabd10 100644 --- a/lib/std/zig.zig +++ b/lib/std/zig.zig @@ -4,6 +4,8 @@ const fmt = @import("zig/fmt.zig"); const assert = std.debug.assert; pub const ErrorBundle = @import("zig/ErrorBundle.zig"); +pub const Server = @import("zig/Server.zig"); +pub const Client = @import("zig/Client.zig"); pub const Token = tokenizer.Token; pub const Tokenizer = tokenizer.Tokenizer; pub const fmtId = fmt.fmtId; diff --git a/lib/std/zig/Client.zig b/lib/std/zig/Client.zig new file mode 100644 index 0000000000..a68c189e57 --- /dev/null +++ b/lib/std/zig/Client.zig @@ -0,0 +1,32 @@ +pub const Message = struct { + pub const Header = extern struct { + tag: Tag, + /// Size of the body only; does not include this Header. + bytes_len: u32, + }; + + pub const Tag = enum(u32) { + /// Tells the compiler to shut down cleanly. + /// No body. + exit, + /// Tells the compiler to detect changes in source files and update the + /// affected output compilation artifacts. + /// If one of the compilation artifacts is an executable that is + /// running as a child process, the compiler will wait for it to exit + /// before performing the update. + /// No body. + update, + /// Tells the compiler to execute the executable as a child process. + /// No body. + run, + /// Tells the compiler to detect changes in source files and update the + /// affected output compilation artifacts. + /// If one of the compilation artifacts is an executable that is + /// running as a child process, the compiler will perform a hot code + /// swap. + /// No body. + hot_update, + + _, + }; +}; diff --git a/lib/std/zig/Server.zig b/lib/std/zig/Server.zig new file mode 100644 index 0000000000..76f2303f6b --- /dev/null +++ b/lib/std/zig/Server.zig @@ -0,0 +1,28 @@ +pub const Message = struct { + pub const Header = extern struct { + tag: Tag, + /// Size of the body only; does not include this Header. + bytes_len: u32, + }; + + pub const Tag = enum(u32) { + /// Body is a UTF-8 string. + zig_version, + /// Body is an ErrorBundle. + error_bundle, + /// Body is a UTF-8 string. + progress, + /// Body is a UTF-8 string. + emit_bin_path, + _, + }; + + /// Trailing: + /// * extra: [extra_len]u32, + /// * string_bytes: [string_bytes_len]u8, + /// See `std.zig.ErrorBundle`. + pub const ErrorBundle = extern struct { + extra_len: u32, + string_bytes_len: u32, + }; +}; diff --git a/src/main.zig b/src/main.zig index 0a16aa5a46..d574681bcd 100644 --- a/src/main.zig +++ b/src/main.zig @@ -668,6 +668,12 @@ const ArgMode = union(enum) { run, }; +const Listen = union(enum) { + none, + ip4: std.net.Ip4Address, + stdio, +}; + fn buildOutputType( gpa: Allocator, arena: Allocator, @@ -689,7 +695,7 @@ fn buildOutputType( var function_sections = false; var no_builtin = false; var watch = false; - var listen_addr: ?std.net.Ip4Address = null; + var listen: Listen = .none; var debug_compile_errors = false; var verbose_link = (builtin.os.tag != .wasi or builtin.link_libc) and std.process.hasEnvVarConstant("ZIG_VERBOSE_LINK"); var verbose_cc = (builtin.os.tag != .wasi or builtin.link_libc) and std.process.hasEnvVarConstant("ZIG_VERBOSE_CC"); @@ -1149,14 +1155,22 @@ fn buildOutputType( } } else if (mem.eql(u8, arg, "--listen")) { const next_arg = args_iter.nextOrFatal(); - // example: --listen 127.0.0.1:9000 - var it = std.mem.split(u8, next_arg, ":"); - const host = it.next().?; - const port_text = it.next() orelse "14735"; - const port = std.fmt.parseInt(u16, port_text, 10) catch |err| - fatal("invalid port number: '{s}': {s}", .{ port_text, @errorName(err) }); - listen_addr = std.net.Ip4Address.parse(host, port) catch |err| - fatal("invalid host: '{s}': {s}", .{ host, @errorName(err) }); + if (mem.eql(u8, next_arg, "-")) { + listen = .stdio; + watch = true; + } else { + // example: --listen 127.0.0.1:9000 + var it = std.mem.split(u8, next_arg, ":"); + const host = it.next().?; + const port_text = it.next() orelse "14735"; + const port = std.fmt.parseInt(u16, port_text, 10) catch |err| + fatal("invalid port number: '{s}': {s}", .{ port_text, @errorName(err) }); + listen = .{ .ip4 = std.net.Ip4Address.parse(host, port) catch |err| + fatal("invalid host: '{s}': {s}", .{ host, @errorName(err) }) }; + watch = true; + } + } else if (mem.eql(u8, arg, "--listen=-")) { + listen = .stdio; watch = true; } else if (mem.eql(u8, arg, "--debug-link-snapshot")) { if (!build_options.enable_link_snapshots) { @@ -3277,6 +3291,47 @@ fn buildOutputType( return cmdTranslateC(comp, arena, have_enable_cache); } + switch (listen) { + .none => {}, + .stdio => { + try serve( + comp, + std.io.getStdIn(), + std.io.getStdOut(), + test_exec_args.items, + self_exe_path, + arg_mode, + all_args, + runtime_args_start, + ); + return cleanExit(); + }, + .ip4 => |ip4_addr| { + var server = std.net.StreamServer.init(.{ + .reuse_address = true, + }); + defer server.deinit(); + + try server.listen(.{ .in = ip4_addr }); + + while (true) { + const conn = try server.accept(); + defer conn.stream.close(); + + try serve( + comp, + .{ .handle = conn.stream.handle }, + .{ .handle = conn.stream.handle }, + test_exec_args.items, + self_exe_path, + arg_mode, + all_args, + runtime_args_start, + ); + } + }, + } + const hook: AfterUpdateHook = blk: { if (!have_enable_cache) break :blk .none; @@ -3354,6 +3409,12 @@ fn buildOutputType( ); } + // TODO move this REPL implementation to the standard library / build + // system and have it be a CLI abstraction layer on top of the real, actual + // binary protocol of the compiler. Make it actually interface through the + // server protocol. This way the REPL does not have any special powers that + // an IDE couldn't also have. + const stdin = std.io.getStdIn().reader(); const stderr = std.io.getStdErr().writer(); var repl_buf: [1024]u8 = undefined; @@ -3367,123 +3428,6 @@ fn buildOutputType( var last_cmd: ReplCmd = .help; - if (listen_addr) |ip4_addr| { - var server = std.net.StreamServer.init(.{ - .reuse_address = true, - }); - defer server.deinit(); - - try server.listen(.{ .in = ip4_addr }); - - while (true) { - const conn = try server.accept(); - defer conn.stream.close(); - - var buf: [100]u8 = undefined; - var child_pid: ?i32 = null; - - while (true) { - try comp.makeBinFileExecutable(); - - const amt = try conn.stream.read(&buf); - const line = buf[0..amt]; - const actual_line = mem.trimRight(u8, line, "\r\n "); - - const cmd: ReplCmd = blk: { - if (mem.eql(u8, actual_line, "update")) { - break :blk .update; - } else if (mem.eql(u8, actual_line, "exit")) { - break; - } else if (mem.eql(u8, actual_line, "help")) { - break :blk .help; - } else if (mem.eql(u8, actual_line, "run")) { - break :blk .run; - } else if (mem.eql(u8, actual_line, "update-and-run")) { - break :blk .update_and_run; - } else if (actual_line.len == 0) { - break :blk last_cmd; - } else { - try stderr.print("unknown command: {s}\n", .{actual_line}); - continue; - } - }; - last_cmd = cmd; - switch (cmd) { - .update => { - tracy.frameMark(); - if (output_mode == .Exe) { - try comp.makeBinFileWritable(); - } - updateModule(gpa, comp, hook) catch |err| switch (err) { - error.SemanticAnalyzeFail => continue, - else => |e| return e, - }; - }, - .help => { - try stderr.writeAll(repl_help); - }, - .run => { - tracy.frameMark(); - try runOrTest( - comp, - gpa, - arena, - test_exec_args.items, - self_exe_path.?, - arg_mode, - target_info, - watch, - &comp_destroyed, - all_args, - runtime_args_start, - link_libc, - ); - }, - .update_and_run => { - tracy.frameMark(); - if (child_pid) |pid| { - try conn.stream.writer().print("hot code swap requested for pid {d}", .{pid}); - try comp.hotCodeSwap(pid); - - var errors = try comp.getAllErrorsAlloc(); - defer errors.deinit(comp.gpa); - - if (errors.errorMessageCount() > 0) { - const ttyconf: std.debug.TTY.Config = switch (comp.color) { - .auto => std.debug.detectTTYConfig(std.io.getStdErr()), - .on => .escape_codes, - .off => .no_color, - }; - try errors.renderToWriter(ttyconf, conn.stream.writer()); - continue; - } - } else { - if (output_mode == .Exe) { - try comp.makeBinFileWritable(); - } - updateModule(gpa, comp, hook) catch |err| switch (err) { - error.SemanticAnalyzeFail => continue, - else => |e| return e, - }; - try comp.makeBinFileExecutable(); - - child_pid = try runOrTestHotSwap( - comp, - gpa, - arena, - test_exec_args.items, - self_exe_path.?, - arg_mode, - all_args, - runtime_args_start, - ); - } - }, - } - } - } - } - while (watch) { try stderr.print("(zig) ", .{}); try comp.makeBinFileExecutable(); @@ -3576,6 +3520,173 @@ fn buildOutputType( return cleanExit(); } +fn serve( + comp: *Compilation, + in: fs.File, + out: fs.File, + test_exec_args: []const ?[]const u8, + self_exe_path: ?[]const u8, + arg_mode: ArgMode, + all_args: []const []const u8, + runtime_args_start: ?usize, +) !void { + const gpa = comp.gpa; + + try serveMessage(out, .{ + .tag = .zig_version, + .bytes_len = build_options.version.len, + }, &.{ + build_options.version, + }); + + var child_pid: ?i32 = null; + var receive_fifo = std.fifo.LinearFifo(u8, .Dynamic).init(gpa); + defer receive_fifo.deinit(); + + while (true) { + const hdr = try receiveMessage(in, &receive_fifo); + + switch (hdr.tag) { + .exit => { + return cleanExit(); + }, + .update => { + tracy.frameMark(); + if (comp.bin_file.options.output_mode == .Exe) { + try comp.makeBinFileWritable(); + } + try comp.update(); + try comp.makeBinFileExecutable(); + try serveUpdateResults(out, comp); + }, + .run => { + if (child_pid != null) { + @panic("TODO block until the child exits"); + } + @panic("TODO call runOrTest"); + //try runOrTest( + // comp, + // gpa, + // arena, + // test_exec_args, + // self_exe_path.?, + // arg_mode, + // target_info, + // true, + // &comp_destroyed, + // all_args, + // runtime_args_start, + // link_libc, + //); + }, + .hot_update => { + tracy.frameMark(); + if (child_pid) |pid| { + try comp.hotCodeSwap(pid); + try serveUpdateResults(out, comp); + } else { + if (comp.bin_file.options.output_mode == .Exe) { + try comp.makeBinFileWritable(); + } + try comp.update(); + try comp.makeBinFileExecutable(); + try serveUpdateResults(out, comp); + + child_pid = try runOrTestHotSwap( + comp, + gpa, + test_exec_args, + self_exe_path.?, + arg_mode, + all_args, + runtime_args_start, + ); + } + }, + _ => { + @panic("TODO unrecognized message from client"); + }, + } + } +} + +fn serveMessage( + out: fs.File, + header: std.zig.Server.Message.Header, + bufs: []const []const u8, +) !void { + var iovecs: [10]std.os.iovec_const = undefined; + iovecs[0] = .{ + .iov_base = @ptrCast([*]const u8, &header), + .iov_len = @sizeOf(std.zig.Server.Message.Header), + }; + for (bufs, iovecs[1 .. bufs.len + 1]) |buf, *iovec| { + iovec.* = .{ + .iov_base = buf.ptr, + .iov_len = buf.len, + }; + } + try out.writevAll(iovecs[0 .. bufs.len + 1]); +} + +fn serveErrorBundle(out: fs.File, error_bundle: std.zig.ErrorBundle) !void { + const eb_hdr: std.zig.Server.Message.ErrorBundle = .{ + .extra_len = @intCast(u32, error_bundle.extra.len), + .string_bytes_len = @intCast(u32, error_bundle.string_bytes.len), + }; + const bytes_len = @sizeOf(std.zig.Server.Message.ErrorBundle) + + 4 * error_bundle.extra.len + error_bundle.string_bytes.len; + try serveMessage(out, .{ + .tag = .error_bundle, + .bytes_len = @intCast(u32, bytes_len), + }, &.{ + std.mem.asBytes(&eb_hdr), + // TODO: implement @ptrCast between slices changing the length + std.mem.sliceAsBytes(error_bundle.extra), + error_bundle.string_bytes, + }); +} + +fn serveUpdateResults(out: fs.File, comp: *Compilation) !void { + const gpa = comp.gpa; + var error_bundle = try comp.getAllErrorsAlloc(); + defer error_bundle.deinit(gpa); + if (error_bundle.errorMessageCount() > 0) { + try serveErrorBundle(out, error_bundle); + } else if (comp.bin_file.options.emit) |emit| { + const full_path = try emit.directory.join(gpa, &.{emit.sub_path}); + defer gpa.free(full_path); + + try serveMessage(out, .{ + .tag = .emit_bin_path, + .bytes_len = @intCast(u32, full_path.len), + }, &.{ + full_path, + }); + } +} + +fn receiveMessage(in: fs.File, fifo: *std.fifo.LinearFifo(u8, .Dynamic)) !std.zig.Client.Message.Header { + const Header = std.zig.Client.Message.Header; + + while (true) { + const buf = fifo.readableSlice(0); + assert(fifo.readableLength() == buf.len); + if (buf.len >= @sizeOf(Header)) { + const header = @ptrCast(*align(1) const Header, buf[0..@sizeOf(Header)]); + if (header.bytes_len != 0) + return error.InvalidClientMessage; + const result = header.*; + fifo.discard(@sizeOf(Header)); + return result; + } + + const write_buffer = try fifo.writableWithSize(256); + const amt = try in.read(write_buffer); + fifo.update(amt); + } +} + const ModuleDepIterator = struct { split: mem.SplitIterator(u8), @@ -3765,7 +3876,6 @@ fn runOrTest( fn runOrTestHotSwap( comp: *Compilation, gpa: Allocator, - arena: Allocator, test_exec_args: []const ?[]const u8, self_exe_path: []const u8, arg_mode: ArgMode, @@ -3775,9 +3885,10 @@ fn runOrTestHotSwap( const exe_emit = comp.bin_file.options.emit.?; // A naive `directory.join` here will indeed get the correct path to the binary, // however, in the case of cwd, we actually want `./foo` so that the path can be executed. - const exe_path = try fs.path.join(arena, &[_][]const u8{ + const exe_path = try fs.path.join(gpa, &[_][]const u8{ exe_emit.directory.path orelse ".", exe_emit.sub_path, }); + defer gpa.free(exe_path); var argv = std.ArrayList([]const u8).init(gpa); defer argv.deinit(); @@ -3807,7 +3918,7 @@ fn runOrTestHotSwap( if (runtime_args_start) |i| { try argv.appendSlice(all_args[i..]); } - var child = std.ChildProcess.init(argv.items, arena); + var child = std.ChildProcess.init(argv.items, gpa); child.stdin_behavior = .Inherit; child.stdout_behavior = .Inherit; @@ -4206,7 +4317,6 @@ pub const usage_build = pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { var color: Color = .auto; - var prominent_compile_errors: bool = false; // We want to release all the locks before executing the child process, so we make a nice // big block here to ensure the cleanup gets run when we extract out our argv. @@ -4267,8 +4377,6 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi i += 1; override_global_cache_dir = args[i]; continue; - } else if (mem.eql(u8, arg, "--prominent-compile-errors")) { - prominent_compile_errors = true; } else if (mem.eql(u8, arg, "-freference-trace")) { try child_argv.append(arg); reference_trace = 256; @@ -4535,12 +4643,8 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi .Exited => |code| { if (code == 0) return cleanExit(); - if (prominent_compile_errors) { - fatal("the build command failed with exit code {d}", .{code}); - } else { - const cmd = try std.mem.join(arena, " ", child_argv); - fatal("the following build command failed with exit code {d}:\n{s}", .{ code, cmd }); - } + const cmd = try std.mem.join(arena, " ", child_argv); + fatal("the following build command failed with exit code {d}:\n{s}", .{ code, cmd }); }, else => { const cmd = try std.mem.join(arena, " ", child_argv); From 7a3dabdc4738c2816bede92571ccdf481d400997 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 28 Feb 2023 16:55:51 -0700 Subject: [PATCH 161/294] build runner: account for debug builds in cleanExit build runner is always compiled in debug mode, so the switch on optimization here was silly. --- lib/build_runner.zig | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/build_runner.zig b/lib/build_runner.zig index b95c36a196..bb3fb42f0e 100644 --- a/lib/build_runner.zig +++ b/lib/build_runner.zig @@ -644,9 +644,8 @@ fn argsRest(args: [][]const u8, idx: usize) ?[][]const u8 { } fn cleanExit() void { - if (builtin.mode == .Debug) { - return; - } else { - process.exit(0); - } + // Perhaps in the future there could be an Advanced Options flag such as + // --debug-build-runner-leaks which would make this function return instead + // of calling exit. + process.exit(0); } From 79440d2b470b3906bd87334ecd90b2a0f2cea05b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 28 Feb 2023 17:15:06 -0700 Subject: [PATCH 162/294] std.Build.CompileStep: obtain the build output dir from protocol Now building successfully works again. --- lib/build_runner.zig | 6 ++++++ lib/std/Build.zig | 9 ++++----- lib/std/Build/CompileStep.zig | 4 ++-- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/lib/build_runner.zig b/lib/build_runner.zig index bb3fb42f0e..603ff770cc 100644 --- a/lib/build_runner.zig +++ b/lib/build_runner.zig @@ -364,12 +364,17 @@ fn runStepNames( } } + // A proper command line application defaults to silently succeeding. + // The user may request verbose mode if they have a different preference. + if (failure_count == 0 and !b.verbose) return cleanExit(); + const stderr = std.io.getStdErr(); const total_count = success_count + failure_count + pending_count; stderr.writer().print("build summary: {d}/{d} steps succeeded; {d} failed\n", .{ success_count, total_count, failure_count, }) catch {}; + if (failure_count == 0) return cleanExit(); for (step_stack.items) |s| switch (s.state) { @@ -493,6 +498,7 @@ fn workerMakeOneStep( stderr.writeAll("error: ") catch break; ttyconf.setColor(stderr, .Reset) catch break; stderr.writeAll(msg) catch break; + stderr.writeAll("\n") catch break; } } diff --git a/lib/std/Build.zig b/lib/std/Build.zig index fc8ec26723..a9d7c31733 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -1154,13 +1154,12 @@ fn allocPrintCmd(ally: Allocator, opt_cwd: ?[]const u8, argv: []const []const u8 for (argv) |arg| { try buf.writer().print("{s} ", .{arg}); } - try buf.append('\n'); return buf.toOwnedSlice(); } fn printCmd(ally: Allocator, cwd: ?[]const u8, argv: []const []const u8) void { const text = allocPrintCmd(ally, cwd, argv) catch @panic("OOM"); - std.debug.print("{s}", .{text}); + std.debug.print("{s}\n", .{text}); } pub fn spawnChildEnvMap(self: *Build, cwd: ?[]const u8, env_map: *const EnvMap, argv: []const []const u8) !void { @@ -1482,7 +1481,7 @@ pub fn execFromStep(b: *Build, argv: []const []const u8, s: *Step) ![]const u8 { @panic("TODO handle progress message"); }, .emit_bin_path => { - @panic("TODO handle emit_bin_path message"); + result = try b.allocator.dupe(u8, body); }, _ => { // Unrecognized message. @@ -1553,7 +1552,7 @@ fn sendMessage(file: fs.File, tag: std.zig.Client.Message.Tag) !void { /// a helpful message. pub fn exec(b: *Build, argv: []const []const u8) []u8 { if (!process.can_spawn) { - std.debug.print("unable to spawn the following command: cannot spawn child process\n{s}", .{ + std.debug.print("unable to spawn the following command: cannot spawn child process\n{s}\n", .{ try allocPrintCmd(b.allocator, null, argv), }); process.exit(1); @@ -1562,7 +1561,7 @@ pub fn exec(b: *Build, argv: []const []const u8) []u8 { var code: u8 = undefined; return b.execAllowFail(argv, &code, .Inherit) catch |err| { const printed_cmd = allocPrintCmd(b.allocator, null, argv) catch @panic("OOM"); - std.debug.print("unable to spawn the following command: {s}\n{s}", .{ + std.debug.print("unable to spawn the following command: {s}\n{s}\n", .{ @errorName(err), printed_cmd, }); process.exit(1); diff --git a/lib/std/Build/CompileStep.zig b/lib/std/Build/CompileStep.zig index c5c2b2a440..a7d2926770 100644 --- a/lib/std/Build/CompileStep.zig +++ b/lib/std/Build/CompileStep.zig @@ -1888,8 +1888,8 @@ fn make(step: *Step) !void { try zig_args.append(resolved_args_file); } - const output_dir_nl = try builder.execFromStep(zig_args.items, &self.step); - const build_output_dir = mem.trimRight(u8, output_dir_nl, "\r\n"); + const output_bin_path = try builder.execFromStep(zig_args.items, &self.step); + const build_output_dir = fs.path.dirname(output_bin_path).?; if (self.output_dir) |output_dir| { var src_dir = try std.fs.cwd().openIterableDir(build_output_dir, .{}); From 27f136e8282e4d5a64420d633e96f2a3ee6da08e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 28 Feb 2023 21:12:10 -0700 Subject: [PATCH 163/294] build.zig: remove redundant dependency of install step on zig exe --- build.zig | 1 - 1 file changed, 1 deletion(-) diff --git a/build.zig b/build.zig index 797c980739..189f50407d 100644 --- a/build.zig +++ b/build.zig @@ -175,7 +175,6 @@ pub fn build(b: *std.Build) !void { test_step.dependOn(&exe.step); } - b.default_step.dependOn(&exe.step); exe.single_threaded = single_threaded; if (target.isWindows() and target.getAbi() == .gnu) { From 7da34bd9e86fe32dec56e6e89a6d4615e2b50794 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 28 Feb 2023 21:13:23 -0700 Subject: [PATCH 164/294] build runner: print a fancy tree with build results on failure --- lib/build_runner.zig | 145 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 118 insertions(+), 27 deletions(-) diff --git a/lib/build_runner.zig b/lib/build_runner.zig index 603ff770cc..d70dbf2919 100644 --- a/lib/build_runner.zig +++ b/lib/build_runner.zig @@ -261,7 +261,6 @@ pub fn main() !void { var progress: std.Progress = .{}; const main_progress_node = progress.start("", 0); - defer main_progress_node.end(); builder.debug_log_scopes = debug_log_scopes.items; builder.resolveInstallPrefix(install_prefix, dir_list); @@ -323,6 +322,8 @@ fn runStepNames( defer thread_pool.deinit(); { + defer parent_prog_node.end(); + var step_prog = parent_prog_node.start("run steps", step_stack.items.len); defer step_prog.end(); @@ -347,20 +348,28 @@ fn runStepNames( var success_count: usize = 0; var failure_count: usize = 0; var pending_count: usize = 0; + var total_compile_errors: usize = 0; for (step_stack.items) |s| { switch (s.state) { .precheck_unstarted => unreachable, .precheck_started => unreachable, .running => unreachable, - // precheck_done is equivalent to dependency_failure in the case of - // transitive dependencies. For example: - // A -> B -> C (failure) - // B will be marked as dependency_failure, while A may never be queued, and thus - // remain in the initial state of precheck_done. - .dependency_failure, .precheck_done => pending_count += 1, + .precheck_done => { + // precheck_done is equivalent to dependency_failure in the case of + // transitive dependencies. For example: + // A -> B -> C (failure) + // B will be marked as dependency_failure, while A may never be queued, and thus + // remain in the initial state of precheck_done. + s.state = .dependency_failure; + pending_count += 1; + }, + .dependency_failure => pending_count += 1, .success => success_count += 1, - .failure => failure_count += 1, + .failure => { + failure_count += 1; + total_compile_errors += s.result_error_bundle.errorMessageCount(); + }, } } @@ -371,36 +380,118 @@ fn runStepNames( const stderr = std.io.getStdErr(); const total_count = success_count + failure_count + pending_count; - stderr.writer().print("build summary: {d}/{d} steps succeeded; {d} failed\n", .{ - success_count, total_count, failure_count, + ttyconf.setColor(stderr, .Cyan) catch {}; + stderr.writeAll("Build Summary: ") catch {}; + ttyconf.setColor(stderr, .Reset) catch {}; + stderr.writer().print("{d}/{d} steps succeeded; {d} failed; {d} total compile errors\n", .{ + success_count, total_count, failure_count, total_compile_errors, }) catch {}; + // Print a fancy tree with build results. + var print_node: PrintNode = .{ .parent = null }; + if (step_names.len == 0) { + print_node.last = true; + printTreeStep(b, b.default_step, stderr, ttyconf, &print_node) catch {}; + } else { + for (step_names, 0..) |step_name, i| { + const tls = b.top_level_steps.get(step_name).?; + print_node.last = i + 1 == b.top_level_steps.count(); + printTreeStep(b, &tls.step, stderr, ttyconf, &print_node) catch {}; + } + } + if (failure_count == 0) return cleanExit(); - for (step_stack.items) |s| switch (s.state) { - .failure => { - // TODO print the dep prefix too - ttyconf.setColor(stderr, .Bold) catch break; - stderr.writeAll(s.name) catch break; - ttyconf.setColor(stderr, .Reset) catch break; - + // Finally, render compile errors at the bottom of the terminal. + if (total_compile_errors > 0) { + for (step_stack.items) |s| { if (s.result_error_bundle.errorMessageCount() > 0) { - stderr.writer().print(": {d} compilation errors:\n", .{ - s.result_error_bundle.errorMessageCount(), - }) catch break; s.result_error_bundle.renderToStdErr(ttyconf); - } else { - stderr.writer().print(": {d} error messages (printed above)\n", .{ - s.result_error_msgs.items.len, - }) catch break; } - }, - else => continue, - }; + } + + // Signal to parent process that we have printed compile errors. The + // parent process may choose to omit the "following command failed" + // line in this case. + process.exit(2); + } process.exit(1); } +const PrintNode = struct { + parent: ?*PrintNode, + last: bool = false, +}; + +fn printTreeStep( + b: *std.Build, + s: *Step, + stderr: std.fs.File, + ttyconf: std.debug.TTY.Config, + parent_node: *PrintNode, +) !void { + var opt_node: ?*PrintNode = parent_node.parent; + while (opt_node) |n| : (opt_node = n.parent) { + if (n.parent == null) break; + if (n.last) { + try stderr.writeAll(" "); + } else { + try stderr.writeAll("│ "); + } + } + + if (parent_node.parent != null) { + if (parent_node.last) { + try stderr.writeAll("└─ "); + } else { + try stderr.writeAll("├─ "); + } + } + + // TODO print the dep prefix too? + try stderr.writeAll(s.name); + + switch (s.state) { + .precheck_unstarted => unreachable, + .precheck_started => unreachable, + .precheck_done => unreachable, + .running => unreachable, + + .dependency_failure => { + try ttyconf.setColor(stderr, .Dim); + try stderr.writeAll(" transitive failure\n"); + try ttyconf.setColor(stderr, .Reset); + }, + + .success => { + try ttyconf.setColor(stderr, .Green); + try stderr.writeAll(" success\n"); + try ttyconf.setColor(stderr, .Reset); + }, + + .failure => { + try ttyconf.setColor(stderr, .Red); + if (s.result_error_bundle.errorMessageCount() > 0) { + try stderr.writer().print(" {d} errors\n", .{ + s.result_error_bundle.errorMessageCount(), + }); + } else { + try stderr.writeAll(" failure\n"); + } + try ttyconf.setColor(stderr, .Reset); + }, + } + + for (s.dependencies.items, 0..) |dep, i| { + var print_node: PrintNode = .{ + .parent = parent_node, + .last = i == s.dependencies.items.len - 1, + }; + try printTreeStep(b, dep, stderr, ttyconf, &print_node); + } +} + fn checkForDependencyLoop( b: *std.Build, s: *Step, From 7efeedcd896b340a95cae50cd8c175a178465d8b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 28 Feb 2023 21:13:54 -0700 Subject: [PATCH 165/294] std.Build.CompileStep: better default step name --- lib/std/Build/CompileStep.zig | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/std/Build/CompileStep.zig b/lib/std/Build/CompileStep.zig index a7d2926770..491c8ac8e4 100644 --- a/lib/std/Build/CompileStep.zig +++ b/lib/std/Build/CompileStep.zig @@ -312,6 +312,12 @@ pub fn create(builder: *std.Build, options: Options) *CompileStep { panic("invalid name: '{s}'. It looks like a file path, but it is supposed to be the library or application name.", .{name}); } + const step_name = builder.fmt("compile {s} {s} {s}", .{ + name, + @tagName(options.optimize), + options.target.zigTriple(builder.allocator) catch @panic("OOM"), + }); + const self = builder.allocator.create(CompileStep) catch @panic("OOM"); self.* = CompileStep{ .strip = null, @@ -328,7 +334,7 @@ pub fn create(builder: *std.Build, options: Options) *CompileStep { .frameworks = StringHashMap(FrameworkLinkInfo).init(builder.allocator), .step = Step.init(builder.allocator, .{ .id = base_id, - .name = name, + .name = step_name, .makeFn = make, }), .version = options.version, From 85b4b6e3b35dd6b70092ac006b6bd621332e258f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 28 Feb 2023 21:14:09 -0700 Subject: [PATCH 166/294] std.Build.InstallArtifactStep: better default step name --- lib/std/Build/InstallArtifactStep.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/Build/InstallArtifactStep.zig b/lib/std/Build/InstallArtifactStep.zig index 43dcffede8..b652ade38f 100644 --- a/lib/std/Build/InstallArtifactStep.zig +++ b/lib/std/Build/InstallArtifactStep.zig @@ -21,7 +21,7 @@ pub fn create(builder: *std.Build, artifact: *CompileStep) *InstallArtifactStep .builder = builder, .step = Step.init(builder.allocator, .{ .id = base_id, - .name = builder.fmt("install {s}", .{artifact.step.name}), + .name = builder.fmt("install {s}", .{artifact.name}), .makeFn = make, }), .artifact = artifact, From 8acbfafefb635390f6b4da102966b5c4a2f3332e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 28 Feb 2023 21:14:32 -0700 Subject: [PATCH 167/294] compiler: update function accepts a std.Progress.Node This makes progress be exposed to the top-level caller of update(). I tossed in a bonus change: when the `zig build` subcommand sees exit code 2, it omits the "following command failed" line, and the build runner uses exit code 2 when there are compile errors. This tidies up the output on build failure by a little bit. --- src/Compilation.zig | 85 +++++++++++++++++++++++---------------------- src/glibc.zig | 19 +++++----- src/libcxx.zig | 8 ++--- src/libtsan.zig | 4 +-- src/libunwind.zig | 4 +-- src/main.zig | 48 ++++++++++++++++++++++--- src/mingw.zig | 14 ++++---- src/musl.zig | 16 ++++----- src/wasi_libc.zig | 16 ++++----- 9 files changed, 130 insertions(+), 84 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index 3a75b44861..478f931718 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -1847,8 +1847,15 @@ fn cleanupTmpArtifactDirectory( } } +pub fn hotCodeSwap(comp: *Compilation, prog_node: *std.Progress.Node, pid: std.os.pid_t) !void { + comp.bin_file.child_pid = pid; + try comp.makeBinFileWritable(); + try comp.update(prog_node); + try comp.makeBinFileExecutable(); +} + /// Detect changes to source files, perform semantic analysis, and update the output files. -pub fn update(comp: *Compilation) !void { +pub fn update(comp: *Compilation, main_progress_node: *std.Progress.Node) !void { const tracy_trace = trace(@src()); defer tracy_trace.end(); @@ -1995,21 +2002,6 @@ pub fn update(comp: *Compilation) !void { } } - // If the terminal is dumb, we dont want to show the user all the output. - var progress: std.Progress = .{ .dont_print_on_dumb = true }; - const main_progress_node = progress.start("", 0); - defer main_progress_node.end(); - switch (comp.color) { - .off => { - progress.terminal = null; - }, - .on => { - progress.terminal = std.io.getStdErr(); - progress.supports_ansi_escape_codes = true; - }, - .auto => {}, - } - try comp.performAllTheWork(main_progress_node); if (comp.bin_file.options.module) |module| { @@ -3057,11 +3049,11 @@ pub fn performAllTheWork( // backend, preventing anonymous Decls from being prematurely destroyed. while (true) { if (comp.work_queue.readItem()) |work_item| { - try processOneJob(comp, work_item); + try processOneJob(comp, work_item, main_progress_node); continue; } if (comp.anon_work_queue.readItem()) |work_item| { - try processOneJob(comp, work_item); + try processOneJob(comp, work_item, main_progress_node); continue; } break; @@ -3069,16 +3061,16 @@ pub fn performAllTheWork( if (comp.job_queued_compiler_rt_lib) { comp.job_queued_compiler_rt_lib = false; - buildCompilerRtOneShot(comp, .Lib, &comp.compiler_rt_lib); + buildCompilerRtOneShot(comp, .Lib, &comp.compiler_rt_lib, main_progress_node); } if (comp.job_queued_compiler_rt_obj) { comp.job_queued_compiler_rt_obj = false; - buildCompilerRtOneShot(comp, .Obj, &comp.compiler_rt_obj); + buildCompilerRtOneShot(comp, .Obj, &comp.compiler_rt_obj, main_progress_node); } } -fn processOneJob(comp: *Compilation, job: Job) !void { +fn processOneJob(comp: *Compilation, job: Job, prog_node: *std.Progress.Node) !void { switch (job) { .codegen_decl => |decl_index| { const module = comp.bin_file.options.module.?; @@ -3230,7 +3222,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { const named_frame = tracy.namedFrame("glibc_crt_file"); defer named_frame.end(); - glibc.buildCRTFile(comp, crt_file) catch |err| { + glibc.buildCRTFile(comp, crt_file, prog_node) catch |err| { // TODO Surface more error details. comp.lockAndSetMiscFailure(.glibc_crt_file, "unable to build glibc CRT file: {s}", .{ @errorName(err), @@ -3241,7 +3233,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { const named_frame = tracy.namedFrame("glibc_shared_objects"); defer named_frame.end(); - glibc.buildSharedObjects(comp) catch |err| { + glibc.buildSharedObjects(comp, prog_node) catch |err| { // TODO Surface more error details. comp.lockAndSetMiscFailure( .glibc_shared_objects, @@ -3254,7 +3246,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { const named_frame = tracy.namedFrame("musl_crt_file"); defer named_frame.end(); - musl.buildCRTFile(comp, crt_file) catch |err| { + musl.buildCRTFile(comp, crt_file, prog_node) catch |err| { // TODO Surface more error details. comp.lockAndSetMiscFailure( .musl_crt_file, @@ -3267,7 +3259,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { const named_frame = tracy.namedFrame("mingw_crt_file"); defer named_frame.end(); - mingw.buildCRTFile(comp, crt_file) catch |err| { + mingw.buildCRTFile(comp, crt_file, prog_node) catch |err| { // TODO Surface more error details. comp.lockAndSetMiscFailure( .mingw_crt_file, @@ -3294,7 +3286,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { const named_frame = tracy.namedFrame("libunwind"); defer named_frame.end(); - libunwind.buildStaticLib(comp) catch |err| { + libunwind.buildStaticLib(comp, prog_node) catch |err| { // TODO Surface more error details. comp.lockAndSetMiscFailure( .libunwind, @@ -3307,7 +3299,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { const named_frame = tracy.namedFrame("libcxx"); defer named_frame.end(); - libcxx.buildLibCXX(comp) catch |err| { + libcxx.buildLibCXX(comp, prog_node) catch |err| { // TODO Surface more error details. comp.lockAndSetMiscFailure( .libcxx, @@ -3320,7 +3312,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { const named_frame = tracy.namedFrame("libcxxabi"); defer named_frame.end(); - libcxx.buildLibCXXABI(comp) catch |err| { + libcxx.buildLibCXXABI(comp, prog_node) catch |err| { // TODO Surface more error details. comp.lockAndSetMiscFailure( .libcxxabi, @@ -3333,7 +3325,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { const named_frame = tracy.namedFrame("libtsan"); defer named_frame.end(); - libtsan.buildTsan(comp) catch |err| { + libtsan.buildTsan(comp, prog_node) catch |err| { // TODO Surface more error details. comp.lockAndSetMiscFailure( .libtsan, @@ -3346,7 +3338,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { const named_frame = tracy.namedFrame("wasi_libc_crt_file"); defer named_frame.end(); - wasi_libc.buildCRTFile(comp, crt_file) catch |err| { + wasi_libc.buildCRTFile(comp, crt_file, prog_node) catch |err| { // TODO Surface more error details. comp.lockAndSetMiscFailure( .wasi_libc_crt_file, @@ -3364,6 +3356,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { .Lib, &comp.libssp_static_lib, .libssp, + prog_node, ) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.SubCompilationFailed => return, // error reported already @@ -3383,6 +3376,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { .Lib, &comp.libc_static_lib, .zig_libc, + prog_node, ) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.SubCompilationFailed => return, // error reported already @@ -3723,8 +3717,15 @@ fn buildCompilerRtOneShot( comp: *Compilation, output_mode: std.builtin.OutputMode, out: *?CRTFile, + prog_node: *std.Progress.Node, ) void { - comp.buildOutputFromZig("compiler_rt.zig", output_mode, out, .compiler_rt) catch |err| switch (err) { + comp.buildOutputFromZig( + "compiler_rt.zig", + output_mode, + out, + .compiler_rt, + prog_node, + ) catch |err| switch (err) { error.SubCompilationFailed => return, // error reported already else => comp.lockAndSetMiscFailure( .compiler_rt, @@ -5248,8 +5249,15 @@ pub fn updateSubCompilation( parent_comp: *Compilation, sub_comp: *Compilation, misc_task: MiscTask, + prog_node: *std.Progress.Node, ) !void { - try sub_comp.update(); + { + var sub_node = prog_node.start(@tagName(misc_task), 0); + sub_node.activate(); + defer sub_node.end(); + + try sub_comp.update(prog_node); + } // Look for compilation errors in this sub compilation const gpa = parent_comp.gpa; @@ -5276,6 +5284,7 @@ fn buildOutputFromZig( output_mode: std.builtin.OutputMode, out: *?CRTFile, misc_task_tag: MiscTask, + prog_node: *std.Progress.Node, ) !void { const tracy_trace = trace(@src()); defer tracy_trace.end(); @@ -5342,7 +5351,7 @@ fn buildOutputFromZig( }); defer sub_compilation.destroy(); - try comp.updateSubCompilation(sub_compilation, misc_task_tag); + try comp.updateSubCompilation(sub_compilation, misc_task_tag, prog_node); assert(out.* == null); out.* = Compilation.CRTFile{ @@ -5358,6 +5367,7 @@ pub fn build_crt_file( root_name: []const u8, output_mode: std.builtin.OutputMode, misc_task_tag: MiscTask, + prog_node: *std.Progress.Node, c_source_files: []const Compilation.CSourceFile, ) !void { const tracy_trace = trace(@src()); @@ -5418,7 +5428,7 @@ pub fn build_crt_file( }); defer sub_compilation.destroy(); - try comp.updateSubCompilation(sub_compilation, misc_task_tag); + try comp.updateSubCompilation(sub_compilation, misc_task_tag, prog_node); try comp.crt_files.ensureUnusedCapacity(comp.gpa, 1); @@ -5470,10 +5480,3 @@ pub fn compilerRtStrip(comp: Compilation) bool { return true; } } - -pub fn hotCodeSwap(comp: *Compilation, pid: std.os.pid_t) !void { - comp.bin_file.child_pid = pid; - try comp.makeBinFileWritable(); - try comp.update(); - try comp.makeBinFileExecutable(); -} diff --git a/src/glibc.zig b/src/glibc.zig index 530f35531a..b37398bffd 100644 --- a/src/glibc.zig +++ b/src/glibc.zig @@ -161,7 +161,7 @@ pub const CRTFile = enum { libc_nonshared_a, }; -pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { +pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progress.Node) !void { if (!build_options.have_llvm) { return error.ZigCompilerNotBuiltWithLLVMExtensions; } @@ -196,7 +196,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { "-DASSEMBLER", "-Wa,--noexecstack", }); - return comp.build_crt_file("crti", .Obj, .@"glibc crti.o", &[1]Compilation.CSourceFile{ + return comp.build_crt_file("crti", .Obj, .@"glibc crti.o", prog_node, &.{ .{ .src_path = try start_asm_path(comp, arena, "crti.S"), .cache_exempt_flags = args.items, @@ -215,7 +215,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { "-DASSEMBLER", "-Wa,--noexecstack", }); - return comp.build_crt_file("crtn", .Obj, .@"glibc crtn.o", &[1]Compilation.CSourceFile{ + return comp.build_crt_file("crtn", .Obj, .@"glibc crtn.o", prog_node, &.{ .{ .src_path = try start_asm_path(comp, arena, "crtn.S"), .cache_exempt_flags = args.items, @@ -265,7 +265,9 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { .cache_exempt_flags = args.items, }; }; - return comp.build_crt_file("Scrt1", .Obj, .@"glibc Scrt1.o", &[_]Compilation.CSourceFile{ start_o, abi_note_o }); + return comp.build_crt_file("Scrt1", .Obj, .@"glibc Scrt1.o", prog_node, &.{ + start_o, abi_note_o, + }); }, .libc_nonshared_a => { const s = path.sep_str; @@ -366,7 +368,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { files_index += 1; } const files = files_buf[0..files_index]; - return comp.build_crt_file("c_nonshared", .Lib, .@"glibc libc_nonshared.a", files); + return comp.build_crt_file("c_nonshared", .Lib, .@"glibc libc_nonshared.a", prog_node, files); }, } } @@ -639,7 +641,7 @@ pub const BuiltSharedObjects = struct { const all_map_basename = "all.map"; -pub fn buildSharedObjects(comp: *Compilation) !void { +pub fn buildSharedObjects(comp: *Compilation, prog_node: *std.Progress.Node) !void { const tracy = trace(@src()); defer tracy.end(); @@ -1023,7 +1025,7 @@ pub fn buildSharedObjects(comp: *Compilation) !void { const asm_file_basename = std.fmt.bufPrint(&lib_name_buf, "{s}.s", .{lib.name}) catch unreachable; try o_directory.handle.writeFile(asm_file_basename, stubs_asm.items); - try buildSharedLib(comp, arena, comp.global_cache_directory, o_directory, asm_file_basename, lib); + try buildSharedLib(comp, arena, comp.global_cache_directory, o_directory, asm_file_basename, lib, prog_node); } man.writeManifest() catch |err| { @@ -1046,6 +1048,7 @@ fn buildSharedLib( bin_directory: Compilation.Directory, asm_file_basename: []const u8, lib: Lib, + prog_node: *std.Progress.Node, ) !void { const tracy = trace(@src()); defer tracy.end(); @@ -1105,7 +1108,7 @@ fn buildSharedLib( }); defer sub_compilation.destroy(); - try comp.updateSubCompilation(sub_compilation, .@"glibc shared object"); + try comp.updateSubCompilation(sub_compilation, .@"glibc shared object", prog_node); } // Return true if glibc has crti/crtn sources for that architecture. diff --git a/src/libcxx.zig b/src/libcxx.zig index c17352c562..9c5dc9426f 100644 --- a/src/libcxx.zig +++ b/src/libcxx.zig @@ -96,7 +96,7 @@ const libcxx_files = [_][]const u8{ "src/verbose_abort.cpp", }; -pub fn buildLibCXX(comp: *Compilation) !void { +pub fn buildLibCXX(comp: *Compilation, prog_node: *std.Progress.Node) !void { if (!build_options.have_llvm) { return error.ZigCompilerNotBuiltWithLLVMExtensions; } @@ -258,7 +258,7 @@ pub fn buildLibCXX(comp: *Compilation) !void { }); defer sub_compilation.destroy(); - try comp.updateSubCompilation(sub_compilation, .libcxx); + try comp.updateSubCompilation(sub_compilation, .libcxx, prog_node); assert(comp.libcxx_static_lib == null); comp.libcxx_static_lib = Compilation.CRTFile{ @@ -269,7 +269,7 @@ pub fn buildLibCXX(comp: *Compilation) !void { }; } -pub fn buildLibCXXABI(comp: *Compilation) !void { +pub fn buildLibCXXABI(comp: *Compilation, prog_node: *std.Progress.Node) !void { if (!build_options.have_llvm) { return error.ZigCompilerNotBuiltWithLLVMExtensions; } @@ -418,7 +418,7 @@ pub fn buildLibCXXABI(comp: *Compilation) !void { }); defer sub_compilation.destroy(); - try comp.updateSubCompilation(sub_compilation, .libcxxabi); + try comp.updateSubCompilation(sub_compilation, .libcxxabi, prog_node); assert(comp.libcxxabi_static_lib == null); comp.libcxxabi_static_lib = Compilation.CRTFile{ diff --git a/src/libtsan.zig b/src/libtsan.zig index 1399b6b76c..54bf00e4b6 100644 --- a/src/libtsan.zig +++ b/src/libtsan.zig @@ -5,7 +5,7 @@ const Compilation = @import("Compilation.zig"); const build_options = @import("build_options"); const trace = @import("tracy.zig").trace; -pub fn buildTsan(comp: *Compilation) !void { +pub fn buildTsan(comp: *Compilation, prog_node: *std.Progress.Node) !void { if (!build_options.have_llvm) { return error.ZigCompilerNotBuiltWithLLVMExtensions; } @@ -235,7 +235,7 @@ pub fn buildTsan(comp: *Compilation) !void { }); defer sub_compilation.destroy(); - try comp.updateSubCompilation(sub_compilation, .libtsan); + try comp.updateSubCompilation(sub_compilation, .libtsan, prog_node); assert(comp.tsan_static_lib == null); comp.tsan_static_lib = Compilation.CRTFile{ diff --git a/src/libunwind.zig b/src/libunwind.zig index 667195a369..aefbfb457d 100644 --- a/src/libunwind.zig +++ b/src/libunwind.zig @@ -7,7 +7,7 @@ const Compilation = @import("Compilation.zig"); const build_options = @import("build_options"); const trace = @import("tracy.zig").trace; -pub fn buildStaticLib(comp: *Compilation) !void { +pub fn buildStaticLib(comp: *Compilation, prog_node: *std.Progress.Node) !void { if (!build_options.have_llvm) { return error.ZigCompilerNotBuiltWithLLVMExtensions; } @@ -130,7 +130,7 @@ pub fn buildStaticLib(comp: *Compilation) !void { }); defer sub_compilation.destroy(); - try comp.updateSubCompilation(sub_compilation, .libunwind); + try comp.updateSubCompilation(sub_compilation, .libunwind, prog_node); assert(comp.libunwind_static_lib == null); diff --git a/src/main.zig b/src/main.zig index d574681bcd..495cc40ed5 100644 --- a/src/main.zig +++ b/src/main.zig @@ -3543,6 +3543,23 @@ fn serve( var receive_fifo = std.fifo.LinearFifo(u8, .Dynamic).init(gpa); defer receive_fifo.deinit(); + var progress: std.Progress = .{ + .terminal = null, + .root = .{ + .context = undefined, + .parent = null, + .name = "", + .unprotected_estimated_total_items = 0, + .unprotected_completed_items = 0, + }, + .columns_written = 0, + .prev_refresh_timestamp = 0, + .timer = null, + .done = false, + }; + const main_progress_node = &progress.root; + main_progress_node.context = &progress; + while (true) { const hdr = try receiveMessage(in, &receive_fifo); @@ -3551,11 +3568,12 @@ fn serve( return cleanExit(); }, .update => { + assert(main_progress_node.recently_updated_child == null); tracy.frameMark(); if (comp.bin_file.options.output_mode == .Exe) { try comp.makeBinFileWritable(); } - try comp.update(); + try comp.update(main_progress_node); try comp.makeBinFileExecutable(); try serveUpdateResults(out, comp); }, @@ -3581,14 +3599,15 @@ fn serve( }, .hot_update => { tracy.frameMark(); + assert(main_progress_node.recently_updated_child == null); if (child_pid) |pid| { - try comp.hotCodeSwap(pid); + try comp.hotCodeSwap(main_progress_node, pid); try serveUpdateResults(out, comp); } else { if (comp.bin_file.options.output_mode == .Exe) { try comp.makeBinFileWritable(); } - try comp.update(); + try comp.update(main_progress_node); try comp.makeBinFileExecutable(); try serveUpdateResults(out, comp); @@ -3936,7 +3955,24 @@ const AfterUpdateHook = union(enum) { }; fn updateModule(gpa: Allocator, comp: *Compilation, hook: AfterUpdateHook) !void { - try comp.update(); + { + // If the terminal is dumb, we dont want to show the user all the output. + var progress: std.Progress = .{ .dont_print_on_dumb = true }; + const main_progress_node = progress.start("", 0); + defer main_progress_node.end(); + switch (comp.color) { + .off => { + progress.terminal = null; + }, + .on => { + progress.terminal = std.io.getStdErr(); + progress.supports_ansi_escape_codes = true; + }, + .auto => {}, + } + + try comp.update(main_progress_node); + } var errors = try comp.getAllErrorsAlloc(); defer errors.deinit(comp.gpa); @@ -4642,6 +4678,10 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi switch (term) { .Exited => |code| { if (code == 0) return cleanExit(); + // Indicates that the build runner has reported compile errors + // and this parent process does not need to report any further + // diagnostics. + if (code == 2) process.exit(2); const cmd = try std.mem.join(arena, " ", child_argv); fatal("the following build command failed with exit code {d}:\n{s}", .{ code, cmd }); diff --git a/src/mingw.zig b/src/mingw.zig index d5c42c3ccc..a85645e80b 100644 --- a/src/mingw.zig +++ b/src/mingw.zig @@ -19,7 +19,7 @@ pub const CRTFile = enum { uuid_lib, }; -pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { +pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progress.Node) !void { if (!build_options.have_llvm) { return error.ZigCompilerNotBuiltWithLLVMExtensions; } @@ -41,7 +41,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { //"-D_UNICODE", //"-DWPRFLAG=1", }); - return comp.build_crt_file("crt2", .Obj, .@"mingw-w64 crt2.o", &[1]Compilation.CSourceFile{ + return comp.build_crt_file("crt2", .Obj, .@"mingw-w64 crt2.o", prog_node, &.{ .{ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "mingw", "crt", "crtexe.c", @@ -60,7 +60,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { "-U__CRTDLL__", "-D__MSVCRT__", }); - return comp.build_crt_file("dllcrt2", .Obj, .@"mingw-w64 dllcrt2.o", &[1]Compilation.CSourceFile{ + return comp.build_crt_file("dllcrt2", .Obj, .@"mingw-w64 dllcrt2.o", prog_node, &.{ .{ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "mingw", "crt", "crtdll.c", @@ -100,7 +100,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { .extra_flags = args.items, }; } - return comp.build_crt_file("mingw32", .Lib, .@"mingw-w64 mingw32.lib", &c_source_files); + return comp.build_crt_file("mingw32", .Lib, .@"mingw-w64 mingw32.lib", prog_node, &c_source_files); }, .msvcrt_os_lib => { @@ -148,7 +148,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { }; } } - return comp.build_crt_file("msvcrt-os", .Lib, .@"mingw-w64 msvcrt-os.lib", c_source_files.items); + return comp.build_crt_file("msvcrt-os", .Lib, .@"mingw-w64 msvcrt-os.lib", prog_node, c_source_files.items); }, .mingwex_lib => { @@ -211,7 +211,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { } else { @panic("unsupported arch"); } - return comp.build_crt_file("mingwex", .Lib, .@"mingw-w64 mingwex.lib", c_source_files.items); + return comp.build_crt_file("mingwex", .Lib, .@"mingw-w64 mingwex.lib", prog_node, c_source_files.items); }, .uuid_lib => { @@ -244,7 +244,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { .extra_flags = extra_flags, }; } - return comp.build_crt_file("uuid", .Lib, .@"mingw-w64 uuid.lib", &c_source_files); + return comp.build_crt_file("uuid", .Lib, .@"mingw-w64 uuid.lib", prog_node, &c_source_files); }, } } diff --git a/src/musl.zig b/src/musl.zig index 7dd224604f..4a3f1e6dde 100644 --- a/src/musl.zig +++ b/src/musl.zig @@ -17,7 +17,7 @@ pub const CRTFile = enum { libc_so, }; -pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { +pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progress.Node) !void { if (!build_options.have_llvm) { return error.ZigCompilerNotBuiltWithLLVMExtensions; } @@ -33,7 +33,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { try args.appendSlice(&[_][]const u8{ "-Qunused-arguments", }); - return comp.build_crt_file("crti", .Obj, .@"musl crti.o", &[1]Compilation.CSourceFile{ + return comp.build_crt_file("crti", .Obj, .@"musl crti.o", prog_node, &.{ .{ .src_path = try start_asm_path(comp, arena, "crti.s"), .extra_flags = args.items, @@ -46,7 +46,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { try args.appendSlice(&[_][]const u8{ "-Qunused-arguments", }); - return comp.build_crt_file("crtn", .Obj, .@"musl crtn.o", &[1]Compilation.CSourceFile{ + return comp.build_crt_file("crtn", .Obj, .@"musl crtn.o", prog_node, &.{ .{ .src_path = try start_asm_path(comp, arena, "crtn.s"), .extra_flags = args.items, @@ -60,7 +60,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { "-fno-stack-protector", "-DCRT", }); - return comp.build_crt_file("crt1", .Obj, .@"musl crt1.o", &[1]Compilation.CSourceFile{ + return comp.build_crt_file("crt1", .Obj, .@"musl crt1.o", prog_node, &.{ .{ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "musl", "crt", "crt1.c", @@ -77,7 +77,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { "-fno-stack-protector", "-DCRT", }); - return comp.build_crt_file("rcrt1", .Obj, .@"musl rcrt1.o", &[1]Compilation.CSourceFile{ + return comp.build_crt_file("rcrt1", .Obj, .@"musl rcrt1.o", prog_node, &.{ .{ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "musl", "crt", "rcrt1.c", @@ -94,7 +94,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { "-fno-stack-protector", "-DCRT", }); - return comp.build_crt_file("Scrt1", .Obj, .@"musl Scrt1.o", &[1]Compilation.CSourceFile{ + return comp.build_crt_file("Scrt1", .Obj, .@"musl Scrt1.o", prog_node, &.{ .{ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "musl", "crt", "Scrt1.c", @@ -187,7 +187,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { .extra_flags = args.items, }; } - return comp.build_crt_file("c", .Lib, .@"musl libc.a", c_source_files.items); + return comp.build_crt_file("c", .Lib, .@"musl libc.a", prog_node, c_source_files.items); }, .libc_so => { const target = comp.getTarget(); @@ -241,7 +241,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { }); defer sub_compilation.destroy(); - try comp.updateSubCompilation(sub_compilation, .@"musl libc.so"); + try comp.updateSubCompilation(sub_compilation, .@"musl libc.so", prog_node); try comp.crt_files.ensureUnusedCapacity(comp.gpa, 1); diff --git a/src/wasi_libc.zig b/src/wasi_libc.zig index c4a4cbc4a5..38a4f17190 100644 --- a/src/wasi_libc.zig +++ b/src/wasi_libc.zig @@ -59,7 +59,7 @@ pub fn execModelCrtFileFullName(wasi_exec_model: std.builtin.WasiExecModel) []co }; } -pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { +pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progress.Node) !void { if (!build_options.have_llvm) { return error.ZigCompilerNotBuiltWithLLVMExtensions; } @@ -74,7 +74,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { var args = std.ArrayList([]const u8).init(arena); try addCCArgs(comp, arena, &args, false); try addLibcBottomHalfIncludes(comp, arena, &args); - return comp.build_crt_file("crt1-reactor", .Obj, .@"wasi crt1-reactor.o", &[1]Compilation.CSourceFile{ + return comp.build_crt_file("crt1-reactor", .Obj, .@"wasi crt1-reactor.o", prog_node, &.{ .{ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", try sanitize(arena, crt1_reactor_src_file), @@ -87,7 +87,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { var args = std.ArrayList([]const u8).init(arena); try addCCArgs(comp, arena, &args, false); try addLibcBottomHalfIncludes(comp, arena, &args); - return comp.build_crt_file("crt1-command", .Obj, .@"wasi crt1-command.o", &[1]Compilation.CSourceFile{ + return comp.build_crt_file("crt1-command", .Obj, .@"wasi crt1-command.o", prog_node, &.{ .{ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", try sanitize(arena, crt1_command_src_file), @@ -145,7 +145,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { } } - try comp.build_crt_file("c", .Lib, .@"wasi libc.a", libc_sources.items); + try comp.build_crt_file("c", .Lib, .@"wasi libc.a", prog_node, libc_sources.items); }, .libwasi_emulated_process_clocks_a => { var args = std.ArrayList([]const u8).init(arena); @@ -161,7 +161,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { .extra_flags = args.items, }); } - try comp.build_crt_file("wasi-emulated-process-clocks", .Lib, .@"libwasi-emulated-process-clocks.a", emu_clocks_sources.items); + try comp.build_crt_file("wasi-emulated-process-clocks", .Lib, .@"libwasi-emulated-process-clocks.a", prog_node, emu_clocks_sources.items); }, .libwasi_emulated_getpid_a => { var args = std.ArrayList([]const u8).init(arena); @@ -177,7 +177,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { .extra_flags = args.items, }); } - try comp.build_crt_file("wasi-emulated-getpid", .Lib, .@"libwasi-emulated-getpid.a", emu_getpid_sources.items); + try comp.build_crt_file("wasi-emulated-getpid", .Lib, .@"libwasi-emulated-getpid.a", prog_node, emu_getpid_sources.items); }, .libwasi_emulated_mman_a => { var args = std.ArrayList([]const u8).init(arena); @@ -193,7 +193,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { .extra_flags = args.items, }); } - try comp.build_crt_file("wasi-emulated-mman", .Lib, .@"libwasi-emulated-mman.a", emu_mman_sources.items); + try comp.build_crt_file("wasi-emulated-mman", .Lib, .@"libwasi-emulated-mman.a", prog_node, emu_mman_sources.items); }, .libwasi_emulated_signal_a => { var emu_signal_sources = std.ArrayList(Compilation.CSourceFile).init(arena); @@ -228,7 +228,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { } } - try comp.build_crt_file("wasi-emulated-signal", .Lib, .@"libwasi-emulated-signal.a", emu_signal_sources.items); + try comp.build_crt_file("wasi-emulated-signal", .Lib, .@"libwasi-emulated-signal.a", prog_node, emu_signal_sources.items); }, } } From 81376e72057026464e1c17330666d4fb3c5a7ce0 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 28 Feb 2023 21:57:59 -0700 Subject: [PATCH 168/294] fix UAF in build runner --- lib/build_runner.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/build_runner.zig b/lib/build_runner.zig index d70dbf2919..9a8f138979 100644 --- a/lib/build_runner.zig +++ b/lib/build_runner.zig @@ -309,7 +309,7 @@ fn runStepNames( } } - const starting_steps = step_stack.items; + const starting_steps = try b.allocator.dupe(*Step, step_stack.items); for (starting_steps) |s| { checkForDependencyLoop(b, s, &step_stack) catch |err| switch (err) { error.DependencyLoopDetected => return error.UncleanExit, From 0e078790feaf49964d7a0da3042117ebd10de13b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 28 Feb 2023 23:58:13 -0700 Subject: [PATCH 169/294] multiplex compiler progress messages into the build runner --- lib/build_runner.zig | 2 +- lib/std/Build.zig | 16 ++++-- lib/std/Build/CheckFileStep.zig | 3 +- lib/std/Build/CheckObjectStep.zig | 3 +- lib/std/Build/CompileStep.zig | 6 +-- lib/std/Build/ConfigHeaderStep.zig | 3 +- lib/std/Build/EmulatableRunStep.zig | 3 +- lib/std/Build/FmtStep.zig | 3 +- lib/std/Build/InstallArtifactStep.zig | 3 +- lib/std/Build/InstallDirStep.zig | 3 +- lib/std/Build/InstallFileStep.zig | 3 +- lib/std/Build/LogStep.zig | 3 +- lib/std/Build/ObjCopyStep.zig | 3 +- lib/std/Build/OptionsStep.zig | 3 +- lib/std/Build/RemoveDirStep.zig | 3 +- lib/std/Build/RunStep.zig | 3 +- lib/std/Build/Step.zig | 13 +++-- lib/std/Build/TranslateCStep.zig | 4 +- lib/std/Build/WriteFileStep.zig | 3 +- src/main.zig | 73 ++++++++++++++++++++++++++- test/tests.zig | 6 ++- 21 files changed, 131 insertions(+), 31 deletions(-) diff --git a/lib/build_runner.zig b/lib/build_runner.zig index 9a8f138979..ea4703bfbb 100644 --- a/lib/build_runner.zig +++ b/lib/build_runner.zig @@ -571,7 +571,7 @@ fn workerMakeOneStep( // For example, CompileStep does some sus things with modifying the saved // *Build object in install header steps that might be able to be removed // by passing the *Build object through the make() functions. - const make_result = s.make(); + const make_result = s.make(&sub_prog_node); // No matter the result, we want to display error/warning messages. if (s.result_error_msgs.items.len > 0) { diff --git a/lib/std/Build.zig b/lib/std/Build.zig index a9d7c31733..ca7ddb591c 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -718,7 +718,8 @@ pub fn getUninstallStep(self: *Build) *Step { return &self.uninstall_tls.step; } -fn makeUninstall(uninstall_step: *Step) anyerror!void { +fn makeUninstall(uninstall_step: *Step, prog_node: *std.Progress.Node) anyerror!void { + _ = prog_node; const uninstall_tls = @fieldParentPtr(TopLevelStep, "step", uninstall_step); const self = @fieldParentPtr(Build, "uninstall_tls", uninstall_tls); @@ -1404,7 +1405,7 @@ pub fn execAllowFail( /// This function is used exclusively for spawning and communicating with the zig compiler. /// TODO: move to build_runner.zig -pub fn execFromStep(b: *Build, argv: []const []const u8, s: *Step) ![]const u8 { +pub fn execFromStep(b: *Build, argv: []const []const u8, s: *Step, prog_node: *std.Progress.Node) ![]const u8 { assert(argv.len != 0); if (b.verbose) { @@ -1439,6 +1440,11 @@ pub fn execFromStep(b: *Build, argv: []const []const u8, s: *Step) ![]const u8 { const Header = std.zig.Server.Message.Header; var result: ?[]const u8 = null; + var node_name: std.ArrayListUnmanaged(u8) = .{}; + defer node_name.deinit(b.allocator); + var sub_prog_node: ?std.Progress.Node = null; + defer if (sub_prog_node) |*n| n.end(); + while (try poller.poll()) { const stdout = poller.fifo(.stdout); const buf = stdout.readableSlice(0); @@ -1478,7 +1484,11 @@ pub fn execFromStep(b: *Build, argv: []const []const u8, s: *Step) ![]const u8 { }; }, .progress => { - @panic("TODO handle progress message"); + if (sub_prog_node) |*n| n.end(); + node_name.clearRetainingCapacity(); + try node_name.appendSlice(b.allocator, body); + sub_prog_node = prog_node.start(node_name.items, 0); + sub_prog_node.?.activate(); }, .emit_bin_path => { result = try b.allocator.dupe(u8, body); diff --git a/lib/std/Build/CheckFileStep.zig b/lib/std/Build/CheckFileStep.zig index 5b2e5b4e5b..9fee947386 100644 --- a/lib/std/Build/CheckFileStep.zig +++ b/lib/std/Build/CheckFileStep.zig @@ -33,7 +33,8 @@ pub fn create( return self; } -fn make(step: *Step) !void { +fn make(step: *Step, prog_node: *std.Progress.Node) !void { + _ = prog_node; const self = @fieldParentPtr(CheckFileStep, "step", step); const src_path = self.source.getPath(self.builder); diff --git a/lib/std/Build/CheckObjectStep.zig b/lib/std/Build/CheckObjectStep.zig index 1775a5d39a..eccbbd1696 100644 --- a/lib/std/Build/CheckObjectStep.zig +++ b/lib/std/Build/CheckObjectStep.zig @@ -300,7 +300,8 @@ pub fn checkComputeCompare( self.checks.append(new_check) catch @panic("OOM"); } -fn make(step: *Step) !void { +fn make(step: *Step, prog_node: *std.Progress.Node) !void { + _ = prog_node; const self = @fieldParentPtr(CheckObjectStep, "step", step); const gpa = self.builder.allocator; diff --git a/lib/std/Build/CompileStep.zig b/lib/std/Build/CompileStep.zig index 491c8ac8e4..711092c762 100644 --- a/lib/std/Build/CompileStep.zig +++ b/lib/std/Build/CompileStep.zig @@ -1160,7 +1160,7 @@ fn constructDepString( } } -fn make(step: *Step) !void { +fn make(step: *Step, prog_node: *std.Progress.Node) !void { const self = @fieldParentPtr(CompileStep, "step", step); const builder = self.builder; @@ -1718,7 +1718,7 @@ fn make(step: *Step) !void { } if (other.installed_headers.items.len > 0) { for (other.installed_headers.items) |install_step| { - try install_step.make(); + try install_step.make(prog_node); } try zig_args.append("-I"); try zig_args.append(builder.pathJoin(&.{ @@ -1894,7 +1894,7 @@ fn make(step: *Step) !void { try zig_args.append(resolved_args_file); } - const output_bin_path = try builder.execFromStep(zig_args.items, &self.step); + const output_bin_path = try builder.execFromStep(zig_args.items, &self.step, prog_node); const build_output_dir = fs.path.dirname(output_bin_path).?; if (self.output_dir) |output_dir| { diff --git a/lib/std/Build/ConfigHeaderStep.zig b/lib/std/Build/ConfigHeaderStep.zig index fb8d92e4e2..c74c03718e 100644 --- a/lib/std/Build/ConfigHeaderStep.zig +++ b/lib/std/Build/ConfigHeaderStep.zig @@ -152,7 +152,8 @@ fn putValue(self: *ConfigHeaderStep, field_name: []const u8, comptime T: type, v } } -fn make(step: *Step) !void { +fn make(step: *Step, prog_node: *std.Progress.Node) !void { + _ = prog_node; const self = @fieldParentPtr(ConfigHeaderStep, "step", step); const gpa = self.builder.allocator; diff --git a/lib/std/Build/EmulatableRunStep.zig b/lib/std/Build/EmulatableRunStep.zig index 6556f4b8c0..44387c36f6 100644 --- a/lib/std/Build/EmulatableRunStep.zig +++ b/lib/std/Build/EmulatableRunStep.zig @@ -71,7 +71,8 @@ pub fn create(builder: *std.Build, name: []const u8, artifact: *CompileStep) *Em return self; } -fn make(step: *Step) !void { +fn make(step: *Step, prog_node: *std.Progress.Node) !void { + _ = prog_node; const self = @fieldParentPtr(EmulatableRunStep, "step", step); const host_info = self.builder.host; diff --git a/lib/std/Build/FmtStep.zig b/lib/std/Build/FmtStep.zig index 04b8ccb3e4..5efada7507 100644 --- a/lib/std/Build/FmtStep.zig +++ b/lib/std/Build/FmtStep.zig @@ -29,7 +29,8 @@ pub fn create(builder: *std.Build, paths: []const []const u8) *FmtStep { return self; } -fn make(step: *Step) !void { +fn make(step: *Step, prog_node: *std.Progress.Node) !void { + _ = prog_node; const self = @fieldParentPtr(FmtStep, "step", step); return self.builder.spawnChild(self.argv); diff --git a/lib/std/Build/InstallArtifactStep.zig b/lib/std/Build/InstallArtifactStep.zig index b652ade38f..d8907eb59f 100644 --- a/lib/std/Build/InstallArtifactStep.zig +++ b/lib/std/Build/InstallArtifactStep.zig @@ -64,7 +64,8 @@ pub fn create(builder: *std.Build, artifact: *CompileStep) *InstallArtifactStep return self; } -fn make(step: *Step) !void { +fn make(step: *Step, prog_node: *std.Progress.Node) !void { + _ = prog_node; const self = @fieldParentPtr(InstallArtifactStep, "step", step); const builder = self.builder; diff --git a/lib/std/Build/InstallDirStep.zig b/lib/std/Build/InstallDirStep.zig index 54a37af1d4..bf89d9e7c7 100644 --- a/lib/std/Build/InstallDirStep.zig +++ b/lib/std/Build/InstallDirStep.zig @@ -56,7 +56,8 @@ pub fn init( }; } -fn make(step: *Step) !void { +fn make(step: *Step, prog_node: *std.Progress.Node) !void { + _ = prog_node; const self = @fieldParentPtr(InstallDirStep, "step", step); const dest_prefix = self.builder.getInstallPath(self.options.install_dir, self.options.install_subdir); const src_builder = self.override_source_builder orelse self.builder; diff --git a/lib/std/Build/InstallFileStep.zig b/lib/std/Build/InstallFileStep.zig index e6c57a8050..f77b22c112 100644 --- a/lib/std/Build/InstallFileStep.zig +++ b/lib/std/Build/InstallFileStep.zig @@ -35,7 +35,8 @@ pub fn init( }; } -fn make(step: *Step) !void { +fn make(step: *Step, prog_node: *std.Progress.Node) !void { + _ = prog_node; const self = @fieldParentPtr(InstallFileStep, "step", step); const src_builder = self.override_source_builder orelse self.builder; const full_src_path = self.source.getPath2(src_builder, step); diff --git a/lib/std/Build/LogStep.zig b/lib/std/Build/LogStep.zig index 008ed6e8bb..25bba747bf 100644 --- a/lib/std/Build/LogStep.zig +++ b/lib/std/Build/LogStep.zig @@ -21,7 +21,8 @@ pub fn init(builder: *std.Build, data: []const u8) LogStep { }; } -fn make(step: *Step) anyerror!void { +fn make(step: *Step, prog_node: *std.Progress.Node) anyerror!void { + _ = prog_node; const self = @fieldParentPtr(LogStep, "step", step); log.info("{s}", .{self.data}); } diff --git a/lib/std/Build/ObjCopyStep.zig b/lib/std/Build/ObjCopyStep.zig index 549bb1ed00..839d95903c 100644 --- a/lib/std/Build/ObjCopyStep.zig +++ b/lib/std/Build/ObjCopyStep.zig @@ -66,7 +66,8 @@ pub fn getOutputSource(self: *const ObjCopyStep) std.Build.FileSource { return .{ .generated = &self.output_file }; } -fn make(step: *Step) !void { +fn make(step: *Step, prog_node: *std.Progress.Node) !void { + _ = prog_node; const self = @fieldParentPtr(ObjCopyStep, "step", step); const b = self.builder; diff --git a/lib/std/Build/OptionsStep.zig b/lib/std/Build/OptionsStep.zig index 8e86578e30..2cb3bb13be 100644 --- a/lib/std/Build/OptionsStep.zig +++ b/lib/std/Build/OptionsStep.zig @@ -219,7 +219,8 @@ pub fn getSource(self: *OptionsStep) FileSource { return .{ .generated = &self.generated_file }; } -fn make(step: *Step) !void { +fn make(step: *Step, prog_node: *std.Progress.Node) !void { + _ = prog_node; const self = @fieldParentPtr(OptionsStep, "step", step); for (self.artifact_args.items) |item| { diff --git a/lib/std/Build/RemoveDirStep.zig b/lib/std/Build/RemoveDirStep.zig index acde60a745..4fc8e6d338 100644 --- a/lib/std/Build/RemoveDirStep.zig +++ b/lib/std/Build/RemoveDirStep.zig @@ -22,7 +22,8 @@ pub fn init(builder: *std.Build, dir_path: []const u8) RemoveDirStep { }; } -fn make(step: *Step) !void { +fn make(step: *Step, prog_node: *std.Progress.Node) !void { + _ = prog_node; const self = @fieldParentPtr(RemoveDirStep, "step", step); const full_path = self.builder.pathFromRoot(self.dir_path); diff --git a/lib/std/Build/RunStep.zig b/lib/std/Build/RunStep.zig index 904ef0935f..7ec60b6d22 100644 --- a/lib/std/Build/RunStep.zig +++ b/lib/std/Build/RunStep.zig @@ -206,7 +206,8 @@ fn needOutputCheck(self: RunStep) bool { return false; } -fn make(step: *Step) !void { +fn make(step: *Step, prog_node: *std.Progress.Node) !void { + _ = prog_node; const self = @fieldParentPtr(RunStep, "step", step); const need_output_check = self.needOutputCheck(); diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig index 4edece8038..53861683ac 100644 --- a/lib/std/Build/Step.zig +++ b/lib/std/Build/Step.zig @@ -1,6 +1,6 @@ id: Id, name: []const u8, -makeFn: *const fn (self: *Step) anyerror!void, +makeFn: MakeFn, dependencies: std.ArrayList(*Step), /// This field is empty during execution of the user's build script, and /// then populated during dependency loop checking in the build runner. @@ -13,6 +13,8 @@ debug_stack_trace: [n_debug_stack_frames]usize, result_error_msgs: std.ArrayListUnmanaged([]const u8), result_error_bundle: std.zig.ErrorBundle, +pub const MakeFn = *const fn (self: *Step, prog_node: *std.Progress.Node) anyerror!void; + const n_debug_stack_frames = 4; pub const State = enum { @@ -72,7 +74,7 @@ pub const Id = enum { pub const Options = struct { id: Id, name: []const u8, - makeFn: *const fn (self: *Step) anyerror!void = makeNoOp, + makeFn: MakeFn = makeNoOp, first_ret_addr: ?usize = null, }; @@ -101,8 +103,8 @@ pub fn init(allocator: Allocator, options: Options) Step { /// If the Step's `make` function reports `error.MakeFailed`, it indicates they /// have already reported the error. Otherwise, we add a simple error report /// here. -pub fn make(s: *Step) error{MakeFailed}!void { - return s.makeFn(s) catch |err| { +pub fn make(s: *Step, prog_node: *std.Progress.Node) error{MakeFailed}!void { + return s.makeFn(s, prog_node) catch |err| { if (err != error.MakeFailed) { const gpa = s.dependencies.allocator; s.result_error_msgs.append(gpa, std.fmt.allocPrint(gpa, "{s} failed: {s}", .{ @@ -129,8 +131,9 @@ pub fn getStackTrace(s: *Step) std.builtin.StackTrace { }; } -fn makeNoOp(self: *Step) anyerror!void { +fn makeNoOp(self: *Step, prog_node: *std.Progress.Node) anyerror!void { _ = self; + _ = prog_node; } pub fn cast(step: *Step, comptime T: type) ?*T { diff --git a/lib/std/Build/TranslateCStep.zig b/lib/std/Build/TranslateCStep.zig index 8c3a254d6a..d19e598d93 100644 --- a/lib/std/Build/TranslateCStep.zig +++ b/lib/std/Build/TranslateCStep.zig @@ -88,7 +88,7 @@ pub fn defineCMacroRaw(self: *TranslateCStep, name_and_value: []const u8) void { self.c_macros.append(self.builder.dupe(name_and_value)) catch @panic("OOM"); } -fn make(step: *Step) !void { +fn make(step: *Step, prog_node: *std.Progress.Node) !void { const self = @fieldParentPtr(TranslateCStep, "step", step); var argv_list = std.ArrayList([]const u8).init(self.builder.allocator); @@ -120,7 +120,7 @@ fn make(step: *Step) !void { try argv_list.append(self.source.getPath(self.builder)); - const output_path_nl = try self.builder.execFromStep(argv_list.items, &self.step); + const output_path_nl = try self.builder.execFromStep(argv_list.items, &self.step, prog_node); const output_path = mem.trimRight(u8, output_path_nl, "\r\n"); self.out_basename = fs.path.basename(output_path); diff --git a/lib/std/Build/WriteFileStep.zig b/lib/std/Build/WriteFileStep.zig index 4f5e768237..62acd6e8ee 100644 --- a/lib/std/Build/WriteFileStep.zig +++ b/lib/std/Build/WriteFileStep.zig @@ -99,7 +99,8 @@ pub fn getFileSource(wf: *WriteFileStep, sub_path: []const u8) ?std.Build.FileSo return null; } -fn make(step: *Step) !void { +fn make(step: *Step, prog_node: *std.Progress.Node) !void { + _ = prog_node; const wf = @fieldParentPtr(WriteFileStep, "step", step); // Writing to source files is kind of an extra capability of this diff --git a/src/main.zig b/src/main.zig index 495cc40ed5..1d5cf57388 100644 --- a/src/main.zig +++ b/src/main.zig @@ -3573,7 +3573,21 @@ fn serve( if (comp.bin_file.options.output_mode == .Exe) { try comp.makeBinFileWritable(); } - try comp.update(main_progress_node); + + { + var reset: std.Thread.ResetEvent = .{}; + + var progress_thread = try std.Thread.spawn(.{}, progressThread, .{ + &progress, out, &reset, + }); + defer { + reset.set(); + progress_thread.join(); + } + + try comp.update(main_progress_node); + } + try comp.makeBinFileExecutable(); try serveUpdateResults(out, comp); }, @@ -3629,6 +3643,63 @@ fn serve( } } +fn progressThread(progress: *std.Progress, out: fs.File, reset: *std.Thread.ResetEvent) void { + while (true) { + if (reset.timedWait(500 * std.time.ns_per_ms)) |_| { + // The Compilation update has completed. + return; + } else |err| switch (err) { + error.Timeout => {}, + } + + var buf: std.BoundedArray(u8, 160) = .{}; + + { + progress.update_mutex.lock(); + defer progress.update_mutex.unlock(); + + var need_ellipse = false; + var maybe_node: ?*std.Progress.Node = &progress.root; + while (maybe_node) |node| { + if (need_ellipse) { + buf.appendSlice("... ") catch {}; + } + need_ellipse = false; + const eti = @atomicLoad(usize, &node.unprotected_estimated_total_items, .Monotonic); + const completed_items = @atomicLoad(usize, &node.unprotected_completed_items, .Monotonic); + const current_item = completed_items + 1; + if (node.name.len != 0 or eti > 0) { + if (node.name.len != 0) { + buf.appendSlice(node.name) catch {}; + need_ellipse = true; + } + if (eti > 0) { + if (need_ellipse) buf.appendSlice(" ") catch {}; + buf.writer().print("[{d}/{d}] ", .{ current_item, eti }) catch {}; + need_ellipse = false; + } else if (completed_items != 0) { + if (need_ellipse) buf.appendSlice(" ") catch {}; + buf.writer().print("[{d}] ", .{current_item}) catch {}; + need_ellipse = false; + } + } + maybe_node = @atomicLoad(?*std.Progress.Node, &node.recently_updated_child, .Acquire); + } + } + + const progress_string = buf.slice(); + + serveMessage(out, .{ + .tag = .progress, + .bytes_len = @intCast(u32, progress_string.len), + }, &.{ + progress_string, + }) catch |err| { + fatal("unable to write to client: {s}", .{@errorName(err)}); + }; + } +} + fn serveMessage( out: fs.File, header: std.zig.Server.Message.Header, diff --git a/test/tests.zig b/test/tests.zig index fe2efbc06e..fceaa173d1 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -875,7 +875,8 @@ pub const StackTracesContext = struct { return ptr; } - fn make(step: *Step) !void { + fn make(step: *Step, prog_node: *std.Progress.Node) !void { + _ = prog_node; const self = @fieldParentPtr(RunAndCompareStep, "step", step); const b = self.context.b; @@ -1218,7 +1219,8 @@ pub const GenHContext = struct { return ptr; } - fn make(step: *Step) !void { + fn make(step: *Step, prog_node: *std.Progress.Node) !void { + _ = prog_node; const self = @fieldParentPtr(GenHCmpOutputStep, "step", step); const b = self.context.b; From 8b2d872020bf8139404d0655e5ab70792cc67873 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 1 Mar 2023 00:34:30 -0700 Subject: [PATCH 170/294] fix std.Build.TranslateCStep --- lib/std/Build.zig | 2 +- lib/std/Build/TranslateCStep.zig | 4 +-- src/main.zig | 51 ++++++++++++++++++-------------- 3 files changed, 32 insertions(+), 25 deletions(-) diff --git a/lib/std/Build.zig b/lib/std/Build.zig index ca7ddb591c..05decec36f 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -1453,7 +1453,7 @@ pub fn execFromStep(b: *Build, argv: []const []const u8, s: *Step, prog_node: *s const header = @ptrCast(*align(1) const Header, buf[0..@sizeOf(Header)]); const header_and_msg_len = header.bytes_len + @sizeOf(Header); if (buf.len >= header_and_msg_len) { - const body = buf[@sizeOf(Header)..]; + const body = buf[@sizeOf(Header)..][0..header.bytes_len]; switch (header.tag) { .zig_version => { if (!mem.eql(u8, builtin.zig_version_string, body)) { diff --git a/lib/std/Build/TranslateCStep.zig b/lib/std/Build/TranslateCStep.zig index d19e598d93..fef644f03c 100644 --- a/lib/std/Build/TranslateCStep.zig +++ b/lib/std/Build/TranslateCStep.zig @@ -97,6 +97,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { try argv_list.append("-lc"); try argv_list.append("--enable-cache"); + try argv_list.append("--listen=-"); if (!self.target.isNative()) { try argv_list.append("-target"); @@ -120,8 +121,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { try argv_list.append(self.source.getPath(self.builder)); - const output_path_nl = try self.builder.execFromStep(argv_list.items, &self.step, prog_node); - const output_path = mem.trimRight(u8, output_path_nl, "\r\n"); + const output_path = try self.builder.execFromStep(argv_list.items, &self.step, prog_node); self.out_basename = fs.path.basename(output_path); const output_dir = fs.path.dirname(output_path).?; diff --git a/src/main.zig b/src/main.zig index 1d5cf57388..630f454272 100644 --- a/src/main.zig +++ b/src/main.zig @@ -3287,10 +3287,6 @@ fn buildOutputType( if (show_builtin) { return std.io.getStdOut().writeAll(try comp.generateBuiltinZigSource(arena)); } - if (arg_mode == .translate_c) { - return cmdTranslateC(comp, arena, have_enable_cache); - } - switch (listen) { .none => {}, .stdio => { @@ -3332,6 +3328,10 @@ fn buildOutputType( }, } + if (arg_mode == .translate_c) { + return cmdTranslateC(comp, arena, null); + } + const hook: AfterUpdateHook = blk: { if (!have_enable_cache) break :blk .none; @@ -3532,12 +3532,7 @@ fn serve( ) !void { const gpa = comp.gpa; - try serveMessage(out, .{ - .tag = .zig_version, - .bytes_len = build_options.version.len, - }, &.{ - build_options.version, - }); + try serveStringMessage(out, .zig_version, build_options.version); var child_pid: ?i32 = null; var receive_fifo = std.fifo.LinearFifo(u8, .Dynamic).init(gpa); @@ -3570,6 +3565,17 @@ fn serve( .update => { assert(main_progress_node.recently_updated_child == null); tracy.frameMark(); + + if (arg_mode == .translate_c) { + var arena_instance = std.heap.ArenaAllocator.init(gpa); + defer arena_instance.deinit(); + const arena = arena_instance.allocator(); + var output_path: []const u8 = undefined; + try cmdTranslateC(comp, arena, &output_path); + try serveStringMessage(out, .emit_bin_path, output_path); + continue; + } + if (comp.bin_file.options.output_mode == .Exe) { try comp.makeBinFileWritable(); } @@ -3746,16 +3752,17 @@ fn serveUpdateResults(out: fs.File, comp: *Compilation) !void { } else if (comp.bin_file.options.emit) |emit| { const full_path = try emit.directory.join(gpa, &.{emit.sub_path}); defer gpa.free(full_path); - - try serveMessage(out, .{ - .tag = .emit_bin_path, - .bytes_len = @intCast(u32, full_path.len), - }, &.{ - full_path, - }); + try serveStringMessage(out, .emit_bin_path, full_path); } } +fn serveStringMessage(out: fs.File, tag: std.zig.Server.Message.Tag, s: []const u8) !void { + try serveMessage(out, .{ + .tag = tag, + .bytes_len = @intCast(u32, s.len), + }, &.{s}); +} + fn receiveMessage(in: fs.File, fifo: *std.fifo.LinearFifo(u8, .Dynamic)) !std.zig.Client.Message.Header { const Header = std.zig.Client.Message.Header; @@ -4100,7 +4107,7 @@ fn updateModule(gpa: Allocator, comp: *Compilation, hook: AfterUpdateHook) !void } } -fn cmdTranslateC(comp: *Compilation, arena: Allocator, enable_cache: bool) !void { +fn cmdTranslateC(comp: *Compilation, arena: Allocator, output_path: ?*[]const u8) !void { if (!build_options.have_llvm) fatal("cannot translate-c: compiler built without LLVM extensions", .{}); @@ -4111,7 +4118,7 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, enable_cache: bool) !void var man: Cache.Manifest = comp.obtainCObjectCacheManifest(); man.want_shared_lock = false; - defer if (enable_cache) man.deinit(); + defer if (output_path != null) man.deinit(); man.hash.add(@as(u16, 0xb945)); // Random number to distinguish translate-c from compiling C objects Compilation.cache_helpers.hashCSource(&man, c_source_file) catch |err| { @@ -4169,6 +4176,7 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, enable_cache: bool) !void error.OutOfMemory => return error.OutOfMemory, error.ASTUnitFailure => fatal("clang API returned errors but due to a clang bug, it is not exposing the errors for zig to see. For more details: https://github.com/ziglang/zig/issues/4455", .{}), error.SemanticAnalyzeFail => { + // TODO convert these to zig errors for (clang_errors) |clang_err| { std.debug.print("{s}:{d}:{d}: {s}\n", .{ if (clang_err.filename_ptr) |p| p[0..clang_err.filename_len] else "(no file)", @@ -4213,12 +4221,11 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, enable_cache: bool) !void break :digest digest; }; - if (enable_cache) { + if (output_path) |out_path| { const full_zig_path = try comp.local_cache_directory.join(arena, &[_][]const u8{ "o", &digest, translated_zig_basename, }); - try io.getStdOut().writer().print("{s}\n", .{full_zig_path}); - return cleanExit(); + out_path.* = full_zig_path; } else { const out_zig_path = try fs.path.join(arena, &[_][]const u8{ "o", &digest, translated_zig_basename }); const zig_file = comp.local_cache_directory.handle.openFile(out_zig_path, .{}) catch |err| { From 01299864e2fc87aa597815743a4d8552e8a0129e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 1 Mar 2023 13:00:04 -0700 Subject: [PATCH 171/294] build runner: fix unicode tree printing --- lib/build_runner.zig | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/lib/build_runner.zig b/lib/build_runner.zig index ea4703bfbb..c2509d0d22 100644 --- a/lib/build_runner.zig +++ b/lib/build_runner.zig @@ -424,6 +424,17 @@ const PrintNode = struct { last: bool = false, }; +fn printPrefix(node: *PrintNode, stderr: std.fs.File) !void { + const parent = node.parent orelse return; + if (parent.parent == null) return; + try printPrefix(parent, stderr); + if (parent.last) { + try stderr.writeAll(" "); + } else { + try stderr.writeAll("│ "); + } +} + fn printTreeStep( b: *std.Build, s: *Step, @@ -431,15 +442,7 @@ fn printTreeStep( ttyconf: std.debug.TTY.Config, parent_node: *PrintNode, ) !void { - var opt_node: ?*PrintNode = parent_node.parent; - while (opt_node) |n| : (opt_node = n.parent) { - if (n.parent == null) break; - if (n.last) { - try stderr.writeAll(" "); - } else { - try stderr.writeAll("│ "); - } - } + try printPrefix(parent_node, stderr); if (parent_node.parent != null) { if (parent_node.last) { From e895d582143f74c9e3a2d06083017e74ad67b770 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 1 Mar 2023 13:08:34 -0700 Subject: [PATCH 172/294] build system: give RunStep a better default step name --- lib/std/Build.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/Build.zig b/lib/std/Build.zig index 05decec36f..3e765be4ba 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -618,7 +618,7 @@ pub fn addRunArtifact(b: *Build, exe: *CompileStep) *RunStep { // It doesn't have to be native. We catch that if you actually try to run it. // Consider that this is declarative; the run step may not be run unless a user // option is supplied. - const run_step = RunStep.create(b, b.fmt("run {s}", .{exe.step.name})); + const run_step = RunStep.create(b, b.fmt("run {s}", .{exe.name})); run_step.addArtifactArg(exe); if (exe.kind == .test_exe) { From a7754d219a60fdc15d3bff0d6d2ac7fee9f7c4db Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 1 Mar 2023 13:08:53 -0700 Subject: [PATCH 173/294] build system: better default name for ConfigHeaderStep --- lib/std/Build/ConfigHeaderStep.zig | 39 ++++++++++++++++++------------ 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/lib/std/Build/ConfigHeaderStep.zig b/lib/std/Build/ConfigHeaderStep.zig index c74c03718e..994b71da32 100644 --- a/lib/std/Build/ConfigHeaderStep.zig +++ b/lib/std/Build/ConfigHeaderStep.zig @@ -51,10 +51,30 @@ pub const Options = struct { pub fn create(builder: *std.Build, options: Options) *ConfigHeaderStep { const self = builder.allocator.create(ConfigHeaderStep) catch @panic("OOM"); + + var include_path: []const u8 = "config.h"; + + if (options.style.getFileSource()) |s| switch (s) { + .path => |p| { + const basename = std.fs.path.basename(p); + if (std.mem.endsWith(u8, basename, ".h.in")) { + include_path = basename[0 .. basename.len - 3]; + } + }, + else => {}, + }; + + if (options.include_path) |p| { + include_path = p; + } + const name = if (options.style.getFileSource()) |s| - builder.fmt("configure {s} header {s}", .{ @tagName(options.style), s.getDisplayName() }) + builder.fmt("configure {s} header {s} to {s}", .{ + @tagName(options.style), s.getDisplayName(), include_path, + }) else - builder.fmt("configure {s} header", .{@tagName(options.style)}); + builder.fmt("configure {s} header to {s}", .{@tagName(options.style), include_path}); + self.* = .{ .builder = builder, .step = Step.init(builder.allocator, .{ @@ -67,23 +87,10 @@ pub fn create(builder: *std.Build, options: Options) *ConfigHeaderStep { .values = std.StringArrayHashMap(Value).init(builder.allocator), .max_bytes = options.max_bytes, - .include_path = "config.h", + .include_path = include_path, .output_file = .{ .step = &self.step }, }; - if (options.style.getFileSource()) |s| switch (s) { - .path => |p| { - const basename = std.fs.path.basename(p); - if (std.mem.endsWith(u8, basename, ".h.in")) { - self.include_path = basename[0 .. basename.len - 3]; - } - }, - else => {}, - }; - - if (options.include_path) |include_path| { - self.include_path = include_path; - } return self; } From b5baa41077dce5ef61667e76d80835c0792a2fc4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 1 Mar 2023 13:31:02 -0700 Subject: [PATCH 174/294] fix zig fmt crash --- src/main.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.zig b/src/main.zig index 630f454272..fcdb52c342 100644 --- a/src/main.zig +++ b/src/main.zig @@ -5120,8 +5120,8 @@ fn fmtPathFile( var tree = try Ast.parse(gpa, source_code, .zig); defer tree.deinit(gpa); - try printAstErrorsToStderr(gpa, tree, file_path, fmt.color); if (tree.errors.len != 0) { + try printAstErrorsToStderr(gpa, tree, file_path, fmt.color); fmt.any_error = true; return; } From 1bcf674a4390ee238bc63477d3db545dbf7f66dd Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 1 Mar 2023 13:44:05 -0700 Subject: [PATCH 175/294] build runner: make step_stack a map to remove redundant steps This prevents compilation errors from being emitted twice. --- lib/build_runner.zig | 52 +++++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/lib/build_runner.zig b/lib/build_runner.zig index c2509d0d22..35aa487484 100644 --- a/lib/build_runner.zig +++ b/lib/build_runner.zig @@ -22,10 +22,9 @@ pub fn main() !void { var thread_safe_arena: std.heap.ThreadSafeAllocator = .{ .child_allocator = single_threaded_arena.allocator(), }; - const allocator = thread_safe_arena.allocator(); + const arena = thread_safe_arena.allocator(); - var args = try process.argsAlloc(allocator); - defer process.argsFree(allocator, args); + var args = try process.argsAlloc(arena); // skip my own exe name var arg_idx: usize = 1; @@ -65,7 +64,7 @@ pub fn main() !void { }; var cache: std.Build.Cache = .{ - .gpa = allocator, + .gpa = arena, .manifest_dir = try local_cache_directory.handle.makeOpenPath("h", .{}), }; cache.addPrefix(.{ .path = null, .handle = std.fs.cwd() }); @@ -75,7 +74,7 @@ pub fn main() !void { cache.hash.addBytes(builtin.zig_version_string); const builder = try std.Build.create( - allocator, + arena, zig_exe, build_root_directory, local_cache_directory, @@ -85,9 +84,9 @@ pub fn main() !void { ); defer builder.destroy(); - var targets = ArrayList([]const u8).init(allocator); - var debug_log_scopes = ArrayList([]const u8).init(allocator); - var thread_pool_options: std.Thread.Pool.Options = .{ .allocator = allocator }; + var targets = ArrayList([]const u8).init(arena); + var debug_log_scopes = ArrayList([]const u8).init(arena); + var thread_pool_options: std.Thread.Pool.Options = .{ .allocator = arena }; const stderr_stream = io.getStdErr().writer(); const stdout_stream = io.getStdOut().writer(); @@ -274,6 +273,7 @@ pub fn main() !void { usageAndErr(builder, true, stderr_stream); runStepNames( + arena, builder, targets.items, main_progress_node, @@ -286,30 +286,32 @@ pub fn main() !void { } fn runStepNames( + arena: std.mem.Allocator, b: *std.Build, step_names: []const []const u8, parent_prog_node: *std.Progress.Node, thread_pool_options: std.Thread.Pool.Options, ttyconf: std.debug.TTY.Config, ) !void { - var step_stack = ArrayList(*Step).init(b.allocator); - defer step_stack.deinit(); + const gpa = b.allocator; + var step_stack: std.AutoArrayHashMapUnmanaged(*Step, void) = .{}; + defer step_stack.deinit(gpa); if (step_names.len == 0) { - try step_stack.append(b.default_step); + try step_stack.put(gpa, b.default_step, {}); } else { - try step_stack.resize(step_names.len); - - for (step_names, 0..) |step_name, i| { + try step_stack.ensureUnusedCapacity(gpa, step_names.len); + for (0..step_names.len) |i| { + const step_name = step_names[step_names.len - i - 1]; const s = b.top_level_steps.get(step_name) orelse { std.debug.print("no step named '{s}'. Access the help menu with 'zig build -h'\n", .{step_name}); process.exit(1); }; - step_stack.items[step_names.len - i - 1] = &s.step; + step_stack.putAssumeCapacity(&s.step, {}); } } - const starting_steps = try b.allocator.dupe(*Step, step_stack.items); + const starting_steps = try arena.dupe(*Step, step_stack.keys()); for (starting_steps) |s| { checkForDependencyLoop(b, s, &step_stack) catch |err| switch (err) { error.DependencyLoopDetected => return error.UncleanExit, @@ -324,7 +326,7 @@ fn runStepNames( { defer parent_prog_node.end(); - var step_prog = parent_prog_node.start("run steps", step_stack.items.len); + var step_prog = parent_prog_node.start("run steps", step_stack.count()); defer step_prog.end(); var wait_group: std.Thread.WaitGroup = .{}; @@ -333,10 +335,9 @@ fn runStepNames( // Here we spawn the initial set of tasks with a nice heuristic - // dependency order. Each worker when it finishes a step will then // check whether it should run any dependants. - var i = step_stack.items.len; - while (i > 0) { - i -= 1; - const step = step_stack.items[i]; + const steps_slice = step_stack.keys(); + for (0..steps_slice.len) |i| { + const step = steps_slice[steps_slice.len - i - 1]; wait_group.start(); thread_pool.spawn(workerMakeOneStep, .{ @@ -350,7 +351,7 @@ fn runStepNames( var pending_count: usize = 0; var total_compile_errors: usize = 0; - for (step_stack.items) |s| { + for (step_stack.keys()) |s| { switch (s.state) { .precheck_unstarted => unreachable, .precheck_started => unreachable, @@ -404,7 +405,7 @@ fn runStepNames( // Finally, render compile errors at the bottom of the terminal. if (total_compile_errors > 0) { - for (step_stack.items) |s| { + for (step_stack.keys()) |s| { if (s.result_error_bundle.errorMessageCount() > 0) { s.result_error_bundle.renderToStdErr(ttyconf); } @@ -498,7 +499,7 @@ fn printTreeStep( fn checkForDependencyLoop( b: *std.Build, s: *Step, - step_stack: *ArrayList(*Step), + step_stack: *std.AutoArrayHashMapUnmanaged(*Step, void), ) !void { switch (s.state) { .precheck_started => { @@ -508,8 +509,9 @@ fn checkForDependencyLoop( .precheck_unstarted => { s.state = .precheck_started; + try step_stack.ensureUnusedCapacity(b.allocator, s.dependencies.items.len); for (s.dependencies.items) |dep| { - try step_stack.append(dep); + try step_stack.put(b.allocator, dep, {}); try dep.dependants.append(b.allocator, s); checkForDependencyLoop(b, dep, step_stack) catch |err| { if (err == error.DependencyLoopDetected) { From 8c250828a2dbcf2428e06fbc9b2d34b17f42ed66 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 1 Mar 2023 14:34:11 -0700 Subject: [PATCH 176/294] std.Build.Step: avoid redundancy in default error message --- lib/std/Build/Step.zig | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig index 53861683ac..29cf38b55b 100644 --- a/lib/std/Build/Step.zig +++ b/lib/std/Build/Step.zig @@ -107,9 +107,7 @@ pub fn make(s: *Step, prog_node: *std.Progress.Node) error{MakeFailed}!void { return s.makeFn(s, prog_node) catch |err| { if (err != error.MakeFailed) { const gpa = s.dependencies.allocator; - s.result_error_msgs.append(gpa, std.fmt.allocPrint(gpa, "{s} failed: {s}", .{ - s.name, @errorName(err), - }) catch @panic("OOM")) catch @panic("OOM"); + s.result_error_msgs.append(gpa, @errorName(err)) catch @panic("OOM"); } return error.MakeFailed; }; From b4997d08902ed312d7504765014c34f4ea862534 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 1 Mar 2023 14:34:40 -0700 Subject: [PATCH 177/294] std.Build.RunStep: better default step name Now it renames itself when an output argument is added. --- lib/std/Build/RunStep.zig | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lib/std/Build/RunStep.zig b/lib/std/Build/RunStep.zig index 7ec60b6d22..84fd7f975d 100644 --- a/lib/std/Build/RunStep.zig +++ b/lib/std/Build/RunStep.zig @@ -48,6 +48,11 @@ condition: enum { output_outdated, always } = .output_outdated, /// that the RunStep should be re-executed. extra_file_dependencies: []const []const u8 = &.{}, +/// After adding an output argument, this step will by default rename itself +/// for a better display name in the build summary. +/// This can be disabled by setting this to false. +rename_step_with_output_arg: bool, + pub const StdIoAction = union(enum) { inherit, ignore, @@ -80,6 +85,7 @@ pub fn create(builder: *std.Build, name: []const u8) *RunStep { .cwd = null, .env_map = null, .print = builder.verbose, + .rename_step_with_output_arg = true, }; return self; } @@ -100,6 +106,11 @@ pub fn addOutputFileArg(rs: *RunStep, basename: []const u8) std.Build.FileSource .basename = rs.builder.dupe(basename), } }) catch @panic("OOM"); + if (rs.rename_step_with_output_arg) { + rs.rename_step_with_output_arg = false; + rs.step.name = rs.builder.fmt("{s} ({s})", .{ rs.step.name, basename }); + } + return .{ .generated = generated_file }; } @@ -207,7 +218,11 @@ fn needOutputCheck(self: RunStep) bool { } fn make(step: *Step, prog_node: *std.Progress.Node) !void { + // Unfortunately we have no way to collect progress from arbitrary programs. + // Perhaps in the future Zig could offer some kind of opt-in IPC mechanism that + // processes could use to supply progress updates. _ = prog_node; + const self = @fieldParentPtr(RunStep, "step", step); const need_output_check = self.needOutputCheck(); From 533c7b56f2624f1df6684a834f5780a38052bb00 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 1 Mar 2023 14:40:14 -0700 Subject: [PATCH 178/294] build runner: hide repeated steps in the build summary --- lib/build_runner.zig | 90 ++++++++++++++++++++++++++------------------ 1 file changed, 53 insertions(+), 37 deletions(-) diff --git a/lib/build_runner.zig b/lib/build_runner.zig index 35aa487484..b78ef075ab 100644 --- a/lib/build_runner.zig +++ b/lib/build_runner.zig @@ -350,6 +350,8 @@ fn runStepNames( var failure_count: usize = 0; var pending_count: usize = 0; var total_compile_errors: usize = 0; + var compile_error_steps: std.ArrayListUnmanaged(*Step) = .{}; + defer compile_error_steps.deinit(gpa); for (step_stack.keys()) |s| { switch (s.state) { @@ -369,7 +371,11 @@ fn runStepNames( .success => success_count += 1, .failure => { failure_count += 1; - total_compile_errors += s.result_error_bundle.errorMessageCount(); + const compile_errors_len = s.result_error_bundle.errorMessageCount(); + if (compile_errors_len > 0) { + total_compile_errors += compile_errors_len; + try compile_error_steps.append(gpa, s); + } }, } } @@ -392,20 +398,22 @@ fn runStepNames( var print_node: PrintNode = .{ .parent = null }; if (step_names.len == 0) { print_node.last = true; - printTreeStep(b, b.default_step, stderr, ttyconf, &print_node) catch {}; + printTreeStep(b, b.default_step, stderr, ttyconf, &print_node, &step_stack) catch {}; } else { for (step_names, 0..) |step_name, i| { const tls = b.top_level_steps.get(step_name).?; print_node.last = i + 1 == b.top_level_steps.count(); - printTreeStep(b, &tls.step, stderr, ttyconf, &print_node) catch {}; + printTreeStep(b, &tls.step, stderr, ttyconf, &print_node, &step_stack) catch {}; } } if (failure_count == 0) return cleanExit(); // Finally, render compile errors at the bottom of the terminal. + // We use a separate compile_error_steps array list because step_stack is destructively + // mutated in printTreeStep above. if (total_compile_errors > 0) { - for (step_stack.keys()) |s| { + for (compile_error_steps.items) |s| { if (s.result_error_bundle.errorMessageCount() > 0) { s.result_error_bundle.renderToStdErr(ttyconf); } @@ -442,7 +450,10 @@ fn printTreeStep( stderr: std.fs.File, ttyconf: std.debug.TTY.Config, parent_node: *PrintNode, + step_stack: *std.AutoArrayHashMapUnmanaged(*Step, void), ) !void { + const first = step_stack.swapRemove(s); + if (!first) try ttyconf.setColor(stderr, .Dim); try printPrefix(parent_node, stderr); if (parent_node.parent != null) { @@ -456,43 +467,48 @@ fn printTreeStep( // TODO print the dep prefix too? try stderr.writeAll(s.name); - switch (s.state) { - .precheck_unstarted => unreachable, - .precheck_started => unreachable, - .precheck_done => unreachable, - .running => unreachable, + if (first) { + switch (s.state) { + .precheck_unstarted => unreachable, + .precheck_started => unreachable, + .precheck_done => unreachable, + .running => unreachable, - .dependency_failure => { - try ttyconf.setColor(stderr, .Dim); - try stderr.writeAll(" transitive failure\n"); - try ttyconf.setColor(stderr, .Reset); - }, + .dependency_failure => { + try ttyconf.setColor(stderr, .Dim); + try stderr.writeAll(" transitive failure\n"); + try ttyconf.setColor(stderr, .Reset); + }, - .success => { - try ttyconf.setColor(stderr, .Green); - try stderr.writeAll(" success\n"); - try ttyconf.setColor(stderr, .Reset); - }, + .success => { + try ttyconf.setColor(stderr, .Green); + try stderr.writeAll(" success\n"); + try ttyconf.setColor(stderr, .Reset); + }, - .failure => { - try ttyconf.setColor(stderr, .Red); - if (s.result_error_bundle.errorMessageCount() > 0) { - try stderr.writer().print(" {d} errors\n", .{ - s.result_error_bundle.errorMessageCount(), - }); - } else { - try stderr.writeAll(" failure\n"); - } - try ttyconf.setColor(stderr, .Reset); - }, - } + .failure => { + try ttyconf.setColor(stderr, .Red); + if (s.result_error_bundle.errorMessageCount() > 0) { + try stderr.writer().print(" {d} errors\n", .{ + s.result_error_bundle.errorMessageCount(), + }); + } else { + try stderr.writeAll(" failure\n"); + } + try ttyconf.setColor(stderr, .Reset); + }, + } - for (s.dependencies.items, 0..) |dep, i| { - var print_node: PrintNode = .{ - .parent = parent_node, - .last = i == s.dependencies.items.len - 1, - }; - try printTreeStep(b, dep, stderr, ttyconf, &print_node); + for (s.dependencies.items, 0..) |dep, i| { + var print_node: PrintNode = .{ + .parent = parent_node, + .last = i == s.dependencies.items.len - 1, + }; + try printTreeStep(b, dep, stderr, ttyconf, &print_node, step_stack); + } + } else { + try stderr.writer().print(" ({d} repeated dependencies)\n", .{s.dependencies.items.len}); + try ttyconf.setColor(stderr, .Reset); } } From b465dc12349dd24fe060e69ad6eb290ddbb3739e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 1 Mar 2023 15:13:26 -0700 Subject: [PATCH 179/294] build runner: slight rewording in build summary --- lib/build_runner.zig | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/build_runner.zig b/lib/build_runner.zig index b78ef075ab..2c5bcdd617 100644 --- a/lib/build_runner.zig +++ b/lib/build_runner.zig @@ -507,7 +507,13 @@ fn printTreeStep( try printTreeStep(b, dep, stderr, ttyconf, &print_node, step_stack); } } else { - try stderr.writer().print(" ({d} repeated dependencies)\n", .{s.dependencies.items.len}); + if (s.dependencies.items.len == 0) { + try stderr.writeAll(" (reused)\n"); + } else { + try stderr.writer().print(" (+{d} more reused dependencies)\n", .{ + s.dependencies.items.len, + }); + } try ttyconf.setColor(stderr, .Reset); } } From d0f675827c28b1d50e8aea6a7d29cb45ad8d4e67 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 1 Mar 2023 22:55:47 -0700 Subject: [PATCH 180/294] link: only write manifest if we have the exclusive lock Fixes assertion tripping when racing multiple processes that will create the same static archive. --- src/link.zig | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/link.zig b/src/link.zig index a2f40eed65..96931dd79e 100644 --- a/src/link.zig +++ b/src/link.zig @@ -1078,9 +1078,11 @@ pub const File = struct { log.warn("failed to save archive hash digest file: {s}", .{@errorName(err)}); }; - man.writeManifest() catch |err| { - log.warn("failed to write cache manifest when archiving: {s}", .{@errorName(err)}); - }; + if (man.have_exclusive_lock) { + man.writeManifest() catch |err| { + log.warn("failed to write cache manifest when archiving: {s}", .{@errorName(err)}); + }; + } base.lock = man.toOwnedLock(); } From 58edefc6d1716c0731ee2fe672ec8d073651aafb Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 1 Mar 2023 22:56:37 -0700 Subject: [PATCH 181/294] zig build: many enhancements related to parallel building Rework std.Build.Step to have an `owner: *Build` field. This simplified the implementation of installation steps, as well as provided some much-needed common API for the new parallelized build system. --verbose is now defined very concretely: it prints to stderr just before spawning a child process. Child process execution is updated to conform to the new parallel-friendly make() function semantics. DRY up the failWithCacheError handling code. It now integrates properly with the step graph instead of incorrectly dumping to stderr and calling process exit. In the main CLI, fix `zig fmt` crash when there are no errors and stdin is used. Deleted steps: * EmulatableRunStep - this entire thing can be removed in favor of a flag added to std.Build.RunStep called `skip_foreign_checks`. * LogStep - this doesn't really fit with a multi-threaded build runner and is effectively superseded by the new build summary output. build runner: * add -fsummary and -fno-summary to override the default behavior, which is to print a summary if any of the build steps fail. * print the dep prefix when emitting error messages for steps. std.Build.FmtStep: * This step now supports exclude paths as well as a check flag. * The check flag decides between two modes, modify mode, and check mode. These can be used to update source files in place, or to fail the build, respectively. Zig's own build.zig: * The `test-fmt` step will do all the `zig fmt` checking that we expect to be done. Since the `test` step depends on this one, we can simply remove the explicit call to `zig fmt` in the CI. * The new `fmt` step will actually perform `zig fmt` and update source files in place. std.Build.RunStep: * expose max_stdio_size is a field (previously an unchangeable hard-coded value). * rework the API. Instead of configuring each stream independently, there is a `stdio` field where you can choose between `infer_from_args`, `inherit`, or `check`. These determine whether the RunStep is considered to have side-effects or not. The previous field, `condition` is gone. * when stdio mode is set to `check` there is a slice of any number of checks to make, which include things like exit code, stderr matching, or stdout matching. * remove the ill-defined `print` field. * when adding an output arg, it takes the opportunity to give itself a better name. * The flag `skip_foreign_checks` is added. If this is true, a RunStep which is configured to check the output of the executed binary will not fail the build if the binary cannot be executed due to being for a foreign binary to the host system which is running the build graph. Command-line arguments such as -fqemu and -fwasmtime may affect whether a binary is detected as foreign, as well as system configuration such as Rosetta (macOS) and binfmt_misc (Linux). - This makes EmulatableRunStep no longer needed. * Fix the child process handling to properly integrate with the new bulid API and to avoid deadlocks in stdout/stderr streams by polling if necessary. std.Build.RemoveDirStep now uses the open build_root directory handle instead of an absolute path. --- build.zig | 24 +- lib/build_runner.zig | 67 ++-- lib/std/Build.zig | 226 +----------- lib/std/Build/CheckFileStep.zig | 18 +- lib/std/Build/CheckObjectStep.zig | 37 +- lib/std/Build/CompileStep.zig | 456 ++++++++++++----------- lib/std/Build/ConfigHeaderStep.zig | 27 +- lib/std/Build/EmulatableRunStep.zig | 218 ----------- lib/std/Build/FmtStep.zig | 78 ++-- lib/std/Build/InstallArtifactStep.zig | 49 +-- lib/std/Build/InstallDirStep.zig | 34 +- lib/std/Build/InstallFileStep.zig | 29 +- lib/std/Build/LogStep.zig | 28 -- lib/std/Build/ObjCopyStep.zig | 33 +- lib/std/Build/OptionsStep.zig | 34 +- lib/std/Build/RemoveDirStep.zig | 29 +- lib/std/Build/RunStep.zig | 509 +++++++++++++++----------- lib/std/Build/Step.zig | 241 +++++++++++- lib/std/Build/TranslateCStep.zig | 40 +- lib/std/Build/WriteFileStep.zig | 63 ++-- src/main.zig | 12 +- test/src/compare_output.zig | 4 +- test/tests.zig | 16 +- 23 files changed, 1109 insertions(+), 1163 deletions(-) delete mode 100644 lib/std/Build/EmulatableRunStep.zig delete mode 100644 lib/std/Build/LogStep.zig diff --git a/build.zig b/build.zig index 189f50407d..5f7e214d35 100644 --- a/build.zig +++ b/build.zig @@ -61,8 +61,6 @@ pub fn build(b: *std.Build) !void { test_cases.stack_size = stack_size; test_cases.single_threaded = single_threaded; - const fmt_build_zig = b.addFmt(&[_][]const u8{"build.zig"}); - const skip_debug = b.option(bool, "skip-debug", "Main test suite skips debug builds") orelse false; const skip_release = b.option(bool, "skip-release", "Main test suite skips release builds") orelse false; const skip_release_small = b.option(bool, "skip-release-small", "Main test suite skips release-small builds") orelse skip_release; @@ -386,10 +384,24 @@ pub fn build(b: *std.Build) !void { } const optimization_modes = chosen_opt_modes_buf[0..chosen_mode_index]; - // run stage1 `zig fmt` on this build.zig file just to make sure it works - test_step.dependOn(&fmt_build_zig.step); - const fmt_step = b.step("test-fmt", "Run zig fmt against build.zig to make sure it works"); - fmt_step.dependOn(&fmt_build_zig.step); + const fmt_include_paths = &.{ "doc", "lib", "src", "test", "tools", "build.zig" }; + const fmt_exclude_paths = &.{ "test/cases" }; + const check_fmt = b.addFmt(.{ + .paths = fmt_include_paths, + .exclude_paths = fmt_exclude_paths, + .check = true, + }); + const do_fmt = b.addFmt(.{ + .paths = fmt_include_paths, + .exclude_paths = fmt_exclude_paths, + }); + + const test_fmt_step = b.step("test-fmt", "Check whether source files have conforming formatting"); + test_fmt_step.dependOn(&check_fmt.step); + + const do_fmt_step = b.step("fmt", "Modify source files in place to have conforming formatting"); + do_fmt_step.dependOn(&do_fmt.step); + test_step.dependOn(tests.addPkgTests( b, diff --git a/lib/build_runner.zig b/lib/build_runner.zig index 2c5bcdd617..40f45d9ac8 100644 --- a/lib/build_runner.zig +++ b/lib/build_runner.zig @@ -93,6 +93,7 @@ pub fn main() !void { var install_prefix: ?[]const u8 = null; var dir_list = std.Build.DirList{}; + var enable_summary: ?bool = null; const Color = enum { auto, off, on }; var color: Color = .auto; @@ -217,6 +218,10 @@ pub fn main() !void { builder.enable_darling = true; } else if (mem.eql(u8, arg, "-fno-darling")) { builder.enable_darling = false; + } else if (mem.eql(u8, arg, "-fsummary")) { + enable_summary = true; + } else if (mem.eql(u8, arg, "-fno-summary")) { + enable_summary = false; } else if (mem.eql(u8, arg, "-freference-trace")) { builder.reference_trace = 256; } else if (mem.startsWith(u8, arg, "-freference-trace=")) { @@ -252,8 +257,9 @@ pub fn main() !void { } } + const stderr = std.io.getStdErr(); const ttyconf: std.debug.TTY.Config = switch (color) { - .auto => std.debug.detectTTYConfig(std.io.getStdErr()), + .auto => std.debug.detectTTYConfig(stderr), .on => .escape_codes, .off => .no_color, }; @@ -279,6 +285,8 @@ pub fn main() !void { main_progress_node, thread_pool_options, ttyconf, + stderr, + enable_summary, ) catch |err| switch (err) { error.UncleanExit => process.exit(1), else => return err, @@ -292,6 +300,8 @@ fn runStepNames( parent_prog_node: *std.Progress.Node, thread_pool_options: std.Thread.Pool.Options, ttyconf: std.debug.TTY.Config, + stderr: std.fs.File, + enable_summary: ?bool, ) !void { const gpa = b.allocator; var step_stack: std.AutoArrayHashMapUnmanaged(*Step, void) = .{}; @@ -382,28 +392,35 @@ fn runStepNames( // A proper command line application defaults to silently succeeding. // The user may request verbose mode if they have a different preference. - if (failure_count == 0 and !b.verbose) return cleanExit(); + if (failure_count == 0 and enable_summary != true) return cleanExit(); - const stderr = std.io.getStdErr(); + if (enable_summary != false) { + const total_count = success_count + failure_count + pending_count; + ttyconf.setColor(stderr, .Cyan) catch {}; + stderr.writeAll("Build Summary:") catch {}; + ttyconf.setColor(stderr, .Reset) catch {}; + stderr.writer().print(" {d}/{d} steps succeeded; {d} failed", .{ + success_count, total_count, failure_count, + }) catch {}; - const total_count = success_count + failure_count + pending_count; - ttyconf.setColor(stderr, .Cyan) catch {}; - stderr.writeAll("Build Summary: ") catch {}; - ttyconf.setColor(stderr, .Reset) catch {}; - stderr.writer().print("{d}/{d} steps succeeded; {d} failed; {d} total compile errors\n", .{ - success_count, total_count, failure_count, total_compile_errors, - }) catch {}; + if (enable_summary == null) { + ttyconf.setColor(stderr, .Dim) catch {}; + stderr.writeAll(" (disable with -fno-summary)") catch {}; + ttyconf.setColor(stderr, .Reset) catch {}; + } + stderr.writeAll("\n") catch {}; - // Print a fancy tree with build results. - var print_node: PrintNode = .{ .parent = null }; - if (step_names.len == 0) { - print_node.last = true; - printTreeStep(b, b.default_step, stderr, ttyconf, &print_node, &step_stack) catch {}; - } else { - for (step_names, 0..) |step_name, i| { - const tls = b.top_level_steps.get(step_name).?; - print_node.last = i + 1 == b.top_level_steps.count(); - printTreeStep(b, &tls.step, stderr, ttyconf, &print_node, &step_stack) catch {}; + // Print a fancy tree with build results. + var print_node: PrintNode = .{ .parent = null }; + if (step_names.len == 0) { + print_node.last = true; + printTreeStep(b, b.default_step, stderr, ttyconf, &print_node, &step_stack) catch {}; + } else { + for (step_names, 0..) |step_name, i| { + const tls = b.top_level_steps.get(step_name).?; + print_node.last = i + 1 == b.top_level_steps.count(); + printTreeStep(b, &tls.step, stderr, ttyconf, &print_node, &step_stack) catch {}; + } } } @@ -453,9 +470,9 @@ fn printTreeStep( step_stack: *std.AutoArrayHashMapUnmanaged(*Step, void), ) !void { const first = step_stack.swapRemove(s); - if (!first) try ttyconf.setColor(stderr, .Dim); try printPrefix(parent_node, stderr); + if (!first) try ttyconf.setColor(stderr, .Dim); if (parent_node.parent != null) { if (parent_node.last) { try stderr.writeAll("└─ "); @@ -464,7 +481,7 @@ fn printTreeStep( } } - // TODO print the dep prefix too? + // dep_prefix omitted here because it is redundant with the tree. try stderr.writeAll(s.name); if (first) { @@ -608,8 +625,10 @@ fn workerMakeOneStep( const stderr = std.io.getStdErr(); for (s.result_error_msgs.items) |msg| { - // TODO print the dep prefix too + // Sometimes it feels like you just can't catch a break. Finally, + // with Zig, you can. ttyconf.setColor(stderr, .Bold) catch break; + stderr.writeAll(s.owner.dep_prefix) catch break; stderr.writeAll(s.name) catch break; stderr.writeAll(": ") catch break; ttyconf.setColor(stderr, .Red) catch break; @@ -735,6 +754,8 @@ fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi \\Advanced Options: \\ -freference-trace[=num] How many lines of reference trace should be shown per compile error \\ -fno-reference-trace Disable reference trace + \\ -fsummary Print the build summary, even on success + \\ -fno-summary Omit the build summary, even on failure \\ --build-file [file] Override path to build.zig \\ --cache-dir [path] Override path to local Zig cache directory \\ --global-cache-dir [path] Override path to global Zig cache directory diff --git a/lib/std/Build.zig b/lib/std/Build.zig index 3e765be4ba..1d87ea961f 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -32,14 +32,12 @@ pub const Step = @import("Build/Step.zig"); pub const CheckFileStep = @import("Build/CheckFileStep.zig"); pub const CheckObjectStep = @import("Build/CheckObjectStep.zig"); pub const ConfigHeaderStep = @import("Build/ConfigHeaderStep.zig"); -pub const EmulatableRunStep = @import("Build/EmulatableRunStep.zig"); pub const FmtStep = @import("Build/FmtStep.zig"); pub const InstallArtifactStep = @import("Build/InstallArtifactStep.zig"); pub const InstallDirStep = @import("Build/InstallDirStep.zig"); pub const InstallFileStep = @import("Build/InstallFileStep.zig"); pub const ObjCopyStep = @import("Build/ObjCopyStep.zig"); pub const CompileStep = @import("Build/CompileStep.zig"); -pub const LogStep = @import("Build/LogStep.zig"); pub const OptionsStep = @import("Build/OptionsStep.zig"); pub const RemoveDirStep = @import("Build/RemoveDirStep.zig"); pub const RunStep = @import("Build/RunStep.zig"); @@ -195,7 +193,7 @@ pub fn create( env_map.* = try process.getEnvMap(allocator); const self = try allocator.create(Build); - self.* = Build{ + self.* = .{ .zig_exe = zig_exe, .build_root = build_root, .cache_root = cache_root, @@ -224,16 +222,18 @@ pub fn create( .dest_dir = env_map.get("DESTDIR"), .installed_files = ArrayList(InstalledFile).init(allocator), .install_tls = .{ - .step = Step.init(allocator, .{ + .step = Step.init(.{ .id = .top_level, .name = "install", + .owner = self, }), .description = "Copy build artifacts to prefix path", }, .uninstall_tls = .{ - .step = Step.init(allocator, .{ + .step = Step.init(.{ .id = .top_level, .name = "uninstall", + .owner = self, .makeFn = makeUninstall, }), .description = "Remove build artifacts from prefix path", @@ -267,16 +267,18 @@ fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: Cache.Direc child.* = .{ .allocator = allocator, .install_tls = .{ - .step = Step.init(allocator, .{ + .step = Step.init(.{ .id = .top_level, .name = "install", + .owner = child, }), .description = "Copy build artifacts to prefix path", }, .uninstall_tls = .{ - .step = Step.init(allocator, .{ + .step = Step.init(.{ .id = .top_level, .name = "uninstall", + .owner = child, .makeFn = makeUninstall, }), .description = "Remove build artifacts from prefix path", @@ -689,21 +691,14 @@ pub fn addWriteFiles(self: *Build) *WriteFileStep { return write_file_step; } -pub fn addLog(self: *Build, comptime format: []const u8, args: anytype) *LogStep { - const data = self.fmt(format, args); - const log_step = self.allocator.create(LogStep) catch @panic("OOM"); - log_step.* = LogStep.init(self, data); - return log_step; -} - pub fn addRemoveDirTree(self: *Build, dir_path: []const u8) *RemoveDirStep { const remove_dir_step = self.allocator.create(RemoveDirStep) catch @panic("OOM"); remove_dir_step.* = RemoveDirStep.init(self, dir_path); return remove_dir_step; } -pub fn addFmt(self: *Build, paths: []const []const u8) *FmtStep { - return FmtStep.create(self, paths); +pub fn addFmt(b: *Build, options: FmtStep.Options) *FmtStep { + return FmtStep.create(b, options); } pub fn addTranslateC(self: *Build, options: TranslateCStep.Options) *TranslateCStep { @@ -870,10 +865,11 @@ pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_ pub fn step(self: *Build, name: []const u8, description: []const u8) *Step { const step_info = self.allocator.create(TopLevelStep) catch @panic("OOM"); - step_info.* = TopLevelStep{ - .step = Step.init(self.allocator, .{ + step_info.* = .{ + .step = Step.init(.{ .id = .top_level, .name = name, + .owner = self, }), .description = self.dupe(description), }; @@ -1145,10 +1141,6 @@ pub fn validateUserInputDidItFail(self: *Build) bool { return self.invalid_user_input; } -pub fn spawnChild(self: *Build, argv: []const []const u8) !void { - return self.spawnChildEnvMap(null, self.env_map, argv); -} - fn allocPrintCmd(ally: Allocator, opt_cwd: ?[]const u8, argv: []const []const u8) ![]u8 { var buf = ArrayList(u8).init(ally); if (opt_cwd) |cwd| try buf.writer().print("cd {s} && ", .{cwd}); @@ -1163,40 +1155,6 @@ fn printCmd(ally: Allocator, cwd: ?[]const u8, argv: []const []const u8) void { std.debug.print("{s}\n", .{text}); } -pub fn spawnChildEnvMap(self: *Build, cwd: ?[]const u8, env_map: *const EnvMap, argv: []const []const u8) !void { - if (self.verbose) { - printCmd(self.allocator, cwd, argv); - } - - if (!process.can_spawn) - return error.ExecNotSupported; - - var child = std.ChildProcess.init(argv, self.allocator); - child.cwd = cwd; - child.env_map = env_map; - - const term = child.spawnAndWait() catch |err| { - log.err("Unable to spawn {s}: {s}", .{ argv[0], @errorName(err) }); - return err; - }; - - switch (term) { - .Exited => |code| { - if (code != 0) { - log.err("The following command exited with error code {}:", .{code}); - printCmd(self.allocator, cwd, argv); - return error.UncleanExit; - } - }, - else => { - log.err("The following command terminated unexpectedly:", .{}); - printCmd(self.allocator, cwd, argv); - - return error.UncleanExit; - }, - } -} - pub fn installArtifact(self: *Build, artifact: *CompileStep) void { self.getInstallStep().dependOn(&self.addInstallArtifact(artifact).step); } @@ -1403,160 +1361,6 @@ pub fn execAllowFail( } } -/// This function is used exclusively for spawning and communicating with the zig compiler. -/// TODO: move to build_runner.zig -pub fn execFromStep(b: *Build, argv: []const []const u8, s: *Step, prog_node: *std.Progress.Node) ![]const u8 { - assert(argv.len != 0); - - if (b.verbose) { - const text = try allocPrintCmd(b.allocator, null, argv); - try s.result_error_msgs.append(b.allocator, text); - } - - if (!process.can_spawn) { - try s.result_error_msgs.append(b.allocator, b.fmt("Unable to spawn the following command: cannot spawn child processes\n{s}", .{ - try allocPrintCmd(b.allocator, null, argv), - })); - return error.MakeFailed; - } - - var child = std.ChildProcess.init(argv, b.allocator); - child.env_map = b.env_map; - child.stdin_behavior = .Pipe; - child.stdout_behavior = .Pipe; - child.stderr_behavior = .Pipe; - - try child.spawn(); - - var poller = std.io.poll(b.allocator, enum { stdout, stderr }, .{ - .stdout = child.stdout.?, - .stderr = child.stderr.?, - }); - defer poller.deinit(); - - try sendMessage(child.stdin.?, .update); - try sendMessage(child.stdin.?, .exit); - - const Header = std.zig.Server.Message.Header; - var result: ?[]const u8 = null; - - var node_name: std.ArrayListUnmanaged(u8) = .{}; - defer node_name.deinit(b.allocator); - var sub_prog_node: ?std.Progress.Node = null; - defer if (sub_prog_node) |*n| n.end(); - - while (try poller.poll()) { - const stdout = poller.fifo(.stdout); - const buf = stdout.readableSlice(0); - assert(stdout.readableLength() == buf.len); - if (buf.len >= @sizeOf(Header)) { - const header = @ptrCast(*align(1) const Header, buf[0..@sizeOf(Header)]); - const header_and_msg_len = header.bytes_len + @sizeOf(Header); - if (buf.len >= header_and_msg_len) { - const body = buf[@sizeOf(Header)..][0..header.bytes_len]; - switch (header.tag) { - .zig_version => { - if (!mem.eql(u8, builtin.zig_version_string, body)) { - try s.result_error_msgs.append( - b.allocator, - b.fmt("zig version mismatch build runner vs compiler: '{s}' vs '{s}'", .{ - builtin.zig_version_string, body, - }), - ); - return error.MakeFailed; - } - }, - .error_bundle => { - const EbHdr = std.zig.Server.Message.ErrorBundle; - const eb_hdr = @ptrCast(*align(1) const EbHdr, body); - const extra_bytes = - body[@sizeOf(EbHdr)..][0 .. @sizeOf(u32) * eb_hdr.extra_len]; - const string_bytes = - body[@sizeOf(EbHdr) + extra_bytes.len ..][0..eb_hdr.string_bytes_len]; - // TODO: use @ptrCast when the compiler supports it - const unaligned_extra = mem.bytesAsSlice(u32, extra_bytes); - const extra_array = try b.allocator.alloc(u32, unaligned_extra.len); - // TODO: use @memcpy when it supports slices - for (extra_array, unaligned_extra) |*dst, src| dst.* = src; - s.result_error_bundle = .{ - .string_bytes = try b.allocator.dupe(u8, string_bytes), - .extra = extra_array, - }; - }, - .progress => { - if (sub_prog_node) |*n| n.end(); - node_name.clearRetainingCapacity(); - try node_name.appendSlice(b.allocator, body); - sub_prog_node = prog_node.start(node_name.items, 0); - sub_prog_node.?.activate(); - }, - .emit_bin_path => { - result = try b.allocator.dupe(u8, body); - }, - _ => { - // Unrecognized message. - }, - } - stdout.discard(header_and_msg_len); - } - } - } - - const stderr = poller.fifo(.stderr); - if (stderr.readableLength() > 0) { - try s.result_error_msgs.append(b.allocator, try stderr.toOwnedSlice()); - } - - // Send EOF to stdin. - child.stdin.?.close(); - child.stdin = null; - - const term = try child.wait(); - switch (term) { - .Exited => |code| { - if (code != 0) { - try s.result_error_msgs.append(b.allocator, b.fmt("the following command exited with error code {d}:\n{s}", .{ - code, try allocPrintCmd(b.allocator, null, argv), - })); - return error.MakeFailed; - } - }, - .Signal, .Stopped, .Unknown => |code| { - _ = code; - try s.result_error_msgs.append(b.allocator, b.fmt("the following command terminated unexpectedly:\n{s}", .{ - try allocPrintCmd(b.allocator, null, argv), - })); - return error.MakeFailed; - }, - } - - if (s.result_error_bundle.errorMessageCount() > 0) { - try s.result_error_msgs.append( - b.allocator, - b.fmt("the following command failed with {d} compilation errors:\n{s}", .{ - s.result_error_bundle.errorMessageCount(), - try allocPrintCmd(b.allocator, null, argv), - }), - ); - return error.MakeFailed; - } - - return result orelse { - try s.result_error_msgs.append(b.allocator, b.fmt("the following command failed to communicate the compilation result:\n{s}", .{ - try allocPrintCmd(b.allocator, null, argv), - })); - return error.MakeFailed; - }; -} - -fn sendMessage(file: fs.File, tag: std.zig.Client.Message.Tag) !void { - const header: std.zig.Client.Message.Header = .{ - .tag = tag, - .bytes_len = 0, - }; - try file.writeAll(std.mem.asBytes(&header)); -} - /// This is a helper function to be called from build.zig scripts, *not* from /// inside step make() functions. If any errors occur, it fails the build with /// a helpful message. @@ -1910,14 +1714,12 @@ pub fn serializeCpu(allocator: Allocator, cpu: std.Target.Cpu) ![]const u8 { test { _ = CheckFileStep; _ = CheckObjectStep; - _ = EmulatableRunStep; _ = FmtStep; _ = InstallArtifactStep; _ = InstallDirStep; _ = InstallFileStep; _ = ObjCopyStep; _ = CompileStep; - _ = LogStep; _ = OptionsStep; _ = RemoveDirStep; _ = RunStep; diff --git a/lib/std/Build/CheckFileStep.zig b/lib/std/Build/CheckFileStep.zig index 9fee947386..f70a29840e 100644 --- a/lib/std/Build/CheckFileStep.zig +++ b/lib/std/Build/CheckFileStep.zig @@ -8,26 +8,25 @@ const CheckFileStep = @This(); pub const base_id = .check_file; step: Step, -builder: *std.Build, expected_matches: []const []const u8, source: std.Build.FileSource, max_bytes: usize = 20 * 1024 * 1024, pub fn create( - builder: *std.Build, + owner: *std.Build, source: std.Build.FileSource, expected_matches: []const []const u8, ) *CheckFileStep { - const self = builder.allocator.create(CheckFileStep) catch @panic("OOM"); + const self = owner.allocator.create(CheckFileStep) catch @panic("OOM"); self.* = CheckFileStep{ - .builder = builder, - .step = Step.init(builder.allocator, .{ + .step = Step.init(.{ .id = .check_file, .name = "CheckFile", + .owner = owner, .makeFn = make, }), - .source = source.dupe(builder), - .expected_matches = builder.dupeStrings(expected_matches), + .source = source.dupe(owner), + .expected_matches = owner.dupeStrings(expected_matches), }; self.source.addStepDependencies(&self.step); return self; @@ -35,10 +34,11 @@ pub fn create( fn make(step: *Step, prog_node: *std.Progress.Node) !void { _ = prog_node; + const b = step.owner; const self = @fieldParentPtr(CheckFileStep, "step", step); - const src_path = self.source.getPath(self.builder); - const contents = try fs.cwd().readFileAlloc(self.builder.allocator, src_path, self.max_bytes); + const src_path = self.source.getPath(b); + const contents = try fs.cwd().readFileAlloc(b.allocator, src_path, self.max_bytes); for (self.expected_matches) |expected_match| { if (mem.indexOf(u8, contents, expected_match) == null) { diff --git a/lib/std/Build/CheckObjectStep.zig b/lib/std/Build/CheckObjectStep.zig index eccbbd1696..57d280da0e 100644 --- a/lib/std/Build/CheckObjectStep.zig +++ b/lib/std/Build/CheckObjectStep.zig @@ -10,29 +10,31 @@ const CheckObjectStep = @This(); const Allocator = mem.Allocator; const Step = std.Build.Step; -const EmulatableRunStep = std.Build.EmulatableRunStep; pub const base_id = .check_object; step: Step, -builder: *std.Build, source: std.Build.FileSource, max_bytes: usize = 20 * 1024 * 1024, checks: std.ArrayList(Check), dump_symtab: bool = false, obj_format: std.Target.ObjectFormat, -pub fn create(builder: *std.Build, source: std.Build.FileSource, obj_format: std.Target.ObjectFormat) *CheckObjectStep { - const gpa = builder.allocator; +pub fn create( + owner: *std.Build, + source: std.Build.FileSource, + obj_format: std.Target.ObjectFormat, +) *CheckObjectStep { + const gpa = owner.allocator; const self = gpa.create(CheckObjectStep) catch @panic("OOM"); self.* = .{ - .builder = builder, - .step = Step.init(gpa, .{ + .step = Step.init(.{ .id = .check_file, .name = "CheckObject", + .owner = owner, .makeFn = make, }), - .source = source.dupe(builder), + .source = source.dupe(owner), .checks = std.ArrayList(Check).init(gpa), .obj_format = obj_format, }; @@ -42,14 +44,18 @@ pub fn create(builder: *std.Build, source: std.Build.FileSource, obj_format: std /// Runs and (optionally) compares the output of a binary. /// Asserts `self` was generated from an executable step. -pub fn runAndCompare(self: *CheckObjectStep) *EmulatableRunStep { +/// TODO this doesn't actually compare, and there's no apparent reason for it +/// to depend on the check object step. I don't see why this function should exist, +/// the caller could just add the run step directly. +pub fn runAndCompare(self: *CheckObjectStep) *std.Build.RunStep { const dependencies_len = self.step.dependencies.items.len; assert(dependencies_len > 0); const exe_step = self.step.dependencies.items[dependencies_len - 1]; const exe = exe_step.cast(std.Build.CompileStep).?; - const emulatable_step = EmulatableRunStep.create(self.builder, "EmulatableRun", exe); - emulatable_step.step.dependOn(&self.step); - return emulatable_step; + const run = self.step.owner.addRunArtifact(exe); + run.skip_foreign_checks = true; + run.step.dependOn(&self.step); + return run; } /// There two types of actions currently suported: @@ -253,7 +259,7 @@ const Check = struct { /// Creates a new sequence of actions with `phrase` as the first anchor searched phrase. pub fn checkStart(self: *CheckObjectStep, phrase: []const u8) void { - var new_check = Check.create(self.builder); + var new_check = Check.create(self.step.owner); new_check.match(phrase); self.checks.append(new_check) catch @panic("OOM"); } @@ -295,17 +301,18 @@ pub fn checkComputeCompare( program: []const u8, expected: ComputeCompareExpected, ) void { - var new_check = Check.create(self.builder); + var new_check = Check.create(self.step.owner); new_check.computeCmp(program, expected); self.checks.append(new_check) catch @panic("OOM"); } fn make(step: *Step, prog_node: *std.Progress.Node) !void { _ = prog_node; + const b = step.owner; + const gpa = b.allocator; const self = @fieldParentPtr(CheckObjectStep, "step", step); - const gpa = self.builder.allocator; - const src_path = self.source.getPath(self.builder); + const src_path = self.source.getPath(b); const contents = try fs.cwd().readFileAllocOptions( gpa, src_path, diff --git a/lib/std/Build/CompileStep.zig b/lib/std/Build/CompileStep.zig index 711092c762..f1a8f71334 100644 --- a/lib/std/Build/CompileStep.zig +++ b/lib/std/Build/CompileStep.zig @@ -22,7 +22,6 @@ const InstallDir = std.Build.InstallDir; const InstallArtifactStep = std.Build.InstallArtifactStep; const GeneratedFile = std.Build.GeneratedFile; const ObjCopyStep = std.Build.ObjCopyStep; -const EmulatableRunStep = std.Build.EmulatableRunStep; const CheckObjectStep = std.Build.CheckObjectStep; const RunStep = std.Build.RunStep; const OptionsStep = std.Build.OptionsStep; @@ -32,7 +31,6 @@ const CompileStep = @This(); pub const base_id: Step.Id = .compile; step: Step, -builder: *std.Build, name: []const u8, target: CrossTarget, target_info: NativeTargetInfo, @@ -305,24 +303,23 @@ pub const EmitOption = union(enum) { } }; -pub fn create(builder: *std.Build, options: Options) *CompileStep { - const name = builder.dupe(options.name); - const root_src: ?FileSource = if (options.root_source_file) |rsrc| rsrc.dupe(builder) else null; +pub fn create(owner: *std.Build, options: Options) *CompileStep { + const name = owner.dupe(options.name); + const root_src: ?FileSource = if (options.root_source_file) |rsrc| rsrc.dupe(owner) else null; if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) { panic("invalid name: '{s}'. It looks like a file path, but it is supposed to be the library or application name.", .{name}); } - const step_name = builder.fmt("compile {s} {s} {s}", .{ + const step_name = owner.fmt("compile {s} {s} {s}", .{ name, @tagName(options.optimize), - options.target.zigTriple(builder.allocator) catch @panic("OOM"), + options.target.zigTriple(owner.allocator) catch @panic("OOM"), }); - const self = builder.allocator.create(CompileStep) catch @panic("OOM"); + const self = owner.allocator.create(CompileStep) catch @panic("OOM"); self.* = CompileStep{ .strip = null, .unwind_tables = null, - .builder = builder, .verbose_link = false, .verbose_cc = false, .optimize = options.optimize, @@ -331,27 +328,28 @@ pub fn create(builder: *std.Build, options: Options) *CompileStep { .kind = options.kind, .root_src = root_src, .name = name, - .frameworks = StringHashMap(FrameworkLinkInfo).init(builder.allocator), - .step = Step.init(builder.allocator, .{ + .frameworks = StringHashMap(FrameworkLinkInfo).init(owner.allocator), + .step = Step.init(.{ .id = base_id, .name = step_name, + .owner = owner, .makeFn = make, }), .version = options.version, .out_filename = undefined, - .out_h_filename = builder.fmt("{s}.h", .{name}), + .out_h_filename = owner.fmt("{s}.h", .{name}), .out_lib_filename = undefined, - .out_pdb_filename = builder.fmt("{s}.pdb", .{name}), + .out_pdb_filename = owner.fmt("{s}.pdb", .{name}), .major_only_filename = null, .name_only_filename = null, - .modules = std.StringArrayHashMap(*Module).init(builder.allocator), - .include_dirs = ArrayList(IncludeDir).init(builder.allocator), - .link_objects = ArrayList(LinkObject).init(builder.allocator), - .c_macros = ArrayList([]const u8).init(builder.allocator), - .lib_paths = ArrayList([]const u8).init(builder.allocator), - .rpaths = ArrayList([]const u8).init(builder.allocator), - .framework_dirs = ArrayList([]const u8).init(builder.allocator), - .installed_headers = ArrayList(*Step).init(builder.allocator), + .modules = std.StringArrayHashMap(*Module).init(owner.allocator), + .include_dirs = ArrayList(IncludeDir).init(owner.allocator), + .link_objects = ArrayList(LinkObject).init(owner.allocator), + .c_macros = ArrayList([]const u8).init(owner.allocator), + .lib_paths = ArrayList([]const u8).init(owner.allocator), + .rpaths = ArrayList([]const u8).init(owner.allocator), + .framework_dirs = ArrayList([]const u8).init(owner.allocator), + .installed_headers = ArrayList(*Step).init(owner.allocator), .object_src = undefined, .c_std = std.Build.CStd.C99, .zig_lib_dir = null, @@ -382,9 +380,10 @@ pub fn create(builder: *std.Build, options: Options) *CompileStep { } fn computeOutFileNames(self: *CompileStep) void { + const b = self.step.owner; const target = self.target_info.target; - self.out_filename = std.zig.binNameAlloc(self.builder.allocator, .{ + self.out_filename = std.zig.binNameAlloc(b.allocator, .{ .root_name = self.name, .target = target, .output_mode = switch (self.kind) { @@ -404,30 +403,30 @@ fn computeOutFileNames(self: *CompileStep) void { self.out_lib_filename = self.out_filename; } else if (self.version) |version| { if (target.isDarwin()) { - self.major_only_filename = self.builder.fmt("lib{s}.{d}.dylib", .{ + self.major_only_filename = b.fmt("lib{s}.{d}.dylib", .{ self.name, version.major, }); - self.name_only_filename = self.builder.fmt("lib{s}.dylib", .{self.name}); + self.name_only_filename = b.fmt("lib{s}.dylib", .{self.name}); self.out_lib_filename = self.out_filename; } else if (target.os.tag == .windows) { - self.out_lib_filename = self.builder.fmt("{s}.lib", .{self.name}); + self.out_lib_filename = b.fmt("{s}.lib", .{self.name}); } else { - self.major_only_filename = self.builder.fmt("lib{s}.so.{d}", .{ self.name, version.major }); - self.name_only_filename = self.builder.fmt("lib{s}.so", .{self.name}); + self.major_only_filename = b.fmt("lib{s}.so.{d}", .{ self.name, version.major }); + self.name_only_filename = b.fmt("lib{s}.so", .{self.name}); self.out_lib_filename = self.out_filename; } } else { if (target.isDarwin()) { self.out_lib_filename = self.out_filename; } else if (target.os.tag == .windows) { - self.out_lib_filename = self.builder.fmt("{s}.lib", .{self.name}); + self.out_lib_filename = b.fmt("{s}.lib", .{self.name}); } else { self.out_lib_filename = self.out_filename; } } if (self.output_dir != null) { - self.output_lib_path_source.path = self.builder.pathJoin( + self.output_lib_path_source.path = b.pathJoin( &.{ self.output_dir.?, self.out_lib_filename }, ); } @@ -435,17 +434,20 @@ fn computeOutFileNames(self: *CompileStep) void { } pub fn setOutputDir(self: *CompileStep, dir: []const u8) void { - self.output_dir = self.builder.dupePath(dir); + const b = self.step.owner; + self.output_dir = b.dupePath(dir); } pub fn install(self: *CompileStep) void { - self.builder.installArtifact(self); + const b = self.step.owner; + b.installArtifact(self); } -pub fn installHeader(a: *CompileStep, src_path: []const u8, dest_rel_path: []const u8) void { - const install_file = a.builder.addInstallHeaderFile(src_path, dest_rel_path); - a.builder.getInstallStep().dependOn(&install_file.step); - a.installed_headers.append(&install_file.step) catch @panic("OOM"); +pub fn installHeader(cs: *CompileStep, src_path: []const u8, dest_rel_path: []const u8) void { + const b = cs.step.owner; + const install_file = b.addInstallHeaderFile(src_path, dest_rel_path); + b.getInstallStep().dependOn(&install_file.step); + cs.installed_headers.append(&install_file.step) catch @panic("OOM"); } pub const InstallConfigHeaderOptions = struct { @@ -459,13 +461,14 @@ pub fn installConfigHeader( options: InstallConfigHeaderOptions, ) void { const dest_rel_path = options.dest_rel_path orelse config_header.include_path; - const install_file = cs.builder.addInstallFileWithDir( + const b = cs.step.owner; + const install_file = b.addInstallFileWithDir( .{ .generated = &config_header.output_file }, options.install_dir, dest_rel_path, ); install_file.step.dependOn(&config_header.step); - cs.builder.getInstallStep().dependOn(&install_file.step); + b.getInstallStep().dependOn(&install_file.step); cs.installed_headers.append(&install_file.step) catch @panic("OOM"); } @@ -482,91 +485,84 @@ pub fn installHeadersDirectory( } pub fn installHeadersDirectoryOptions( - a: *CompileStep, + cs: *CompileStep, options: std.Build.InstallDirStep.Options, ) void { - const install_dir = a.builder.addInstallDirectory(options); - a.builder.getInstallStep().dependOn(&install_dir.step); - a.installed_headers.append(&install_dir.step) catch @panic("OOM"); + const b = cs.step.owner; + const install_dir = b.addInstallDirectory(options); + b.getInstallStep().dependOn(&install_dir.step); + cs.installed_headers.append(&install_dir.step) catch @panic("OOM"); } -pub fn installLibraryHeaders(a: *CompileStep, l: *CompileStep) void { +pub fn installLibraryHeaders(cs: *CompileStep, l: *CompileStep) void { assert(l.kind == .lib); - const install_step = a.builder.getInstallStep(); + const b = cs.step.owner; + const install_step = b.getInstallStep(); // Copy each element from installed_headers, modifying the builder // to be the new parent's builder. for (l.installed_headers.items) |step| { const step_copy = switch (step.id) { inline .install_file, .install_dir => |id| blk: { const T = id.Type(); - const ptr = a.builder.allocator.create(T) catch @panic("OOM"); + const ptr = b.allocator.create(T) catch @panic("OOM"); ptr.* = step.cast(T).?.*; - ptr.override_source_builder = ptr.builder; - ptr.builder = a.builder; + ptr.dest_builder = b; break :blk &ptr.step; }, else => unreachable, }; - a.installed_headers.append(step_copy) catch @panic("OOM"); + cs.installed_headers.append(step_copy) catch @panic("OOM"); install_step.dependOn(step_copy); } - a.installed_headers.appendSlice(l.installed_headers.items) catch @panic("OOM"); + cs.installed_headers.appendSlice(l.installed_headers.items) catch @panic("OOM"); } pub fn addObjCopy(cs: *CompileStep, options: ObjCopyStep.Options) *ObjCopyStep { + const b = cs.step.owner; var copy = options; if (copy.basename == null) { if (options.format) |f| { - copy.basename = cs.builder.fmt("{s}.{s}", .{ cs.name, @tagName(f) }); + copy.basename = b.fmt("{s}.{s}", .{ cs.name, @tagName(f) }); } else { copy.basename = cs.name; } } - return cs.builder.addObjCopy(cs.getOutputSource(), copy); + return b.addObjCopy(cs.getOutputSource(), copy); } /// Deprecated: use `std.Build.addRunArtifact` /// This function will run in the context of the package that created the executable, /// which is undesirable when running an executable provided by a dependency package. -pub fn run(exe: *CompileStep) *RunStep { - return exe.builder.addRunArtifact(exe); -} - -/// Creates an `EmulatableRunStep` with an executable built with `addExecutable`. -/// Allows running foreign binaries through emulation platforms such as Qemu or Rosetta. -/// When a binary cannot be ran through emulation or the option is disabled, a warning -/// will be printed and the binary will *NOT* be ran. -pub fn runEmulatable(exe: *CompileStep) *EmulatableRunStep { - assert(exe.kind == .exe or exe.kind == .test_exe); - - const run_step = EmulatableRunStep.create(exe.builder, exe.builder.fmt("run {s}", .{exe.step.name}), exe); - if (exe.vcpkg_bin_path) |path| { - RunStep.addPathDirInternal(&run_step.step, exe.builder, path); - } - return run_step; +pub fn run(cs: *CompileStep) *RunStep { + return cs.step.owner.addRunArtifact(cs); } pub fn checkObject(self: *CompileStep, obj_format: std.Target.ObjectFormat) *CheckObjectStep { - return CheckObjectStep.create(self.builder, self.getOutputSource(), obj_format); + const b = self.step.owner; + return CheckObjectStep.create(b, self.getOutputSource(), obj_format); } pub fn setLinkerScriptPath(self: *CompileStep, source: FileSource) void { - self.linker_script = source.dupe(self.builder); + const b = self.step.owner; + self.linker_script = source.dupe(b); source.addStepDependencies(&self.step); } pub fn linkFramework(self: *CompileStep, framework_name: []const u8) void { - self.frameworks.put(self.builder.dupe(framework_name), .{}) catch @panic("OOM"); + const b = self.step.owner; + self.frameworks.put(b.dupe(framework_name), .{}) catch @panic("OOM"); } pub fn linkFrameworkNeeded(self: *CompileStep, framework_name: []const u8) void { - self.frameworks.put(self.builder.dupe(framework_name), .{ + const b = self.step.owner; + self.frameworks.put(b.dupe(framework_name), .{ .needed = true, }) catch @panic("OOM"); } pub fn linkFrameworkWeak(self: *CompileStep, framework_name: []const u8) void { - self.frameworks.put(self.builder.dupe(framework_name), .{ + const b = self.step.owner; + self.frameworks.put(b.dupe(framework_name), .{ .weak = true, }) catch @panic("OOM"); } @@ -619,21 +615,24 @@ pub fn linkLibCpp(self: *CompileStep) void { /// If the value is omitted, it is set to 1. /// `name` and `value` need not live longer than the function call. pub fn defineCMacro(self: *CompileStep, name: []const u8, value: ?[]const u8) void { - const macro = std.Build.constructCMacro(self.builder.allocator, name, value); + const b = self.step.owner; + const macro = std.Build.constructCMacro(b.allocator, name, value); self.c_macros.append(macro) catch @panic("OOM"); } /// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1. pub fn defineCMacroRaw(self: *CompileStep, name_and_value: []const u8) void { - self.c_macros.append(self.builder.dupe(name_and_value)) catch @panic("OOM"); + const b = self.step.owner; + self.c_macros.append(b.dupe(name_and_value)) catch @panic("OOM"); } /// This one has no integration with anything, it just puts -lname on the command line. /// Prefer to use `linkSystemLibrary` instead. pub fn linkSystemLibraryName(self: *CompileStep, name: []const u8) void { + const b = self.step.owner; self.link_objects.append(.{ .system_lib = .{ - .name = self.builder.dupe(name), + .name = b.dupe(name), .needed = false, .weak = false, .use_pkg_config = .no, @@ -644,9 +643,10 @@ pub fn linkSystemLibraryName(self: *CompileStep, name: []const u8) void { /// This one has no integration with anything, it just puts -needed-lname on the command line. /// Prefer to use `linkSystemLibraryNeeded` instead. pub fn linkSystemLibraryNeededName(self: *CompileStep, name: []const u8) void { + const b = self.step.owner; self.link_objects.append(.{ .system_lib = .{ - .name = self.builder.dupe(name), + .name = b.dupe(name), .needed = true, .weak = false, .use_pkg_config = .no, @@ -657,9 +657,10 @@ pub fn linkSystemLibraryNeededName(self: *CompileStep, name: []const u8) void { /// Darwin-only. This one has no integration with anything, it just puts -weak-lname on the /// command line. Prefer to use `linkSystemLibraryWeak` instead. pub fn linkSystemLibraryWeakName(self: *CompileStep, name: []const u8) void { + const b = self.step.owner; self.link_objects.append(.{ .system_lib = .{ - .name = self.builder.dupe(name), + .name = b.dupe(name), .needed = false, .weak = true, .use_pkg_config = .no, @@ -670,9 +671,10 @@ pub fn linkSystemLibraryWeakName(self: *CompileStep, name: []const u8) void { /// This links against a system library, exclusively using pkg-config to find the library. /// Prefer to use `linkSystemLibrary` instead. pub fn linkSystemLibraryPkgConfigOnly(self: *CompileStep, lib_name: []const u8) void { + const b = self.step.owner; self.link_objects.append(.{ .system_lib = .{ - .name = self.builder.dupe(lib_name), + .name = b.dupe(lib_name), .needed = false, .weak = false, .use_pkg_config = .force, @@ -683,9 +685,10 @@ pub fn linkSystemLibraryPkgConfigOnly(self: *CompileStep, lib_name: []const u8) /// This links against a system library, exclusively using pkg-config to find the library. /// Prefer to use `linkSystemLibraryNeeded` instead. pub fn linkSystemLibraryNeededPkgConfigOnly(self: *CompileStep, lib_name: []const u8) void { + const b = self.step.owner; self.link_objects.append(.{ .system_lib = .{ - .name = self.builder.dupe(lib_name), + .name = b.dupe(lib_name), .needed = true, .weak = false, .use_pkg_config = .force, @@ -696,13 +699,14 @@ pub fn linkSystemLibraryNeededPkgConfigOnly(self: *CompileStep, lib_name: []cons /// Run pkg-config for the given library name and parse the output, returning the arguments /// that should be passed to zig to link the given library. pub fn runPkgConfig(self: *CompileStep, lib_name: []const u8) ![]const []const u8 { + const b = self.step.owner; const pkg_name = match: { // First we have to map the library name to pkg config name. Unfortunately, // there are several examples where this is not straightforward: // -lSDL2 -> pkg-config sdl2 // -lgdk-3 -> pkg-config gdk-3.0 // -latk-1.0 -> pkg-config atk - const pkgs = try getPkgConfigList(self.builder); + const pkgs = try getPkgConfigList(b); // Exact match means instant winner. for (pkgs) |pkg| { @@ -742,7 +746,7 @@ pub fn runPkgConfig(self: *CompileStep, lib_name: []const u8) ![]const []const u }; var code: u8 = undefined; - const stdout = if (self.builder.execAllowFail(&[_][]const u8{ + const stdout = if (b.execAllowFail(&[_][]const u8{ "pkg-config", pkg_name, "--cflags", @@ -755,7 +759,7 @@ pub fn runPkgConfig(self: *CompileStep, lib_name: []const u8) ![]const []const u else => return err, }; - var zig_args = ArrayList([]const u8).init(self.builder.allocator); + var zig_args = ArrayList([]const u8).init(b.allocator); defer zig_args.deinit(); var it = mem.tokenize(u8, stdout, " \r\n\t"); @@ -780,7 +784,7 @@ pub fn runPkgConfig(self: *CompileStep, lib_name: []const u8) ![]const []const u try zig_args.appendSlice(&[_][]const u8{ "-D", macro }); } else if (mem.startsWith(u8, tok, "-D")) { try zig_args.append(tok); - } else if (self.builder.verbose) { + } else if (b.verbose) { log.warn("Ignoring pkg-config flag '{s}'", .{tok}); } } @@ -804,6 +808,7 @@ fn linkSystemLibraryInner(self: *CompileStep, name: []const u8, opts: struct { needed: bool = false, weak: bool = false, }) void { + const b = self.step.owner; if (isLibCLibrary(name)) { self.linkLibC(); return; @@ -815,7 +820,7 @@ fn linkSystemLibraryInner(self: *CompileStep, name: []const u8, opts: struct { self.link_objects.append(.{ .system_lib = .{ - .name = self.builder.dupe(name), + .name = b.dupe(name), .needed = opts.needed, .weak = opts.weak, .use_pkg_config = .yes, @@ -824,26 +829,30 @@ fn linkSystemLibraryInner(self: *CompileStep, name: []const u8, opts: struct { } pub fn setNamePrefix(self: *CompileStep, text: []const u8) void { + const b = self.step.owner; assert(self.kind == .@"test" or self.kind == .test_exe); - self.name_prefix = self.builder.dupe(text); + self.name_prefix = b.dupe(text); } pub fn setFilter(self: *CompileStep, text: ?[]const u8) void { + const b = self.step.owner; assert(self.kind == .@"test" or self.kind == .test_exe); - self.filter = if (text) |t| self.builder.dupe(t) else null; + self.filter = if (text) |t| b.dupe(t) else null; } pub fn setTestRunner(self: *CompileStep, path: ?[]const u8) void { + const b = self.step.owner; assert(self.kind == .@"test" or self.kind == .test_exe); - self.test_runner = if (path) |p| self.builder.dupePath(p) else null; + self.test_runner = if (path) |p| b.dupePath(p) else null; } /// Handy when you have many C/C++ source files and want them all to have the same flags. pub fn addCSourceFiles(self: *CompileStep, files: []const []const u8, flags: []const []const u8) void { - const c_source_files = self.builder.allocator.create(CSourceFiles) catch @panic("OOM"); + const b = self.step.owner; + const c_source_files = b.allocator.create(CSourceFiles) catch @panic("OOM"); - const files_copy = self.builder.dupeStrings(files); - const flags_copy = self.builder.dupeStrings(flags); + const files_copy = b.dupeStrings(files); + const flags_copy = b.dupeStrings(flags); c_source_files.* = .{ .files = files_copy, @@ -860,8 +869,9 @@ pub fn addCSourceFile(self: *CompileStep, file: []const u8, flags: []const []con } pub fn addCSourceFileSource(self: *CompileStep, source: CSourceFile) void { - const c_source_file = self.builder.allocator.create(CSourceFile) catch @panic("OOM"); - c_source_file.* = source.dupe(self.builder); + const b = self.step.owner; + const c_source_file = b.allocator.create(CSourceFile) catch @panic("OOM"); + c_source_file.* = source.dupe(b); self.link_objects.append(.{ .c_source_file = c_source_file }) catch @panic("OOM"); source.source.addStepDependencies(&self.step); } @@ -875,15 +885,18 @@ pub fn setVerboseCC(self: *CompileStep, value: bool) void { } pub fn overrideZigLibDir(self: *CompileStep, dir_path: []const u8) void { - self.zig_lib_dir = self.builder.dupePath(dir_path); + const b = self.step.owner; + self.zig_lib_dir = b.dupePath(dir_path); } pub fn setMainPkgPath(self: *CompileStep, dir_path: []const u8) void { - self.main_pkg_path = self.builder.dupePath(dir_path); + const b = self.step.owner; + self.main_pkg_path = b.dupePath(dir_path); } pub fn setLibCFile(self: *CompileStep, libc_file: ?FileSource) void { - self.libc_file = if (libc_file) |f| f.dupe(self.builder) else null; + const b = self.step.owner; + self.libc_file = if (libc_file) |f| f.dupe(b) else null; } /// Returns the generated executable, library or object file. @@ -914,13 +927,15 @@ pub fn getOutputPdbSource(self: *CompileStep) FileSource { } pub fn addAssemblyFile(self: *CompileStep, path: []const u8) void { + const b = self.step.owner; self.link_objects.append(.{ - .assembly_file = .{ .path = self.builder.dupe(path) }, + .assembly_file = .{ .path = b.dupe(path) }, }) catch @panic("OOM"); } pub fn addAssemblyFileSource(self: *CompileStep, source: FileSource) void { - const source_duped = source.dupe(self.builder); + const b = self.step.owner; + const source_duped = source.dupe(b); self.link_objects.append(.{ .assembly_file = source_duped }) catch @panic("OOM"); source_duped.addStepDependencies(&self.step); } @@ -930,7 +945,8 @@ pub fn addObjectFile(self: *CompileStep, source_file: []const u8) void { } pub fn addObjectFileSource(self: *CompileStep, source: FileSource) void { - self.link_objects.append(.{ .static_path = source.dupe(self.builder) }) catch @panic("OOM"); + const b = self.step.owner; + self.link_objects.append(.{ .static_path = source.dupe(b) }) catch @panic("OOM"); source.addStepDependencies(&self.step); } @@ -945,11 +961,13 @@ pub const addLibPath = @compileError("deprecated, use addLibraryPath"); pub const addFrameworkDir = @compileError("deprecated, use addFrameworkPath"); pub fn addSystemIncludePath(self: *CompileStep, path: []const u8) void { - self.include_dirs.append(IncludeDir{ .raw_path_system = self.builder.dupe(path) }) catch @panic("OOM"); + const b = self.step.owner; + self.include_dirs.append(IncludeDir{ .raw_path_system = b.dupe(path) }) catch @panic("OOM"); } pub fn addIncludePath(self: *CompileStep, path: []const u8) void { - self.include_dirs.append(IncludeDir{ .raw_path = self.builder.dupe(path) }) catch @panic("OOM"); + const b = self.step.owner; + self.include_dirs.append(IncludeDir{ .raw_path = b.dupe(path) }) catch @panic("OOM"); } pub fn addConfigHeader(self: *CompileStep, config_header: *ConfigHeaderStep) void { @@ -958,23 +976,27 @@ pub fn addConfigHeader(self: *CompileStep, config_header: *ConfigHeaderStep) voi } pub fn addLibraryPath(self: *CompileStep, path: []const u8) void { - self.lib_paths.append(self.builder.dupe(path)) catch @panic("OOM"); + const b = self.step.owner; + self.lib_paths.append(b.dupe(path)) catch @panic("OOM"); } pub fn addRPath(self: *CompileStep, path: []const u8) void { - self.rpaths.append(self.builder.dupe(path)) catch @panic("OOM"); + const b = self.step.owner; + self.rpaths.append(b.dupe(path)) catch @panic("OOM"); } pub fn addFrameworkPath(self: *CompileStep, dir_path: []const u8) void { - self.framework_dirs.append(self.builder.dupe(dir_path)) catch @panic("OOM"); + const b = self.step.owner; + self.framework_dirs.append(b.dupe(dir_path)) catch @panic("OOM"); } /// Adds a module to be used with `@import` and exposing it in the current /// package's module table using `name`. pub fn addModule(cs: *CompileStep, name: []const u8, module: *Module) void { - cs.modules.put(cs.builder.dupe(name), module) catch @panic("OOM"); + const b = cs.step.owner; + cs.modules.put(b.dupe(name), module) catch @panic("OOM"); - var done = std.AutoHashMap(*Module, void).init(cs.builder.allocator); + var done = std.AutoHashMap(*Module, void).init(b.allocator); defer done.deinit(); cs.addRecursiveBuildDeps(module, &done) catch @panic("OOM"); } @@ -982,7 +1004,8 @@ pub fn addModule(cs: *CompileStep, name: []const u8, module: *Module) void { /// Adds a module to be used with `@import` without exposing it in the current /// package's module table. pub fn addAnonymousModule(cs: *CompileStep, name: []const u8, options: std.Build.CreateModuleOptions) void { - const module = cs.builder.createModule(options); + const b = cs.step.owner; + const module = b.createModule(options); return addModule(cs, name, module); } @@ -1002,12 +1025,13 @@ fn addRecursiveBuildDeps(cs: *CompileStep, module: *Module, done: *std.AutoHashM /// If Vcpkg was found on the system, it will be added to include and lib /// paths for the specified target. pub fn addVcpkgPaths(self: *CompileStep, linkage: CompileStep.Linkage) !void { + const b = self.step.owner; // Ideally in the Unattempted case we would call the function recursively // after findVcpkgRoot and have only one switch statement, but the compiler // cannot resolve the error set. - switch (self.builder.vcpkg_root) { + switch (b.vcpkg_root) { .unattempted => { - self.builder.vcpkg_root = if (try findVcpkgRoot(self.builder.allocator)) |root| + b.vcpkg_root = if (try findVcpkgRoot(b.allocator)) |root| VcpkgRoot{ .found = root } else .not_found; @@ -1016,31 +1040,32 @@ pub fn addVcpkgPaths(self: *CompileStep, linkage: CompileStep.Linkage) !void { .found => {}, } - switch (self.builder.vcpkg_root) { + switch (b.vcpkg_root) { .unattempted => unreachable, .not_found => return error.VcpkgNotFound, .found => |root| { - const allocator = self.builder.allocator; + const allocator = b.allocator; const triplet = try self.target.vcpkgTriplet(allocator, if (linkage == .static) .Static else .Dynamic); - defer self.builder.allocator.free(triplet); + defer b.allocator.free(triplet); - const include_path = self.builder.pathJoin(&.{ root, "installed", triplet, "include" }); + const include_path = b.pathJoin(&.{ root, "installed", triplet, "include" }); errdefer allocator.free(include_path); try self.include_dirs.append(IncludeDir{ .raw_path = include_path }); - const lib_path = self.builder.pathJoin(&.{ root, "installed", triplet, "lib" }); + const lib_path = b.pathJoin(&.{ root, "installed", triplet, "lib" }); try self.lib_paths.append(lib_path); - self.vcpkg_bin_path = self.builder.pathJoin(&.{ root, "installed", triplet, "bin" }); + self.vcpkg_bin_path = b.pathJoin(&.{ root, "installed", triplet, "bin" }); }, } } pub fn setExecCmd(self: *CompileStep, args: []const ?[]const u8) void { + const b = self.step.owner; assert(self.kind == .@"test"); - const duped_args = self.builder.allocator.alloc(?[]u8, args.len) catch @panic("OOM"); + const duped_args = b.allocator.alloc(?[]u8, args.len) catch @panic("OOM"); for (args, 0..) |arg, i| { - duped_args[i] = if (arg) |a| self.builder.dupe(a) else null; + duped_args[i] = if (arg) |a| b.dupe(a) else null; } self.exec_cmd_args = duped_args; } @@ -1055,16 +1080,17 @@ fn appendModuleArgs( cs: *CompileStep, zig_args: *ArrayList([]const u8), ) error{OutOfMemory}!void { + const b = cs.step.owner; // First, traverse the whole dependency graph and give every module a unique name, ideally one // named after what it's called somewhere in the graph. It will help here to have both a mapping // from module to name and a set of all the currently-used names. - var mod_names = std.AutoHashMap(*Module, []const u8).init(cs.builder.allocator); - var names = std.StringHashMap(void).init(cs.builder.allocator); + var mod_names = std.AutoHashMap(*Module, []const u8).init(b.allocator); + var names = std.StringHashMap(void).init(b.allocator); var to_name = std.ArrayList(struct { name: []const u8, mod: *Module, - }).init(cs.builder.allocator); + }).init(b.allocator); { var it = cs.modules.iterator(); while (it.next()) |kv| { @@ -1085,7 +1111,7 @@ fn appendModuleArgs( if (mod_names.contains(dep.mod)) continue; // We'll use this buffer to store the name we decide on - var buf = try cs.builder.allocator.alloc(u8, dep.name.len + 32); + var buf = try b.allocator.alloc(u8, dep.name.len + 32); // First, try just the exposed dependency name std.mem.copy(u8, buf, dep.name); var name = buf[0..dep.name.len]; @@ -1122,15 +1148,15 @@ fn appendModuleArgs( const mod = kv.key_ptr.*; const name = kv.value_ptr.*; - const deps_str = try constructDepString(cs.builder.allocator, mod_names, mod.dependencies); + const deps_str = try constructDepString(b.allocator, mod_names, mod.dependencies); const src = mod.builder.pathFromRoot(mod.source_file.getPath(mod.builder)); try zig_args.append("--mod"); - try zig_args.append(try std.fmt.allocPrint(cs.builder.allocator, "{s}:{s}:{s}", .{ name, deps_str, src })); + try zig_args.append(try std.fmt.allocPrint(b.allocator, "{s}:{s}:{s}", .{ name, deps_str, src })); } } // Lastly, output the root dependencies - const deps_str = try constructDepString(cs.builder.allocator, mod_names, cs.modules); + const deps_str = try constructDepString(b.allocator, mod_names, cs.modules); if (deps_str.len > 0) { try zig_args.append("--deps"); try zig_args.append(deps_str); @@ -1161,18 +1187,18 @@ fn constructDepString( } fn make(step: *Step, prog_node: *std.Progress.Node) !void { + const b = step.owner; const self = @fieldParentPtr(CompileStep, "step", step); - const builder = self.builder; if (self.root_src == null and self.link_objects.items.len == 0) { log.err("{s}: linker needs 1 or more objects to link", .{self.step.name}); return error.NeedAnObject; } - var zig_args = ArrayList([]const u8).init(builder.allocator); + var zig_args = ArrayList([]const u8).init(b.allocator); defer zig_args.deinit(); - try zig_args.append(builder.zig_exe); + try zig_args.append(b.zig_exe); const cmd = switch (self.kind) { .lib => "build-lib", @@ -1183,15 +1209,15 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { }; try zig_args.append(cmd); - if (builder.reference_trace) |some| { - try zig_args.append(try std.fmt.allocPrint(builder.allocator, "-freference-trace={d}", .{some})); + if (b.reference_trace) |some| { + try zig_args.append(try std.fmt.allocPrint(b.allocator, "-freference-trace={d}", .{some})); } try addFlag(&zig_args, "LLVM", self.use_llvm); try addFlag(&zig_args, "LLD", self.use_lld); if (self.target.ofmt) |ofmt| { - try zig_args.append(try std.fmt.allocPrint(builder.allocator, "-ofmt={s}", .{@tagName(ofmt)})); + try zig_args.append(try std.fmt.allocPrint(b.allocator, "-ofmt={s}", .{@tagName(ofmt)})); } if (self.entry_symbol_name) |entry| { @@ -1201,18 +1227,18 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { if (self.stack_size) |stack_size| { try zig_args.append("--stack"); - try zig_args.append(try std.fmt.allocPrint(builder.allocator, "{}", .{stack_size})); + try zig_args.append(try std.fmt.allocPrint(b.allocator, "{}", .{stack_size})); } - if (self.root_src) |root_src| try zig_args.append(root_src.getPath(builder)); + if (self.root_src) |root_src| try zig_args.append(root_src.getPath(b)); // We will add link objects from transitive dependencies, but we want to keep // all link objects in the same order provided. // This array is used to keep self.link_objects immutable. var transitive_deps: TransitiveDeps = .{ - .link_objects = ArrayList(LinkObject).init(builder.allocator), - .seen_system_libs = StringHashMap(void).init(builder.allocator), - .seen_steps = std.AutoHashMap(*const Step, void).init(builder.allocator), + .link_objects = ArrayList(LinkObject).init(b.allocator), + .seen_system_libs = StringHashMap(void).init(b.allocator), + .seen_steps = std.AutoHashMap(*const Step, void).init(b.allocator), .is_linking_libcpp = self.is_linking_libcpp, .is_linking_libc = self.is_linking_libc, .frameworks = &self.frameworks, @@ -1225,14 +1251,14 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { for (transitive_deps.link_objects.items) |link_object| { switch (link_object) { - .static_path => |static_path| try zig_args.append(static_path.getPath(builder)), + .static_path => |static_path| try zig_args.append(static_path.getPath(b)), .other_step => |other| switch (other.kind) { .exe => @panic("Cannot link with an executable build artifact"), .test_exe => @panic("Cannot link with an executable build artifact"), .@"test" => @panic("Cannot link with a test"), .obj => { - try zig_args.append(other.getOutputSource().getPath(builder)); + try zig_args.append(other.getOutputSource().getPath(b)); }, .lib => l: { if (self.isStaticLibrary() and other.isStaticLibrary()) { @@ -1240,7 +1266,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { break :l; } - const full_path_lib = other.getOutputLibSource().getPath(builder); + const full_path_lib = other.getOutputLibSource().getPath(b); try zig_args.append(full_path_lib); if (other.linkage == Linkage.dynamic and !self.target.isWindows()) { @@ -1262,7 +1288,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { break :prefix "-l"; }; switch (system_lib.use_pkg_config) { - .no => try zig_args.append(builder.fmt("{s}{s}", .{ prefix, system_lib.name })), + .no => try zig_args.append(b.fmt("{s}{s}", .{ prefix, system_lib.name })), .yes, .force => { if (self.runPkgConfig(system_lib.name)) |args| { try zig_args.appendSlice(args); @@ -1276,7 +1302,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { .yes => { // pkg-config failed, so fall back to linking the library // by name directly. - try zig_args.append(builder.fmt("{s}{s}", .{ + try zig_args.append(b.fmt("{s}{s}", .{ prefix, system_lib.name, })); @@ -1299,7 +1325,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { try zig_args.append("--"); prev_has_extra_flags = false; } - try zig_args.append(asm_file.getPath(builder)); + try zig_args.append(asm_file.getPath(b)); }, .c_source_file => |c_source_file| { @@ -1316,7 +1342,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { } try zig_args.append("--"); } - try zig_args.append(c_source_file.source.getPath(builder)); + try zig_args.append(c_source_file.source.getPath(b)); }, .c_source_files => |c_source_files| { @@ -1334,7 +1360,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { try zig_args.append("--"); } for (c_source_files.files) |file| { - try zig_args.append(builder.pathFromRoot(file)); + try zig_args.append(b.pathFromRoot(file)); } }, } @@ -1350,7 +1376,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { if (self.image_base) |image_base| { try zig_args.append("--image-base"); - try zig_args.append(builder.fmt("0x{x}", .{image_base})); + try zig_args.append(b.fmt("0x{x}", .{image_base})); } if (self.filter) |filter| { @@ -1369,32 +1395,32 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { if (self.test_runner) |test_runner| { try zig_args.append("--test-runner"); - try zig_args.append(builder.pathFromRoot(test_runner)); + try zig_args.append(b.pathFromRoot(test_runner)); } - for (builder.debug_log_scopes) |log_scope| { + for (b.debug_log_scopes) |log_scope| { try zig_args.append("--debug-log"); try zig_args.append(log_scope); } - if (builder.debug_compile_errors) { + if (b.debug_compile_errors) { try zig_args.append("--debug-compile-errors"); } - if (builder.verbose_cimport) try zig_args.append("--verbose-cimport"); - if (builder.verbose_air) try zig_args.append("--verbose-air"); - if (builder.verbose_llvm_ir) try zig_args.append("--verbose-llvm-ir"); - if (builder.verbose_link or self.verbose_link) try zig_args.append("--verbose-link"); - if (builder.verbose_cc or self.verbose_cc) try zig_args.append("--verbose-cc"); - if (builder.verbose_llvm_cpu_features) try zig_args.append("--verbose-llvm-cpu-features"); + if (b.verbose_cimport) try zig_args.append("--verbose-cimport"); + if (b.verbose_air) try zig_args.append("--verbose-air"); + if (b.verbose_llvm_ir) try zig_args.append("--verbose-llvm-ir"); + if (b.verbose_link or self.verbose_link) try zig_args.append("--verbose-link"); + if (b.verbose_cc or self.verbose_cc) try zig_args.append("--verbose-cc"); + if (b.verbose_llvm_cpu_features) try zig_args.append("--verbose-llvm-cpu-features"); - if (self.emit_analysis.getArg(builder, "emit-analysis")) |arg| try zig_args.append(arg); - if (self.emit_asm.getArg(builder, "emit-asm")) |arg| try zig_args.append(arg); - if (self.emit_bin.getArg(builder, "emit-bin")) |arg| try zig_args.append(arg); - if (self.emit_docs.getArg(builder, "emit-docs")) |arg| try zig_args.append(arg); - if (self.emit_implib.getArg(builder, "emit-implib")) |arg| try zig_args.append(arg); - if (self.emit_llvm_bc.getArg(builder, "emit-llvm-bc")) |arg| try zig_args.append(arg); - if (self.emit_llvm_ir.getArg(builder, "emit-llvm-ir")) |arg| try zig_args.append(arg); + if (self.emit_analysis.getArg(b, "emit-analysis")) |arg| try zig_args.append(arg); + if (self.emit_asm.getArg(b, "emit-asm")) |arg| try zig_args.append(arg); + if (self.emit_bin.getArg(b, "emit-bin")) |arg| try zig_args.append(arg); + if (self.emit_docs.getArg(b, "emit-docs")) |arg| try zig_args.append(arg); + if (self.emit_implib.getArg(b, "emit-implib")) |arg| try zig_args.append(arg); + if (self.emit_llvm_bc.getArg(b, "emit-llvm-bc")) |arg| try zig_args.append(arg); + if (self.emit_llvm_ir.getArg(b, "emit-llvm-ir")) |arg| try zig_args.append(arg); if (self.emit_h) try zig_args.append("-femit-h"); @@ -1435,31 +1461,31 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { } if (self.link_z_common_page_size) |size| { try zig_args.append("-z"); - try zig_args.append(builder.fmt("common-page-size={d}", .{size})); + try zig_args.append(b.fmt("common-page-size={d}", .{size})); } if (self.link_z_max_page_size) |size| { try zig_args.append("-z"); - try zig_args.append(builder.fmt("max-page-size={d}", .{size})); + try zig_args.append(b.fmt("max-page-size={d}", .{size})); } if (self.libc_file) |libc_file| { try zig_args.append("--libc"); - try zig_args.append(libc_file.getPath(builder)); - } else if (builder.libc_file) |libc_file| { + try zig_args.append(libc_file.getPath(b)); + } else if (b.libc_file) |libc_file| { try zig_args.append("--libc"); try zig_args.append(libc_file); } switch (self.optimize) { .Debug => {}, // Skip since it's the default. - else => try zig_args.append(builder.fmt("-O{s}", .{@tagName(self.optimize)})), + else => try zig_args.append(b.fmt("-O{s}", .{@tagName(self.optimize)})), } try zig_args.append("--cache-dir"); - try zig_args.append(builder.cache_root.path orelse "."); + try zig_args.append(b.cache_root.path orelse "."); try zig_args.append("--global-cache-dir"); - try zig_args.append(builder.global_cache_root.path orelse "."); + try zig_args.append(b.global_cache_root.path orelse "."); try zig_args.append("--name"); try zig_args.append(self.name); @@ -1471,11 +1497,11 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic) { if (self.version) |version| { try zig_args.append("--version"); - try zig_args.append(builder.fmt("{}", .{version})); + try zig_args.append(b.fmt("{}", .{version})); } if (self.target.isDarwin()) { - const install_name = self.install_name orelse builder.fmt("@rpath/{s}{s}{s}", .{ + const install_name = self.install_name orelse b.fmt("@rpath/{s}{s}{s}", .{ self.target.libPrefix(), self.name, self.target.dynamicLibSuffix(), @@ -1489,7 +1515,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { try zig_args.appendSlice(&[_][]const u8{ "--entitlements", entitlements }); } if (self.pagezero_size) |pagezero_size| { - const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{pagezero_size}); + const size = try std.fmt.allocPrint(b.allocator, "{x}", .{pagezero_size}); try zig_args.appendSlice(&[_][]const u8{ "-pagezero_size", size }); } if (self.search_strategy) |strat| switch (strat) { @@ -1497,7 +1523,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { .dylibs_first => try zig_args.append("-search_dylibs_first"), }; if (self.headerpad_size) |headerpad_size| { - const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{headerpad_size}); + const size = try std.fmt.allocPrint(b.allocator, "{x}", .{headerpad_size}); try zig_args.appendSlice(&[_][]const u8{ "-headerpad", size }); } if (self.headerpad_max_install_names) { @@ -1545,16 +1571,16 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { try zig_args.append("--export-table"); } if (self.initial_memory) |initial_memory| { - try zig_args.append(builder.fmt("--initial-memory={d}", .{initial_memory})); + try zig_args.append(b.fmt("--initial-memory={d}", .{initial_memory})); } if (self.max_memory) |max_memory| { - try zig_args.append(builder.fmt("--max-memory={d}", .{max_memory})); + try zig_args.append(b.fmt("--max-memory={d}", .{max_memory})); } if (self.shared_memory) { try zig_args.append("--shared-memory"); } if (self.global_base) |global_base| { - try zig_args.append(builder.fmt("--global-base={d}", .{global_base})); + try zig_args.append(b.fmt("--global-base={d}", .{global_base})); } if (self.code_model != .default) { @@ -1562,16 +1588,16 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { try zig_args.append(@tagName(self.code_model)); } if (self.wasi_exec_model) |model| { - try zig_args.append(builder.fmt("-mexec-model={s}", .{@tagName(model)})); + try zig_args.append(b.fmt("-mexec-model={s}", .{@tagName(model)})); } for (self.export_symbol_names) |symbol_name| { - try zig_args.append(builder.fmt("--export={s}", .{symbol_name})); + try zig_args.append(b.fmt("--export={s}", .{symbol_name})); } if (!self.target.isNative()) { try zig_args.appendSlice(&.{ - "-target", try self.target.zigTriple(builder.allocator), - "-mcpu", try std.Build.serializeCpu(builder.allocator, self.target.getCpu()), + "-target", try self.target.zigTriple(b.allocator), + "-mcpu", try std.Build.serializeCpu(b.allocator, self.target.getCpu()), }); if (self.target.dynamic_linker.get()) |dynamic_linker| { @@ -1582,12 +1608,12 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { if (self.linker_script) |linker_script| { try zig_args.append("--script"); - try zig_args.append(linker_script.getPath(builder)); + try zig_args.append(linker_script.getPath(b)); } if (self.version_script) |version_script| { try zig_args.append("--version-script"); - try zig_args.append(builder.pathFromRoot(version_script)); + try zig_args.append(b.pathFromRoot(version_script)); } if (self.kind == .@"test") { @@ -1603,23 +1629,23 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { } else { const need_cross_glibc = self.target.isGnuLibC() and transitive_deps.is_linking_libc; - switch (builder.host.getExternalExecutor(self.target_info, .{ - .qemu_fixes_dl = need_cross_glibc and builder.glibc_runtimes_dir != null, + switch (b.host.getExternalExecutor(self.target_info, .{ + .qemu_fixes_dl = need_cross_glibc and b.glibc_runtimes_dir != null, .link_libc = transitive_deps.is_linking_libc, })) { .native => {}, .bad_dl, .bad_os_or_cpu => { try zig_args.append("--test-no-exec"); }, - .rosetta => if (builder.enable_rosetta) { + .rosetta => if (b.enable_rosetta) { try zig_args.append("--test-cmd-bin"); } else { try zig_args.append("--test-no-exec"); }, .qemu => |bin_name| ok: { - if (builder.enable_qemu) qemu: { + if (b.enable_qemu) qemu: { const glibc_dir_arg = if (need_cross_glibc) - builder.glibc_runtimes_dir orelse break :qemu + b.glibc_runtimes_dir orelse break :qemu else null; try zig_args.append("--test-cmd"); @@ -1636,7 +1662,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { "i686" else @tagName(cpu_arch); - const full_dir = try std.fmt.allocPrint(builder.allocator, fmt_str, .{ + const full_dir = try std.fmt.allocPrint(b.allocator, fmt_str, .{ dir, cpu_arch_name, @tagName(os_tag), @tagName(abi), }); @@ -1650,14 +1676,14 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { } try zig_args.append("--test-no-exec"); }, - .wine => |bin_name| if (builder.enable_wine) { + .wine => |bin_name| if (b.enable_wine) { try zig_args.append("--test-cmd"); try zig_args.append(bin_name); try zig_args.append("--test-cmd-bin"); } else { try zig_args.append("--test-no-exec"); }, - .wasmtime => |bin_name| if (builder.enable_wasmtime) { + .wasmtime => |bin_name| if (b.enable_wasmtime) { try zig_args.append("--test-cmd"); try zig_args.append(bin_name); try zig_args.append("--test-cmd"); @@ -1666,7 +1692,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { } else { try zig_args.append("--test-no-exec"); }, - .darling => |bin_name| if (builder.enable_darling) { + .darling => |bin_name| if (b.enable_darling) { try zig_args.append("--test-cmd"); try zig_args.append(bin_name); try zig_args.append("--test-cmd-bin"); @@ -1685,18 +1711,18 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { switch (include_dir) { .raw_path => |include_path| { try zig_args.append("-I"); - try zig_args.append(builder.pathFromRoot(include_path)); + try zig_args.append(b.pathFromRoot(include_path)); }, .raw_path_system => |include_path| { - if (builder.sysroot != null) { + if (b.sysroot != null) { try zig_args.append("-iwithsysroot"); } else { try zig_args.append("-isystem"); } - const resolved_include_path = builder.pathFromRoot(include_path); + const resolved_include_path = b.pathFromRoot(include_path); - const common_include_path = if (builtin.os.tag == .windows and builder.sysroot != null and fs.path.isAbsolute(resolved_include_path)) blk: { + const common_include_path = if (builtin.os.tag == .windows and b.sysroot != null and fs.path.isAbsolute(resolved_include_path)) blk: { // We need to check for disk designator and strip it out from dir path so // that zig/clang can concat resolved_include_path with sysroot. const disk_designator = fs.path.diskDesignatorWindows(resolved_include_path); @@ -1712,7 +1738,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { }, .other_step => |other| { if (other.emit_h) { - const h_path = other.getOutputHSource().getPath(builder); + const h_path = other.getOutputHSource().getPath(b); try zig_args.append("-isystem"); try zig_args.append(fs.path.dirname(h_path).?); } @@ -1721,8 +1747,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { try install_step.make(prog_node); } try zig_args.append("-I"); - try zig_args.append(builder.pathJoin(&.{ - other.builder.install_prefix, "include", + try zig_args.append(b.pathJoin(&.{ + other.step.owner.install_prefix, "include", })); } }, @@ -1751,7 +1777,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { if (self.target.isDarwin()) { for (self.framework_dirs.items) |dir| { - if (builder.sysroot != null) { + if (b.sysroot != null) { try zig_args.append("-iframeworkwithsysroot"); } else { try zig_args.append("-iframework"); @@ -1784,17 +1810,17 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { } } - if (builder.sysroot) |sysroot| { + if (b.sysroot) |sysroot| { try zig_args.appendSlice(&[_][]const u8{ "--sysroot", sysroot }); } - for (builder.search_prefixes.items) |search_prefix| { + for (b.search_prefixes.items) |search_prefix| { try zig_args.append("-L"); - try zig_args.append(builder.pathJoin(&.{ + try zig_args.append(b.pathJoin(&.{ search_prefix, "lib", })); try zig_args.append("-I"); - try zig_args.append(builder.pathJoin(&.{ + try zig_args.append(b.pathJoin(&.{ search_prefix, "include", })); } @@ -1805,15 +1831,15 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { if (self.zig_lib_dir) |dir| { try zig_args.append("--zig-lib-dir"); - try zig_args.append(builder.pathFromRoot(dir)); - } else if (builder.zig_lib_dir) |dir| { + try zig_args.append(b.pathFromRoot(dir)); + } else if (b.zig_lib_dir) |dir| { try zig_args.append("--zig-lib-dir"); try zig_args.append(dir); } if (self.main_pkg_path) |dir| { try zig_args.append("--main-pkg-path"); - try zig_args.append(builder.pathFromRoot(dir)); + try zig_args.append(b.pathFromRoot(dir)); } try addFlag(&zig_args, "PIC", self.force_pic); @@ -1846,15 +1872,15 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { args_length += arg.len + 1; // +1 to account for null terminator } if (args_length >= 30 * 1024) { - try builder.cache_root.handle.makePath("args"); + try b.cache_root.handle.makePath("args"); const args_to_escape = zig_args.items[2..]; - var escaped_args = try ArrayList([]const u8).initCapacity(builder.allocator, args_to_escape.len); + var escaped_args = try ArrayList([]const u8).initCapacity(b.allocator, args_to_escape.len); arg_blk: for (args_to_escape) |arg| { for (arg, 0..) |c, arg_idx| { if (c == '\\' or c == '"') { // Slow path for arguments that need to be escaped. We'll need to allocate and copy - var escaped = try ArrayList(u8).initCapacity(builder.allocator, arg.len + 1); + var escaped = try ArrayList(u8).initCapacity(b.allocator, arg.len + 1); const writer = escaped.writer(); try writer.writeAll(arg[0..arg_idx]); for (arg[arg_idx..]) |to_escape| { @@ -1870,8 +1896,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { // Write the args to zig-cache/args/ to avoid conflicts with // other zig build commands running in parallel. - const partially_quoted = try std.mem.join(builder.allocator, "\" \"", escaped_args.items); - const args = try std.mem.concat(builder.allocator, u8, &[_][]const u8{ "\"", partially_quoted, "\"" }); + const partially_quoted = try std.mem.join(b.allocator, "\" \"", escaped_args.items); + const args = try std.mem.concat(b.allocator, u8, &[_][]const u8{ "\"", partially_quoted, "\"" }); var args_hash: [Sha256.digest_length]u8 = undefined; Sha256.hash(args, &args_hash, .{}); @@ -1883,18 +1909,18 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { ); const args_file = "args" ++ fs.path.sep_str ++ args_hex_hash; - try builder.cache_root.handle.writeFile(args_file, args); + try b.cache_root.handle.writeFile(args_file, args); - const resolved_args_file = try mem.concat(builder.allocator, u8, &.{ + const resolved_args_file = try mem.concat(b.allocator, u8, &.{ "@", - try builder.cache_root.join(builder.allocator, &.{args_file}), + try b.cache_root.join(b.allocator, &.{args_file}), }); zig_args.shrinkRetainingCapacity(2); try zig_args.append(resolved_args_file); } - const output_bin_path = try builder.execFromStep(zig_args.items, &self.step, prog_node); + const output_bin_path = try step.evalZigProcess(zig_args.items, prog_node); const build_output_dir = fs.path.dirname(output_bin_path).?; if (self.output_dir) |output_dir| { @@ -1928,25 +1954,25 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { // Update generated files if (self.output_dir != null) { - self.output_path_source.path = builder.pathJoin( + self.output_path_source.path = b.pathJoin( &.{ self.output_dir.?, self.out_filename }, ); if (self.emit_h) { - self.output_h_path_source.path = builder.pathJoin( + self.output_h_path_source.path = b.pathJoin( &.{ self.output_dir.?, self.out_h_filename }, ); } if (self.target.isWindows() or self.target.isUefi()) { - self.output_pdb_path_source.path = builder.pathJoin( + self.output_pdb_path_source.path = b.pathJoin( &.{ self.output_dir.?, self.out_pdb_filename }, ); } } if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic and self.version != null and self.target.wantSharedLibSymLinks()) { - try doAtomicSymLinks(builder.allocator, self.getOutputSource().getPath(builder), self.major_only_filename.?, self.name_only_filename.?); + try doAtomicSymLinks(b.allocator, self.getOutputSource().getPath(b), self.major_only_filename.?, self.name_only_filename.?); } } diff --git a/lib/std/Build/ConfigHeaderStep.zig b/lib/std/Build/ConfigHeaderStep.zig index 994b71da32..8b4c05bab7 100644 --- a/lib/std/Build/ConfigHeaderStep.zig +++ b/lib/std/Build/ConfigHeaderStep.zig @@ -34,7 +34,6 @@ pub const Value = union(enum) { }; step: Step, -builder: *std.Build, values: std.StringArrayHashMap(Value), output_file: std.Build.GeneratedFile, @@ -49,8 +48,8 @@ pub const Options = struct { first_ret_addr: ?usize = null, }; -pub fn create(builder: *std.Build, options: Options) *ConfigHeaderStep { - const self = builder.allocator.create(ConfigHeaderStep) catch @panic("OOM"); +pub fn create(owner: *std.Build, options: Options) *ConfigHeaderStep { + const self = owner.allocator.create(ConfigHeaderStep) catch @panic("OOM"); var include_path: []const u8 = "config.h"; @@ -69,29 +68,28 @@ pub fn create(builder: *std.Build, options: Options) *ConfigHeaderStep { } const name = if (options.style.getFileSource()) |s| - builder.fmt("configure {s} header {s} to {s}", .{ + owner.fmt("configure {s} header {s} to {s}", .{ @tagName(options.style), s.getDisplayName(), include_path, }) else - builder.fmt("configure {s} header to {s}", .{@tagName(options.style), include_path}); + owner.fmt("configure {s} header to {s}", .{ @tagName(options.style), include_path }); self.* = .{ - .builder = builder, - .step = Step.init(builder.allocator, .{ + .step = Step.init(.{ .id = base_id, .name = name, + .owner = owner, .makeFn = make, .first_ret_addr = options.first_ret_addr orelse @returnAddress(), }), .style = options.style, - .values = std.StringArrayHashMap(Value).init(builder.allocator), + .values = std.StringArrayHashMap(Value).init(owner.allocator), .max_bytes = options.max_bytes, .include_path = include_path, .output_file = .{ .step = &self.step }, }; - return self; } @@ -161,8 +159,9 @@ fn putValue(self: *ConfigHeaderStep, field_name: []const u8, comptime T: type, v fn make(step: *Step, prog_node: *std.Progress.Node) !void { _ = prog_node; + const b = step.owner; const self = @fieldParentPtr(ConfigHeaderStep, "step", step); - const gpa = self.builder.allocator; + const gpa = b.allocator; // The cache is used here not really as a way to speed things up - because writing // the data to a file would probably be very fast - but as a way to find a canonical @@ -191,13 +190,13 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { switch (self.style) { .autoconf => |file_source| { try output.appendSlice(c_generated_line); - const src_path = file_source.getPath(self.builder); + const src_path = file_source.getPath(b); const contents = try std.fs.cwd().readFileAlloc(gpa, src_path, self.max_bytes); try render_autoconf(contents, &output, self.values, src_path); }, .cmake => |file_source| { try output.appendSlice(c_generated_line); - const src_path = file_source.getPath(self.builder); + const src_path = file_source.getPath(b); const contents = try std.fs.cwd().readFileAlloc(gpa, src_path, self.max_bytes); try render_cmake(contents, &output, self.values, src_path); }, @@ -222,7 +221,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { .{std.fmt.fmtSliceHexLower(&digest)}, ) catch unreachable; - const output_dir = try self.builder.cache_root.join(gpa, &.{ "o", &hash_basename }); + const output_dir = try b.cache_root.join(gpa, &.{ "o", &hash_basename }); // If output_path has directory parts, deal with them. Example: // output_dir is zig-cache/o/HASH @@ -242,7 +241,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { try dir.writeFile(std.fs.path.basename(self.include_path), output.items); - self.output_file.path = try std.fs.path.join(self.builder.allocator, &.{ + self.output_file.path = try std.fs.path.join(b.allocator, &.{ output_dir, self.include_path, }); } diff --git a/lib/std/Build/EmulatableRunStep.zig b/lib/std/Build/EmulatableRunStep.zig deleted file mode 100644 index 44387c36f6..0000000000 --- a/lib/std/Build/EmulatableRunStep.zig +++ /dev/null @@ -1,218 +0,0 @@ -//! Unlike `RunStep` this step will provide emulation, when enabled, to run foreign binaries. -//! When a binary is foreign, but emulation for the target is disabled, the specified binary -//! will not be run and therefore also not validated against its output. -//! This step can be useful when wishing to run a built binary on multiple platforms, -//! without having to verify if it's possible to be ran against. - -const std = @import("../std.zig"); -const Step = std.Build.Step; -const CompileStep = std.Build.CompileStep; -const RunStep = std.Build.RunStep; - -const fs = std.fs; -const process = std.process; -const EnvMap = process.EnvMap; - -const EmulatableRunStep = @This(); - -pub const base_id = .emulatable_run; - -const max_stdout_size = 1 * 1024 * 1024; // 1 MiB - -step: Step, -builder: *std.Build, - -/// The artifact (executable) to be run by this step -exe: *CompileStep, - -/// Set this to `null` to ignore the exit code for the purpose of determining a successful execution -expected_term: ?std.ChildProcess.Term = .{ .Exited = 0 }, - -/// Override this field to modify the environment -env_map: ?*EnvMap, - -/// Set this to modify the current working directory -cwd: ?[]const u8, - -stdout_action: RunStep.StdIoAction = .inherit, -stderr_action: RunStep.StdIoAction = .inherit, - -/// When set to true, hides the warning of skipping a foreign binary which cannot be run on the host -/// or through emulation. -hide_foreign_binaries_warning: bool, - -/// Creates a step that will execute the given artifact. This step will allow running the -/// binary through emulation when any of the emulation options such as `enable_rosetta` are set to true. -/// When set to false, and the binary is foreign, running the executable is skipped. -/// Asserts given artifact is an executable. -pub fn create(builder: *std.Build, name: []const u8, artifact: *CompileStep) *EmulatableRunStep { - std.debug.assert(artifact.kind == .exe or artifact.kind == .test_exe); - const self = builder.allocator.create(EmulatableRunStep) catch @panic("OOM"); - - const option_name = "hide-foreign-warnings"; - const hide_warnings = if (builder.available_options_map.get(option_name) == null) warn: { - break :warn builder.option(bool, option_name, "Hide the warning when a foreign binary which is incompatible is skipped") orelse false; - } else false; - - self.* = .{ - .builder = builder, - .step = Step.init(builder.allocator, .{ - .id = .emulatable_run, - .name = name, - .makeFn = make, - }), - .exe = artifact, - .env_map = null, - .cwd = null, - .hide_foreign_binaries_warning = hide_warnings, - }; - self.step.dependOn(&artifact.step); - - return self; -} - -fn make(step: *Step, prog_node: *std.Progress.Node) !void { - _ = prog_node; - const self = @fieldParentPtr(EmulatableRunStep, "step", step); - const host_info = self.builder.host; - - var argv_list = std.ArrayList([]const u8).init(self.builder.allocator); - defer argv_list.deinit(); - - const need_cross_glibc = self.exe.target.isGnuLibC() and self.exe.is_linking_libc; - switch (host_info.getExternalExecutor(self.exe.target_info, .{ - .qemu_fixes_dl = need_cross_glibc and self.builder.glibc_runtimes_dir != null, - .link_libc = self.exe.is_linking_libc, - })) { - .native => {}, - .rosetta => if (!self.builder.enable_rosetta) return warnAboutForeignBinaries(self), - .wine => |bin_name| if (self.builder.enable_wine) { - try argv_list.append(bin_name); - } else return, - .qemu => |bin_name| if (self.builder.enable_qemu) { - const glibc_dir_arg = if (need_cross_glibc) - self.builder.glibc_runtimes_dir orelse return - else - null; - try argv_list.append(bin_name); - if (glibc_dir_arg) |dir| { - // TODO look into making this a call to `linuxTriple`. This - // needs the directory to be called "i686" rather than - // "x86" which is why we do it manually here. - const fmt_str = "{s}" ++ fs.path.sep_str ++ "{s}-{s}-{s}"; - const cpu_arch = self.exe.target.getCpuArch(); - const os_tag = self.exe.target.getOsTag(); - const abi = self.exe.target.getAbi(); - const cpu_arch_name: []const u8 = if (cpu_arch == .x86) - "i686" - else - @tagName(cpu_arch); - const full_dir = try std.fmt.allocPrint(self.builder.allocator, fmt_str, .{ - dir, cpu_arch_name, @tagName(os_tag), @tagName(abi), - }); - - try argv_list.append("-L"); - try argv_list.append(full_dir); - } - } else return warnAboutForeignBinaries(self), - .darling => |bin_name| if (self.builder.enable_darling) { - try argv_list.append(bin_name); - } else return warnAboutForeignBinaries(self), - .wasmtime => |bin_name| if (self.builder.enable_wasmtime) { - try argv_list.append(bin_name); - try argv_list.append("--dir=."); - } else return warnAboutForeignBinaries(self), - else => return warnAboutForeignBinaries(self), - } - - if (self.exe.target.isWindows()) { - // On Windows we don't have rpaths so we have to add .dll search paths to PATH - RunStep.addPathForDynLibsInternal(&self.step, self.builder, self.exe); - } - - const executable_path = self.exe.installed_path orelse self.exe.getOutputSource().getPath(self.builder); - try argv_list.append(executable_path); - - try RunStep.runCommand( - argv_list.items, - self.builder, - self.expected_term, - self.stdout_action, - self.stderr_action, - .Inherit, - self.env_map, - self.cwd, - false, - ); -} - -pub fn expectStdErrEqual(self: *EmulatableRunStep, bytes: []const u8) void { - self.stderr_action = .{ .expect_exact = self.builder.dupe(bytes) }; -} - -pub fn expectStdOutEqual(self: *EmulatableRunStep, bytes: []const u8) void { - self.stdout_action = .{ .expect_exact = self.builder.dupe(bytes) }; -} - -fn warnAboutForeignBinaries(step: *EmulatableRunStep) void { - if (step.hide_foreign_binaries_warning) return; - const builder = step.builder; - const artifact = step.exe; - - const host_name = builder.host.target.zigTriple(builder.allocator) catch @panic("unhandled error"); - const foreign_name = artifact.target.zigTriple(builder.allocator) catch @panic("unhandled error"); - const target_info = std.zig.system.NativeTargetInfo.detect(artifact.target) catch @panic("unhandled error"); - const need_cross_glibc = artifact.target.isGnuLibC() and artifact.is_linking_libc; - switch (builder.host.getExternalExecutor(target_info, .{ - .qemu_fixes_dl = need_cross_glibc and builder.glibc_runtimes_dir != null, - .link_libc = artifact.is_linking_libc, - })) { - .native => unreachable, - .bad_dl => |foreign_dl| { - const host_dl = builder.host.dynamic_linker.get() orelse "(none)"; - std.debug.print("the host system does not appear to be capable of executing binaries from the target because the host dynamic linker is '{s}', while the target dynamic linker is '{s}'. Consider setting the dynamic linker as '{s}'.\n", .{ - host_dl, foreign_dl, host_dl, - }); - }, - .bad_os_or_cpu => { - std.debug.print("the host system ({s}) does not appear to be capable of executing binaries from the target ({s}).\n", .{ - host_name, foreign_name, - }); - }, - .darling => if (!builder.enable_darling) { - std.debug.print( - "the host system ({s}) does not appear to be capable of executing binaries " ++ - "from the target ({s}). Consider enabling darling.\n", - .{ host_name, foreign_name }, - ); - }, - .rosetta => if (!builder.enable_rosetta) { - std.debug.print( - "the host system ({s}) does not appear to be capable of executing binaries " ++ - "from the target ({s}). Consider enabling rosetta.\n", - .{ host_name, foreign_name }, - ); - }, - .wine => if (!builder.enable_wine) { - std.debug.print( - "the host system ({s}) does not appear to be capable of executing binaries " ++ - "from the target ({s}). Consider enabling wine.\n", - .{ host_name, foreign_name }, - ); - }, - .qemu => if (!builder.enable_qemu) { - std.debug.print( - "the host system ({s}) does not appear to be capable of executing binaries " ++ - "from the target ({s}). Consider enabling qemu.\n", - .{ host_name, foreign_name }, - ); - }, - .wasmtime => { - std.debug.print( - "the host system ({s}) does not appear to be capable of executing binaries " ++ - "from the target ({s}). Consider enabling wasmtime.\n", - .{ host_name, foreign_name }, - ); - }, - } -} diff --git a/lib/std/Build/FmtStep.zig b/lib/std/Build/FmtStep.zig index 5efada7507..2a82342336 100644 --- a/lib/std/Build/FmtStep.zig +++ b/lib/std/Build/FmtStep.zig @@ -1,37 +1,73 @@ -const std = @import("../std.zig"); -const Step = std.Build.Step; -const FmtStep = @This(); +//! This step has two modes: +//! * Modify mode: directly modify source files, formatting them in place. +//! * Check mode: fail the step if a non-conforming file is found. + +step: Step, +paths: []const []const u8, +exclude_paths: []const []const u8, +check: bool, pub const base_id = .fmt; -step: Step, -builder: *std.Build, -argv: [][]const u8, +pub const Options = struct { + paths: []const []const u8 = &.{}, + exclude_paths: []const []const u8 = &.{}, + /// If true, fails the build step when any non-conforming files are encountered. + check: bool = false, +}; -pub fn create(builder: *std.Build, paths: []const []const u8) *FmtStep { - const self = builder.allocator.create(FmtStep) catch @panic("OOM"); - const name = "zig fmt"; - self.* = FmtStep{ - .step = Step.init(builder.allocator, .{ - .id = .fmt, +pub fn create(owner: *std.Build, options: Options) *FmtStep { + const self = owner.allocator.create(FmtStep) catch @panic("OOM"); + const name = if (options.check) "zig fmt --check" else "zig fmt"; + self.* = .{ + .step = Step.init(.{ + .id = base_id, .name = name, + .owner = owner, .makeFn = make, }), - .builder = builder, - .argv = builder.allocator.alloc([]u8, paths.len + 2) catch @panic("OOM"), + .paths = options.paths, + .exclude_paths = options.exclude_paths, + .check = options.check, }; - - self.argv[0] = builder.zig_exe; - self.argv[1] = "fmt"; - for (paths, 0..) |path, i| { - self.argv[2 + i] = builder.pathFromRoot(path); - } return self; } fn make(step: *Step, prog_node: *std.Progress.Node) !void { + // zig fmt is fast enough that no progress is needed. _ = prog_node; + + // TODO: if check=false, this means we are modifying source files in place, which + // is an operation that could race against other operations also modifying source files + // in place. In this case, this step should obtain a write lock while making those + // modifications. + + const b = step.owner; + const arena = b.allocator; const self = @fieldParentPtr(FmtStep, "step", step); - return self.builder.spawnChild(self.argv); + var argv: std.ArrayListUnmanaged([]const u8) = .{}; + try argv.ensureUnusedCapacity(arena, 2 + 1 + self.paths.len + 2 * self.exclude_paths.len); + + argv.appendAssumeCapacity(b.zig_exe); + argv.appendAssumeCapacity("fmt"); + + if (self.check) { + argv.appendAssumeCapacity("--check"); + } + + for (self.paths) |p| { + argv.appendAssumeCapacity(b.pathFromRoot(p)); + } + + for (self.exclude_paths) |p| { + argv.appendAssumeCapacity("--exclude"); + argv.appendAssumeCapacity(b.pathFromRoot(p)); + } + + return step.evalChildProcess(argv.items); } + +const std = @import("../std.zig"); +const Step = std.Build.Step; +const FmtStep = @This(); diff --git a/lib/std/Build/InstallArtifactStep.zig b/lib/std/Build/InstallArtifactStep.zig index d8907eb59f..7e35d0a5ee 100644 --- a/lib/std/Build/InstallArtifactStep.zig +++ b/lib/std/Build/InstallArtifactStep.zig @@ -7,23 +7,24 @@ const InstallArtifactStep = @This(); pub const base_id = .install_artifact; step: Step, -builder: *std.Build, +dest_builder: *std.Build, artifact: *CompileStep, dest_dir: InstallDir, pdb_dir: ?InstallDir, h_dir: ?InstallDir, -pub fn create(builder: *std.Build, artifact: *CompileStep) *InstallArtifactStep { +pub fn create(owner: *std.Build, artifact: *CompileStep) *InstallArtifactStep { if (artifact.install_step) |s| return s; - const self = builder.allocator.create(InstallArtifactStep) catch @panic("OOM"); + const self = owner.allocator.create(InstallArtifactStep) catch @panic("OOM"); self.* = InstallArtifactStep{ - .builder = builder, - .step = Step.init(builder.allocator, .{ + .step = Step.init(.{ .id = base_id, - .name = builder.fmt("install {s}", .{artifact.name}), + .name = owner.fmt("install {s}", .{artifact.name}), + .owner = owner, .makeFn = make, }), + .dest_builder = owner, .artifact = artifact, .dest_dir = artifact.override_dest_dir orelse switch (artifact.kind) { .obj => @panic("Cannot install a .obj build artifact."), @@ -43,48 +44,52 @@ pub fn create(builder: *std.Build, artifact: *CompileStep) *InstallArtifactStep self.step.dependOn(&artifact.step); artifact.install_step = self; - builder.pushInstalledFile(self.dest_dir, artifact.out_filename); + owner.pushInstalledFile(self.dest_dir, artifact.out_filename); if (self.artifact.isDynamicLibrary()) { if (artifact.major_only_filename) |name| { - builder.pushInstalledFile(.lib, name); + owner.pushInstalledFile(.lib, name); } if (artifact.name_only_filename) |name| { - builder.pushInstalledFile(.lib, name); + owner.pushInstalledFile(.lib, name); } if (self.artifact.target.isWindows()) { - builder.pushInstalledFile(.lib, artifact.out_lib_filename); + owner.pushInstalledFile(.lib, artifact.out_lib_filename); } } if (self.pdb_dir) |pdb_dir| { - builder.pushInstalledFile(pdb_dir, artifact.out_pdb_filename); + owner.pushInstalledFile(pdb_dir, artifact.out_pdb_filename); } if (self.h_dir) |h_dir| { - builder.pushInstalledFile(h_dir, artifact.out_h_filename); + owner.pushInstalledFile(h_dir, artifact.out_h_filename); } return self; } fn make(step: *Step, prog_node: *std.Progress.Node) !void { _ = prog_node; + const src_builder = step.owner; const self = @fieldParentPtr(InstallArtifactStep, "step", step); - const builder = self.builder; + const dest_builder = self.dest_builder; - const full_dest_path = builder.getInstallPath(self.dest_dir, self.artifact.out_filename); - try builder.updateFile(self.artifact.getOutputSource().getPath(builder), full_dest_path); + const full_dest_path = dest_builder.getInstallPath(self.dest_dir, self.artifact.out_filename); + try src_builder.updateFile( + self.artifact.getOutputSource().getPath(src_builder), + full_dest_path, + ); if (self.artifact.isDynamicLibrary() and self.artifact.version != null and self.artifact.target.wantSharedLibSymLinks()) { - try CompileStep.doAtomicSymLinks(builder.allocator, full_dest_path, self.artifact.major_only_filename.?, self.artifact.name_only_filename.?); + try CompileStep.doAtomicSymLinks(src_builder.allocator, full_dest_path, self.artifact.major_only_filename.?, self.artifact.name_only_filename.?); } if (self.artifact.isDynamicLibrary() and self.artifact.target.isWindows() and self.artifact.emit_implib != .no_emit) { - const full_implib_path = builder.getInstallPath(self.dest_dir, self.artifact.out_lib_filename); - try builder.updateFile(self.artifact.getOutputLibSource().getPath(builder), full_implib_path); + const full_implib_path = dest_builder.getInstallPath(self.dest_dir, self.artifact.out_lib_filename); + try src_builder.updateFile(self.artifact.getOutputLibSource().getPath(src_builder), full_implib_path); } if (self.pdb_dir) |pdb_dir| { - const full_pdb_path = builder.getInstallPath(pdb_dir, self.artifact.out_pdb_filename); - try builder.updateFile(self.artifact.getOutputPdbSource().getPath(builder), full_pdb_path); + const full_pdb_path = dest_builder.getInstallPath(pdb_dir, self.artifact.out_pdb_filename); + try src_builder.updateFile(self.artifact.getOutputPdbSource().getPath(src_builder), full_pdb_path); } if (self.h_dir) |h_dir| { - const full_h_path = builder.getInstallPath(h_dir, self.artifact.out_h_filename); - try builder.updateFile(self.artifact.getOutputHSource().getPath(builder), full_h_path); + const full_h_path = dest_builder.getInstallPath(h_dir, self.artifact.out_h_filename); + try src_builder.updateFile(self.artifact.getOutputHSource().getPath(src_builder), full_h_path); } self.artifact.installed_path = full_dest_path; } diff --git a/lib/std/Build/InstallDirStep.zig b/lib/std/Build/InstallDirStep.zig index bf89d9e7c7..11553d9bc7 100644 --- a/lib/std/Build/InstallDirStep.zig +++ b/lib/std/Build/InstallDirStep.zig @@ -7,11 +7,10 @@ const InstallDirStep = @This(); const log = std.log; step: Step, -builder: *std.Build, options: Options, /// This is used by the build system when a file being installed comes from one /// package but is being installed by another. -override_source_builder: ?*std.Build = null, +dest_builder: *std.Build, pub const base_id = .install_dir; @@ -40,27 +39,26 @@ pub const Options = struct { } }; -pub fn init( - builder: *std.Build, - options: Options, -) InstallDirStep { - builder.pushInstalledFile(options.install_dir, options.install_subdir); +pub fn init(owner: *std.Build, options: Options) InstallDirStep { + owner.pushInstalledFile(options.install_dir, options.install_subdir); return .{ - .builder = builder, - .step = Step.init(builder.allocator, .{ + .step = Step.init(.{ .id = .install_dir, - .name = builder.fmt("install {s}/", .{options.source_dir}), + .name = owner.fmt("install {s}/", .{options.source_dir}), + .owner = owner, .makeFn = make, }), - .options = options.dupe(builder), + .options = options.dupe(owner), + .dest_builder = owner, }; } fn make(step: *Step, prog_node: *std.Progress.Node) !void { _ = prog_node; const self = @fieldParentPtr(InstallDirStep, "step", step); - const dest_prefix = self.builder.getInstallPath(self.options.install_dir, self.options.install_subdir); - const src_builder = self.override_source_builder orelse self.builder; + const dest_builder = self.dest_builder; + const dest_prefix = dest_builder.getInstallPath(self.options.install_dir, self.options.install_subdir); + const src_builder = self.step.owner; const full_src_dir = src_builder.pathFromRoot(self.options.source_dir); var src_dir = std.fs.cwd().openIterableDir(full_src_dir, .{}) catch |err| { log.err("InstallDirStep: unable to open source directory '{s}': {s}", .{ @@ -69,7 +67,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { return error.StepFailed; }; defer src_dir.close(); - var it = try src_dir.walk(self.builder.allocator); + var it = try src_dir.walk(dest_builder.allocator); next_entry: while (try it.next()) |entry| { for (self.options.exclude_extensions) |ext| { if (mem.endsWith(u8, entry.path, ext)) { @@ -77,20 +75,20 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { } } - const full_path = self.builder.pathJoin(&.{ full_src_dir, entry.path }); - const dest_path = self.builder.pathJoin(&.{ dest_prefix, entry.path }); + const full_path = dest_builder.pathJoin(&.{ full_src_dir, entry.path }); + const dest_path = dest_builder.pathJoin(&.{ dest_prefix, entry.path }); switch (entry.kind) { .Directory => try fs.cwd().makePath(dest_path), .File => { for (self.options.blank_extensions) |ext| { if (mem.endsWith(u8, entry.path, ext)) { - try self.builder.truncateFile(dest_path); + try dest_builder.truncateFile(dest_path); continue :next_entry; } } - try self.builder.updateFile(full_path, dest_path); + try dest_builder.updateFile(full_path, dest_path); }, else => continue, } diff --git a/lib/std/Build/InstallFileStep.zig b/lib/std/Build/InstallFileStep.zig index f77b22c112..ed7576f42c 100644 --- a/lib/std/Build/InstallFileStep.zig +++ b/lib/std/Build/InstallFileStep.zig @@ -7,39 +7,40 @@ const InstallFileStep = @This(); pub const base_id = .install_file; step: Step, -builder: *std.Build, source: FileSource, dir: InstallDir, dest_rel_path: []const u8, /// This is used by the build system when a file being installed comes from one /// package but is being installed by another. -override_source_builder: ?*std.Build = null, +dest_builder: *std.Build, pub fn init( - builder: *std.Build, + owner: *std.Build, source: FileSource, dir: InstallDir, dest_rel_path: []const u8, ) InstallFileStep { - builder.pushInstalledFile(dir, dest_rel_path); + owner.pushInstalledFile(dir, dest_rel_path); return InstallFileStep{ - .builder = builder, - .step = Step.init(builder.allocator, .{ - .id = .install_file, - .name = builder.fmt("install {s} to {s}", .{ source.getDisplayName(), dest_rel_path }), + .step = Step.init(.{ + .id = base_id, + .name = owner.fmt("install {s} to {s}", .{ source.getDisplayName(), dest_rel_path }), + .owner = owner, .makeFn = make, }), - .source = source.dupe(builder), - .dir = dir.dupe(builder), - .dest_rel_path = builder.dupePath(dest_rel_path), + .source = source.dupe(owner), + .dir = dir.dupe(owner), + .dest_rel_path = owner.dupePath(dest_rel_path), + .dest_builder = owner, }; } fn make(step: *Step, prog_node: *std.Progress.Node) !void { _ = prog_node; + const src_builder = step.owner; const self = @fieldParentPtr(InstallFileStep, "step", step); - const src_builder = self.override_source_builder orelse self.builder; + const dest_builder = self.dest_builder; const full_src_path = self.source.getPath2(src_builder, step); - const full_dest_path = self.builder.getInstallPath(self.dir, self.dest_rel_path); - try self.builder.updateFile(full_src_path, full_dest_path); + const full_dest_path = dest_builder.getInstallPath(self.dir, self.dest_rel_path); + try dest_builder.updateFile(full_src_path, full_dest_path); } diff --git a/lib/std/Build/LogStep.zig b/lib/std/Build/LogStep.zig deleted file mode 100644 index 25bba747bf..0000000000 --- a/lib/std/Build/LogStep.zig +++ /dev/null @@ -1,28 +0,0 @@ -const std = @import("../std.zig"); -const log = std.log; -const Step = std.Build.Step; -const LogStep = @This(); - -pub const base_id = .log; - -step: Step, -builder: *std.Build, -data: []const u8, - -pub fn init(builder: *std.Build, data: []const u8) LogStep { - return LogStep{ - .builder = builder, - .step = Step.init(builder.allocator, .{ - .id = .log, - .name = builder.fmt("log {s}", .{data}), - .makeFn = make, - }), - .data = builder.dupe(data), - }; -} - -fn make(step: *Step, prog_node: *std.Progress.Node) anyerror!void { - _ = prog_node; - const self = @fieldParentPtr(LogStep, "step", step); - log.info("{s}", .{self.data}); -} diff --git a/lib/std/Build/ObjCopyStep.zig b/lib/std/Build/ObjCopyStep.zig index 839d95903c..7199431ee6 100644 --- a/lib/std/Build/ObjCopyStep.zig +++ b/lib/std/Build/ObjCopyStep.zig @@ -21,7 +21,6 @@ pub const RawFormat = enum { }; step: Step, -builder: *std.Build, file_source: std.Build.FileSource, basename: []const u8, output_file: std.Build.GeneratedFile, @@ -38,18 +37,18 @@ pub const Options = struct { }; pub fn create( - builder: *std.Build, + owner: *std.Build, file_source: std.Build.FileSource, options: Options, ) *ObjCopyStep { - const self = builder.allocator.create(ObjCopyStep) catch @panic("OOM"); + const self = owner.allocator.create(ObjCopyStep) catch @panic("OOM"); self.* = ObjCopyStep{ - .step = Step.init(builder.allocator, .{ + .step = Step.init(.{ .id = base_id, - .name = builder.fmt("objcopy {s}", .{file_source.getDisplayName()}), + .name = owner.fmt("objcopy {s}", .{file_source.getDisplayName()}), + .owner = owner, .makeFn = make, }), - .builder = builder, .file_source = file_source, .basename = options.basename orelse file_source.getDisplayName(), .output_file = std.Build.GeneratedFile{ .step = &self.step }, @@ -67,9 +66,8 @@ pub fn getOutputSource(self: *const ObjCopyStep) std.Build.FileSource { } fn make(step: *Step, prog_node: *std.Progress.Node) !void { - _ = prog_node; + const b = step.owner; const self = @fieldParentPtr(ObjCopyStep, "step", step); - const b = self.builder; var man = b.cache.obtain(); defer man.deinit(); @@ -84,7 +82,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { man.hash.addOptional(self.pad_to); man.hash.addOptional(self.format); - if (man.hit() catch |err| failWithCacheError(man, err)) { + if (try step.cacheHit(&man)) { // Cache hit, skip subprocess execution. const digest = man.final(); self.output_file.path = try b.cache_root.join(b.allocator, &.{ @@ -116,23 +114,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { }; try argv.appendSlice(&.{ full_src_path, full_dest_path }); - _ = try self.builder.execFromStep(argv.items, &self.step); + _ = try step.spawnZigProcess(argv.items, prog_node); self.output_file.path = full_dest_path; try man.writeManifest(); } - -/// TODO consolidate this with the same function in RunStep? -/// Also properly deal with concurrency (see open PR) -fn failWithCacheError(man: std.Build.Cache.Manifest, err: anyerror) noreturn { - const i = man.failed_file_index orelse failWithSimpleError(err); - const pp = man.files.items[i].prefixed_path orelse failWithSimpleError(err); - const prefix = man.cache.prefixes()[pp.prefix].path orelse ""; - std.debug.print("{s}: {s}/{s}\n", .{ @errorName(err), prefix, pp.sub_path }); - std.process.exit(1); -} - -fn failWithSimpleError(err: anyerror) noreturn { - std.debug.print("{s}\n", .{@errorName(err)}); - std.process.exit(1); -} diff --git a/lib/std/Build/OptionsStep.zig b/lib/std/Build/OptionsStep.zig index 2cb3bb13be..859d0b68c9 100644 --- a/lib/std/Build/OptionsStep.zig +++ b/lib/std/Build/OptionsStep.zig @@ -12,25 +12,24 @@ pub const base_id = .options; step: Step, generated_file: GeneratedFile, -builder: *std.Build, contents: std.ArrayList(u8), artifact_args: std.ArrayList(OptionArtifactArg), file_source_args: std.ArrayList(OptionFileSourceArg), -pub fn create(builder: *std.Build) *OptionsStep { - const self = builder.allocator.create(OptionsStep) catch @panic("OOM"); +pub fn create(owner: *std.Build) *OptionsStep { + const self = owner.allocator.create(OptionsStep) catch @panic("OOM"); self.* = .{ - .builder = builder, - .step = Step.init(builder.allocator, .{ + .step = Step.init(.{ .id = base_id, .name = "options", + .owner = owner, .makeFn = make, }), .generated_file = undefined, - .contents = std.ArrayList(u8).init(builder.allocator), - .artifact_args = std.ArrayList(OptionArtifactArg).init(builder.allocator), - .file_source_args = std.ArrayList(OptionFileSourceArg).init(builder.allocator), + .contents = std.ArrayList(u8).init(owner.allocator), + .artifact_args = std.ArrayList(OptionArtifactArg).init(owner.allocator), + .file_source_args = std.ArrayList(OptionFileSourceArg).init(owner.allocator), }; self.generated_file = .{ .step = &self.step }; @@ -196,7 +195,7 @@ pub fn addOptionFileSource( ) void { self.file_source_args.append(.{ .name = name, - .source = source.dupe(self.builder), + .source = source.dupe(self.step.owner), }) catch @panic("OOM"); source.addStepDependencies(&self.step); } @@ -204,12 +203,12 @@ pub fn addOptionFileSource( /// The value is the path in the cache dir. /// Adds a dependency automatically. pub fn addOptionArtifact(self: *OptionsStep, name: []const u8, artifact: *CompileStep) void { - self.artifact_args.append(.{ .name = self.builder.dupe(name), .artifact = artifact }) catch @panic("OOM"); + self.artifact_args.append(.{ .name = self.step.owner.dupe(name), .artifact = artifact }) catch @panic("OOM"); self.step.dependOn(&artifact.step); } pub fn createModule(self: *OptionsStep) *std.Build.Module { - return self.builder.createModule(.{ + return self.step.owner.createModule(.{ .source_file = self.getSource(), .dependencies = &.{}, }); @@ -220,14 +219,17 @@ pub fn getSource(self: *OptionsStep) FileSource { } fn make(step: *Step, prog_node: *std.Progress.Node) !void { + // This step completes so quickly that no progress is necessary. _ = prog_node; + + const b = step.owner; const self = @fieldParentPtr(OptionsStep, "step", step); for (self.artifact_args.items) |item| { self.addOption( []const u8, item.name, - self.builder.pathFromRoot(item.artifact.getOutputSource().getPath(self.builder)), + b.pathFromRoot(item.artifact.getOutputSource().getPath(b)), ); } @@ -235,20 +237,18 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { self.addOption( []const u8, item.name, - item.source.getPath(self.builder), + item.source.getPath(b), ); } - var options_dir = try self.builder.cache_root.handle.makeOpenPath("options", .{}); + var options_dir = try b.cache_root.handle.makeOpenPath("options", .{}); defer options_dir.close(); const basename = self.hashContentsToFileName(); try options_dir.writeFile(&basename, self.contents.items); - self.generated_file.path = try self.builder.cache_root.join(self.builder.allocator, &.{ - "options", &basename, - }); + self.generated_file.path = try b.cache_root.join(b.allocator, &.{ "options", &basename }); } fn hashContentsToFileName(self: *OptionsStep) [64]u8 { diff --git a/lib/std/Build/RemoveDirStep.zig b/lib/std/Build/RemoveDirStep.zig index 4fc8e6d338..9f291c7523 100644 --- a/lib/std/Build/RemoveDirStep.zig +++ b/lib/std/Build/RemoveDirStep.zig @@ -7,28 +7,37 @@ const RemoveDirStep = @This(); pub const base_id = .remove_dir; step: Step, -builder: *std.Build, dir_path: []const u8, -pub fn init(builder: *std.Build, dir_path: []const u8) RemoveDirStep { +pub fn init(owner: *std.Build, dir_path: []const u8) RemoveDirStep { return RemoveDirStep{ - .builder = builder, - .step = Step.init(builder.allocator, .{ + .step = Step.init(.{ .id = .remove_dir, - .name = builder.fmt("RemoveDir {s}", .{dir_path}), + .name = owner.fmt("RemoveDir {s}", .{dir_path}), + .owner = owner, .makeFn = make, }), - .dir_path = builder.dupePath(dir_path), + .dir_path = owner.dupePath(dir_path), }; } fn make(step: *Step, prog_node: *std.Progress.Node) !void { + // TODO update progress node while walking file system. + // Should the standard library support this use case?? _ = prog_node; + + const b = step.owner; const self = @fieldParentPtr(RemoveDirStep, "step", step); - const full_path = self.builder.pathFromRoot(self.dir_path); - fs.cwd().deleteTree(full_path) catch |err| { - log.err("Unable to remove {s}: {s}", .{ full_path, @errorName(err) }); - return err; + b.build_root.handle.deleteTree(self.dir_path) catch |err| { + if (b.build_root.path) |base| { + return step.fail("unable to recursively delete path '{s}/{s}': {s}", .{ + base, self.dir_path, @errorName(err), + }); + } else { + return step.fail("unable to recursively delete path '{s}': {s}", .{ + self.dir_path, @errorName(err), + }); + } }; } diff --git a/lib/std/Build/RunStep.zig b/lib/std/Build/RunStep.zig index 84fd7f975d..087483fea8 100644 --- a/lib/std/Build/RunStep.zig +++ b/lib/std/Build/RunStep.zig @@ -11,14 +11,11 @@ const EnvMap = process.EnvMap; const Allocator = mem.Allocator; const ExecError = std.Build.ExecError; -const max_stdout_size = 1 * 1024 * 1024; // 1 MiB - const RunStep = @This(); pub const base_id: Step.Id = .run; step: Step, -builder: *std.Build, /// See also addArg and addArgs to modifying this directly argv: ArrayList(Arg), @@ -29,35 +26,68 @@ cwd: ?[]const u8, /// Override this field to modify the environment, or use setEnvironmentVariable env_map: ?*EnvMap, -stdout_action: StdIoAction = .inherit, -stderr_action: StdIoAction = .inherit, - -stdin_behavior: std.ChildProcess.StdIo = .Inherit, - -/// Set this to `null` to ignore the exit code for the purpose of determining a successful execution -expected_term: ?std.ChildProcess.Term = .{ .Exited = 0 }, - -/// Print the command before running it -print: bool, -/// Controls whether execution is skipped if the output file is up-to-date. -/// The default is to always run if there is no output file, and to skip -/// running if all output files are up-to-date. -condition: enum { output_outdated, always } = .output_outdated, +/// Configures whether the RunStep is considered to have side-effects, and also +/// whether the RunStep will inherit stdio streams, forwarding them to the +/// parent process, in which case will require a global lock to prevent other +/// steps from interfering with stdio while the subprocess associated with this +/// RunStep is running. +/// If the RunStep is determined to not have side-effects, then execution will +/// be skipped if all output files are up-to-date and input files are +/// unchanged. +stdio: StdIo = .infer_from_args, /// Additional file paths relative to build.zig that, when modified, indicate /// that the RunStep should be re-executed. +/// If the RunStep is determined to have side-effects, this field is ignored +/// and the RunStep is always executed when it appears in the build graph. extra_file_dependencies: []const []const u8 = &.{}, /// After adding an output argument, this step will by default rename itself /// for a better display name in the build summary. /// This can be disabled by setting this to false. -rename_step_with_output_arg: bool, +rename_step_with_output_arg: bool = true, -pub const StdIoAction = union(enum) { +/// If this is true, a RunStep which is configured to check the output of the +/// executed binary will not fail the build if the binary cannot be executed +/// due to being for a foreign binary to the host system which is running the +/// build graph. +/// Command-line arguments such as -fqemu and -fwasmtime may affect whether a +/// binary is detected as foreign, as well as system configuration such as +/// Rosetta (macOS) and binfmt_misc (Linux). +skip_foreign_checks: bool = false, + +/// If stderr or stdout exceeds this amount, the child process is killed and +/// the step fails. +max_stdio_size: usize = 10 * 1024 * 1024, + +pub const StdIo = union(enum) { + /// Whether the RunStep has side-effects will be determined by whether or not one + /// of the args is an output file (added with `addOutputFileArg`). + /// If the RunStep is determined to have side-effects, this is the same as `inherit`. + /// The step will fail if the subprocess crashes or returns a non-zero exit code. + infer_from_args, + /// Causes the RunStep to be considered to have side-effects, and therefore + /// always execute when it appears in the build graph. + /// It also means that this step will obtain a global lock to prevent other + /// steps from running in the meantime. + /// The step will fail if the subprocess crashes or returns a non-zero exit code. inherit, - ignore, - expect_exact: []const u8, - expect_matches: []const []const u8, + /// Causes the RunStep to be considered to *not* have side-effects. The + /// process will be re-executed if any of the input dependencies are + /// modified. The exit code and standard I/O streams will be checked for + /// certain conditions, and the step will succeed or fail based on these + /// conditions. + /// Note that an explicit check for exit code 0 needs to be added to this + /// list if such a check is desireable. + check: []const Check, + + pub const Check = union(enum) { + expect_stderr_exact: []const u8, + expect_stderr_match: []const u8, + expect_stdout_exact: []const u8, + expect_stdout_match: []const u8, + expect_term: std.ChildProcess.Term, + }; }; pub const Arg = union(enum) { @@ -72,20 +102,20 @@ pub const Arg = union(enum) { }; }; -pub fn create(builder: *std.Build, name: []const u8) *RunStep { - const self = builder.allocator.create(RunStep) catch @panic("OOM"); +pub fn create(owner: *std.Build, name: []const u8) *RunStep { + const self = owner.allocator.create(RunStep) catch @panic("OOM"); self.* = .{ - .builder = builder, - .step = Step.init(builder.allocator, .{ + .step = Step.init(.{ .id = base_id, .name = name, + .owner = owner, .makeFn = make, }), - .argv = ArrayList(Arg).init(builder.allocator), + .argv = ArrayList(Arg).init(owner.allocator), .cwd = null, .env_map = null, - .print = builder.verbose, .rename_step_with_output_arg = true, + .max_stdio_size = 10 * 1024 * 1024, }; return self; } @@ -99,16 +129,17 @@ pub fn addArtifactArg(self: *RunStep, artifact: *CompileStep) void { /// run, and returns a FileSource which can be used as inputs to other APIs /// throughout the build system. pub fn addOutputFileArg(rs: *RunStep, basename: []const u8) std.Build.FileSource { - const generated_file = rs.builder.allocator.create(std.Build.GeneratedFile) catch @panic("OOM"); + const b = rs.step.owner; + const generated_file = b.allocator.create(std.Build.GeneratedFile) catch @panic("OOM"); generated_file.* = .{ .step = &rs.step }; rs.argv.append(.{ .output = .{ .generated_file = generated_file, - .basename = rs.builder.dupe(basename), + .basename = b.dupe(basename), } }) catch @panic("OOM"); if (rs.rename_step_with_output_arg) { rs.rename_step_with_output_arg = false; - rs.step.name = rs.builder.fmt("{s} ({s})", .{ rs.step.name, basename }); + rs.step.name = b.fmt("{s} ({s})", .{ rs.step.name, basename }); } return .{ .generated = generated_file }; @@ -116,13 +147,13 @@ pub fn addOutputFileArg(rs: *RunStep, basename: []const u8) std.Build.FileSource pub fn addFileSourceArg(self: *RunStep, file_source: std.Build.FileSource) void { self.argv.append(Arg{ - .file_source = file_source.dupe(self.builder), + .file_source = file_source.dupe(self.step.owner), }) catch @panic("OOM"); file_source.addStepDependencies(&self.step); } pub fn addArg(self: *RunStep, arg: []const u8) void { - self.argv.append(Arg{ .bytes = self.builder.dupe(arg) }) catch @panic("OOM"); + self.argv.append(Arg{ .bytes = self.step.owner.dupe(arg) }) catch @panic("OOM"); } pub fn addArgs(self: *RunStep, args: []const []const u8) void { @@ -132,13 +163,14 @@ pub fn addArgs(self: *RunStep, args: []const []const u8) void { } pub fn clearEnvironment(self: *RunStep) void { - const new_env_map = self.builder.allocator.create(EnvMap) catch @panic("OOM"); - new_env_map.* = EnvMap.init(self.builder.allocator); + const b = self.step.owner; + const new_env_map = b.allocator.create(EnvMap) catch @panic("OOM"); + new_env_map.* = EnvMap.init(b.allocator); self.env_map = new_env_map; } pub fn addPathDir(self: *RunStep, search_path: []const u8) void { - addPathDirInternal(&self.step, self.builder, search_path); + addPathDirInternal(&self.step, self.step.owner, search_path); } /// For internal use only, users of `RunStep` should use `addPathDir` directly. @@ -157,13 +189,12 @@ pub fn addPathDirInternal(step: *Step, builder: *std.Build, search_path: []const } pub fn getEnvMap(self: *RunStep) *EnvMap { - return getEnvMapInternal(&self.step, self.builder.allocator); + return getEnvMapInternal(&self.step, self.step.owner.allocator); } fn getEnvMapInternal(step: *Step, allocator: Allocator) *EnvMap { const maybe_env_map = switch (step.id) { .run => step.cast(RunStep).?.env_map, - .emulatable_run => step.cast(std.Build.EmulatableRunStep).?.env_map, else => unreachable, }; return maybe_env_map orelse { @@ -171,7 +202,6 @@ fn getEnvMapInternal(step: *Step, allocator: Allocator) *EnvMap { env_map.* = process.getEnvMap(allocator) catch @panic("unhandled error"); switch (step.id) { .run => step.cast(RunStep).?.env_map = env_map, - .emulatable_run => step.cast(RunStep).?.env_map = env_map, else => unreachable, } return env_map; @@ -179,41 +209,85 @@ fn getEnvMapInternal(step: *Step, allocator: Allocator) *EnvMap { } pub fn setEnvironmentVariable(self: *RunStep, key: []const u8, value: []const u8) void { + const b = self.step.owner; const env_map = self.getEnvMap(); - env_map.put( - self.builder.dupe(key), - self.builder.dupe(value), - ) catch @panic("unhandled error"); + env_map.put(b.dupe(key), b.dupe(value)) catch @panic("unhandled error"); } pub fn expectStdErrEqual(self: *RunStep, bytes: []const u8) void { - self.stderr_action = .{ .expect_exact = self.builder.dupe(bytes) }; + const new_check: StdIo.Check = .{ .expect_stderr_exact = self.step.owner.dupe(bytes) }; + self.addCheck(new_check); } pub fn expectStdOutEqual(self: *RunStep, bytes: []const u8) void { - self.stdout_action = .{ .expect_exact = self.builder.dupe(bytes) }; + const new_check: StdIo.Check = .{ .expect_stdout_exact = self.step.owner.dupe(bytes) }; + self.addCheck(new_check); } -fn stdIoActionToBehavior(action: StdIoAction) std.ChildProcess.StdIo { - return switch (action) { - .ignore => .Ignore, - .inherit => .Inherit, - .expect_exact, .expect_matches => .Pipe, +pub fn expectExitCode(self: *RunStep, code: u8) void { + const new_check: StdIo.Check = .{ .expect_term = .{ .Exited = code } }; + self.addCheck(new_check); +} + +pub fn addCheck(self: *RunStep, new_check: StdIo.Check) void { + const arena = self.step.owner.allocator; + switch (self.stdio) { + .infer_from_args => { + const list = arena.create([1]StdIo.Check) catch @panic("OOM"); + list.* = .{new_check}; + self.stdio = .{ .check = list }; + }, + .check => |checks| { + const new_list = arena.alloc(StdIo.Check, checks.len + 1) catch @panic("OOM"); + std.mem.copy(StdIo.Check, new_list, checks); + new_list[checks.len] = new_check; + }, + else => @panic("illegal call to addCheck: conflicting helper method calls. Suggest to directly set stdio field of RunStep instead"), + } +} + +/// Returns whether the RunStep has side effects *other than* updating the output arguments. +fn hasSideEffects(self: RunStep) bool { + return switch (self.stdio) { + .infer_from_args => !self.hasAnyOutputArgs(), + .inherit => true, + .check => false, }; } -fn needOutputCheck(self: RunStep) bool { - switch (self.condition) { - .always => return false, - .output_outdated => {}, - } - if (self.extra_file_dependencies.len > 0) return true; - +fn hasAnyOutputArgs(self: RunStep) bool { for (self.argv.items) |arg| switch (arg) { .output => return true, else => continue, }; + return false; +} +fn checksContainStdout(checks: []const StdIo.Check) bool { + for (checks) |check| switch (check) { + .expect_stderr_exact, + .expect_stderr_match, + .expect_term, + => continue, + + .expect_stdout_exact, + .expect_stdout_match, + => return true, + }; + return false; +} + +fn checksContainStderr(checks: []const StdIo.Check) bool { + for (checks) |check| switch (check) { + .expect_stdout_exact, + .expect_stdout_match, + .expect_term, + => continue, + + .expect_stderr_exact, + .expect_stderr_match, + => return true, + }; return false; } @@ -223,16 +297,17 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { // processes could use to supply progress updates. _ = prog_node; + const b = step.owner; const self = @fieldParentPtr(RunStep, "step", step); - const need_output_check = self.needOutputCheck(); + const has_side_effects = self.hasSideEffects(); - var argv_list = ArrayList([]const u8).init(self.builder.allocator); + var argv_list = ArrayList([]const u8).init(b.allocator); var output_placeholders = ArrayList(struct { index: usize, output: Arg.Output, - }).init(self.builder.allocator); + }).init(b.allocator); - var man = self.builder.cache.obtain(); + var man = b.cache.obtain(); defer man.deinit(); for (self.argv.items) |arg| { @@ -242,7 +317,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { man.hash.addBytes(bytes); }, .file_source => |file| { - const file_path = file.getPath(self.builder); + const file_path = file.getPath(b); try argv_list.append(file_path); _ = try man.addFile(file_path, null); }, @@ -252,7 +327,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { self.addPathForDynLibs(artifact); } const file_path = artifact.installed_path orelse - artifact.getOutputSource().getPath(self.builder); + artifact.getOutputSource().getPath(b); try argv_list.append(file_path); @@ -272,17 +347,17 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { } } - if (need_output_check) { + if (!has_side_effects) { for (self.extra_file_dependencies) |file_path| { - _ = try man.addFile(self.builder.pathFromRoot(file_path), null); + _ = try man.addFile(b.pathFromRoot(file_path), null); } - if (man.hit() catch |err| failWithCacheError(man, err)) { + if (try step.cacheHit(&man)) { // cache hit, skip running command const digest = man.final(); for (output_placeholders.items) |placeholder| { - placeholder.output.generated_file.path = try self.builder.cache_root.join( - self.builder.allocator, + placeholder.output.generated_file.path = try b.cache_root.join( + b.allocator, &.{ "o", &digest, placeholder.output.basename }, ); } @@ -292,8 +367,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { const digest = man.final(); for (output_placeholders.items) |placeholder| { - const output_path = try self.builder.cache_root.join( - self.builder.allocator, + const output_path = try b.cache_root.join( + b.allocator, &.{ "o", &digest, placeholder.output.basename }, ); const output_dir = fs.path.dirname(output_path).?; @@ -308,18 +383,16 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { } try runCommand( - argv_list.items, - self.builder, - self.expected_term, - self.stdout_action, - self.stderr_action, - self.stdin_behavior, - self.env_map, + step, self.cwd, - self.print, + argv_list.items, + self.env_map, + self.stdio, + has_side_effects, + self.max_stdio_size, ); - if (need_output_check) { + if (!has_side_effects) { try man.writeManifest(); } } @@ -369,165 +442,171 @@ fn termMatches(expected: ?std.ChildProcess.Term, actual: std.ChildProcess.Term) }; } -pub fn runCommand( +fn runCommand( + step: *Step, + opt_cwd: ?[]const u8, argv: []const []const u8, - builder: *std.Build, - expected_term: ?std.ChildProcess.Term, - stdout_action: StdIoAction, - stderr_action: StdIoAction, - stdin_behavior: std.ChildProcess.StdIo, env_map: ?*EnvMap, - maybe_cwd: ?[]const u8, - print: bool, + stdio: StdIo, + has_side_effects: bool, + max_stdio_size: usize, ) !void { - const cwd = if (maybe_cwd) |cwd| builder.pathFromRoot(cwd) else builder.build_root.path; + const b = step.owner; + const arena = b.allocator; + const cwd = if (opt_cwd) |cwd| b.pathFromRoot(cwd) else b.build_root.path; - if (!std.process.can_spawn) { - const cmd = try std.mem.join(builder.allocator, " ", argv); - std.debug.print("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{ - @tagName(builtin.os.tag), cmd, - }); - builder.allocator.free(cmd); - return ExecError.ExecNotSupported; - } + try step.handleChildProcUnsupported(opt_cwd, argv); + try Step.handleVerbose(step.owner, opt_cwd, argv); - var child = std.ChildProcess.init(argv, builder.allocator); + var child = std.ChildProcess.init(argv, arena); child.cwd = cwd; - child.env_map = env_map orelse builder.env_map; + child.env_map = env_map orelse b.env_map; - child.stdin_behavior = stdin_behavior; - child.stdout_behavior = stdIoActionToBehavior(stdout_action); - child.stderr_behavior = stdIoActionToBehavior(stderr_action); - - if (print) - printCmd(cwd, argv); - - child.spawn() catch |err| { - std.debug.print("Unable to spawn {s}: {s}\n", .{ argv[0], @errorName(err) }); - return err; + child.stdin_behavior = switch (stdio) { + .infer_from_args => if (has_side_effects) .Inherit else .Ignore, + .inherit => .Inherit, + .check => .Close, + }; + child.stdout_behavior = switch (stdio) { + .infer_from_args => if (has_side_effects) .Inherit else .Ignore, + .inherit => .Inherit, + .check => |checks| if (checksContainStdout(checks)) .Pipe else .Ignore, + }; + child.stderr_behavior = switch (stdio) { + .infer_from_args => if (has_side_effects) .Inherit else .Pipe, + .inherit => .Inherit, + .check => .Pipe, }; - // TODO need to poll to read these streams to prevent a deadlock (or rely on evented I/O). + child.spawn() catch |err| return step.fail("unable to spawn {s}: {s}", .{ + argv[0], @errorName(err), + }); - var stdout: ?[]const u8 = null; - defer if (stdout) |s| builder.allocator.free(s); + var stdout_bytes: ?[]const u8 = null; + var stderr_bytes: ?[]const u8 = null; - switch (stdout_action) { - .expect_exact, .expect_matches => { - stdout = try child.stdout.?.reader().readAllAlloc(builder.allocator, max_stdout_size); - }, - .inherit, .ignore => {}, + if (child.stdout) |stdout| { + if (child.stderr) |stderr| { + var poller = std.io.poll(arena, enum { stdout, stderr }, .{ + .stdout = stdout, + .stderr = stderr, + }); + defer poller.deinit(); + + while (try poller.poll()) { + if (poller.fifo(.stdout).count > max_stdio_size) + return error.StdoutStreamTooLong; + if (poller.fifo(.stderr).count > max_stdio_size) + return error.StderrStreamTooLong; + } + + stdout_bytes = try poller.fifo(.stdout).toOwnedSlice(); + stderr_bytes = try poller.fifo(.stderr).toOwnedSlice(); + } else { + stdout_bytes = try stdout.reader().readAllAlloc(arena, max_stdio_size); + } + } else if (child.stderr) |stderr| { + stderr_bytes = try stderr.reader().readAllAlloc(arena, max_stdio_size); } - var stderr: ?[]const u8 = null; - defer if (stderr) |s| builder.allocator.free(s); - - switch (stderr_action) { - .expect_exact, .expect_matches => { - stderr = try child.stderr.?.reader().readAllAlloc(builder.allocator, max_stdout_size); - }, - .inherit, .ignore => {}, - } + if (stderr_bytes) |stderr| if (stderr.len > 0) { + const stderr_is_diagnostic = switch (stdio) { + .check => |checks| !checksContainStderr(checks), + else => true, + }; + if (stderr_is_diagnostic) { + try step.result_error_msgs.append(arena, stderr); + } + }; const term = child.wait() catch |err| { - std.debug.print("Unable to spawn {s}: {s}\n", .{ argv[0], @errorName(err) }); - return err; + return step.fail("unable to wait for {s}: {s}", .{ argv[0], @errorName(err) }); }; - if (!termMatches(expected_term, term)) { - std.debug.print("The following command {} (expected {}):\n", .{ fmtTerm(term), fmtTerm(expected_term) }); - printCmd(cwd, argv); - return error.UnexpectedExit; - } - - switch (stderr_action) { - .inherit, .ignore => {}, - .expect_exact => |expected_bytes| { - if (!mem.eql(u8, expected_bytes, stderr.?)) { - std.debug.print( - \\ - \\========= Expected this stderr: ========= - \\{s} - \\========= But found: ==================== - \\{s} - \\ - , .{ expected_bytes, stderr.? }); - printCmd(cwd, argv); - return error.TestFailed; - } + switch (stdio) { + .check => |checks| for (checks) |check| switch (check) { + .expect_stderr_exact => |expected_bytes| { + if (!mem.eql(u8, expected_bytes, stderr_bytes.?)) { + return step.fail( + \\========= expected this stderr: ========= + \\{s} + \\========= but found: ==================== + \\{s} + \\========= from the following command: === + \\{s} + , .{ + expected_bytes, + stderr_bytes.?, + try Step.allocPrintCmd(arena, opt_cwd, argv), + }); + } + }, + .expect_stderr_match => |match| { + if (mem.indexOf(u8, stderr_bytes.?, match) == null) { + return step.fail( + \\========= expected to find in stderr: ========= + \\{s} + \\========= but stderr does not contain it: ===== + \\{s} + \\========= from the following command: ========= + \\{s} + , .{ + match, + stderr_bytes.?, + try Step.allocPrintCmd(arena, opt_cwd, argv), + }); + } + }, + .expect_stdout_exact => |expected_bytes| { + if (!mem.eql(u8, expected_bytes, stdout_bytes.?)) { + return step.fail( + \\========= expected this stdout: ========= + \\{s} + \\========= but found: ==================== + \\{s} + \\========= from the following command: === + \\{s} + , .{ + expected_bytes, + stdout_bytes.?, + try Step.allocPrintCmd(arena, opt_cwd, argv), + }); + } + }, + .expect_stdout_match => |match| { + if (mem.indexOf(u8, stdout_bytes.?, match) == null) { + return step.fail( + \\========= expected to find in stdout: ========= + \\{s} + \\========= but stdout does not contain it: ===== + \\{s} + \\========= from the following command: ========= + \\{s} + , .{ + match, + stdout_bytes.?, + try Step.allocPrintCmd(arena, opt_cwd, argv), + }); + } + }, + .expect_term => |expected_term| { + if (!termMatches(expected_term, term)) { + return step.fail("the following command {} (expected {}):\n{s}", .{ + fmtTerm(term), + fmtTerm(expected_term), + try Step.allocPrintCmd(arena, opt_cwd, argv), + }); + } + }, }, - .expect_matches => |matches| for (matches) |match| { - if (mem.indexOf(u8, stderr.?, match) == null) { - std.debug.print( - \\ - \\========= Expected to find in stderr: ========= - \\{s} - \\========= But stderr does not contain it: ===== - \\{s} - \\ - , .{ match, stderr.? }); - printCmd(cwd, argv); - return error.TestFailed; - } + else => { + try step.handleChildProcessTerm(term, opt_cwd, argv); }, } - - switch (stdout_action) { - .inherit, .ignore => {}, - .expect_exact => |expected_bytes| { - if (!mem.eql(u8, expected_bytes, stdout.?)) { - std.debug.print( - \\ - \\========= Expected this stdout: ========= - \\{s} - \\========= But found: ==================== - \\{s} - \\ - , .{ expected_bytes, stdout.? }); - printCmd(cwd, argv); - return error.TestFailed; - } - }, - .expect_matches => |matches| for (matches) |match| { - if (mem.indexOf(u8, stdout.?, match) == null) { - std.debug.print( - \\ - \\========= Expected to find in stdout: ========= - \\{s} - \\========= But stdout does not contain it: ===== - \\{s} - \\ - , .{ match, stdout.? }); - printCmd(cwd, argv); - return error.TestFailed; - } - }, - } -} - -fn failWithCacheError(man: std.Build.Cache.Manifest, err: anyerror) noreturn { - const i = man.failed_file_index orelse failWithSimpleError(err); - const pp = man.files.items[i].prefixed_path orelse failWithSimpleError(err); - const prefix = man.cache.prefixes()[pp.prefix].path orelse ""; - std.debug.print("{s}: {s}/{s}\n", .{ @errorName(err), prefix, pp.sub_path }); - std.process.exit(1); -} - -fn failWithSimpleError(err: anyerror) noreturn { - std.debug.print("{s}\n", .{@errorName(err)}); - std.process.exit(1); -} - -fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void { - if (cwd) |yes_cwd| std.debug.print("cd {s} && ", .{yes_cwd}); - for (argv) |arg| { - std.debug.print("{s} ", .{arg}); - } - std.debug.print("\n", .{}); } fn addPathForDynLibs(self: *RunStep, artifact: *CompileStep) void { - addPathForDynLibsInternal(&self.step, self.builder, artifact); + addPathForDynLibsInternal(&self.step, self.step.owner, artifact); } /// This should only be used for internal usage, this is called automatically diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig index 29cf38b55b..f5ac9e1821 100644 --- a/lib/std/Build/Step.zig +++ b/lib/std/Build/Step.zig @@ -1,5 +1,6 @@ id: Id, name: []const u8, +owner: *Build, makeFn: MakeFn, dependencies: std.ArrayList(*Step), /// This field is empty during execution of the user's build script, and @@ -39,7 +40,6 @@ pub const Id = enum { translate_c, write_file, run, - emulatable_run, check_file, check_object, config_header, @@ -60,7 +60,6 @@ pub const Id = enum { .translate_c => Build.TranslateCStep, .write_file => Build.WriteFileStep, .run => Build.RunStep, - .emulatable_run => Build.EmulatableRunStep, .check_file => Build.CheckFileStep, .check_object => Build.CheckObjectStep, .config_header => Build.ConfigHeaderStep, @@ -74,11 +73,14 @@ pub const Id = enum { pub const Options = struct { id: Id, name: []const u8, + owner: *Build, makeFn: MakeFn = makeNoOp, first_ret_addr: ?usize = null, }; -pub fn init(allocator: Allocator, options: Options) Step { +pub fn init(options: Options) Step { + const arena = options.owner.allocator; + var addresses = [1]usize{0} ** n_debug_stack_frames; const first_ret_addr = options.first_ret_addr orelse @returnAddress(); var stack_trace = std.builtin.StackTrace{ @@ -89,9 +91,10 @@ pub fn init(allocator: Allocator, options: Options) Step { return .{ .id = options.id, - .name = allocator.dupe(u8, options.name) catch @panic("OOM"), + .name = arena.dupe(u8, options.name) catch @panic("OOM"), + .owner = options.owner, .makeFn = options.makeFn, - .dependencies = std.ArrayList(*Step).init(allocator), + .dependencies = std.ArrayList(*Step).init(arena), .dependants = .{}, .state = .precheck_unstarted, .debug_stack_trace = addresses, @@ -168,3 +171,231 @@ const std = @import("../std.zig"); const Build = std.Build; const Allocator = std.mem.Allocator; const assert = std.debug.assert; +const builtin = @import("builtin"); + +pub fn evalChildProcess(s: *Step, argv: []const []const u8) !void { + const arena = s.owner.allocator; + + try handleChildProcUnsupported(s, null, argv); + try handleVerbose(s.owner, null, argv); + + const result = std.ChildProcess.exec(.{ + .allocator = arena, + .argv = argv, + }) catch |err| return s.fail("unable to spawn {s}: {s}", .{ argv[0], @errorName(err) }); + + if (result.stderr.len > 0) { + try s.result_error_msgs.append(arena, result.stderr); + } + + try handleChildProcessTerm(s, result.term, null, argv); +} + +pub fn fail(step: *Step, comptime fmt: []const u8, args: anytype) error{ OutOfMemory, MakeFailed } { + const arena = step.owner.allocator; + const msg = try std.fmt.allocPrint(arena, fmt, args); + try step.result_error_msgs.append(arena, msg); + return error.MakeFailed; +} + +/// Assumes that argv contains `--listen=-` and that the process being spawned +/// is the zig compiler - the same version that compiled the build runner. +pub fn evalZigProcess( + s: *Step, + argv: []const []const u8, + prog_node: *std.Progress.Node, +) ![]const u8 { + assert(argv.len != 0); + const b = s.owner; + const arena = b.allocator; + const gpa = arena; + + try handleChildProcUnsupported(s, null, argv); + try handleVerbose(s.owner, null, argv); + + var child = std.ChildProcess.init(argv, arena); + child.env_map = b.env_map; + child.stdin_behavior = .Pipe; + child.stdout_behavior = .Pipe; + child.stderr_behavior = .Pipe; + + child.spawn() catch |err| return s.fail("unable to spawn {s}: {s}", .{ + argv[0], @errorName(err), + }); + + var poller = std.io.poll(gpa, enum { stdout, stderr }, .{ + .stdout = child.stdout.?, + .stderr = child.stderr.?, + }); + defer poller.deinit(); + + try sendMessage(child.stdin.?, .update); + try sendMessage(child.stdin.?, .exit); + + const Header = std.zig.Server.Message.Header; + var result: ?[]const u8 = null; + + var node_name: std.ArrayListUnmanaged(u8) = .{}; + defer node_name.deinit(gpa); + var sub_prog_node: ?std.Progress.Node = null; + defer if (sub_prog_node) |*n| n.end(); + + while (try poller.poll()) { + const stdout = poller.fifo(.stdout); + const buf = stdout.readableSlice(0); + assert(stdout.readableLength() == buf.len); + if (buf.len >= @sizeOf(Header)) { + const header = @ptrCast(*align(1) const Header, buf[0..@sizeOf(Header)]); + const header_and_msg_len = header.bytes_len + @sizeOf(Header); + if (buf.len >= header_and_msg_len) { + const body = buf[@sizeOf(Header)..][0..header.bytes_len]; + switch (header.tag) { + .zig_version => { + if (!std.mem.eql(u8, builtin.zig_version_string, body)) { + return s.fail( + "zig version mismatch build runner vs compiler: '{s}' vs '{s}'", + .{ builtin.zig_version_string, body }, + ); + } + }, + .error_bundle => { + const EbHdr = std.zig.Server.Message.ErrorBundle; + const eb_hdr = @ptrCast(*align(1) const EbHdr, body); + const extra_bytes = + body[@sizeOf(EbHdr)..][0 .. @sizeOf(u32) * eb_hdr.extra_len]; + const string_bytes = + body[@sizeOf(EbHdr) + extra_bytes.len ..][0..eb_hdr.string_bytes_len]; + // TODO: use @ptrCast when the compiler supports it + const unaligned_extra = std.mem.bytesAsSlice(u32, extra_bytes); + const extra_array = try arena.alloc(u32, unaligned_extra.len); + // TODO: use @memcpy when it supports slices + for (extra_array, unaligned_extra) |*dst, src| dst.* = src; + s.result_error_bundle = .{ + .string_bytes = try arena.dupe(u8, string_bytes), + .extra = extra_array, + }; + }, + .progress => { + if (sub_prog_node) |*n| n.end(); + node_name.clearRetainingCapacity(); + try node_name.appendSlice(gpa, body); + sub_prog_node = prog_node.start(node_name.items, 0); + sub_prog_node.?.activate(); + }, + .emit_bin_path => { + result = try arena.dupe(u8, body); + }, + _ => { + // Unrecognized message. + }, + } + stdout.discard(header_and_msg_len); + } + } + } + + const stderr = poller.fifo(.stderr); + if (stderr.readableLength() > 0) { + try s.result_error_msgs.append(arena, try stderr.toOwnedSlice()); + } + + // Send EOF to stdin. + child.stdin.?.close(); + child.stdin = null; + + const term = child.wait() catch |err| { + return s.fail("unable to wait for {s}: {s}", .{ argv[0], @errorName(err) }); + }; + try handleChildProcessTerm(s, term, null, argv); + + if (s.result_error_bundle.errorMessageCount() > 0) { + return s.fail("the following command failed with {d} compilation errors:\n{s}", .{ + s.result_error_bundle.errorMessageCount(), + try allocPrintCmd(arena, null, argv), + }); + } + + return result orelse return s.fail( + "the following command failed to communicate the compilation result:\n{s}", + .{try allocPrintCmd(arena, null, argv)}, + ); +} + +fn sendMessage(file: std.fs.File, tag: std.zig.Client.Message.Tag) !void { + const header: std.zig.Client.Message.Header = .{ + .tag = tag, + .bytes_len = 0, + }; + try file.writeAll(std.mem.asBytes(&header)); +} + +pub fn handleVerbose( + b: *Build, + opt_cwd: ?[]const u8, + argv: []const []const u8, +) error{OutOfMemory}!void { + if (b.verbose) { + // Intention of verbose is to print all sub-process command lines to + // stderr before spawning them. + const text = try allocPrintCmd(b.allocator, opt_cwd, argv); + std.debug.print("{s}\n", .{text}); + } +} + +pub inline fn handleChildProcUnsupported( + s: *Step, + opt_cwd: ?[]const u8, + argv: []const []const u8, +) error{ OutOfMemory, MakeFailed }!void { + if (!std.process.can_spawn) { + return s.fail( + "unable to execute the following command: host cannot spawn child processes\n{s}", + .{try allocPrintCmd(s.owner.allocator, opt_cwd, argv)}, + ); + } +} + +pub fn handleChildProcessTerm( + s: *Step, + term: std.ChildProcess.Term, + opt_cwd: ?[]const u8, + argv: []const []const u8, +) error{ MakeFailed, OutOfMemory }!void { + const arena = s.owner.allocator; + switch (term) { + .Exited => |code| { + if (code != 0) { + return s.fail( + "the following command exited with error code {d}:\n{s}", + .{ code, try allocPrintCmd(arena, opt_cwd, argv) }, + ); + } + }, + .Signal, .Stopped, .Unknown => { + return s.fail( + "the following command terminated unexpectedly:\n{s}", + .{try allocPrintCmd(arena, opt_cwd, argv)}, + ); + }, + } +} + +pub fn allocPrintCmd(arena: Allocator, opt_cwd: ?[]const u8, argv: []const []const u8) ![]u8 { + var buf: std.ArrayListUnmanaged(u8) = .{}; + if (opt_cwd) |cwd| try buf.writer(arena).print("cd {s} && ", .{cwd}); + for (argv) |arg| { + try buf.writer(arena).print("{s} ", .{arg}); + } + return buf.toOwnedSlice(arena); +} + +pub fn cacheHit(s: *Step, man: *std.Build.Cache.Manifest) !bool { + return man.hit() catch |err| return failWithCacheError(s, man, err); +} + +fn failWithCacheError(s: *Step, man: *const std.Build.Cache.Manifest, err: anyerror) anyerror { + const i = man.failed_file_index orelse return err; + const pp = man.files.items[i].prefixed_path orelse return err; + const prefix = man.cache.prefixes()[pp.prefix].path orelse ""; + return s.fail("{s}: {s}/{s}\n", .{ @errorName(err), prefix, pp.sub_path }); +} diff --git a/lib/std/Build/TranslateCStep.zig b/lib/std/Build/TranslateCStep.zig index fef644f03c..dbb93d8c61 100644 --- a/lib/std/Build/TranslateCStep.zig +++ b/lib/std/Build/TranslateCStep.zig @@ -11,7 +11,6 @@ const TranslateCStep = @This(); pub const base_id = .translate_c; step: Step, -builder: *std.Build, source: std.Build.FileSource, include_dirs: std.ArrayList([]const u8), c_macros: std.ArrayList([]const u8), @@ -26,19 +25,19 @@ pub const Options = struct { optimize: std.builtin.OptimizeMode, }; -pub fn create(builder: *std.Build, options: Options) *TranslateCStep { - const self = builder.allocator.create(TranslateCStep) catch @panic("OOM"); - const source = options.source_file.dupe(builder); +pub fn create(owner: *std.Build, options: Options) *TranslateCStep { + const self = owner.allocator.create(TranslateCStep) catch @panic("OOM"); + const source = options.source_file.dupe(owner); self.* = TranslateCStep{ - .step = Step.init(builder.allocator, .{ + .step = Step.init(.{ .id = .translate_c, .name = "translate-c", + .owner = owner, .makeFn = make, }), - .builder = builder, .source = source, - .include_dirs = std.ArrayList([]const u8).init(builder.allocator), - .c_macros = std.ArrayList([]const u8).init(builder.allocator), + .include_dirs = std.ArrayList([]const u8).init(owner.allocator), + .c_macros = std.ArrayList([]const u8).init(owner.allocator), .out_basename = undefined, .target = options.target, .optimize = options.optimize, @@ -58,7 +57,7 @@ pub const AddExecutableOptions = struct { /// Creates a step to build an executable from the translated source. pub fn addExecutable(self: *TranslateCStep, options: AddExecutableOptions) *CompileStep { - return self.builder.addExecutable(.{ + return self.step.owner.addExecutable(.{ .root_source_file = .{ .generated = &self.output_file }, .name = options.name orelse "translated_c", .version = options.version, @@ -69,30 +68,31 @@ pub fn addExecutable(self: *TranslateCStep, options: AddExecutableOptions) *Comp } pub fn addIncludeDir(self: *TranslateCStep, include_dir: []const u8) void { - self.include_dirs.append(self.builder.dupePath(include_dir)) catch @panic("OOM"); + self.include_dirs.append(self.step.owner.dupePath(include_dir)) catch @panic("OOM"); } pub fn addCheckFile(self: *TranslateCStep, expected_matches: []const []const u8) *CheckFileStep { - return CheckFileStep.create(self.builder, .{ .generated = &self.output_file }, self.builder.dupeStrings(expected_matches)); + return CheckFileStep.create(self.step.owner, .{ .generated = &self.output_file }, self.step.owner.dupeStrings(expected_matches)); } /// If the value is omitted, it is set to 1. /// `name` and `value` need not live longer than the function call. pub fn defineCMacro(self: *TranslateCStep, name: []const u8, value: ?[]const u8) void { - const macro = std.Build.constructCMacro(self.builder.allocator, name, value); + const macro = std.Build.constructCMacro(self.step.owner.allocator, name, value); self.c_macros.append(macro) catch @panic("OOM"); } /// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1. pub fn defineCMacroRaw(self: *TranslateCStep, name_and_value: []const u8) void { - self.c_macros.append(self.builder.dupe(name_and_value)) catch @panic("OOM"); + self.c_macros.append(self.step.owner.dupe(name_and_value)) catch @panic("OOM"); } fn make(step: *Step, prog_node: *std.Progress.Node) !void { + const b = step.owner; const self = @fieldParentPtr(TranslateCStep, "step", step); - var argv_list = std.ArrayList([]const u8).init(self.builder.allocator); - try argv_list.append(self.builder.zig_exe); + var argv_list = std.ArrayList([]const u8).init(b.allocator); + try argv_list.append(b.zig_exe); try argv_list.append("translate-c"); try argv_list.append("-lc"); @@ -101,12 +101,12 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { if (!self.target.isNative()) { try argv_list.append("-target"); - try argv_list.append(try self.target.zigTriple(self.builder.allocator)); + try argv_list.append(try self.target.zigTriple(b.allocator)); } switch (self.optimize) { .Debug => {}, // Skip since it's the default. - else => try argv_list.append(self.builder.fmt("-O{s}", .{@tagName(self.optimize)})), + else => try argv_list.append(b.fmt("-O{s}", .{@tagName(self.optimize)})), } for (self.include_dirs.items) |include_dir| { @@ -119,15 +119,15 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { try argv_list.append(c_macro); } - try argv_list.append(self.source.getPath(self.builder)); + try argv_list.append(self.source.getPath(b)); - const output_path = try self.builder.execFromStep(argv_list.items, &self.step, prog_node); + const output_path = try step.evalZigProcess(argv_list.items, prog_node); self.out_basename = fs.path.basename(output_path); const output_dir = fs.path.dirname(output_path).?; self.output_file.path = try fs.path.join( - self.builder.allocator, + b.allocator, &[_][]const u8{ output_dir, self.out_basename }, ); } diff --git a/lib/std/Build/WriteFileStep.zig b/lib/std/Build/WriteFileStep.zig index 62acd6e8ee..d554d6efc1 100644 --- a/lib/std/Build/WriteFileStep.zig +++ b/lib/std/Build/WriteFileStep.zig @@ -10,7 +10,6 @@ //! control. step: Step, -builder: *std.Build, /// The elements here are pointers because we need stable pointers for the /// GeneratedFile field. files: std.ArrayListUnmanaged(*File), @@ -34,12 +33,12 @@ pub const Contents = union(enum) { copy: std.Build.FileSource, }; -pub fn init(builder: *std.Build) WriteFileStep { +pub fn init(owner: *std.Build) WriteFileStep { return .{ - .builder = builder, - .step = Step.init(builder.allocator, .{ + .step = Step.init(.{ .id = .write_file, .name = "writefile", + .owner = owner, .makeFn = make, }), .files = .{}, @@ -48,12 +47,13 @@ pub fn init(builder: *std.Build) WriteFileStep { } pub fn add(wf: *WriteFileStep, sub_path: []const u8, bytes: []const u8) void { - const gpa = wf.builder.allocator; + const b = wf.step.owner; + const gpa = b.allocator; const file = gpa.create(File) catch @panic("OOM"); file.* = .{ .generated_file = .{ .step = &wf.step }, - .sub_path = wf.builder.dupePath(sub_path), - .contents = .{ .bytes = wf.builder.dupe(bytes) }, + .sub_path = b.dupePath(sub_path), + .contents = .{ .bytes = b.dupe(bytes) }, }; wf.files.append(gpa, file) catch @panic("OOM"); } @@ -66,11 +66,12 @@ pub fn add(wf: *WriteFileStep, sub_path: []const u8, bytes: []const u8) void { /// required sub-path exists. /// This is the option expected to be used most commonly with `addCopyFile`. pub fn addCopyFile(wf: *WriteFileStep, source: std.Build.FileSource, sub_path: []const u8) void { - const gpa = wf.builder.allocator; + const b = wf.step.owner; + const gpa = b.allocator; const file = gpa.create(File) catch @panic("OOM"); file.* = .{ .generated_file = .{ .step = &wf.step }, - .sub_path = wf.builder.dupePath(sub_path), + .sub_path = b.dupePath(sub_path), .contents = .{ .copy = source }, }; wf.files.append(gpa, file) catch @panic("OOM"); @@ -83,7 +84,8 @@ pub fn addCopyFile(wf: *WriteFileStep, source: std.Build.FileSource, sub_path: [ /// those changes to version control. /// A file added this way is not available with `getFileSource`. pub fn addCopyFileToSource(wf: *WriteFileStep, source: std.Build.FileSource, sub_path: []const u8) void { - wf.output_source_files.append(wf.builder.allocator, .{ + const b = wf.step.owner; + wf.output_source_files.append(b.allocator, .{ .contents = .{ .copy = source }, .sub_path = sub_path, }) catch @panic("OOM"); @@ -101,6 +103,7 @@ pub fn getFileSource(wf: *WriteFileStep, sub_path: []const u8) ?std.Build.FileSo fn make(step: *Step, prog_node: *std.Progress.Node) !void { _ = prog_node; + const b = step.owner; const wf = @fieldParentPtr(WriteFileStep, "step", step); // Writing to source files is kind of an extra capability of this @@ -110,11 +113,11 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { for (wf.output_source_files.items) |output_source_file| { const basename = fs.path.basename(output_source_file.sub_path); if (fs.path.dirname(output_source_file.sub_path)) |dirname| { - var dir = try wf.builder.build_root.handle.makeOpenPath(dirname, .{}); + var dir = try b.build_root.handle.makeOpenPath(dirname, .{}); defer dir.close(); try writeFile(wf, dir, output_source_file.contents, basename); } else { - try writeFile(wf, wf.builder.build_root.handle, output_source_file.contents, basename); + try writeFile(wf, b.build_root.handle, output_source_file.contents, basename); } } @@ -125,7 +128,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { // If, for example, a hard-coded path was used as the location to put WriteFileStep // files, then two WriteFileSteps executing in parallel might clobber each other. - var man = wf.builder.cache.obtain(); + var man = b.cache.obtain(); defer man.deinit(); // Random bytes to make WriteFileStep unique. Refresh this with @@ -140,17 +143,17 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { man.hash.addBytes(bytes); }, .copy => |file_source| { - _ = try man.addFile(file_source.getPath(wf.builder), null); + _ = try man.addFile(file_source.getPath(b), null); }, } } - if (man.hit() catch |err| failWithCacheError(man, err)) { + if (try step.cacheHit(&man)) { // Cache hit, skip writing file data. const digest = man.final(); for (wf.files.items) |file| { - file.generated_file.path = try wf.builder.cache_root.join( - wf.builder.allocator, + file.generated_file.path = try b.cache_root.join( + b.allocator, &.{ "o", &digest, file.sub_path }, ); } @@ -160,7 +163,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { const digest = man.final(); const cache_path = "o" ++ fs.path.sep_str ++ digest; - var cache_dir = wf.builder.cache_root.handle.makeOpenPath(cache_path, .{}) catch |err| { + var cache_dir = b.cache_root.handle.makeOpenPath(cache_path, .{}) catch |err| { std.debug.print("unable to make path {s}: {s}\n", .{ cache_path, @errorName(err) }); return err; }; @@ -169,15 +172,15 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { for (wf.files.items) |file| { const basename = fs.path.basename(file.sub_path); if (fs.path.dirname(file.sub_path)) |dirname| { - var dir = try wf.builder.cache_root.handle.makeOpenPath(dirname, .{}); + var dir = try b.cache_root.handle.makeOpenPath(dirname, .{}); defer dir.close(); try writeFile(wf, dir, file.contents, basename); } else { try writeFile(wf, cache_dir, file.contents, basename); } - file.generated_file.path = try wf.builder.cache_root.join( - wf.builder.allocator, + file.generated_file.path = try b.cache_root.join( + b.allocator, &.{ cache_path, file.sub_path }, ); } @@ -186,32 +189,18 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { } fn writeFile(wf: *WriteFileStep, dir: fs.Dir, contents: Contents, basename: []const u8) !void { + const b = wf.step.owner; // TODO after landing concurrency PR, improve error reporting here switch (contents) { .bytes => |bytes| return dir.writeFile(basename, bytes), .copy => |file_source| { - const source_path = file_source.getPath(wf.builder); + const source_path = file_source.getPath(b); const prev_status = try fs.Dir.updateFile(fs.cwd(), source_path, dir, basename, .{}); _ = prev_status; // TODO logging (affected by open PR regarding concurrency) }, } } -/// TODO consolidate this with the same function in RunStep? -/// Also properly deal with concurrency (see open PR) -fn failWithCacheError(man: std.Build.Cache.Manifest, err: anyerror) noreturn { - const i = man.failed_file_index orelse failWithSimpleError(err); - const pp = man.files.items[i].prefixed_path orelse failWithSimpleError(err); - const prefix = man.cache.prefixes()[pp.prefix].path orelse ""; - std.debug.print("{s}: {s}/{s}\n", .{ @errorName(err), prefix, pp.sub_path }); - std.process.exit(1); -} - -fn failWithSimpleError(err: anyerror) noreturn { - std.debug.print("{s}\n", .{@errorName(err)}); - std.process.exit(1); -} - const std = @import("../std.zig"); const Step = std.Build.Step; const fs = std.fs; diff --git a/src/main.zig b/src/main.zig index fcdb52c342..6bf7177a9e 100644 --- a/src/main.zig +++ b/src/main.zig @@ -4419,6 +4419,8 @@ pub const usage_build = \\Options: \\ -freference-trace[=num] How many lines of reference trace should be shown per compile error \\ -fno-reference-trace Disable reference trace + \\ -fsummary Print the build summary, even on success + \\ -fno-summary Omit the build summary, even on failure \\ --build-file [file] Override path to build.zig \\ --cache-dir [path] Override path to local Zig cache directory \\ --global-cache-dir [path] Override path to global Zig cache directory @@ -4920,8 +4922,6 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void }; defer tree.deinit(gpa); - try printAstErrorsToStderr(gpa, tree, "", color); - var has_ast_error = false; if (check_ast_flag) { var file: Module.File = .{ .status = .never_loaded, @@ -4957,11 +4957,11 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void var error_bundle = try wip_errors.toOwnedBundle(); defer error_bundle.deinit(gpa); error_bundle.renderToStdErr(ttyconf); - has_ast_error = true; + process.exit(2); } - } - if (tree.errors.len != 0 or has_ast_error) { - process.exit(1); + } else if (tree.errors.len != 0) { + try printAstErrorsToStderr(gpa, tree, "", color); + process.exit(2); } const formatted = try tree.render(gpa); defer gpa.free(formatted); diff --git a/test/src/compare_output.zig b/test/src/compare_output.zig index 3bda3bdacd..20bd62d8e2 100644 --- a/test/src/compare_output.zig +++ b/test/src/compare_output.zig @@ -166,9 +166,7 @@ pub const CompareOutputContext = struct { const run = exe.run(); run.addArgs(case.cli_args); - run.stderr_action = .ignore; - run.stdout_action = .ignore; - run.expected_term = .{ .Exited = 126 }; + run.expectExitCode(126); self.step.dependOn(&run.step); }, diff --git a/test/tests.zig b/test/tests.zig index fceaa173d1..494ae0ea41 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -858,10 +858,11 @@ pub const StackTracesContext = struct { const allocator = context.b.allocator; const ptr = allocator.create(RunAndCompareStep) catch unreachable; ptr.* = RunAndCompareStep{ - .step = Step.init(allocator, .{ + .step = Step.init(.{ .id = .custom, .name = "StackTraceCompareOutputStep", .makeFn = make, + .owner = context.b, }), .context = context, .exe = exe, @@ -1121,10 +1122,7 @@ pub const StandaloneContext = struct { defer zig_args.resize(zig_args_base_len) catch unreachable; const run_cmd = b.addSystemCommand(zig_args.items); - const log_step = b.addLog("PASS {s} ({s})", .{ annotated_case_name, @tagName(optimize_mode) }); - log_step.step.dependOn(&run_cmd.step); - - self.step.dependOn(&log_step.step); + self.step.dependOn(&run_cmd.step); } } @@ -1150,10 +1148,7 @@ pub const StandaloneContext = struct { exe.linkSystemLibrary("c"); } - const log_step = b.addLog("PASS {s}", .{annotated_case_name}); - log_step.step.dependOn(&exe.step); - - self.step.dependOn(&log_step.step); + self.step.dependOn(&exe.step); } } }; @@ -1203,9 +1198,10 @@ pub const GenHContext = struct { const allocator = context.b.allocator; const ptr = allocator.create(GenHCmpOutputStep) catch unreachable; ptr.* = GenHCmpOutputStep{ - .step = Step.init(allocator, .{ + .step = Step.init(.{ .id = .custom, .name = "ParseCCmpOutput", + .owner = context.b, .makeFn = make, }), .context = context, From a2dc49a0f3d5761eae271d9b353542edb6e8f6e9 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 1 Mar 2023 23:45:54 -0700 Subject: [PATCH 182/294] fix Step.evalZigProcess to handle more than 1 message per poll --- lib/std/Build/Step.zig | 97 +++++++++++++++++++++--------------------- 1 file changed, 49 insertions(+), 48 deletions(-) diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig index f5ac9e1821..3219588050 100644 --- a/lib/std/Build/Step.zig +++ b/lib/std/Build/Step.zig @@ -240,57 +240,58 @@ pub fn evalZigProcess( var sub_prog_node: ?std.Progress.Node = null; defer if (sub_prog_node) |*n| n.end(); - while (try poller.poll()) { - const stdout = poller.fifo(.stdout); - const buf = stdout.readableSlice(0); - assert(stdout.readableLength() == buf.len); - if (buf.len >= @sizeOf(Header)) { + const stdout = poller.fifo(.stdout); + + poll: while (try poller.poll()) { + while (true) { + const buf = stdout.readableSlice(0); + assert(stdout.readableLength() == buf.len); + if (buf.len < @sizeOf(Header)) continue :poll; const header = @ptrCast(*align(1) const Header, buf[0..@sizeOf(Header)]); const header_and_msg_len = header.bytes_len + @sizeOf(Header); - if (buf.len >= header_and_msg_len) { - const body = buf[@sizeOf(Header)..][0..header.bytes_len]; - switch (header.tag) { - .zig_version => { - if (!std.mem.eql(u8, builtin.zig_version_string, body)) { - return s.fail( - "zig version mismatch build runner vs compiler: '{s}' vs '{s}'", - .{ builtin.zig_version_string, body }, - ); - } - }, - .error_bundle => { - const EbHdr = std.zig.Server.Message.ErrorBundle; - const eb_hdr = @ptrCast(*align(1) const EbHdr, body); - const extra_bytes = - body[@sizeOf(EbHdr)..][0 .. @sizeOf(u32) * eb_hdr.extra_len]; - const string_bytes = - body[@sizeOf(EbHdr) + extra_bytes.len ..][0..eb_hdr.string_bytes_len]; - // TODO: use @ptrCast when the compiler supports it - const unaligned_extra = std.mem.bytesAsSlice(u32, extra_bytes); - const extra_array = try arena.alloc(u32, unaligned_extra.len); - // TODO: use @memcpy when it supports slices - for (extra_array, unaligned_extra) |*dst, src| dst.* = src; - s.result_error_bundle = .{ - .string_bytes = try arena.dupe(u8, string_bytes), - .extra = extra_array, - }; - }, - .progress => { - if (sub_prog_node) |*n| n.end(); - node_name.clearRetainingCapacity(); - try node_name.appendSlice(gpa, body); - sub_prog_node = prog_node.start(node_name.items, 0); - sub_prog_node.?.activate(); - }, - .emit_bin_path => { - result = try arena.dupe(u8, body); - }, - _ => { - // Unrecognized message. - }, - } - stdout.discard(header_and_msg_len); + if (buf.len < header_and_msg_len) continue :poll; + const body = buf[@sizeOf(Header)..][0..header.bytes_len]; + switch (header.tag) { + .zig_version => { + if (!std.mem.eql(u8, builtin.zig_version_string, body)) { + return s.fail( + "zig version mismatch build runner vs compiler: '{s}' vs '{s}'", + .{ builtin.zig_version_string, body }, + ); + } + }, + .error_bundle => { + const EbHdr = std.zig.Server.Message.ErrorBundle; + const eb_hdr = @ptrCast(*align(1) const EbHdr, body); + const extra_bytes = + body[@sizeOf(EbHdr)..][0 .. @sizeOf(u32) * eb_hdr.extra_len]; + const string_bytes = + body[@sizeOf(EbHdr) + extra_bytes.len ..][0..eb_hdr.string_bytes_len]; + // TODO: use @ptrCast when the compiler supports it + const unaligned_extra = std.mem.bytesAsSlice(u32, extra_bytes); + const extra_array = try arena.alloc(u32, unaligned_extra.len); + // TODO: use @memcpy when it supports slices + for (extra_array, unaligned_extra) |*dst, src| dst.* = src; + s.result_error_bundle = .{ + .string_bytes = try arena.dupe(u8, string_bytes), + .extra = extra_array, + }; + }, + .progress => { + if (sub_prog_node) |*n| n.end(); + node_name.clearRetainingCapacity(); + try node_name.appendSlice(gpa, body); + sub_prog_node = prog_node.start(node_name.items, 0); + sub_prog_node.?.activate(); + }, + .emit_bin_path => { + result = try arena.dupe(u8, body); + }, + _ => { + // Unrecognized message. + }, } + stdout.discard(header_and_msg_len); } } From e0561ad79be81f55d525cc9b847e0fc7f481ee16 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 2 Mar 2023 16:31:16 -0700 Subject: [PATCH 183/294] std.Build.Cache.Directory: add a format() method --- lib/std/Build/Cache.zig | 46 +++++++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/lib/std/Build/Cache.zig b/lib/std/Build/Cache.zig index d4dbe6ec14..bd2b8a8927 100644 --- a/lib/std/Build/Cache.zig +++ b/lib/std/Build/Cache.zig @@ -7,27 +7,27 @@ pub const Directory = struct { /// directly, but it is needed when passing the directory to a child process. /// `null` means cwd. path: ?[]const u8, - handle: std.fs.Dir, + handle: fs.Dir, pub fn join(self: Directory, allocator: Allocator, paths: []const []const u8) ![]u8 { if (self.path) |p| { // TODO clean way to do this with only 1 allocation - const part2 = try std.fs.path.join(allocator, paths); + const part2 = try fs.path.join(allocator, paths); defer allocator.free(part2); - return std.fs.path.join(allocator, &[_][]const u8{ p, part2 }); + return fs.path.join(allocator, &[_][]const u8{ p, part2 }); } else { - return std.fs.path.join(allocator, paths); + return fs.path.join(allocator, paths); } } pub fn joinZ(self: Directory, allocator: Allocator, paths: []const []const u8) ![:0]u8 { if (self.path) |p| { // TODO clean way to do this with only 1 allocation - const part2 = try std.fs.path.join(allocator, paths); + const part2 = try fs.path.join(allocator, paths); defer allocator.free(part2); - return std.fs.path.joinZ(allocator, &[_][]const u8{ p, part2 }); + return fs.path.joinZ(allocator, &[_][]const u8{ p, part2 }); } else { - return std.fs.path.joinZ(allocator, paths); + return fs.path.joinZ(allocator, paths); } } @@ -39,6 +39,20 @@ pub const Directory = struct { if (self.path) |p| gpa.free(p); self.* = undefined; } + + pub fn format( + self: Directory, + comptime fmt_string: []const u8, + options: fmt.FormatOptions, + writer: anytype, + ) !void { + _ = options; + if (fmt_string.len != 0) fmt.invalidFmtError(fmt, self); + if (self.path) |p| { + try writer.writeAll(p); + try writer.writeAll(fs.path.sep_str); + } + } }; gpa: Allocator, @@ -243,10 +257,10 @@ pub const HashHelper = struct { hh.hasher.final(&bin_digest); var out_digest: [hex_digest_len]u8 = undefined; - _ = std.fmt.bufPrint( + _ = fmt.bufPrint( &out_digest, "{s}", - .{std.fmt.fmtSliceHexLower(&bin_digest)}, + .{fmt.fmtSliceHexLower(&bin_digest)}, ) catch unreachable; return out_digest; } @@ -365,10 +379,10 @@ pub const Manifest = struct { var bin_digest: BinDigest = undefined; self.hash.hasher.final(&bin_digest); - _ = std.fmt.bufPrint( + _ = fmt.bufPrint( &self.hex_digest, "{s}", - .{std.fmt.fmtSliceHexLower(&bin_digest)}, + .{fmt.fmtSliceHexLower(&bin_digest)}, ) catch unreachable; self.hash.hasher = hasher_init; @@ -469,7 +483,7 @@ pub const Manifest = struct { cache_hash_file.stat.size = fmt.parseInt(u64, size, 10) catch return error.InvalidFormat; cache_hash_file.stat.inode = fmt.parseInt(fs.File.INode, inode, 10) catch return error.InvalidFormat; cache_hash_file.stat.mtime = fmt.parseInt(i64, mtime_nsec_str, 10) catch return error.InvalidFormat; - _ = std.fmt.hexToBytes(&cache_hash_file.bin_digest, digest_str) catch return error.InvalidFormat; + _ = fmt.hexToBytes(&cache_hash_file.bin_digest, digest_str) catch return error.InvalidFormat; const prefix = fmt.parseInt(u8, prefix_str, 10) catch return error.InvalidFormat; if (prefix >= self.cache.prefixes_len) return error.InvalidFormat; @@ -806,10 +820,10 @@ pub const Manifest = struct { self.hash.hasher.final(&bin_digest); var out_digest: [hex_digest_len]u8 = undefined; - _ = std.fmt.bufPrint( + _ = fmt.bufPrint( &out_digest, "{s}", - .{std.fmt.fmtSliceHexLower(&bin_digest)}, + .{fmt.fmtSliceHexLower(&bin_digest)}, ) catch unreachable; return out_digest; @@ -831,10 +845,10 @@ pub const Manifest = struct { var encoded_digest: [hex_digest_len]u8 = undefined; for (self.files.items) |file| { - _ = std.fmt.bufPrint( + _ = fmt.bufPrint( &encoded_digest, "{s}", - .{std.fmt.fmtSliceHexLower(&file.bin_digest)}, + .{fmt.fmtSliceHexLower(&file.bin_digest)}, ) catch unreachable; try writer.print("{d} {d} {d} {s} {d} {s}\n", .{ file.stat.size, From 7ffdbb3b855ef9e4aa25a8ac911fce752a71e16d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 2 Mar 2023 22:36:27 -0700 Subject: [PATCH 184/294] std.debug.TTY.Config: add yellow --- lib/std/debug.zig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 97acf81af6..3c5f6d2edf 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -635,6 +635,7 @@ pub const TTY = struct { pub const Color = enum { Red, Green, + Yellow, Cyan, White, Dim, @@ -659,6 +660,7 @@ pub const TTY = struct { const color_string = switch (color) { .Red => "\x1b[31;1m", .Green => "\x1b[32;1m", + .Yellow => "\x1b[33;1m", .Cyan => "\x1b[36;1m", .White => "\x1b[37;1m", .Bold => "\x1b[1m", @@ -671,6 +673,7 @@ pub const TTY = struct { const attributes = switch (color) { .Red => windows.FOREGROUND_RED | windows.FOREGROUND_INTENSITY, .Green => windows.FOREGROUND_GREEN | windows.FOREGROUND_INTENSITY, + .Yellow => windows.FOREGROUND_RED | windows.FOREGROUND_GREEN | windows.FOREGROUND_INTENSITY, .Cyan => windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY, .White, .Bold => windows.FOREGROUND_RED | windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY, .Dim => windows.FOREGROUND_INTENSITY, From 9bf63b09963ca6ea1179dfaa9142498556bfac9d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 2 Mar 2023 22:37:07 -0700 Subject: [PATCH 185/294] stage2: avoid linux-only APIs on other operating systems --- src/Compilation.zig | 2 +- src/link.zig | 26 +++++++++++++------- src/link/Elf.zig | 58 ++++++++++++++++++++++++++------------------- src/main.zig | 6 ++--- src/test.zig | 3 +-- 5 files changed, 57 insertions(+), 38 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index 478f931718..28e7c43702 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -1847,7 +1847,7 @@ fn cleanupTmpArtifactDirectory( } } -pub fn hotCodeSwap(comp: *Compilation, prog_node: *std.Progress.Node, pid: std.os.pid_t) !void { +pub fn hotCodeSwap(comp: *Compilation, prog_node: *std.Progress.Node, pid: std.ChildProcess.Id) !void { comp.bin_file.child_pid = pid; try comp.makeBinFileWritable(); try comp.update(prog_node); diff --git a/src/link.zig b/src/link.zig index 96931dd79e..e68f9c97d0 100644 --- a/src/link.zig +++ b/src/link.zig @@ -264,7 +264,7 @@ pub const File = struct { /// of this linking operation. lock: ?Cache.Lock = null, - child_pid: ?std.os.pid_t = null, + child_pid: ?std.ChildProcess.Id = null, /// Attempts incremental linking, if the file already exists. If /// incremental linking fails, falls back to truncating the file and @@ -388,10 +388,14 @@ pub const File = struct { }); try emit.directory.handle.copyFile(emit.sub_path, emit.directory.handle, tmp_sub_path, .{}); try emit.directory.handle.rename(tmp_sub_path, emit.sub_path); - - switch (std.os.errno(std.os.linux.ptrace(std.os.linux.PTRACE.ATTACH, pid, 0, 0, 0))) { - .SUCCESS => {}, - else => |errno| log.warn("ptrace failure: {s}", .{@tagName(errno)}), + switch (builtin.os.tag) { + .linux => { + switch (std.os.errno(std.os.linux.ptrace(std.os.linux.PTRACE.ATTACH, pid, 0, 0, 0))) { + .SUCCESS => {}, + else => |errno| log.warn("ptrace failure: {s}", .{@tagName(errno)}), + } + }, + else => return error.HotSwapUnavailableOnHostOperatingSystem, } } base.file = try emit.directory.handle.createFile(emit.sub_path, .{ @@ -444,9 +448,14 @@ pub const File = struct { base.file = null; if (base.child_pid) |pid| { - switch (std.os.errno(std.os.linux.ptrace(std.os.linux.PTRACE.DETACH, pid, 0, 0, 0))) { - .SUCCESS => {}, - else => |errno| log.warn("ptrace failure: {s}", .{@tagName(errno)}), + switch (builtin.os.tag) { + .linux => { + switch (std.os.errno(std.os.linux.ptrace(std.os.linux.PTRACE.DETACH, pid, 0, 0, 0))) { + .SUCCESS => {}, + else => |errno| log.warn("ptrace failure: {s}", .{@tagName(errno)}), + } + }, + else => return error.HotSwapUnavailableOnHostOperatingSystem, } } }, @@ -487,6 +496,7 @@ pub const File = struct { NetNameDeleted, DeviceBusy, InvalidArgument, + HotSwapUnavailableOnHostOperatingSystem, }; /// Called from within the CodeGen to lower a local variable instantion as an unnamed diff --git a/src/link/Elf.zig b/src/link/Elf.zig index b849dcc9d2..5943277908 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -2453,18 +2453,23 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s const file_offset = self.sections.items(.shdr)[shdr_index].sh_offset + section_offset; if (self.base.child_pid) |pid| { - var code_vec: [1]std.os.iovec_const = .{.{ - .iov_base = code.ptr, - .iov_len = code.len, - }}; - var remote_vec: [1]std.os.iovec_const = .{.{ - .iov_base = @intToPtr([*]u8, local_sym.st_value), - .iov_len = code.len, - }}; - const rc = std.os.linux.process_vm_writev(pid, &code_vec, &remote_vec, 0); - switch (std.os.errno(rc)) { - .SUCCESS => assert(rc == code.len), - else => |errno| log.warn("process_vm_writev failure: {s}", .{@tagName(errno)}), + switch (builtin.os.tag) { + .linux => { + var code_vec: [1]std.os.iovec_const = .{.{ + .iov_base = code.ptr, + .iov_len = code.len, + }}; + var remote_vec: [1]std.os.iovec_const = .{.{ + .iov_base = @intToPtr([*]u8, local_sym.st_value), + .iov_len = code.len, + }}; + const rc = std.os.linux.process_vm_writev(pid, &code_vec, &remote_vec, 0); + switch (std.os.errno(rc)) { + .SUCCESS => assert(rc == code.len), + else => |errno| log.warn("process_vm_writev failure: {s}", .{@tagName(errno)}), + } + }, + else => return error.HotSwapUnavailableOnHostOperatingSystem, } } @@ -2856,18 +2861,23 @@ fn writeOffsetTableEntry(self: *Elf, index: usize) !void { try self.base.file.?.pwriteAll(&buf, off); if (self.base.child_pid) |pid| { - var local_vec: [1]std.os.iovec_const = .{.{ - .iov_base = &buf, - .iov_len = buf.len, - }}; - var remote_vec: [1]std.os.iovec_const = .{.{ - .iov_base = @intToPtr([*]u8, vaddr), - .iov_len = buf.len, - }}; - const rc = std.os.linux.process_vm_writev(pid, &local_vec, &remote_vec, 0); - switch (std.os.errno(rc)) { - .SUCCESS => assert(rc == buf.len), - else => |errno| log.warn("process_vm_writev failure: {s}", .{@tagName(errno)}), + switch (builtin.os.tag) { + .linux => { + var local_vec: [1]std.os.iovec_const = .{.{ + .iov_base = &buf, + .iov_len = buf.len, + }}; + var remote_vec: [1]std.os.iovec_const = .{.{ + .iov_base = @intToPtr([*]u8, vaddr), + .iov_len = buf.len, + }}; + const rc = std.os.linux.process_vm_writev(pid, &local_vec, &remote_vec, 0); + switch (std.os.errno(rc)) { + .SUCCESS => assert(rc == buf.len), + else => |errno| log.warn("process_vm_writev failure: {s}", .{@tagName(errno)}), + } + }, + else => return error.HotSwapUnavailableOnHostOperatingSystem, } } }, diff --git a/src/main.zig b/src/main.zig index 6bf7177a9e..70051d2cc7 100644 --- a/src/main.zig +++ b/src/main.zig @@ -3534,7 +3534,7 @@ fn serve( try serveStringMessage(out, .zig_version, build_options.version); - var child_pid: ?i32 = null; + var child_pid: ?std.ChildProcess.Id = null; var receive_fifo = std.fifo.LinearFifo(u8, .Dynamic).init(gpa); defer receive_fifo.deinit(); @@ -3978,7 +3978,7 @@ fn runOrTestHotSwap( arg_mode: ArgMode, all_args: []const []const u8, runtime_args_start: ?usize, -) !i32 { +) !std.ChildProcess.Id { const exe_emit = comp.bin_file.options.emit.?; // A naive `directory.join` here will indeed get the correct path to the binary, // however, in the case of cwd, we actually want `./foo` so that the path can be executed. @@ -4023,7 +4023,7 @@ fn runOrTestHotSwap( try child.spawn(); - return child.pid; + return child.id; } const AfterUpdateHook = union(enum) { diff --git a/src/test.zig b/src/test.zig index 663c4f1aff..5b73e516c6 100644 --- a/src/test.zig +++ b/src/test.zig @@ -1606,9 +1606,8 @@ pub const TestContext = struct { var module_node = update_node.start("parse/analysis/codegen", 0); module_node.activate(); - module_node.context.refresh(); try comp.makeBinFileWritable(); - try comp.update(); + try comp.update(&module_node); module_node.end(); if (update.case != .Error) { From dcec4d55e36f48e459f4e8f218b8619d9be925db Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 2 Mar 2023 22:38:07 -0700 Subject: [PATCH 186/294] eliminate stderr usage in std.Build make() functions * Eliminate all uses of `std.debug.print` in make() functions, instead properly using the step failure reporting mechanism. * Introduce the concept of skipped build steps. These do not cause the build to fail, and they do allow their dependants to run. * RunStep gains a new flag, `skip_foreign_checks` which causes the RunStep to be skipped if stdio mode is `check` and the binary cannot be executed due to it being a foreign executable. - RunStep is improved to automatically use known interpreters to execute binaries if possible (integrating with flags such as -fqemu and -fwasmtime). It only does this after attempting a native execution and receiving a "exec file format" error. - Update RunStep to use an ArrayList for the checks rather than this ad-hoc reallocation/copying mechanism. - `expectStdOutEqual` now also implicitly adds an exit_code==0 check if there is not already an expected termination. This matches previously expected behavior from older API and can be overridden by directly setting the checks array. * Add `dest_sub_path` to `InstallArtifactStep` which allows choosing an arbitrary subdirectory relative to the prefix, as well as overriding the basename. - Delete the custom InstallWithRename step that I found deep in the test/ directory. * WriteFileStep will now update its step display name after the first file is added. * Add missing stdout checks to various standalone test case build scripts. --- build.zig | 3 +- lib/build_runner.zig | 35 +- lib/std/Build/CheckFileStep.zig | 7 +- lib/std/Build/CheckObjectStep.zig | 138 ++++--- lib/std/Build/CompileStep.zig | 3 +- lib/std/Build/ConfigHeaderStep.zig | 23 +- lib/std/Build/InstallArtifactStep.zig | 8 +- lib/std/Build/ObjCopyStep.zig | 3 +- lib/std/Build/RunStep.zig | 454 +++++++++++++++------- lib/std/Build/Step.zig | 23 +- lib/std/Build/WriteFileStep.zig | 114 ++++-- lib/std/child_process.zig | 1 - test/link/macho/bugs/13457/build.zig | 4 +- test/link/macho/empty/build.zig | 3 +- test/link/macho/needed_library/build.zig | 1 + test/link/macho/objc/build.zig | 4 +- test/link/macho/search_strategy/build.zig | 3 +- test/link/macho/stack_size/build.zig | 1 + test/link/macho/uuid/build.zig | 76 +--- test/link/wasm/extern/build.zig | 3 +- 20 files changed, 554 insertions(+), 353 deletions(-) diff --git a/build.zig b/build.zig index 5f7e214d35..d32f666dac 100644 --- a/build.zig +++ b/build.zig @@ -385,7 +385,7 @@ pub fn build(b: *std.Build) !void { const optimization_modes = chosen_opt_modes_buf[0..chosen_mode_index]; const fmt_include_paths = &.{ "doc", "lib", "src", "test", "tools", "build.zig" }; - const fmt_exclude_paths = &.{ "test/cases" }; + const fmt_exclude_paths = &.{"test/cases"}; const check_fmt = b.addFmt(.{ .paths = fmt_include_paths, .exclude_paths = fmt_exclude_paths, @@ -402,7 +402,6 @@ pub fn build(b: *std.Build) !void { const do_fmt_step = b.step("fmt", "Modify source files in place to have conforming formatting"); do_fmt_step.dependOn(&do_fmt.step); - test_step.dependOn(tests.addPkgTests( b, test_filter, diff --git a/lib/build_runner.zig b/lib/build_runner.zig index 40f45d9ac8..aa846ce799 100644 --- a/lib/build_runner.zig +++ b/lib/build_runner.zig @@ -357,6 +357,7 @@ fn runStepNames( } var success_count: usize = 0; + var skipped_count: usize = 0; var failure_count: usize = 0; var pending_count: usize = 0; var total_compile_errors: usize = 0; @@ -379,6 +380,7 @@ fn runStepNames( }, .dependency_failure => pending_count += 1, .success => success_count += 1, + .skipped => skipped_count += 1, .failure => { failure_count += 1; const compile_errors_len = s.result_error_bundle.errorMessageCount(); @@ -395,13 +397,13 @@ fn runStepNames( if (failure_count == 0 and enable_summary != true) return cleanExit(); if (enable_summary != false) { - const total_count = success_count + failure_count + pending_count; + const total_count = success_count + failure_count + pending_count + skipped_count; ttyconf.setColor(stderr, .Cyan) catch {}; stderr.writeAll("Build Summary:") catch {}; ttyconf.setColor(stderr, .Reset) catch {}; - stderr.writer().print(" {d}/{d} steps succeeded; {d} failed", .{ - success_count, total_count, failure_count, - }) catch {}; + stderr.writer().print(" {d}/{d} steps succeeded", .{ success_count, total_count }) catch {}; + if (skipped_count > 0) stderr.writer().print("; {d} skipped", .{skipped_count}) catch {}; + if (failure_count > 0) stderr.writer().print("; {d} failed", .{failure_count}) catch {}; if (enable_summary == null) { ttyconf.setColor(stderr, .Dim) catch {}; @@ -503,6 +505,12 @@ fn printTreeStep( try ttyconf.setColor(stderr, .Reset); }, + .skipped => { + try ttyconf.setColor(stderr, .Yellow); + try stderr.writeAll(" skipped\n"); + try ttyconf.setColor(stderr, .Reset); + }, + .failure => { try ttyconf.setColor(stderr, .Red); if (s.result_error_bundle.errorMessageCount() > 0) { @@ -569,6 +577,7 @@ fn checkForDependencyLoop( .running => unreachable, .success => unreachable, .failure => unreachable, + .skipped => unreachable, } } @@ -587,7 +596,7 @@ fn workerMakeOneStep( // queue this step up again when dependencies are met. for (s.dependencies.items) |dep| { switch (@atomicLoad(Step.State, &dep.state, .SeqCst)) { - .success => continue, + .success, .skipped => continue, .failure, .dependency_failure => { @atomicStore(Step.State, &s.state, .dependency_failure, .SeqCst); return; @@ -639,13 +648,15 @@ fn workerMakeOneStep( } } - make_result catch |err| { - assert(err == error.MakeFailed); - @atomicStore(Step.State, &s.state, .failure, .SeqCst); - return; - }; - - @atomicStore(Step.State, &s.state, .success, .SeqCst); + if (make_result) |_| { + @atomicStore(Step.State, &s.state, .success, .SeqCst); + } else |err| switch (err) { + error.MakeFailed => { + @atomicStore(Step.State, &s.state, .failure, .SeqCst); + return; + }, + error.MakeSkipped => @atomicStore(Step.State, &s.state, .skipped, .SeqCst), + } // Successful completion of a step, so we queue up its dependants as well. for (s.dependants.items) |dep| { diff --git a/lib/std/Build/CheckFileStep.zig b/lib/std/Build/CheckFileStep.zig index f70a29840e..03b23d0b03 100644 --- a/lib/std/Build/CheckFileStep.zig +++ b/lib/std/Build/CheckFileStep.zig @@ -42,15 +42,14 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { for (self.expected_matches) |expected_match| { if (mem.indexOf(u8, contents, expected_match) == null) { - std.debug.print( + return step.fail( \\ - \\========= Expected to find: =================== + \\========= expected to find: =================== \\{s} - \\========= But file does not contain it: ======= + \\========= but file does not contain it: ======= \\{s} \\ , .{ expected_match, contents }); - return error.TestFailed; } } } diff --git a/lib/std/Build/CheckObjectStep.zig b/lib/std/Build/CheckObjectStep.zig index 57d280da0e..2a58850fab 100644 --- a/lib/std/Build/CheckObjectStep.zig +++ b/lib/std/Build/CheckObjectStep.zig @@ -133,7 +133,8 @@ const Action = struct { /// Will return true if the `phrase` is correctly parsed into an RPN program and /// its reduced, computed value compares using `op` with the expected value, either /// a literal or another extracted variable. - fn computeCmp(act: Action, gpa: Allocator, global_vars: anytype) !bool { + fn computeCmp(act: Action, step: *Step, global_vars: anytype) !bool { + const gpa = step.owner.allocator; var op_stack = std.ArrayList(enum { add, sub, mod, mul }).init(gpa); var values = std.ArrayList(u64).init(gpa); @@ -150,11 +151,11 @@ const Action = struct { } else { const val = std.fmt.parseInt(u64, next, 0) catch blk: { break :blk global_vars.get(next) orelse { - std.debug.print( + try step.addError( \\ - \\========= Variable was not extracted: =========== + \\========= variable was not extracted: =========== \\{s} - \\ + \\================================================= , .{next}); return error.UnknownVariable; }; @@ -186,11 +187,11 @@ const Action = struct { const exp_value = switch (act.expected.?.value) { .variable => |name| global_vars.get(name) orelse { - std.debug.print( + try step.addError( \\ - \\========= Variable was not extracted: =========== + \\========= variable was not extracted: =========== \\{s} - \\ + \\================================================= , .{name}); return error.UnknownVariable; }, @@ -323,14 +324,12 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { ); const output = switch (self.obj_format) { - .macho => try MachODumper.parseAndDump(contents, .{ - .gpa = gpa, + .macho => try MachODumper.parseAndDump(step, contents, .{ .dump_symtab = self.dump_symtab, }), .elf => @panic("TODO elf parser"), .coff => @panic("TODO coff parser"), - .wasm => try WasmDumper.parseAndDump(contents, .{ - .gpa = gpa, + .wasm => try WasmDumper.parseAndDump(step, contents, .{ .dump_symtab = self.dump_symtab, }), else => unreachable, @@ -346,54 +345,50 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { while (it.next()) |line| { if (try act.match(line, &vars)) break; } else { - std.debug.print( + return step.fail( \\ - \\========= Expected to find: ========================== + \\========= expected to find: ========================== \\{s} - \\========= But parsed file does not contain it: ======= + \\========= but parsed file does not contain it: ======= \\{s} - \\ + \\====================================================== , .{ act.phrase, output }); - return error.TestFailed; } }, .not_present => { while (it.next()) |line| { if (try act.match(line, &vars)) { - std.debug.print( + return step.fail( \\ - \\========= Expected not to find: =================== + \\========= expected not to find: =================== \\{s} - \\========= But parsed file does contain it: ======== + \\========= but parsed file does contain it: ======== \\{s} - \\ + \\=================================================== , .{ act.phrase, output }); - return error.TestFailed; } } }, .compute_cmp => { - const res = act.computeCmp(gpa, vars) catch |err| switch (err) { + const res = act.computeCmp(step, vars) catch |err| switch (err) { error.UnknownVariable => { - std.debug.print( - \\========= From parsed file: ===================== + return step.fail( + \\========= from parsed file: ===================== \\{s} - \\ + \\================================================= , .{output}); - return error.TestFailed; }, else => |e| return e, }; if (!res) { - std.debug.print( + return step.fail( \\ - \\========= Comparison failed for action: =========== + \\========= comparison failed for action: =========== \\{s} {} - \\========= From parsed file: ======================= + \\========= from parsed file: ======================= \\{s} - \\ + \\=================================================== , .{ act.phrase, act.expected.?, output }); - return error.TestFailed; } }, } @@ -402,7 +397,6 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { } const Opts = struct { - gpa: ?Allocator = null, dump_symtab: bool = false, }; @@ -410,8 +404,8 @@ const MachODumper = struct { const LoadCommandIterator = macho.LoadCommandIterator; const symtab_label = "symtab"; - fn parseAndDump(bytes: []align(@alignOf(u64)) const u8, opts: Opts) ![]const u8 { - const gpa = opts.gpa orelse unreachable; // MachO dumper requires an allocator + fn parseAndDump(step: *Step, bytes: []align(@alignOf(u64)) const u8, opts: Opts) ![]const u8 { + const gpa = step.owner.allocator; var stream = std.io.fixedBufferStream(bytes); const reader = stream.reader(); @@ -693,8 +687,8 @@ const MachODumper = struct { const WasmDumper = struct { const symtab_label = "symbols"; - fn parseAndDump(bytes: []const u8, opts: Opts) ![]const u8 { - const gpa = opts.gpa orelse unreachable; // Wasm dumper requires an allocator + fn parseAndDump(step: *Step, bytes: []const u8, opts: Opts) ![]const u8 { + const gpa = step.owner.allocator; if (opts.dump_symtab) { @panic("TODO: Implement symbol table parsing and dumping"); } @@ -715,20 +709,24 @@ const WasmDumper = struct { const writer = output.writer(); while (reader.readByte()) |current_byte| { - const section = std.meta.intToEnum(std.wasm.Section, current_byte) catch |err| { - std.debug.print("Found invalid section id '{d}'\n", .{current_byte}); - return err; + const section = std.meta.intToEnum(std.wasm.Section, current_byte) catch { + return step.fail("Found invalid section id '{d}'", .{current_byte}); }; const section_length = try std.leb.readULEB128(u32, reader); - try parseAndDumpSection(section, bytes[fbs.pos..][0..section_length], writer); + try parseAndDumpSection(step, section, bytes[fbs.pos..][0..section_length], writer); fbs.pos += section_length; } else |_| {} // reached end of stream return output.toOwnedSlice(); } - fn parseAndDumpSection(section: std.wasm.Section, data: []const u8, writer: anytype) !void { + fn parseAndDumpSection( + step: *Step, + section: std.wasm.Section, + data: []const u8, + writer: anytype, + ) !void { var fbs = std.io.fixedBufferStream(data); const reader = fbs.reader(); @@ -751,7 +749,7 @@ const WasmDumper = struct { => { const entries = try std.leb.readULEB128(u32, reader); try writer.print("\nentries {d}\n", .{entries}); - try dumpSection(section, data[fbs.pos..], entries, writer); + try dumpSection(step, section, data[fbs.pos..], entries, writer); }, .custom => { const name_length = try std.leb.readULEB128(u32, reader); @@ -760,7 +758,7 @@ const WasmDumper = struct { try writer.print("\nname {s}\n", .{name}); if (mem.eql(u8, name, "name")) { - try parseDumpNames(reader, writer, data); + try parseDumpNames(step, reader, writer, data); } else if (mem.eql(u8, name, "producers")) { try parseDumpProducers(reader, writer, data); } else if (mem.eql(u8, name, "target_features")) { @@ -776,7 +774,7 @@ const WasmDumper = struct { } } - fn dumpSection(section: std.wasm.Section, data: []const u8, entries: u32, writer: anytype) !void { + fn dumpSection(step: *Step, section: std.wasm.Section, data: []const u8, entries: u32, writer: anytype) !void { var fbs = std.io.fixedBufferStream(data); const reader = fbs.reader(); @@ -786,19 +784,18 @@ const WasmDumper = struct { while (i < entries) : (i += 1) { const func_type = try reader.readByte(); if (func_type != std.wasm.function_type) { - std.debug.print("Expected function type, found byte '{d}'\n", .{func_type}); - return error.UnexpectedByte; + return step.fail("expected function type, found byte '{d}'", .{func_type}); } const params = try std.leb.readULEB128(u32, reader); try writer.print("params {d}\n", .{params}); var index: u32 = 0; while (index < params) : (index += 1) { - try parseDumpType(std.wasm.Valtype, reader, writer); + try parseDumpType(step, std.wasm.Valtype, reader, writer); } else index = 0; const returns = try std.leb.readULEB128(u32, reader); try writer.print("returns {d}\n", .{returns}); while (index < returns) : (index += 1) { - try parseDumpType(std.wasm.Valtype, reader, writer); + try parseDumpType(step, std.wasm.Valtype, reader, writer); } } }, @@ -812,9 +809,8 @@ const WasmDumper = struct { const name = data[fbs.pos..][0..name_len]; fbs.pos += name_len; - const kind = std.meta.intToEnum(std.wasm.ExternalKind, try reader.readByte()) catch |err| { - std.debug.print("Invalid import kind\n", .{}); - return err; + const kind = std.meta.intToEnum(std.wasm.ExternalKind, try reader.readByte()) catch { + return step.fail("invalid import kind", .{}); }; try writer.print( @@ -831,11 +827,11 @@ const WasmDumper = struct { try parseDumpLimits(reader, writer); }, .global => { - try parseDumpType(std.wasm.Valtype, reader, writer); + try parseDumpType(step, std.wasm.Valtype, reader, writer); try writer.print("mutable {}\n", .{0x01 == try std.leb.readULEB128(u32, reader)}); }, .table => { - try parseDumpType(std.wasm.RefType, reader, writer); + try parseDumpType(step, std.wasm.RefType, reader, writer); try parseDumpLimits(reader, writer); }, } @@ -850,7 +846,7 @@ const WasmDumper = struct { .table => { var i: u32 = 0; while (i < entries) : (i += 1) { - try parseDumpType(std.wasm.RefType, reader, writer); + try parseDumpType(step, std.wasm.RefType, reader, writer); try parseDumpLimits(reader, writer); } }, @@ -863,9 +859,9 @@ const WasmDumper = struct { .global => { var i: u32 = 0; while (i < entries) : (i += 1) { - try parseDumpType(std.wasm.Valtype, reader, writer); + try parseDumpType(step, std.wasm.Valtype, reader, writer); try writer.print("mutable {}\n", .{0x01 == try std.leb.readULEB128(u1, reader)}); - try parseDumpInit(reader, writer); + try parseDumpInit(step, reader, writer); } }, .@"export" => { @@ -875,9 +871,8 @@ const WasmDumper = struct { const name = data[fbs.pos..][0..name_len]; fbs.pos += name_len; const kind_byte = try std.leb.readULEB128(u8, reader); - const kind = std.meta.intToEnum(std.wasm.ExternalKind, kind_byte) catch |err| { - std.debug.print("invalid export kind value '{d}'\n", .{kind_byte}); - return err; + const kind = std.meta.intToEnum(std.wasm.ExternalKind, kind_byte) catch { + return step.fail("invalid export kind value '{d}'", .{kind_byte}); }; const index = try std.leb.readULEB128(u32, reader); try writer.print( @@ -892,7 +887,7 @@ const WasmDumper = struct { var i: u32 = 0; while (i < entries) : (i += 1) { try writer.print("table index {d}\n", .{try std.leb.readULEB128(u32, reader)}); - try parseDumpInit(reader, writer); + try parseDumpInit(step, reader, writer); const function_indexes = try std.leb.readULEB128(u32, reader); var function_index: u32 = 0; @@ -908,7 +903,7 @@ const WasmDumper = struct { while (i < entries) : (i += 1) { const index = try std.leb.readULEB128(u32, reader); try writer.print("memory index 0x{x}\n", .{index}); - try parseDumpInit(reader, writer); + try parseDumpInit(step, reader, writer); const size = try std.leb.readULEB128(u32, reader); try writer.print("size {d}\n", .{size}); try reader.skipBytes(size, .{}); // we do not care about the content of the segments @@ -918,11 +913,10 @@ const WasmDumper = struct { } } - fn parseDumpType(comptime WasmType: type, reader: anytype, writer: anytype) !void { + fn parseDumpType(step: *Step, comptime WasmType: type, reader: anytype, writer: anytype) !void { const type_byte = try reader.readByte(); - const valtype = std.meta.intToEnum(WasmType, type_byte) catch |err| { - std.debug.print("Invalid wasm type value '{d}'\n", .{type_byte}); - return err; + const valtype = std.meta.intToEnum(WasmType, type_byte) catch { + return step.fail("Invalid wasm type value '{d}'", .{type_byte}); }; try writer.print("type {s}\n", .{@tagName(valtype)}); } @@ -937,11 +931,10 @@ const WasmDumper = struct { } } - fn parseDumpInit(reader: anytype, writer: anytype) !void { + fn parseDumpInit(step: *Step, reader: anytype, writer: anytype) !void { const byte = try std.leb.readULEB128(u8, reader); - const opcode = std.meta.intToEnum(std.wasm.Opcode, byte) catch |err| { - std.debug.print("invalid wasm opcode '{d}'\n", .{byte}); - return err; + const opcode = std.meta.intToEnum(std.wasm.Opcode, byte) catch { + return step.fail("invalid wasm opcode '{d}'", .{byte}); }; switch (opcode) { .i32_const => try writer.print("i32.const {x}\n", .{try std.leb.readILEB128(i32, reader)}), @@ -953,14 +946,13 @@ const WasmDumper = struct { } const end_opcode = try std.leb.readULEB128(u8, reader); if (end_opcode != std.wasm.opcode(.end)) { - std.debug.print("expected 'end' opcode in init expression\n", .{}); - return error.MissingEndOpcode; + return step.fail("expected 'end' opcode in init expression", .{}); } } - fn parseDumpNames(reader: anytype, writer: anytype, data: []const u8) !void { + fn parseDumpNames(step: *Step, reader: anytype, writer: anytype, data: []const u8) !void { while (reader.context.pos < data.len) { - try parseDumpType(std.wasm.NameSubsection, reader, writer); + try parseDumpType(step, std.wasm.NameSubsection, reader, writer); const size = try std.leb.readULEB128(u32, reader); const entries = try std.leb.readULEB128(u32, reader); try writer.print( diff --git a/lib/std/Build/CompileStep.zig b/lib/std/Build/CompileStep.zig index f1a8f71334..99d99694c3 100644 --- a/lib/std/Build/CompileStep.zig +++ b/lib/std/Build/CompileStep.zig @@ -538,8 +538,7 @@ pub fn run(cs: *CompileStep) *RunStep { } pub fn checkObject(self: *CompileStep, obj_format: std.Target.ObjectFormat) *CheckObjectStep { - const b = self.step.owner; - return CheckObjectStep.create(b, self.getOutputSource(), obj_format); + return CheckObjectStep.create(self.step.owner, self.getOutputSource(), obj_format); } pub fn setLinkerScriptPath(self: *CompileStep, source: FileSource) void { diff --git a/lib/std/Build/ConfigHeaderStep.zig b/lib/std/Build/ConfigHeaderStep.zig index 8b4c05bab7..37b04e75a4 100644 --- a/lib/std/Build/ConfigHeaderStep.zig +++ b/lib/std/Build/ConfigHeaderStep.zig @@ -192,13 +192,13 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { try output.appendSlice(c_generated_line); const src_path = file_source.getPath(b); const contents = try std.fs.cwd().readFileAlloc(gpa, src_path, self.max_bytes); - try render_autoconf(contents, &output, self.values, src_path); + try render_autoconf(step, contents, &output, self.values, src_path); }, .cmake => |file_source| { try output.appendSlice(c_generated_line); const src_path = file_source.getPath(b); const contents = try std.fs.cwd().readFileAlloc(gpa, src_path, self.max_bytes); - try render_cmake(contents, &output, self.values, src_path); + try render_cmake(step, contents, &output, self.values, src_path); }, .blank => { try output.appendSlice(c_generated_line); @@ -234,8 +234,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { output_dir; var dir = std.fs.cwd().makeOpenPath(sub_dir_path, .{}) catch |err| { - std.debug.print("unable to make path {s}: {s}\n", .{ output_dir, @errorName(err) }); - return err; + return step.fail("unable to make path '{s}': {s}", .{ output_dir, @errorName(err) }); }; defer dir.close(); @@ -247,6 +246,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { } fn render_autoconf( + step: *Step, contents: []const u8, output: *std.ArrayList(u8), values: std.StringArrayHashMap(Value), @@ -273,7 +273,7 @@ fn render_autoconf( } const name = it.rest(); const kv = values_copy.fetchSwapRemove(name) orelse { - std.debug.print("{s}:{d}: error: unspecified config header value: '{s}'\n", .{ + try step.addError("{s}:{d}: error: unspecified config header value: '{s}'", .{ src_path, line_index + 1, name, }); any_errors = true; @@ -283,15 +283,17 @@ fn render_autoconf( } for (values_copy.keys()) |name| { - std.debug.print("{s}: error: config header value unused: '{s}'\n", .{ src_path, name }); + try step.addError("{s}: error: config header value unused: '{s}'", .{ src_path, name }); + any_errors = true; } if (any_errors) { - return error.HeaderConfigFailed; + return error.MakeFailed; } } fn render_cmake( + step: *Step, contents: []const u8, output: *std.ArrayList(u8), values: std.StringArrayHashMap(Value), @@ -317,14 +319,14 @@ fn render_cmake( continue; } const name = it.next() orelse { - std.debug.print("{s}:{d}: error: missing define name\n", .{ + try step.addError("{s}:{d}: error: missing define name", .{ src_path, line_index + 1, }); any_errors = true; continue; }; const kv = values_copy.fetchSwapRemove(name) orelse { - std.debug.print("{s}:{d}: error: unspecified config header value: '{s}'\n", .{ + try step.addError("{s}:{d}: error: unspecified config header value: '{s}'", .{ src_path, line_index + 1, name, }); any_errors = true; @@ -334,7 +336,8 @@ fn render_cmake( } for (values_copy.keys()) |name| { - std.debug.print("{s}: error: config header value unused: '{s}'\n", .{ src_path, name }); + try step.addError("{s}: error: config header value unused: '{s}'", .{ src_path, name }); + any_errors = true; } if (any_errors) { diff --git a/lib/std/Build/InstallArtifactStep.zig b/lib/std/Build/InstallArtifactStep.zig index 7e35d0a5ee..377cba301c 100644 --- a/lib/std/Build/InstallArtifactStep.zig +++ b/lib/std/Build/InstallArtifactStep.zig @@ -12,6 +12,9 @@ artifact: *CompileStep, dest_dir: InstallDir, pdb_dir: ?InstallDir, h_dir: ?InstallDir, +/// If non-null, adds additional path components relative to dest_dir, and +/// overrides the basename of the CompileStep. +dest_sub_path: ?[]const u8, pub fn create(owner: *std.Build, artifact: *CompileStep) *InstallArtifactStep { if (artifact.install_step) |s| return s; @@ -40,6 +43,7 @@ pub fn create(owner: *std.Build, artifact: *CompileStep) *InstallArtifactStep { } } else null, .h_dir = if (artifact.kind == .lib and artifact.emit_h) .header else null, + .dest_sub_path = null, }; self.step.dependOn(&artifact.step); artifact.install_step = self; @@ -71,7 +75,9 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { const self = @fieldParentPtr(InstallArtifactStep, "step", step); const dest_builder = self.dest_builder; - const full_dest_path = dest_builder.getInstallPath(self.dest_dir, self.artifact.out_filename); + const dest_sub_path = if (self.dest_sub_path) |sub_path| sub_path else self.artifact.out_filename; + const full_dest_path = dest_builder.getInstallPath(self.dest_dir, dest_sub_path); + try src_builder.updateFile( self.artifact.getOutputSource().getPath(src_builder), full_dest_path, diff --git a/lib/std/Build/ObjCopyStep.zig b/lib/std/Build/ObjCopyStep.zig index 7199431ee6..13046b3efe 100644 --- a/lib/std/Build/ObjCopyStep.zig +++ b/lib/std/Build/ObjCopyStep.zig @@ -95,8 +95,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { const full_dest_path = try b.cache_root.join(b.allocator, &.{ "o", &digest, self.basename }); const cache_path = "o" ++ fs.path.sep_str ++ digest; b.cache_root.handle.makePath(cache_path) catch |err| { - std.debug.print("unable to make path {s}: {s}\n", .{ cache_path, @errorName(err) }); - return err; + return step.fail("unable to make path {s}: {s}", .{ cache_path, @errorName(err) }); }; var argv = std.ArrayList([]const u8).init(b.allocator); diff --git a/lib/std/Build/RunStep.zig b/lib/std/Build/RunStep.zig index 087483fea8..9304cc758e 100644 --- a/lib/std/Build/RunStep.zig +++ b/lib/std/Build/RunStep.zig @@ -10,6 +10,7 @@ const ArrayList = std.ArrayList; const EnvMap = process.EnvMap; const Allocator = mem.Allocator; const ExecError = std.Build.ExecError; +const assert = std.debug.assert; const RunStep = @This(); @@ -54,6 +55,8 @@ rename_step_with_output_arg: bool = true, /// Command-line arguments such as -fqemu and -fwasmtime may affect whether a /// binary is detected as foreign, as well as system configuration such as /// Rosetta (macOS) and binfmt_misc (Linux). +/// If this RunStep is considered to have side-effects, then this flag does +/// nothing. skip_foreign_checks: bool = false, /// If stderr or stdout exceeds this amount, the child process is killed and @@ -79,7 +82,7 @@ pub const StdIo = union(enum) { /// conditions. /// Note that an explicit check for exit code 0 needs to be added to this /// list if such a check is desireable. - check: []const Check, + check: std.ArrayList(Check), pub const Check = union(enum) { expect_stderr_exact: []const u8, @@ -214,14 +217,20 @@ pub fn setEnvironmentVariable(self: *RunStep, key: []const u8, value: []const u8 env_map.put(b.dupe(key), b.dupe(value)) catch @panic("unhandled error"); } +/// Adds a check for exact stderr match. Does not add any other checks. pub fn expectStdErrEqual(self: *RunStep, bytes: []const u8) void { const new_check: StdIo.Check = .{ .expect_stderr_exact = self.step.owner.dupe(bytes) }; self.addCheck(new_check); } +/// Adds a check for exact stdout match as well as a check for exit code 0, if +/// there is not already an expected termination check. pub fn expectStdOutEqual(self: *RunStep, bytes: []const u8) void { const new_check: StdIo.Check = .{ .expect_stdout_exact = self.step.owner.dupe(bytes) }; self.addCheck(new_check); + if (!self.hasTermCheck()) { + self.expectExitCode(0); + } } pub fn expectExitCode(self: *RunStep, code: u8) void { @@ -229,19 +238,21 @@ pub fn expectExitCode(self: *RunStep, code: u8) void { self.addCheck(new_check); } +pub fn hasTermCheck(self: RunStep) bool { + for (self.stdio.check.items) |check| switch (check) { + .expect_term => return true, + else => continue, + }; + return false; +} + pub fn addCheck(self: *RunStep, new_check: StdIo.Check) void { - const arena = self.step.owner.allocator; switch (self.stdio) { .infer_from_args => { - const list = arena.create([1]StdIo.Check) catch @panic("OOM"); - list.* = .{new_check}; - self.stdio = .{ .check = list }; - }, - .check => |checks| { - const new_list = arena.alloc(StdIo.Check, checks.len + 1) catch @panic("OOM"); - std.mem.copy(StdIo.Check, new_list, checks); - new_list[checks.len] = new_check; + self.stdio = .{ .check = std.ArrayList(StdIo.Check).init(self.step.owner.allocator) }; + self.stdio.check.append(new_check) catch @panic("OOM"); }, + .check => |*checks| checks.append(new_check) catch @panic("OOM"), else => @panic("illegal call to addCheck: conflicting helper method calls. Suggest to directly set stdio field of RunStep instead"), } } @@ -298,14 +309,15 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { _ = prog_node; const b = step.owner; + const arena = b.allocator; const self = @fieldParentPtr(RunStep, "step", step); const has_side_effects = self.hasSideEffects(); - var argv_list = ArrayList([]const u8).init(b.allocator); + var argv_list = ArrayList([]const u8).init(arena); var output_placeholders = ArrayList(struct { index: usize, output: Arg.Output, - }).init(b.allocator); + }).init(arena); var man = b.cache.obtain(); defer man.deinit(); @@ -357,7 +369,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { const digest = man.final(); for (output_placeholders.items) |placeholder| { placeholder.output.generated_file.path = try b.cache_root.join( - b.allocator, + arena, &.{ "o", &digest, placeholder.output.basename }, ); } @@ -367,30 +379,21 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { const digest = man.final(); for (output_placeholders.items) |placeholder| { - const output_path = try b.cache_root.join( - b.allocator, - &.{ "o", &digest, placeholder.output.basename }, - ); - const output_dir = fs.path.dirname(output_path).?; - fs.cwd().makePath(output_dir) catch |err| { - std.debug.print("unable to make path {s}: {s}\n", .{ output_dir, @errorName(err) }); - return err; + const output_components = .{ "o", &digest, placeholder.output.basename }; + const output_sub_path = try fs.path.join(arena, &output_components); + const output_sub_dir_path = fs.path.dirname(output_sub_path).?; + b.cache_root.handle.makePath(output_sub_dir_path) catch |err| { + return step.fail("unable to make path '{}{s}': {s}", .{ + b.cache_root, output_sub_dir_path, @errorName(err), + }); }; - + const output_path = try b.cache_root.join(arena, &output_components); placeholder.output.generated_file.path = output_path; argv_list.items[placeholder.index] = output_path; } } - try runCommand( - step, - self.cwd, - argv_list.items, - self.env_map, - self.stdio, - has_side_effects, - self.max_stdio_size, - ); + try runCommand(self, argv_list.items, has_side_effects); if (!has_side_effects) { try man.writeManifest(); @@ -442,49 +445,265 @@ fn termMatches(expected: ?std.ChildProcess.Term, actual: std.ChildProcess.Term) }; } -fn runCommand( - step: *Step, - opt_cwd: ?[]const u8, - argv: []const []const u8, - env_map: ?*EnvMap, - stdio: StdIo, - has_side_effects: bool, - max_stdio_size: usize, -) !void { +fn runCommand(self: *RunStep, argv: []const []const u8, has_side_effects: bool) !void { + const step = &self.step; const b = step.owner; const arena = b.allocator; - const cwd = if (opt_cwd) |cwd| b.pathFromRoot(cwd) else b.build_root.path; - try step.handleChildProcUnsupported(opt_cwd, argv); - try Step.handleVerbose(step.owner, opt_cwd, argv); + try step.handleChildProcUnsupported(self.cwd, argv); + try Step.handleVerbose(step.owner, self.cwd, argv); + + var stdout_bytes: ?[]const u8 = null; + var stderr_bytes: ?[]const u8 = null; + + const term = spawnChildAndCollect(self, argv, &stdout_bytes, &stderr_bytes, has_side_effects) catch |err| term: { + if (err == error.InvalidExe) interpret: { + // TODO: learn the target from the binary directly rather than from + // relying on it being a CompileStep. This will make this logic + // work even for the edge case that the binary was produced by a + // third party. + const exe = switch (self.argv.items[0]) { + .artifact => |exe| exe, + else => break :interpret, + }; + if (exe.kind != .exe) break :interpret; + + var interp_argv = std.ArrayList([]const u8).init(b.allocator); + defer interp_argv.deinit(); + + const need_cross_glibc = exe.target.isGnuLibC() and exe.is_linking_libc; + switch (b.host.getExternalExecutor(exe.target_info, .{ + .qemu_fixes_dl = need_cross_glibc and b.glibc_runtimes_dir != null, + .link_libc = exe.is_linking_libc, + })) { + .native, .rosetta => { + if (self.stdio == .check and self.skip_foreign_checks) + return error.MakeSkipped; + + break :interpret; + }, + .wine => |bin_name| { + if (b.enable_wine) { + try interp_argv.append(bin_name); + } else { + return failForeign(self, "-fwine", argv[0], exe); + } + }, + .qemu => |bin_name| { + if (b.enable_qemu) { + const glibc_dir_arg = if (need_cross_glibc) + b.glibc_runtimes_dir orelse return + else + null; + + try interp_argv.append(bin_name); + + if (glibc_dir_arg) |dir| { + // TODO look into making this a call to `linuxTriple`. This + // needs the directory to be called "i686" rather than + // "x86" which is why we do it manually here. + const fmt_str = "{s}" ++ fs.path.sep_str ++ "{s}-{s}-{s}"; + const cpu_arch = exe.target.getCpuArch(); + const os_tag = exe.target.getOsTag(); + const abi = exe.target.getAbi(); + const cpu_arch_name: []const u8 = if (cpu_arch == .x86) + "i686" + else + @tagName(cpu_arch); + const full_dir = try std.fmt.allocPrint(b.allocator, fmt_str, .{ + dir, cpu_arch_name, @tagName(os_tag), @tagName(abi), + }); + + try interp_argv.append("-L"); + try interp_argv.append(full_dir); + } + } else { + return failForeign(self, "-fqemu", argv[0], exe); + } + }, + .darling => |bin_name| { + if (b.enable_darling) { + try interp_argv.append(bin_name); + } else { + return failForeign(self, "-fdarling", argv[0], exe); + } + }, + .wasmtime => |bin_name| { + if (b.enable_wasmtime) { + try interp_argv.append(bin_name); + try interp_argv.append("--dir=."); + } else { + return failForeign(self, "-fwasmtime", argv[0], exe); + } + }, + .bad_dl => |foreign_dl| { + if (self.stdio == .check and self.skip_foreign_checks) + return error.MakeSkipped; + + const host_dl = b.host.dynamic_linker.get() orelse "(none)"; + + return step.fail( + \\the host system is unable to execute binaries from the target + \\ because the host dynamic linker is '{s}', + \\ while the target dynamic linker is '{s}'. + \\ consider setting the dynamic linker or enabling skip_foreign_checks in the Run step + , .{ host_dl, foreign_dl }); + }, + .bad_os_or_cpu => { + if (self.stdio == .check and self.skip_foreign_checks) + return error.MakeSkipped; + + const host_name = try b.host.target.zigTriple(b.allocator); + const foreign_name = try exe.target.zigTriple(b.allocator); + + return step.fail("the host system ({s}) is unable to execute binaries from the target ({s})", .{ + host_name, foreign_name, + }); + }, + } + + if (exe.target.isWindows()) { + // On Windows we don't have rpaths so we have to add .dll search paths to PATH + RunStep.addPathForDynLibsInternal(&self.step, b, exe); + } + + try interp_argv.append(argv[0]); + + try Step.handleVerbose(step.owner, self.cwd, interp_argv.items); + + assert(stdout_bytes == null); + assert(stderr_bytes == null); + break :term spawnChildAndCollect(self, interp_argv.items, &stdout_bytes, &stderr_bytes, has_side_effects) catch |inner_err| { + return step.fail("unable to spawn {s}: {s}", .{ + interp_argv.items[0], @errorName(inner_err), + }); + }; + } + + return step.fail("unable to spawn {s}: {s}", .{ argv[0], @errorName(err) }); + }; + + switch (self.stdio) { + .check => |checks| for (checks.items) |check| switch (check) { + .expect_stderr_exact => |expected_bytes| { + if (!mem.eql(u8, expected_bytes, stderr_bytes.?)) { + return step.fail( + \\ + \\========= expected this stderr: ========= + \\{s} + \\========= but found: ==================== + \\{s} + \\========= from the following command: === + \\{s} + , .{ + expected_bytes, + stderr_bytes.?, + try Step.allocPrintCmd(arena, self.cwd, argv), + }); + } + }, + .expect_stderr_match => |match| { + if (mem.indexOf(u8, stderr_bytes.?, match) == null) { + return step.fail( + \\ + \\========= expected to find in stderr: ========= + \\{s} + \\========= but stderr does not contain it: ===== + \\{s} + \\========= from the following command: ========= + \\{s} + , .{ + match, + stderr_bytes.?, + try Step.allocPrintCmd(arena, self.cwd, argv), + }); + } + }, + .expect_stdout_exact => |expected_bytes| { + if (!mem.eql(u8, expected_bytes, stdout_bytes.?)) { + return step.fail( + \\ + \\========= expected this stdout: ========= + \\{s} + \\========= but found: ==================== + \\{s} + \\========= from the following command: === + \\{s} + , .{ + expected_bytes, + stdout_bytes.?, + try Step.allocPrintCmd(arena, self.cwd, argv), + }); + } + }, + .expect_stdout_match => |match| { + if (mem.indexOf(u8, stdout_bytes.?, match) == null) { + return step.fail( + \\ + \\========= expected to find in stdout: ========= + \\{s} + \\========= but stdout does not contain it: ===== + \\{s} + \\========= from the following command: ========= + \\{s} + , .{ + match, + stdout_bytes.?, + try Step.allocPrintCmd(arena, self.cwd, argv), + }); + } + }, + .expect_term => |expected_term| { + if (!termMatches(expected_term, term)) { + return step.fail("the following command {} (expected {}):\n{s}", .{ + fmtTerm(term), + fmtTerm(expected_term), + try Step.allocPrintCmd(arena, self.cwd, argv), + }); + } + }, + }, + else => { + try step.handleChildProcessTerm(term, self.cwd, argv); + }, + } +} + +fn spawnChildAndCollect( + self: *RunStep, + argv: []const []const u8, + stdout_bytes: *?[]const u8, + stderr_bytes: *?[]const u8, + has_side_effects: bool, +) !std.ChildProcess.Term { + const b = self.step.owner; + const arena = b.allocator; + const cwd = if (self.cwd) |cwd| b.pathFromRoot(cwd) else b.build_root.path; var child = std.ChildProcess.init(argv, arena); child.cwd = cwd; - child.env_map = env_map orelse b.env_map; + child.env_map = self.env_map orelse b.env_map; - child.stdin_behavior = switch (stdio) { + child.stdin_behavior = switch (self.stdio) { .infer_from_args => if (has_side_effects) .Inherit else .Ignore, .inherit => .Inherit, .check => .Close, }; - child.stdout_behavior = switch (stdio) { + child.stdout_behavior = switch (self.stdio) { .infer_from_args => if (has_side_effects) .Inherit else .Ignore, .inherit => .Inherit, - .check => |checks| if (checksContainStdout(checks)) .Pipe else .Ignore, + .check => |checks| if (checksContainStdout(checks.items)) .Pipe else .Ignore, }; - child.stderr_behavior = switch (stdio) { + child.stderr_behavior = switch (self.stdio) { .infer_from_args => if (has_side_effects) .Inherit else .Pipe, .inherit => .Inherit, .check => .Pipe, }; - child.spawn() catch |err| return step.fail("unable to spawn {s}: {s}", .{ + child.spawn() catch |err| return self.step.fail("unable to spawn {s}: {s}", .{ argv[0], @errorName(err), }); - var stdout_bytes: ?[]const u8 = null; - var stderr_bytes: ?[]const u8 = null; - if (child.stdout) |stdout| { if (child.stderr) |stderr| { var poller = std.io.poll(arena, enum { stdout, stderr }, .{ @@ -494,115 +713,32 @@ fn runCommand( defer poller.deinit(); while (try poller.poll()) { - if (poller.fifo(.stdout).count > max_stdio_size) + if (poller.fifo(.stdout).count > self.max_stdio_size) return error.StdoutStreamTooLong; - if (poller.fifo(.stderr).count > max_stdio_size) + if (poller.fifo(.stderr).count > self.max_stdio_size) return error.StderrStreamTooLong; } - stdout_bytes = try poller.fifo(.stdout).toOwnedSlice(); - stderr_bytes = try poller.fifo(.stderr).toOwnedSlice(); + stdout_bytes.* = try poller.fifo(.stdout).toOwnedSlice(); + stderr_bytes.* = try poller.fifo(.stderr).toOwnedSlice(); } else { - stdout_bytes = try stdout.reader().readAllAlloc(arena, max_stdio_size); + stdout_bytes.* = try stdout.reader().readAllAlloc(arena, self.max_stdio_size); } } else if (child.stderr) |stderr| { - stderr_bytes = try stderr.reader().readAllAlloc(arena, max_stdio_size); + stderr_bytes.* = try stderr.reader().readAllAlloc(arena, self.max_stdio_size); } - if (stderr_bytes) |stderr| if (stderr.len > 0) { - const stderr_is_diagnostic = switch (stdio) { - .check => |checks| !checksContainStderr(checks), + if (stderr_bytes.*) |stderr| if (stderr.len > 0) { + const stderr_is_diagnostic = switch (self.stdio) { + .check => |checks| !checksContainStderr(checks.items), else => true, }; if (stderr_is_diagnostic) { - try step.result_error_msgs.append(arena, stderr); + try self.step.result_error_msgs.append(arena, stderr); } }; - const term = child.wait() catch |err| { - return step.fail("unable to wait for {s}: {s}", .{ argv[0], @errorName(err) }); - }; - - switch (stdio) { - .check => |checks| for (checks) |check| switch (check) { - .expect_stderr_exact => |expected_bytes| { - if (!mem.eql(u8, expected_bytes, stderr_bytes.?)) { - return step.fail( - \\========= expected this stderr: ========= - \\{s} - \\========= but found: ==================== - \\{s} - \\========= from the following command: === - \\{s} - , .{ - expected_bytes, - stderr_bytes.?, - try Step.allocPrintCmd(arena, opt_cwd, argv), - }); - } - }, - .expect_stderr_match => |match| { - if (mem.indexOf(u8, stderr_bytes.?, match) == null) { - return step.fail( - \\========= expected to find in stderr: ========= - \\{s} - \\========= but stderr does not contain it: ===== - \\{s} - \\========= from the following command: ========= - \\{s} - , .{ - match, - stderr_bytes.?, - try Step.allocPrintCmd(arena, opt_cwd, argv), - }); - } - }, - .expect_stdout_exact => |expected_bytes| { - if (!mem.eql(u8, expected_bytes, stdout_bytes.?)) { - return step.fail( - \\========= expected this stdout: ========= - \\{s} - \\========= but found: ==================== - \\{s} - \\========= from the following command: === - \\{s} - , .{ - expected_bytes, - stdout_bytes.?, - try Step.allocPrintCmd(arena, opt_cwd, argv), - }); - } - }, - .expect_stdout_match => |match| { - if (mem.indexOf(u8, stdout_bytes.?, match) == null) { - return step.fail( - \\========= expected to find in stdout: ========= - \\{s} - \\========= but stdout does not contain it: ===== - \\{s} - \\========= from the following command: ========= - \\{s} - , .{ - match, - stdout_bytes.?, - try Step.allocPrintCmd(arena, opt_cwd, argv), - }); - } - }, - .expect_term => |expected_term| { - if (!termMatches(expected_term, term)) { - return step.fail("the following command {} (expected {}):\n{s}", .{ - fmtTerm(term), - fmtTerm(expected_term), - try Step.allocPrintCmd(arena, opt_cwd, argv), - }); - } - }, - }, - else => { - try step.handleChildProcessTerm(term, opt_cwd, argv); - }, - } + return child.wait(); } fn addPathForDynLibs(self: *RunStep, artifact: *CompileStep) void { @@ -624,3 +760,29 @@ pub fn addPathForDynLibsInternal(step: *Step, builder: *std.Build, artifact: *Co } } } + +fn failForeign( + self: *RunStep, + suggested_flag: []const u8, + argv0: []const u8, + exe: *CompileStep, +) error{ MakeFailed, MakeSkipped, OutOfMemory } { + switch (self.stdio) { + .check => { + if (self.skip_foreign_checks) + return error.MakeSkipped; + + const b = self.step.owner; + const host_name = try b.host.target.zigTriple(b.allocator); + const foreign_name = try exe.target.zigTriple(b.allocator); + + return self.step.fail( + \\unable to spawn foreign binary '{s}' ({s}) on host system ({s}) + \\ consider using {s} or enabling skip_foreign_checks in the Run step + , .{ argv0, foreign_name, host_name, suggested_flag }); + }, + else => { + return self.step.fail("unable to spawn foreign binary '{s}'", .{argv0}); + }, + } +} diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig index 3219588050..928e7e8735 100644 --- a/lib/std/Build/Step.zig +++ b/lib/std/Build/Step.zig @@ -26,6 +26,9 @@ pub const State = enum { dependency_failure, success, failure, + /// This state indicates that the step did not complete, however, it also did not fail, + /// and it is safe to continue executing its dependencies. + skipped, }; pub const Id = enum { @@ -106,13 +109,15 @@ pub fn init(options: Options) Step { /// If the Step's `make` function reports `error.MakeFailed`, it indicates they /// have already reported the error. Otherwise, we add a simple error report /// here. -pub fn make(s: *Step, prog_node: *std.Progress.Node) error{MakeFailed}!void { - return s.makeFn(s, prog_node) catch |err| { - if (err != error.MakeFailed) { +pub fn make(s: *Step, prog_node: *std.Progress.Node) error{ MakeFailed, MakeSkipped }!void { + return s.makeFn(s, prog_node) catch |err| switch (err) { + error.MakeFailed => return error.MakeFailed, + error.MakeSkipped => return error.MakeSkipped, + else => { const gpa = s.dependencies.allocator; s.result_error_msgs.append(gpa, @errorName(err)) catch @panic("OOM"); - } - return error.MakeFailed; + return error.MakeFailed; + }, }; } @@ -192,10 +197,14 @@ pub fn evalChildProcess(s: *Step, argv: []const []const u8) !void { } pub fn fail(step: *Step, comptime fmt: []const u8, args: anytype) error{ OutOfMemory, MakeFailed } { + try step.addError(fmt, args); + return error.MakeFailed; +} + +pub fn addError(step: *Step, comptime fmt: []const u8, args: anytype) error{OutOfMemory}!void { const arena = step.owner.allocator; const msg = try std.fmt.allocPrint(arena, fmt, args); try step.result_error_msgs.append(arena, msg); - return error.MakeFailed; } /// Assumes that argv contains `--listen=-` and that the process being spawned @@ -398,5 +407,5 @@ fn failWithCacheError(s: *Step, man: *const std.Build.Cache.Manifest, err: anyer const i = man.failed_file_index orelse return err; const pp = man.files.items[i].prefixed_path orelse return err; const prefix = man.cache.prefixes()[pp.prefix].path orelse ""; - return s.fail("{s}: {s}/{s}\n", .{ @errorName(err), prefix, pp.sub_path }); + return s.fail("{s}: {s}/{s}", .{ @errorName(err), prefix, pp.sub_path }); } diff --git a/lib/std/Build/WriteFileStep.zig b/lib/std/Build/WriteFileStep.zig index d554d6efc1..b5164d5730 100644 --- a/lib/std/Build/WriteFileStep.zig +++ b/lib/std/Build/WriteFileStep.zig @@ -37,7 +37,7 @@ pub fn init(owner: *std.Build) WriteFileStep { return .{ .step = Step.init(.{ .id = .write_file, - .name = "writefile", + .name = "WriteFile", .owner = owner, .makeFn = make, }), @@ -56,6 +56,8 @@ pub fn add(wf: *WriteFileStep, sub_path: []const u8, bytes: []const u8) void { .contents = .{ .bytes = b.dupe(bytes) }, }; wf.files.append(gpa, file) catch @panic("OOM"); + + wf.maybeUpdateName(); } /// Place the file into the generated directory within the local cache, @@ -75,6 +77,8 @@ pub fn addCopyFile(wf: *WriteFileStep, source: std.Build.FileSource, sub_path: [ .contents = .{ .copy = source }, }; wf.files.append(gpa, file) catch @panic("OOM"); + + wf.maybeUpdateName(); } /// A path relative to the package root. @@ -101,6 +105,15 @@ pub fn getFileSource(wf: *WriteFileStep, sub_path: []const u8) ?std.Build.FileSo return null; } +fn maybeUpdateName(wf: *WriteFileStep) void { + if (wf.files.items.len == 1) { + // First time adding a file; update name. + if (std.mem.eql(u8, wf.step.name, "WriteFile")) { + wf.step.name = wf.step.owner.fmt("WriteFile {s}", .{wf.files.items[0].sub_path}); + } + } +} + fn make(step: *Step, prog_node: *std.Progress.Node) !void { _ = prog_node; const b = step.owner; @@ -110,14 +123,39 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { // WriteFileStep - arguably it should be a different step. But anyway here // it is, it happens unconditionally and does not interact with the other // files here. + var any_miss = false; for (wf.output_source_files.items) |output_source_file| { - const basename = fs.path.basename(output_source_file.sub_path); if (fs.path.dirname(output_source_file.sub_path)) |dirname| { - var dir = try b.build_root.handle.makeOpenPath(dirname, .{}); - defer dir.close(); - try writeFile(wf, dir, output_source_file.contents, basename); - } else { - try writeFile(wf, b.build_root.handle, output_source_file.contents, basename); + b.build_root.handle.makePath(dirname) catch |err| { + return step.fail("unable to make path '{}{s}': {s}", .{ + b.build_root, dirname, @errorName(err), + }); + }; + } + switch (output_source_file.contents) { + .bytes => |bytes| { + b.build_root.handle.writeFile(output_source_file.sub_path, bytes) catch |err| { + return step.fail("unable to write file '{}{s}': {s}", .{ + b.build_root, output_source_file.sub_path, @errorName(err), + }); + }; + any_miss = true; + }, + .copy => |file_source| { + const source_path = file_source.getPath(b); + const prev_status = fs.Dir.updateFile( + fs.cwd(), + source_path, + b.build_root.handle, + output_source_file.sub_path, + .{}, + ) catch |err| { + return step.fail("unable to update file from '{s}' to '{}{s}': {s}", .{ + source_path, b.build_root, output_source_file.sub_path, @errorName(err), + }); + }; + any_miss = any_miss or prev_status == .stale; + }, } } @@ -164,19 +202,52 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { const cache_path = "o" ++ fs.path.sep_str ++ digest; var cache_dir = b.cache_root.handle.makeOpenPath(cache_path, .{}) catch |err| { - std.debug.print("unable to make path {s}: {s}\n", .{ cache_path, @errorName(err) }); - return err; + return step.fail("unable to make path '{}{s}': {s}", .{ + b.cache_root, cache_path, @errorName(err), + }); }; defer cache_dir.close(); for (wf.files.items) |file| { - const basename = fs.path.basename(file.sub_path); if (fs.path.dirname(file.sub_path)) |dirname| { - var dir = try b.cache_root.handle.makeOpenPath(dirname, .{}); - defer dir.close(); - try writeFile(wf, dir, file.contents, basename); - } else { - try writeFile(wf, cache_dir, file.contents, basename); + cache_dir.makePath(dirname) catch |err| { + return step.fail("unable to make path '{}{s}{c}{s}': {s}", .{ + b.cache_root, cache_path, fs.path.sep, dirname, @errorName(err), + }); + }; + } + switch (file.contents) { + .bytes => |bytes| { + cache_dir.writeFile(file.sub_path, bytes) catch |err| { + return step.fail("unable to write file '{}{s}{c}{s}': {s}", .{ + b.cache_root, cache_path, fs.path.sep, file.sub_path, @errorName(err), + }); + }; + }, + .copy => |file_source| { + const source_path = file_source.getPath(b); + const prev_status = fs.Dir.updateFile( + fs.cwd(), + source_path, + cache_dir, + file.sub_path, + .{}, + ) catch |err| { + return step.fail("unable to update file from '{s}' to '{}{s}{c}{s}': {s}", .{ + source_path, + b.cache_root, + cache_path, + fs.path.sep, + file.sub_path, + @errorName(err), + }); + }; + // At this point we already will mark the step as a cache miss. + // But this is kind of a partial cache hit since individual + // file copies may be avoided. Oh well, this information is + // discarded. + _ = prev_status; + }, } file.generated_file.path = try b.cache_root.join( @@ -188,19 +259,6 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { try man.writeManifest(); } -fn writeFile(wf: *WriteFileStep, dir: fs.Dir, contents: Contents, basename: []const u8) !void { - const b = wf.step.owner; - // TODO after landing concurrency PR, improve error reporting here - switch (contents) { - .bytes => |bytes| return dir.writeFile(basename, bytes), - .copy => |file_source| { - const source_path = file_source.getPath(b); - const prev_status = try fs.Dir.updateFile(fs.cwd(), source_path, dir, basename, .{}); - _ = prev_status; // TODO logging (affected by open PR regarding concurrency) - }, - } -} - const std = @import("../std.zig"); const Step = std.Build.Step; const fs = std.fs; diff --git a/lib/std/child_process.zig b/lib/std/child_process.zig index f8cba85874..3bdef3177a 100644 --- a/lib/std/child_process.zig +++ b/lib/std/child_process.zig @@ -185,7 +185,6 @@ pub const ChildProcess = struct { } /// Blocks until child process terminates and then cleans up all resources. - /// TODO: set the pid to undefined in this function. pub fn wait(self: *ChildProcess) !Term { const term = if (builtin.os.tag == .windows) try self.waitWindows() diff --git a/test/link/macho/bugs/13457/build.zig b/test/link/macho/bugs/13457/build.zig index 3560b4a168..7ac1435015 100644 --- a/test/link/macho/bugs/13457/build.zig +++ b/test/link/macho/bugs/13457/build.zig @@ -13,6 +13,8 @@ pub fn build(b: *std.Build) void { .target = target, }); - const run = exe.runEmulatable(); + const run = b.addRunArtifact(exe); + run.skip_foreign_checks = true; + run.expectStdOutEqual(""); test_step.dependOn(&run.step); } diff --git a/test/link/macho/empty/build.zig b/test/link/macho/empty/build.zig index 586da1511b..12eb67d9c8 100644 --- a/test/link/macho/empty/build.zig +++ b/test/link/macho/empty/build.zig @@ -16,7 +16,8 @@ pub fn build(b: *std.Build) void { exe.addCSourceFile("empty.c", &[0][]const u8{}); exe.linkLibC(); - const run_cmd = std.Build.EmulatableRunStep.create(b, "run", exe); + const run_cmd = b.addRunArtifact(exe); + run_cmd.skip_foreign_checks = true; run_cmd.expectStdOutEqual("Hello!\n"); test_step.dependOn(&run_cmd.step); } diff --git a/test/link/macho/needed_library/build.zig b/test/link/macho/needed_library/build.zig index 92a73d22b7..e8a58d5381 100644 --- a/test/link/macho/needed_library/build.zig +++ b/test/link/macho/needed_library/build.zig @@ -36,5 +36,6 @@ pub fn build(b: *std.Build) void { check.checkNext("name @rpath/liba.dylib"); const run_cmd = check.runAndCompare(); + run_cmd.expectStdOutEqual(""); test_step.dependOn(&run_cmd.step); } diff --git a/test/link/macho/objc/build.zig b/test/link/macho/objc/build.zig index 10d293baab..9398e28a80 100644 --- a/test/link/macho/objc/build.zig +++ b/test/link/macho/objc/build.zig @@ -17,6 +17,8 @@ pub fn build(b: *std.Build) void { // populate paths to the sysroot here. exe.linkFramework("Foundation"); - const run_cmd = std.Build.EmulatableRunStep.create(b, "run", exe); + const run_cmd = b.addRunArtifact(exe); + run_cmd.skip_foreign_checks = true; + run_cmd.expectStdOutEqual(""); test_step.dependOn(&run_cmd.step); } diff --git a/test/link/macho/search_strategy/build.zig b/test/link/macho/search_strategy/build.zig index 62757f885b..a8a4167865 100644 --- a/test/link/macho/search_strategy/build.zig +++ b/test/link/macho/search_strategy/build.zig @@ -27,7 +27,8 @@ pub fn build(b: *std.Build) void { const exe = createScenario(b, optimize, target); exe.search_strategy = .paths_first; - const run = std.Build.EmulatableRunStep.create(b, "run", exe); + const run = b.addRunArtifact(exe); + run.skip_foreign_checks = true; run.cwd = b.pathFromRoot("."); run.expectStdOutEqual("Hello world"); test_step.dependOn(&run.step); diff --git a/test/link/macho/stack_size/build.zig b/test/link/macho/stack_size/build.zig index 3529a134eb..874f53fbff 100644 --- a/test/link/macho/stack_size/build.zig +++ b/test/link/macho/stack_size/build.zig @@ -21,5 +21,6 @@ pub fn build(b: *std.Build) void { check_exe.checkNext("stacksize 100000000"); const run = check_exe.runAndCompare(); + run.expectStdOutEqual(""); test_step.dependOn(&run.step); } diff --git a/test/link/macho/uuid/build.zig b/test/link/macho/uuid/build.zig index ec7664cd72..6ff45e1175 100644 --- a/test/link/macho/uuid/build.zig +++ b/test/link/macho/uuid/build.zig @@ -1,5 +1,4 @@ const std = @import("std"); -const Builder = std.Build.Builder; const CompileStep = std.Build.CompileStep; const FileSource = std.Build.FileSource; const Step = std.Build.Step; @@ -38,13 +37,15 @@ fn testUuid( // stay the same across builds. { const dylib = simpleDylib(b, optimize, target); - const install_step = installWithRename(dylib, "test1.dylib"); + const install_step = b.addInstallArtifact(dylib); + install_step.dest_sub_path = "test1.dylib"; install_step.step.dependOn(&dylib.step); } { const dylib = simpleDylib(b, optimize, target); dylib.strip = true; - const install_step = installWithRename(dylib, "test2.dylib"); + const install_step = b.addInstallArtifact(dylib); + install_step.dest_sub_path = "test2.dylib"; install_step.step.dependOn(&dylib.step); } @@ -68,70 +69,23 @@ fn simpleDylib( return dylib; } -fn installWithRename(cs: *CompileStep, name: []const u8) *InstallWithRename { - const step = InstallWithRename.create(cs.builder, cs.getOutputSource(), name); - cs.builder.getInstallStep().dependOn(&step.step); - return step; -} - -const InstallWithRename = struct { - pub const base_id = .custom; - - step: Step, - builder: *Builder, - source: FileSource, - name: []const u8, - - pub fn create( - builder: *Builder, - source: FileSource, - name: []const u8, - ) *InstallWithRename { - const self = builder.allocator.create(InstallWithRename) catch @panic("OOM"); - self.* = InstallWithRename{ - .builder = builder, - .step = Step.init(builder.allocator, .{ - .id = .custom, - .name = builder.fmt("install and rename: {s} -> {s}", .{ - source.getDisplayName(), name, - }), - .makeFn = make, - }), - .source = source, - .name = builder.dupe(name), - }; - return self; - } - - fn make(step: *Step) anyerror!void { - const self = @fieldParentPtr(InstallWithRename, "step", step); - const source_path = self.source.getPath(self.builder); - const target_path = self.builder.getInstallPath(.lib, self.name); - self.builder.updateFile(source_path, target_path) catch |err| { - std.log.err("Unable to rename: {s} -> {s}", .{ source_path, target_path }); - return err; - }; - } -}; - const CompareUuid = struct { pub const base_id = .custom; step: Step, - builder: *Builder, lhs: []const u8, rhs: []const u8, - pub fn create(builder: *Builder, lhs: []const u8, rhs: []const u8) *CompareUuid { - const self = builder.allocator.create(CompareUuid) catch @panic("OOM"); + pub fn create(owner: *std.Build, lhs: []const u8, rhs: []const u8) *CompareUuid { + const self = owner.allocator.create(CompareUuid) catch @panic("OOM"); self.* = CompareUuid{ - .builder = builder, - .step = Step.init(builder.allocator, .{ - .id = .custom, - .name = builder.fmt("compare uuid: {s} and {s}", .{ + .step = Step.init(.{ + .id = base_id, + .name = owner.fmt("compare uuid: {s} and {s}", .{ lhs, rhs, }), + .owner = owner, .makeFn = make, }), .lhs = lhs, @@ -140,16 +94,18 @@ const CompareUuid = struct { return self; } - fn make(step: *Step) anyerror!void { + fn make(step: *Step, prog_node: *std.Progress.Node) anyerror!void { + _ = prog_node; + const b = step.owner; const self = @fieldParentPtr(CompareUuid, "step", step); - const gpa = self.builder.allocator; + const gpa = b.allocator; var lhs_uuid: [16]u8 = undefined; - const lhs_path = self.builder.getInstallPath(.lib, self.lhs); + const lhs_path = b.getInstallPath(.lib, self.lhs); try parseUuid(gpa, lhs_path, &lhs_uuid); var rhs_uuid: [16]u8 = undefined; - const rhs_path = self.builder.getInstallPath(.lib, self.rhs); + const rhs_path = b.getInstallPath(.lib, self.rhs); try parseUuid(gpa, rhs_path, &rhs_uuid); try std.testing.expectEqualStrings(&lhs_uuid, &rhs_uuid); diff --git a/test/link/wasm/extern/build.zig b/test/link/wasm/extern/build.zig index 569d94091a..fede2b18ab 100644 --- a/test/link/wasm/extern/build.zig +++ b/test/link/wasm/extern/build.zig @@ -11,7 +11,8 @@ pub fn build(b: *std.Build) void { exe.use_llvm = false; exe.use_lld = false; - const run = exe.runEmulatable(); + const run = b.addRunArtifact(exe); + run.skip_foreign_checks = true; run.expectStdOutEqual("Result: 30"); const test_step = b.step("test", "Run linker test"); From d6f7766da298accfa84120dbc7f770a6ab5b25e0 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 2 Mar 2023 23:46:25 -0700 Subject: [PATCH 187/294] stage2: avoid networking when generating zig2.c Avoid dragging networking into zig2.c because it adds dependencies on some linker symbols that are annoying to satisfy while bootstrapping. --- src/main.zig | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main.zig b/src/main.zig index 70051d2cc7..93082a964d 100644 --- a/src/main.zig +++ b/src/main.zig @@ -668,9 +668,13 @@ const ArgMode = union(enum) { run, }; +/// Avoid dragging networking into zig2.c because it adds dependencies on some +/// linker symbols that are annoying to satisfy while bootstrapping. +const Ip4Address = if (build_options.omit_pkg_fetching_code) void else std.net.Ip4Address; + const Listen = union(enum) { none, - ip4: std.net.Ip4Address, + ip4: Ip4Address, stdio, }; @@ -1159,6 +1163,7 @@ fn buildOutputType( listen = .stdio; watch = true; } else { + if (build_options.omit_pkg_fetching_code) unreachable; // example: --listen 127.0.0.1:9000 var it = std.mem.split(u8, next_arg, ":"); const host = it.next().?; @@ -3303,6 +3308,8 @@ fn buildOutputType( return cleanExit(); }, .ip4 => |ip4_addr| { + if (build_options.omit_pkg_fetching_code) unreachable; + var server = std.net.StreamServer.init(.{ .reuse_address = true, }); From d0cf34a328d7818203e87b8d379e22067b7041e3 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 2 Mar 2023 23:56:28 -0700 Subject: [PATCH 188/294] stage2: fix compilation on 32-bit targets --- src/link/Elf.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 5943277908..f1ab98372e 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -2460,7 +2460,7 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s .iov_len = code.len, }}; var remote_vec: [1]std.os.iovec_const = .{.{ - .iov_base = @intToPtr([*]u8, local_sym.st_value), + .iov_base = @intToPtr([*]u8, @intCast(usize, local_sym.st_value)), .iov_len = code.len, }}; const rc = std.os.linux.process_vm_writev(pid, &code_vec, &remote_vec, 0); @@ -2868,7 +2868,7 @@ fn writeOffsetTableEntry(self: *Elf, index: usize) !void { .iov_len = buf.len, }}; var remote_vec: [1]std.os.iovec_const = .{.{ - .iov_base = @intToPtr([*]u8, vaddr), + .iov_base = @intToPtr([*]u8, @intCast(usize, vaddr)), .iov_len = buf.len, }}; const rc = std.os.linux.process_vm_writev(pid, &local_vec, &remote_vec, 0); From 677a0e294116b0fcc1d69eea99c2fa62eb9873fe Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 2 Mar 2023 23:56:56 -0700 Subject: [PATCH 189/294] stage2: avoid bloat when using -Donly-c --- build.zig | 2 +- src/main.zig | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/build.zig b/build.zig index d32f666dac..40210500d8 100644 --- a/build.zig +++ b/build.zig @@ -192,7 +192,7 @@ pub fn build(b: *std.Build) !void { exe_options.addOption(bool, "llvm_has_arc", llvm_has_arc); exe_options.addOption(bool, "force_gpa", force_gpa); exe_options.addOption(bool, "only_c", only_c); - exe_options.addOption(bool, "omit_pkg_fetching_code", false); + exe_options.addOption(bool, "omit_pkg_fetching_code", only_c); if (link_libc) { exe.linkLibC(); diff --git a/src/main.zig b/src/main.zig index 93082a964d..f81911ffd0 100644 --- a/src/main.zig +++ b/src/main.zig @@ -3295,6 +3295,7 @@ fn buildOutputType( switch (listen) { .none => {}, .stdio => { + if (build_options.only_c) unreachable; try serve( comp, std.io.getStdIn(), From 8b054e190a977ccd27302debd5d0f95c28597005 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 3 Mar 2023 13:23:22 -0700 Subject: [PATCH 190/294] std.Build.RunStep: work around a miscompilation See #14783 Also, set the cwd directory handle when spawning the child process if available. --- lib/std/Build/RunStep.zig | 104 ++++++++++++++++++++++++-------------- 1 file changed, 67 insertions(+), 37 deletions(-) diff --git a/lib/std/Build/RunStep.zig b/lib/std/Build/RunStep.zig index 9304cc758e..6224c62897 100644 --- a/lib/std/Build/RunStep.zig +++ b/lib/std/Build/RunStep.zig @@ -22,6 +22,8 @@ step: Step, argv: ArrayList(Arg), /// Set this to modify the current working directory +/// TODO change this to a Build.Cache.Directory to better integrate with +/// future child process cwd API. cwd: ?[]const u8, /// Override this field to modify the environment, or use setEnvironmentVariable @@ -89,7 +91,7 @@ pub const StdIo = union(enum) { expect_stderr_match: []const u8, expect_stdout_exact: []const u8, expect_stdout_match: []const u8, - expect_term: std.ChildProcess.Term, + expect_term: std.process.Child.Term, }; }; @@ -401,7 +403,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { } fn formatTerm( - term: ?std.ChildProcess.Term, + term: ?std.process.Child.Term, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype, @@ -417,11 +419,11 @@ fn formatTerm( try writer.writeAll("exited with any code"); } } -fn fmtTerm(term: ?std.ChildProcess.Term) std.fmt.Formatter(formatTerm) { +fn fmtTerm(term: ?std.process.Child.Term) std.fmt.Formatter(formatTerm) { return .{ .data = term }; } -fn termMatches(expected: ?std.ChildProcess.Term, actual: std.ChildProcess.Term) bool { +fn termMatches(expected: ?std.process.Child.Term, actual: std.process.Child.Term) bool { return if (expected) |e| switch (e) { .Exited => |expected_code| switch (actual) { .Exited => |actual_code| expected_code == actual_code, @@ -453,10 +455,7 @@ fn runCommand(self: *RunStep, argv: []const []const u8, has_side_effects: bool) try step.handleChildProcUnsupported(self.cwd, argv); try Step.handleVerbose(step.owner, self.cwd, argv); - var stdout_bytes: ?[]const u8 = null; - var stderr_bytes: ?[]const u8 = null; - - const term = spawnChildAndCollect(self, argv, &stdout_bytes, &stderr_bytes, has_side_effects) catch |err| term: { + const result = spawnChildAndCollect(self, argv, has_side_effects) catch |err| term: { if (err == error.InvalidExe) interpret: { // TODO: learn the target from the binary directly rather than from // relying on it being a CompileStep. This will make this logic @@ -571,11 +570,9 @@ fn runCommand(self: *RunStep, argv: []const []const u8, has_side_effects: bool) try Step.handleVerbose(step.owner, self.cwd, interp_argv.items); - assert(stdout_bytes == null); - assert(stderr_bytes == null); - break :term spawnChildAndCollect(self, interp_argv.items, &stdout_bytes, &stderr_bytes, has_side_effects) catch |inner_err| { + break :term spawnChildAndCollect(self, interp_argv.items, has_side_effects) catch |e| { return step.fail("unable to spawn {s}: {s}", .{ - interp_argv.items[0], @errorName(inner_err), + interp_argv.items[0], @errorName(e), }); }; } @@ -586,7 +583,8 @@ fn runCommand(self: *RunStep, argv: []const []const u8, has_side_effects: bool) switch (self.stdio) { .check => |checks| for (checks.items) |check| switch (check) { .expect_stderr_exact => |expected_bytes| { - if (!mem.eql(u8, expected_bytes, stderr_bytes.?)) { + assert(!result.stderr_null); + if (!mem.eql(u8, expected_bytes, result.stderr)) { return step.fail( \\ \\========= expected this stderr: ========= @@ -597,13 +595,14 @@ fn runCommand(self: *RunStep, argv: []const []const u8, has_side_effects: bool) \\{s} , .{ expected_bytes, - stderr_bytes.?, + result.stderr, try Step.allocPrintCmd(arena, self.cwd, argv), }); } }, .expect_stderr_match => |match| { - if (mem.indexOf(u8, stderr_bytes.?, match) == null) { + assert(!result.stderr_null); + if (mem.indexOf(u8, result.stderr, match) == null) { return step.fail( \\ \\========= expected to find in stderr: ========= @@ -614,13 +613,14 @@ fn runCommand(self: *RunStep, argv: []const []const u8, has_side_effects: bool) \\{s} , .{ match, - stderr_bytes.?, + result.stderr, try Step.allocPrintCmd(arena, self.cwd, argv), }); } }, .expect_stdout_exact => |expected_bytes| { - if (!mem.eql(u8, expected_bytes, stdout_bytes.?)) { + assert(!result.stdout_null); + if (!mem.eql(u8, expected_bytes, result.stdout)) { return step.fail( \\ \\========= expected this stdout: ========= @@ -631,13 +631,14 @@ fn runCommand(self: *RunStep, argv: []const []const u8, has_side_effects: bool) \\{s} , .{ expected_bytes, - stdout_bytes.?, + result.stdout, try Step.allocPrintCmd(arena, self.cwd, argv), }); } }, .expect_stdout_match => |match| { - if (mem.indexOf(u8, stdout_bytes.?, match) == null) { + assert(!result.stdout_null); + if (mem.indexOf(u8, result.stdout, match) == null) { return step.fail( \\ \\========= expected to find in stdout: ========= @@ -648,15 +649,15 @@ fn runCommand(self: *RunStep, argv: []const []const u8, has_side_effects: bool) \\{s} , .{ match, - stdout_bytes.?, + result.stdout, try Step.allocPrintCmd(arena, self.cwd, argv), }); } }, .expect_term => |expected_term| { - if (!termMatches(expected_term, term)) { + if (!termMatches(expected_term, result.term)) { return step.fail("the following command {} (expected {}):\n{s}", .{ - fmtTerm(term), + fmtTerm(result.term), fmtTerm(expected_term), try Step.allocPrintCmd(arena, self.cwd, argv), }); @@ -664,24 +665,36 @@ fn runCommand(self: *RunStep, argv: []const []const u8, has_side_effects: bool) }, }, else => { - try step.handleChildProcessTerm(term, self.cwd, argv); + try step.handleChildProcessTerm(result.term, self.cwd, argv); }, } } +const ChildProcResult = struct { + // These use boolean flags instead of optionals as a workaround for + // https://github.com/ziglang/zig/issues/14783 + stdout: []const u8, + stderr: []const u8, + stdout_null: bool, + stderr_null: bool, + term: std.process.Child.Term, +}; + fn spawnChildAndCollect( self: *RunStep, argv: []const []const u8, - stdout_bytes: *?[]const u8, - stderr_bytes: *?[]const u8, has_side_effects: bool, -) !std.ChildProcess.Term { +) !ChildProcResult { const b = self.step.owner; const arena = b.allocator; - const cwd = if (self.cwd) |cwd| b.pathFromRoot(cwd) else b.build_root.path; - var child = std.ChildProcess.init(argv, arena); - child.cwd = cwd; + var child = std.process.Child.init(argv, arena); + if (self.cwd) |cwd| { + child.cwd = b.pathFromRoot(cwd); + } else { + child.cwd = b.build_root.path; + child.cwd_dir = b.build_root.handle; + } child.env_map = self.env_map orelse b.env_map; child.stdin_behavior = switch (self.stdio) { @@ -704,6 +717,13 @@ fn spawnChildAndCollect( argv[0], @errorName(err), }); + // These are not optionals, as a workaround for + // https://github.com/ziglang/zig/issues/14783 + var stdout_bytes: []const u8 = undefined; + var stderr_bytes: []const u8 = undefined; + var stdout_null = true; + var stderr_null = true; + if (child.stdout) |stdout| { if (child.stderr) |stderr| { var poller = std.io.poll(arena, enum { stdout, stderr }, .{ @@ -719,26 +739,36 @@ fn spawnChildAndCollect( return error.StderrStreamTooLong; } - stdout_bytes.* = try poller.fifo(.stdout).toOwnedSlice(); - stderr_bytes.* = try poller.fifo(.stderr).toOwnedSlice(); + stdout_bytes = try poller.fifo(.stdout).toOwnedSlice(); + stderr_bytes = try poller.fifo(.stderr).toOwnedSlice(); + stdout_null = false; + stderr_null = false; } else { - stdout_bytes.* = try stdout.reader().readAllAlloc(arena, self.max_stdio_size); + stdout_bytes = try stdout.reader().readAllAlloc(arena, self.max_stdio_size); + stdout_null = false; } } else if (child.stderr) |stderr| { - stderr_bytes.* = try stderr.reader().readAllAlloc(arena, self.max_stdio_size); + stderr_bytes = try stderr.reader().readAllAlloc(arena, self.max_stdio_size); + stderr_null = false; } - if (stderr_bytes.*) |stderr| if (stderr.len > 0) { + if (!stderr_null and stderr_bytes.len > 0) { const stderr_is_diagnostic = switch (self.stdio) { .check => |checks| !checksContainStderr(checks.items), else => true, }; if (stderr_is_diagnostic) { - try self.step.result_error_msgs.append(arena, stderr); + try self.step.result_error_msgs.append(arena, stderr_bytes); } - }; + } - return child.wait(); + return .{ + .stdout = stdout_bytes, + .stderr = stderr_bytes, + .stdout_null = stdout_null, + .stderr_null = stderr_null, + .term = try child.wait(), + }; } fn addPathForDynLibs(self: *RunStep, artifact: *CompileStep) void { From 41a5ad28c9a651efd2822f43486a71ca48ace726 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 3 Mar 2023 15:27:53 -0700 Subject: [PATCH 191/294] std: child process API supports rusage data --- lib/std/c.zig | 3 ++- lib/std/child_process.zig | 49 ++++++++++++++++++++++++++++++++++++++- lib/std/os.zig | 22 +++++++++++++++++- lib/std/os/linux.zig | 10 ++++++++ 4 files changed, 81 insertions(+), 3 deletions(-) diff --git a/lib/std/c.zig b/lib/std/c.zig index 9fc3b1d57e..a0b65c31c8 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -153,7 +153,8 @@ pub extern "c" fn linkat(oldfd: c.fd_t, oldpath: [*:0]const u8, newfd: c.fd_t, n pub extern "c" fn unlink(path: [*:0]const u8) c_int; pub extern "c" fn unlinkat(dirfd: c.fd_t, path: [*:0]const u8, flags: c_uint) c_int; pub extern "c" fn getcwd(buf: [*]u8, size: usize) ?[*]u8; -pub extern "c" fn waitpid(pid: c.pid_t, stat_loc: ?*c_int, options: c_int) c.pid_t; +pub extern "c" fn waitpid(pid: c.pid_t, status: ?*c_int, options: c_int) c.pid_t; +pub extern "c" fn wait4(pid: c.pid_t, status: ?*c_int, options: c_int, ru: ?*c.rusage) c.pid_t; pub extern "c" fn fork() c_int; pub extern "c" fn access(path: [*:0]const u8, mode: c_uint) c_int; pub extern "c" fn faccessat(dirfd: c.fd_t, path: [*:0]const u8, mode: c_uint, flags: c_uint) c_int; diff --git a/lib/std/child_process.zig b/lib/std/child_process.zig index 3bdef3177a..f9b2007b3e 100644 --- a/lib/std/child_process.zig +++ b/lib/std/child_process.zig @@ -17,6 +17,7 @@ const Os = std.builtin.Os; const TailQueue = std.TailQueue; const maxInt = std.math.maxInt; const assert = std.debug.assert; +const is_darwin = builtin.target.isDarwin(); pub const ChildProcess = struct { pub const Id = switch (builtin.os.tag) { @@ -70,6 +71,43 @@ pub const ChildProcess = struct { /// Darwin-only. Start child process in suspended state as if SIGSTOP was sent. start_suspended: bool = false, + /// Set to true to obtain rusage information for the child process. + /// Depending on the target platform and implementation status, the + /// requested statistics may or may not be available. If they are + /// available, then the `resource_usage_statistics` field will be populated + /// after calling `wait`. + /// On Linux, this obtains rusage statistics from wait4(). + request_resource_usage_statistics: bool = false, + + /// This is available after calling wait if + /// `request_resource_usage_statistics` was set to `true` before calling + /// `spawn`. + resource_usage_statistics: ResourceUsageStatistics = .{}, + + pub const ResourceUsageStatistics = struct { + rusage: @TypeOf(rusage_init) = rusage_init, + + /// Returns the peak resident set size of the child process, in bytes, + /// if available. + pub inline fn getMaxRss(rus: ResourceUsageStatistics) ?usize { + switch (builtin.os.tag) { + .linux => { + if (rus.rusage) |ru| { + return @intCast(usize, ru.maxrss) * 1024; + } else { + return null; + } + }, + else => return null, + } + } + + const rusage_init = switch (builtin.os.tag) { + .linux => @as(?std.os.rusage, null), + else => {}, + }; + }; + pub const Arg0Expand = os.Arg0Expand; pub const SpawnError = error{ @@ -332,7 +370,16 @@ pub const ChildProcess = struct { } fn waitUnwrapped(self: *ChildProcess) !void { - const res: os.WaitPidResult = os.waitpid(self.id, 0); + const res: os.WaitPidResult = res: { + if (builtin.os.tag == .linux and self.request_resource_usage_statistics) { + var ru: std.os.rusage = undefined; + const res = os.wait4(self.id, 0, &ru); + self.resource_usage_statistics.rusage = ru; + break :res res; + } + + break :res os.waitpid(self.id, 0); + }; const status = res.status; self.cleanupStreams(); self.handleWaitResult(status); diff --git a/lib/std/os.zig b/lib/std/os.zig index 069a910408..9d5625c13e 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -4000,8 +4000,28 @@ pub const WaitPidResult = struct { pub fn waitpid(pid: pid_t, flags: u32) WaitPidResult { const Status = if (builtin.link_libc) c_int else u32; var status: Status = undefined; + const coerced_flags = if (builtin.link_libc) @intCast(c_int, flags) else flags; while (true) { - const rc = system.waitpid(pid, &status, if (builtin.link_libc) @intCast(c_int, flags) else flags); + const rc = system.waitpid(pid, &status, coerced_flags); + switch (errno(rc)) { + .SUCCESS => return .{ + .pid = @intCast(pid_t, rc), + .status = @bitCast(u32, status), + }, + .INTR => continue, + .CHILD => unreachable, // The process specified does not exist. It would be a race condition to handle this error. + .INVAL => unreachable, // Invalid flags. + else => unreachable, + } + } +} + +pub fn wait4(pid: pid_t, flags: u32, ru: ?*rusage) WaitPidResult { + const Status = if (builtin.link_libc) c_int else u32; + var status: Status = undefined; + const coerced_flags = if (builtin.link_libc) @intCast(c_int, flags) else flags; + while (true) { + const rc = system.wait4(pid, &status, coerced_flags, ru); switch (errno(rc)) { .SUCCESS => return .{ .pid = @intCast(pid_t, rc), diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index 15b0dbc17b..53f6030b5f 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -944,6 +944,16 @@ pub fn waitpid(pid: pid_t, status: *u32, flags: u32) usize { return syscall4(.wait4, @bitCast(usize, @as(isize, pid)), @ptrToInt(status), flags, 0); } +pub fn wait4(pid: pid_t, status: *u32, flags: u32, usage: ?*rusage) usize { + return syscall4( + .wait4, + @bitCast(usize, @as(isize, pid)), + @ptrToInt(status), + flags, + @ptrToInt(usage), + ); +} + pub fn waitid(id_type: P, id: i32, infop: *siginfo_t, flags: u32) usize { return syscall5(.waitid, @enumToInt(id_type), @bitCast(usize, @as(isize, id)), @ptrToInt(infop), flags, 0); } From d695f36e70f162e4e84087df1ccad5430e53d786 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 3 Mar 2023 15:28:38 -0700 Subject: [PATCH 192/294] build runner supports reporting cached status and duration --- lib/build_runner.zig | 36 +++++++++++++++++++++++++++++++++++- lib/std/Build/Step.zig | 7 +++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/lib/build_runner.zig b/lib/build_runner.zig index aa846ce799..1c07efd944 100644 --- a/lib/build_runner.zig +++ b/lib/build_runner.zig @@ -501,8 +501,42 @@ fn printTreeStep( .success => { try ttyconf.setColor(stderr, .Green); - try stderr.writeAll(" success\n"); + if (s.result_cached) { + try stderr.writeAll(" cached"); + } else { + try stderr.writeAll(" success"); + } try ttyconf.setColor(stderr, .Reset); + if (s.result_duration_ns) |ns| { + try ttyconf.setColor(stderr, .Dim); + if (ns >= std.time.ns_per_min) { + try stderr.writer().print(" {d}m", .{ns / std.time.ns_per_min}); + } else if (ns >= std.time.ns_per_s) { + try stderr.writer().print(" {d}s", .{ns / std.time.ns_per_s}); + } else if (ns >= std.time.ns_per_ms) { + try stderr.writer().print(" {d}ms", .{ns / std.time.ns_per_ms}); + } else { + try stderr.writer().print(" {d}ns", .{ns}); + } + try ttyconf.setColor(stderr, .Reset); + } + if (s.result_peak_rss != 0) { + const rss = s.result_peak_rss; + try ttyconf.setColor(stderr, .Dim); + if (rss >= 1000_000_000_000) { + try stderr.writer().print(" {d}G MaxRSS", .{rss / 1000_000_000_000}); + } else if (rss >= 1000_000_000) { + try stderr.writer().print(" {d}M MaxRSS", .{rss / 1000_000_000}); + } else if (rss >= 1000_000) { + try stderr.writer().print(" {d}M MaxRSS", .{rss / 1000_000}); + } else if (rss >= 1000) { + try stderr.writer().print(" {d}K MaxRSS", .{rss / 1000}); + } else { + try stderr.writer().print(" {d}B MaxRSS", .{rss}); + } + try ttyconf.setColor(stderr, .Reset); + } + try stderr.writeAll("\n"); }, .skipped => { diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig index 928e7e8735..be14373afe 100644 --- a/lib/std/Build/Step.zig +++ b/lib/std/Build/Step.zig @@ -13,6 +13,10 @@ debug_stack_trace: [n_debug_stack_frames]usize, result_error_msgs: std.ArrayListUnmanaged([]const u8), result_error_bundle: std.zig.ErrorBundle, +result_cached: bool, +result_duration_ns: ?u64, +/// 0 means unavailable or not reported. +result_peak_rss: usize, pub const MakeFn = *const fn (self: *Step, prog_node: *std.Progress.Node) anyerror!void; @@ -103,6 +107,9 @@ pub fn init(options: Options) Step { .debug_stack_trace = addresses, .result_error_msgs = .{}, .result_error_bundle = std.zig.ErrorBundle.empty, + .result_cached = false, + .result_duration_ns = null, + .result_peak_rss = 0, }; } From 2b23625510e4f250ae925f9a3a4b090fba69f6a8 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 3 Mar 2023 15:29:02 -0700 Subject: [PATCH 193/294] std.Build.RunStep: report duration and cached status --- lib/std/Build/RunStep.zig | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/std/Build/RunStep.zig b/lib/std/Build/RunStep.zig index 6224c62897..d671ff7ae8 100644 --- a/lib/std/Build/RunStep.zig +++ b/lib/std/Build/RunStep.zig @@ -375,6 +375,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { &.{ "o", &digest, placeholder.output.basename }, ); } + step.result_cached = true; return; } @@ -580,6 +581,9 @@ fn runCommand(self: *RunStep, argv: []const []const u8, has_side_effects: bool) return step.fail("unable to spawn {s}: {s}", .{ argv[0], @errorName(err) }); }; + step.result_duration_ns = result.elapsed_ns; + step.result_peak_rss = result.peak_rss; + switch (self.stdio) { .check => |checks| for (checks.items) |check| switch (check) { .expect_stderr_exact => |expected_bytes| { @@ -678,6 +682,8 @@ const ChildProcResult = struct { stdout_null: bool, stderr_null: bool, term: std.process.Child.Term, + elapsed_ns: u64, + peak_rss: usize, }; fn spawnChildAndCollect( @@ -696,6 +702,7 @@ fn spawnChildAndCollect( child.cwd_dir = b.build_root.handle; } child.env_map = self.env_map orelse b.env_map; + child.request_resource_usage_statistics = true; child.stdin_behavior = switch (self.stdio) { .infer_from_args => if (has_side_effects) .Inherit else .Ignore, @@ -716,6 +723,7 @@ fn spawnChildAndCollect( child.spawn() catch |err| return self.step.fail("unable to spawn {s}: {s}", .{ argv[0], @errorName(err), }); + var timer = try std.time.Timer.start(); // These are not optionals, as a workaround for // https://github.com/ziglang/zig/issues/14783 @@ -762,12 +770,17 @@ fn spawnChildAndCollect( } } + const term = try child.wait(); + const elapsed_ns = timer.read(); + return .{ .stdout = stdout_bytes, .stderr = stderr_bytes, .stdout_null = stdout_null, .stderr_null = stderr_null, - .term = try child.wait(), + .term = term, + .elapsed_ns = elapsed_ns, + .peak_rss = child.resource_usage_statistics.getMaxRss() orelse 0, }; } From 0322e292e8e55a16f5d61defa9b079c5364aabb9 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 3 Mar 2023 15:35:07 -0700 Subject: [PATCH 194/294] update test/standalone/sigpipe build.zig script to latest API --- test/standalone/sigpipe/build.zig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/standalone/sigpipe/build.zig b/test/standalone/sigpipe/build.zig index 763df5fe46..400f1a970d 100644 --- a/test/standalone/sigpipe/build.zig +++ b/test/standalone/sigpipe/build.zig @@ -23,12 +23,12 @@ pub fn build(b: *std.build.Builder) !void { .root_source_file = .{ .path = "breakpipe.zig" }, }); exe.addOptions("build_options", options); - const run = exe.run(); + const run = b.addRunArtifact(exe); if (keep_sigpipe) { - run.expected_term = .{ .Signal = std.os.SIG.PIPE }; + run.addCheck(.{ .expect_term = .{ .Signal = std.os.SIG.PIPE } }); } else { - run.stdout_action = .{ .expect_exact = "BrokenPipe\n" }; - run.expected_term = .{ .Exited = 123 }; + run.addCheck(.{ .expect_stdout_exact = "BrokenPipe\n" }); + run.addCheck(.{ .expect_term = .{ .Exited = 123 } }); } test_step.dependOn(&run.step); } From 80d1976db9efd21920a45890cbf368d808b166e5 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 3 Mar 2023 15:35:42 -0700 Subject: [PATCH 195/294] build runner: add microseconds to elapsed in build summary --- lib/build_runner.zig | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/build_runner.zig b/lib/build_runner.zig index 1c07efd944..da07bc5b26 100644 --- a/lib/build_runner.zig +++ b/lib/build_runner.zig @@ -515,6 +515,8 @@ fn printTreeStep( try stderr.writer().print(" {d}s", .{ns / std.time.ns_per_s}); } else if (ns >= std.time.ns_per_ms) { try stderr.writer().print(" {d}ms", .{ns / std.time.ns_per_ms}); + } else if (ns >= std.time.ns_per_us) { + try stderr.writer().print(" {d}us", .{ns / std.time.ns_per_us}); } else { try stderr.writer().print(" {d}ns", .{ns}); } @@ -524,15 +526,15 @@ fn printTreeStep( const rss = s.result_peak_rss; try ttyconf.setColor(stderr, .Dim); if (rss >= 1000_000_000_000) { - try stderr.writer().print(" {d}G MaxRSS", .{rss / 1000_000_000_000}); + try stderr.writer().print(" MaxRSS:{d}G", .{rss / 1000_000_000_000}); } else if (rss >= 1000_000_000) { - try stderr.writer().print(" {d}M MaxRSS", .{rss / 1000_000_000}); + try stderr.writer().print(" MaxRSS:{d}M", .{rss / 1000_000_000}); } else if (rss >= 1000_000) { - try stderr.writer().print(" {d}M MaxRSS", .{rss / 1000_000}); + try stderr.writer().print(" MaxRSS:{d}M", .{rss / 1000_000}); } else if (rss >= 1000) { - try stderr.writer().print(" {d}K MaxRSS", .{rss / 1000}); + try stderr.writer().print(" MaxRSS:{d}K", .{rss / 1000}); } else { - try stderr.writer().print(" {d}B MaxRSS", .{rss}); + try stderr.writer().print(" MaxRSS:{d}B", .{rss}); } try ttyconf.setColor(stderr, .Reset); } From 2996eb558756c697e2ccfe9691356e536c88916b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 3 Mar 2023 16:39:08 -0700 Subject: [PATCH 196/294] std.Build.RunStep: add maxrss, duration, and cached status --- lib/std/Build/ObjCopyStep.zig | 2 +- lib/std/Build/Step.zig | 10 ++++++++- lib/std/zig/Server.zig | 13 ++++++++++- src/Compilation.zig | 3 +++ src/main.zig | 41 ++++++++++++++++++++++++++++------- 5 files changed, 58 insertions(+), 11 deletions(-) diff --git a/lib/std/Build/ObjCopyStep.zig b/lib/std/Build/ObjCopyStep.zig index 13046b3efe..5f675ed383 100644 --- a/lib/std/Build/ObjCopyStep.zig +++ b/lib/std/Build/ObjCopyStep.zig @@ -113,7 +113,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { }; try argv.appendSlice(&.{ full_src_path, full_dest_path }); - _ = try step.spawnZigProcess(argv.items, prog_node); + _ = try step.evalZigProcess(argv.items, prog_node); self.output_file.path = full_dest_path; try man.writeManifest(); diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig index be14373afe..37bd66678b 100644 --- a/lib/std/Build/Step.zig +++ b/lib/std/Build/Step.zig @@ -234,10 +234,12 @@ pub fn evalZigProcess( child.stdin_behavior = .Pipe; child.stdout_behavior = .Pipe; child.stderr_behavior = .Pipe; + child.request_resource_usage_statistics = true; child.spawn() catch |err| return s.fail("unable to spawn {s}: {s}", .{ argv[0], @errorName(err), }); + var timer = try std.time.Timer.start(); var poller = std.io.poll(gpa, enum { stdout, stderr }, .{ .stdout = child.stdout.?, @@ -301,7 +303,10 @@ pub fn evalZigProcess( sub_prog_node.?.activate(); }, .emit_bin_path => { - result = try arena.dupe(u8, body); + const EbpHdr = std.zig.Server.Message.EmitBinPath; + const ebp_hdr = @ptrCast(*align(1) const EbpHdr, body); + s.result_cached = ebp_hdr.flags.cache_hit; + result = try arena.dupe(u8, body[@sizeOf(EbpHdr)..]); }, _ => { // Unrecognized message. @@ -323,6 +328,9 @@ pub fn evalZigProcess( const term = child.wait() catch |err| { return s.fail("unable to wait for {s}: {s}", .{ argv[0], @errorName(err) }); }; + s.result_duration_ns = timer.read(); + s.result_peak_rss = child.resource_usage_statistics.getMaxRss() orelse 0; + try handleChildProcessTerm(s, term, null, argv); if (s.result_error_bundle.errorMessageCount() > 0) { diff --git a/lib/std/zig/Server.zig b/lib/std/zig/Server.zig index 76f2303f6b..d34b2193e9 100644 --- a/lib/std/zig/Server.zig +++ b/lib/std/zig/Server.zig @@ -12,7 +12,7 @@ pub const Message = struct { error_bundle, /// Body is a UTF-8 string. progress, - /// Body is a UTF-8 string. + /// Body is a EmitBinPath. emit_bin_path, _, }; @@ -25,4 +25,15 @@ pub const Message = struct { extra_len: u32, string_bytes_len: u32, }; + + /// Trailing: + /// * the file system path the emitted binary can be found + pub const EmitBinPath = extern struct { + flags: Flags, + + pub const Flags = packed struct(u8) { + cache_hit: bool, + reserved: u7 = 0, + }; + }; }; diff --git a/src/Compilation.zig b/src/Compilation.zig index 28e7c43702..b542c86511 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -100,6 +100,7 @@ job_queued_compiler_rt_lib: bool = false, job_queued_compiler_rt_obj: bool = false, alloc_failure_occurred: bool = false, formatted_panics: bool = false, +last_update_was_cache_hit: bool = false, c_source_files: []const CSourceFile, clang_argv: []const []const u8, @@ -1860,6 +1861,7 @@ pub fn update(comp: *Compilation, main_progress_node: *std.Progress.Node) !void defer tracy_trace.end(); comp.clearMiscFailures(); + comp.last_update_was_cache_hit = false; var man: Cache.Manifest = undefined; defer if (comp.whole_cache_manifest != null) man.deinit(); @@ -1887,6 +1889,7 @@ pub fn update(comp: *Compilation, main_progress_node: *std.Progress.Node) !void return err; }; if (is_hit) { + comp.last_update_was_cache_hit = true; log.debug("CacheMode.whole cache hit for {s}", .{comp.bin_file.options.root_name}); const digest = man.final(); diff --git a/src/main.zig b/src/main.zig index f81911ffd0..699122d26f 100644 --- a/src/main.zig +++ b/src/main.zig @@ -3578,9 +3578,11 @@ fn serve( var arena_instance = std.heap.ArenaAllocator.init(gpa); defer arena_instance.deinit(); const arena = arena_instance.allocator(); - var output_path: []const u8 = undefined; - try cmdTranslateC(comp, arena, &output_path); - try serveStringMessage(out, .emit_bin_path, output_path); + var output: TranslateCOutput = undefined; + try cmdTranslateC(comp, arena, &output); + try serveEmitBinPath(out, output.path, .{ + .flags = .{ .cache_hit = output.cache_hit }, + }); continue; } @@ -3760,10 +3762,26 @@ fn serveUpdateResults(out: fs.File, comp: *Compilation) !void { } else if (comp.bin_file.options.emit) |emit| { const full_path = try emit.directory.join(gpa, &.{emit.sub_path}); defer gpa.free(full_path); - try serveStringMessage(out, .emit_bin_path, full_path); + try serveEmitBinPath(out, full_path, .{ + .flags = .{ .cache_hit = comp.last_update_was_cache_hit }, + }); } } +fn serveEmitBinPath( + out: fs.File, + fs_path: []const u8, + header: std.zig.Server.Message.EmitBinPath, +) !void { + try serveMessage(out, .{ + .tag = .emit_bin_path, + .bytes_len = @intCast(u32, fs_path.len + @sizeOf(std.zig.Server.Message.EmitBinPath)), + }, &.{ + std.mem.asBytes(&header), + fs_path, + }); +} + fn serveStringMessage(out: fs.File, tag: std.zig.Server.Message.Tag, s: []const u8) !void { try serveMessage(out, .{ .tag = tag, @@ -4115,7 +4133,12 @@ fn updateModule(gpa: Allocator, comp: *Compilation, hook: AfterUpdateHook) !void } } -fn cmdTranslateC(comp: *Compilation, arena: Allocator, output_path: ?*[]const u8) !void { +const TranslateCOutput = struct { + path: []const u8, + cache_hit: bool, +}; + +fn cmdTranslateC(comp: *Compilation, arena: Allocator, fancy_output: ?*TranslateCOutput) !void { if (!build_options.have_llvm) fatal("cannot translate-c: compiler built without LLVM extensions", .{}); @@ -4126,14 +4149,16 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, output_path: ?*[]const u8 var man: Cache.Manifest = comp.obtainCObjectCacheManifest(); man.want_shared_lock = false; - defer if (output_path != null) man.deinit(); + defer man.deinit(); man.hash.add(@as(u16, 0xb945)); // Random number to distinguish translate-c from compiling C objects Compilation.cache_helpers.hashCSource(&man, c_source_file) catch |err| { fatal("unable to process '{s}': {s}", .{ c_source_file.src_path, @errorName(err) }); }; + if (fancy_output) |p| p.cache_hit = true; const digest = if (try man.hit()) man.final() else digest: { + if (fancy_output) |p| p.cache_hit = false; var argv = std.ArrayList([]const u8).init(arena); try argv.append(""); // argv[0] is program name, actual args start at [1] @@ -4229,11 +4254,11 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, output_path: ?*[]const u8 break :digest digest; }; - if (output_path) |out_path| { + if (fancy_output) |p| { const full_zig_path = try comp.local_cache_directory.join(arena, &[_][]const u8{ "o", &digest, translated_zig_basename, }); - out_path.* = full_zig_path; + p.path = full_zig_path; } else { const out_zig_path = try fs.path.join(arena, &[_][]const u8{ "o", &digest, translated_zig_basename }); const zig_file = comp.local_cache_directory.handle.openFile(out_zig_path, .{}) catch |err| { From 2cc33f5f4e0b55dcc1fb7cc4fb5d3b565b3a50d2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 3 Mar 2023 17:29:20 -0700 Subject: [PATCH 197/294] std.Build.Step.cacheHit marks step as cached on hit --- lib/std/Build/Step.zig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig index 37bd66678b..1a7fe24e7c 100644 --- a/lib/std/Build/Step.zig +++ b/lib/std/Build/Step.zig @@ -415,7 +415,8 @@ pub fn allocPrintCmd(arena: Allocator, opt_cwd: ?[]const u8, argv: []const []con } pub fn cacheHit(s: *Step, man: *std.Build.Cache.Manifest) !bool { - return man.hit() catch |err| return failWithCacheError(s, man, err); + s.result_cached = man.hit() catch |err| return failWithCacheError(s, man, err); + return s.result_cached; } fn failWithCacheError(s: *Step, man: *const std.Build.Cache.Manifest, err: anyerror) anyerror { From 405bf1b091bd1dba3a2c904c70aa562f41a6b3a3 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 3 Mar 2023 17:29:59 -0700 Subject: [PATCH 198/294] std.Build.ConfigHeaderStep: integrate with the cache system --- lib/std/Build/ConfigHeaderStep.zig | 75 ++++++++++++++---------------- lib/std/Build/WriteFileStep.zig | 1 - 2 files changed, 34 insertions(+), 42 deletions(-) diff --git a/lib/std/Build/ConfigHeaderStep.zig b/lib/std/Build/ConfigHeaderStep.zig index 37b04e75a4..c1849b410e 100644 --- a/lib/std/Build/ConfigHeaderStep.zig +++ b/lib/std/Build/ConfigHeaderStep.zig @@ -1,9 +1,3 @@ -const std = @import("../std.zig"); -const ConfigHeaderStep = @This(); -const Step = std.Build.Step; - -pub const base_id: Step.Id = .config_header; - pub const Style = union(enum) { /// The configure format supported by autotools. It uses `#undef foo` to /// mark lines that can be substituted with different values. @@ -41,6 +35,8 @@ style: Style, max_bytes: usize, include_path: []const u8, +pub const base_id: Step.Id = .config_header; + pub const Options = struct { style: Style = .blank, max_bytes: usize = 2 * 1024 * 1024, @@ -162,23 +158,15 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { const b = step.owner; const self = @fieldParentPtr(ConfigHeaderStep, "step", step); const gpa = b.allocator; + const arena = b.allocator; - // The cache is used here not really as a way to speed things up - because writing - // the data to a file would probably be very fast - but as a way to find a canonical - // location to put build artifacts. + var man = b.cache.obtain(); + defer man.deinit(); - // If, for example, a hard-coded path was used as the location to put ConfigHeaderStep - // files, then two ConfigHeaderStep executing in parallel might clobber each other. - - // TODO port the cache system from the compiler to zig std lib. Until then - // we construct the path directly, and no "cache hit" detection happens; - // the files are always written. - // Note there is very similar code over in WriteFileStep - const Hasher = std.crypto.auth.siphash.SipHash128(1, 3); // Random bytes to make ConfigHeaderStep unique. Refresh this with new // random bytes when ConfigHeaderStep implementation is modified in a // non-backwards-compatible way. - var hash = Hasher.init("PGuDTpidxyMqnkGM"); + man.hash.add(@as(u32, 0xdef08d23)); var output = std.ArrayList(u8).init(gpa); defer output.deinit(); @@ -191,13 +179,13 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { .autoconf => |file_source| { try output.appendSlice(c_generated_line); const src_path = file_source.getPath(b); - const contents = try std.fs.cwd().readFileAlloc(gpa, src_path, self.max_bytes); + const contents = try std.fs.cwd().readFileAlloc(arena, src_path, self.max_bytes); try render_autoconf(step, contents, &output, self.values, src_path); }, .cmake => |file_source| { try output.appendSlice(c_generated_line); const src_path = file_source.getPath(b); - const contents = try std.fs.cwd().readFileAlloc(gpa, src_path, self.max_bytes); + const contents = try std.fs.cwd().readFileAlloc(arena, src_path, self.max_bytes); try render_cmake(step, contents, &output, self.values, src_path); }, .blank => { @@ -210,39 +198,40 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { }, } - hash.update(output.items); + man.hash.addBytes(output.items); - var digest: [16]u8 = undefined; - hash.final(&digest); - var hash_basename: [digest.len * 2]u8 = undefined; - _ = std.fmt.bufPrint( - &hash_basename, - "{s}", - .{std.fmt.fmtSliceHexLower(&digest)}, - ) catch unreachable; + if (try step.cacheHit(&man)) { + const digest = man.final(); + self.output_file.path = try b.cache_root.join(arena, &.{ + "o", &digest, self.include_path, + }); + return; + } - const output_dir = try b.cache_root.join(gpa, &.{ "o", &hash_basename }); + const digest = man.final(); // If output_path has directory parts, deal with them. Example: // output_dir is zig-cache/o/HASH // output_path is libavutil/avconfig.h // We want to open directory zig-cache/o/HASH/libavutil/ // but keep output_dir as zig-cache/o/HASH for -I include - const sub_dir_path = if (std.fs.path.dirname(self.include_path)) |d| - try std.fs.path.join(gpa, &.{ output_dir, d }) - else - output_dir; + const sub_path = try std.fs.path.join(arena, &.{ "o", &digest, self.include_path }); + const sub_path_dirname = std.fs.path.dirname(sub_path).?; - var dir = std.fs.cwd().makeOpenPath(sub_dir_path, .{}) catch |err| { - return step.fail("unable to make path '{s}': {s}", .{ output_dir, @errorName(err) }); + b.cache_root.handle.makePath(sub_path_dirname) catch |err| { + return step.fail("unable to make path '{}{s}': {s}", .{ + b.cache_root, sub_path_dirname, @errorName(err), + }); }; - defer dir.close(); - try dir.writeFile(std.fs.path.basename(self.include_path), output.items); + b.cache_root.handle.writeFile(sub_path, output.items) catch |err| { + return step.fail("unable to write file '{}{s}': {s}", .{ + b.cache_root, sub_path, @errorName(err), + }); + }; - self.output_file.path = try std.fs.path.join(b.allocator, &.{ - output_dir, self.include_path, - }); + self.output_file.path = try b.cache_root.join(arena, &.{sub_path}); + try man.writeManifest(); } fn render_autoconf( @@ -442,3 +431,7 @@ fn renderValueNasm(output: *std.ArrayList(u8), name: []const u8, value: Value) ! }, } } + +const std = @import("../std.zig"); +const ConfigHeaderStep = @This(); +const Step = std.Build.Step; diff --git a/lib/std/Build/WriteFileStep.zig b/lib/std/Build/WriteFileStep.zig index b5164d5730..1109cf5426 100644 --- a/lib/std/Build/WriteFileStep.zig +++ b/lib/std/Build/WriteFileStep.zig @@ -187,7 +187,6 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { } if (try step.cacheHit(&man)) { - // Cache hit, skip writing file data. const digest = man.final(); for (wf.files.items) |file| { file.generated_file.path = try b.cache_root.join( From bb1960c2a4ae3257fab3eedd7fdf2293fde59cec Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 3 Mar 2023 17:49:14 -0700 Subject: [PATCH 199/294] std.Build.InstallDirStep: avoid std.log And better make use of open directory handles. --- lib/std/Build/InstallDirStep.zig | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/lib/std/Build/InstallDirStep.zig b/lib/std/Build/InstallDirStep.zig index 11553d9bc7..2807d641f3 100644 --- a/lib/std/Build/InstallDirStep.zig +++ b/lib/std/Build/InstallDirStep.zig @@ -4,7 +4,6 @@ const fs = std.fs; const Step = std.Build.Step; const InstallDir = std.Build.InstallDir; const InstallDirStep = @This(); -const log = std.log; step: Step, options: Options, @@ -57,17 +56,17 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { _ = prog_node; const self = @fieldParentPtr(InstallDirStep, "step", step); const dest_builder = self.dest_builder; + const arena = dest_builder.allocator; const dest_prefix = dest_builder.getInstallPath(self.options.install_dir, self.options.install_subdir); const src_builder = self.step.owner; - const full_src_dir = src_builder.pathFromRoot(self.options.source_dir); - var src_dir = std.fs.cwd().openIterableDir(full_src_dir, .{}) catch |err| { - log.err("InstallDirStep: unable to open source directory '{s}': {s}", .{ - full_src_dir, @errorName(err), + var src_dir = src_builder.build_root.handle.openIterableDir(self.options.source_dir, .{}) catch |err| { + return step.fail("unable to open source directory '{}{s}': {s}", .{ + src_builder.build_root, self.options.source_dir, @errorName(err), }); - return error.StepFailed; }; defer src_dir.close(); - var it = try src_dir.walk(dest_builder.allocator); + var it = try src_dir.walk(arena); + var all_cached = true; next_entry: while (try it.next()) |entry| { for (self.options.exclude_extensions) |ext| { if (mem.endsWith(u8, entry.path, ext)) { @@ -75,11 +74,13 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { } } - const full_path = dest_builder.pathJoin(&.{ full_src_dir, entry.path }); - const dest_path = dest_builder.pathJoin(&.{ dest_prefix, entry.path }); + // relative to src build root + const src_sub_path = try fs.path.join(arena, &.{ self.options.source_dir, entry.path }); + const dest_path = try fs.path.join(arena, &.{ dest_prefix, entry.path }); + const cwd = fs.cwd(); switch (entry.kind) { - .Directory => try fs.cwd().makePath(dest_path), + .Directory => try cwd.makePath(dest_path), .File => { for (self.options.blank_extensions) |ext| { if (mem.endsWith(u8, entry.path, ext)) { @@ -88,9 +89,18 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { } } - try dest_builder.updateFile(full_path, dest_path); + const prev_status = try fs.Dir.updateFile( + src_builder.build_root.handle, + src_sub_path, + cwd, + dest_path, + .{}, + ); + all_cached = all_cached and prev_status == .fresh; }, else => continue, } } + + step.result_cached = all_cached; } From 1e63573d359f15042ebdcc9b0c32d7ce4662899f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 3 Mar 2023 20:39:40 -0700 Subject: [PATCH 200/294] std.build.CompileStep: eliminate std.log usage --- lib/build_runner.zig | 3 + lib/std/Build.zig | 2 + lib/std/Build/CompileStep.zig | 81 ++++++++++++--------------- lib/std/Build/InstallArtifactStep.zig | 2 +- lib/std/Build/RemoveDirStep.zig | 1 - 5 files changed, 42 insertions(+), 47 deletions(-) diff --git a/lib/build_runner.zig b/lib/build_runner.zig index da07bc5b26..c99d55b783 100644 --- a/lib/build_runner.zig +++ b/lib/build_runner.zig @@ -179,6 +179,8 @@ pub fn main() !void { usageAndErr(builder, false, stderr_stream); }; try debug_log_scopes.append(next_arg); + } else if (mem.eql(u8, arg, "--debug-pkg-config")) { + builder.debug_pkg_config = true; } else if (mem.eql(u8, arg, "--debug-compile-errors")) { builder.debug_compile_errors = true; } else if (mem.eql(u8, arg, "--glibc-runtimes")) { @@ -809,6 +811,7 @@ fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi \\ --zig-lib-dir [arg] Override path to Zig lib directory \\ --build-runner [file] Override path to build runner \\ --debug-log [scope] Enable debugging the compiler + \\ --debug-pkg-config Fail if unknown pkg-config flags encountered \\ --verbose-link Enable compiler debug output for linking \\ --verbose-air Enable compiler debug output for Zig AIR \\ --verbose-llvm-ir Enable compiler debug output for LLVM IR diff --git a/lib/std/Build.zig b/lib/std/Build.zig index 1d87ea961f..8f46499fdc 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -85,6 +85,7 @@ pkg_config_pkg_list: ?(PkgConfigError![]const PkgConfigPkg) = null, args: ?[][]const u8 = null, debug_log_scopes: []const []const u8 = &.{}, debug_compile_errors: bool = false, +debug_pkg_config: bool = false, /// Experimental. Use system Darling installation to run cross compiled macOS build artifacts. enable_darling: bool = false, @@ -316,6 +317,7 @@ fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: Cache.Direc .zig_lib_dir = parent.zig_lib_dir, .debug_log_scopes = parent.debug_log_scopes, .debug_compile_errors = parent.debug_compile_errors, + .debug_pkg_config = parent.debug_pkg_config, .enable_darling = parent.enable_darling, .enable_qemu = parent.enable_qemu, .enable_rosetta = parent.enable_rosetta, diff --git a/lib/std/Build/CompileStep.zig b/lib/std/Build/CompileStep.zig index 99d99694c3..6802f4fb70 100644 --- a/lib/std/Build/CompileStep.zig +++ b/lib/std/Build/CompileStep.zig @@ -1,7 +1,6 @@ const builtin = @import("builtin"); const std = @import("../std.zig"); const mem = std.mem; -const log = std.log; const fs = std.fs; const assert = std.debug.assert; const panic = std.debug.panic; @@ -697,7 +696,7 @@ pub fn linkSystemLibraryNeededPkgConfigOnly(self: *CompileStep, lib_name: []cons /// Run pkg-config for the given library name and parse the output, returning the arguments /// that should be passed to zig to link the given library. -pub fn runPkgConfig(self: *CompileStep, lib_name: []const u8) ![]const []const u8 { +fn runPkgConfig(self: *CompileStep, lib_name: []const u8) ![]const []const u8 { const b = self.step.owner; const pkg_name = match: { // First we have to map the library name to pkg config name. Unfortunately, @@ -783,8 +782,8 @@ pub fn runPkgConfig(self: *CompileStep, lib_name: []const u8) ![]const []const u try zig_args.appendSlice(&[_][]const u8{ "-D", macro }); } else if (mem.startsWith(u8, tok, "-D")) { try zig_args.append(tok); - } else if (b.verbose) { - log.warn("Ignoring pkg-config flag '{s}'", .{tok}); + } else if (b.debug_pkg_config) { + return self.step.fail("unknown pkg-config flag '{s}'", .{tok}); } } @@ -1190,8 +1189,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { const self = @fieldParentPtr(CompileStep, "step", step); if (self.root_src == null and self.link_objects.items.len == 0) { - log.err("{s}: linker needs 1 or more objects to link", .{self.step.name}); - return error.NeedAnObject; + return step.fail("the linker needs one or more objects to link", .{}); } var zig_args = ArrayList([]const u8).init(b.allocator); @@ -1280,10 +1278,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { .system_lib => |system_lib| { const prefix: []const u8 = prefix: { if (system_lib.needed) break :prefix "-needed-l"; - if (system_lib.weak) { - if (self.target.isDarwin()) break :prefix "-weak-l"; - log.warn("Weak library import used for a non-darwin target, this will be converted to normally library import `-lname`", .{}); - } + if (system_lib.weak) break :prefix "-weak-l"; break :prefix "-l"; }; switch (system_lib.use_pkg_config) { @@ -1774,18 +1769,18 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { try zig_args.append(c_macro); } - if (self.target.isDarwin()) { - for (self.framework_dirs.items) |dir| { - if (b.sysroot != null) { - try zig_args.append("-iframeworkwithsysroot"); - } else { - try zig_args.append("-iframework"); - } - try zig_args.append(dir); - try zig_args.append("-F"); - try zig_args.append(dir); + for (self.framework_dirs.items) |dir| { + if (b.sysroot != null) { + try zig_args.append("-iframeworkwithsysroot"); + } else { + try zig_args.append("-iframework"); } + try zig_args.append(dir); + try zig_args.append("-F"); + try zig_args.append(dir); + } + { var it = self.frameworks.iterator(); while (it.next()) |entry| { const name = entry.key_ptr.*; @@ -1799,14 +1794,6 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { } try zig_args.append(name); } - } else { - if (self.framework_dirs.items.len > 0) { - log.info("Framework directories have been added for a non-darwin target, this will have no affect on the build", .{}); - } - - if (self.frameworks.count() > 0) { - log.info("Frameworks have been added for a non-darwin target, this will have no affect on the build", .{}); - } } if (b.sysroot) |sysroot| { @@ -1970,8 +1957,15 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { } } - if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic and self.version != null and self.target.wantSharedLibSymLinks()) { - try doAtomicSymLinks(b.allocator, self.getOutputSource().getPath(b), self.major_only_filename.?, self.name_only_filename.?); + if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic and + self.version != null and self.target.wantSharedLibSymLinks()) + { + try doAtomicSymLinks( + step, + self.getOutputSource().getPath(b), + self.major_only_filename.?, + self.name_only_filename.?, + ); } } @@ -2013,30 +2007,27 @@ fn findVcpkgRoot(allocator: Allocator) !?[]const u8 { } pub fn doAtomicSymLinks( - allocator: Allocator, + step: *Step, output_path: []const u8, filename_major_only: []const u8, filename_name_only: []const u8, ) !void { + const arena = step.owner.allocator; const out_dir = fs.path.dirname(output_path) orelse "."; const out_basename = fs.path.basename(output_path); // sym link for libfoo.so.1 to libfoo.so.1.2.3 - const major_only_path = try fs.path.join( - allocator, - &[_][]const u8{ out_dir, filename_major_only }, - ); - fs.atomicSymLink(allocator, out_basename, major_only_path) catch |err| { - log.err("Unable to symlink {s} -> {s}", .{ major_only_path, out_basename }); - return err; + const major_only_path = try fs.path.join(arena, &.{ out_dir, filename_major_only }); + fs.atomicSymLink(arena, out_basename, major_only_path) catch |err| { + return step.fail("unable to symlink {s} -> {s}: {s}", .{ + major_only_path, out_basename, @errorName(err), + }); }; // sym link for libfoo.so to libfoo.so.1 - const name_only_path = try fs.path.join( - allocator, - &[_][]const u8{ out_dir, filename_name_only }, - ); - fs.atomicSymLink(allocator, filename_major_only, name_only_path) catch |err| { - log.err("Unable to symlink {s} -> {s}", .{ name_only_path, filename_major_only }); - return err; + const name_only_path = try fs.path.join(arena, &.{ out_dir, filename_name_only }); + fs.atomicSymLink(arena, filename_major_only, name_only_path) catch |err| { + return step.fail("Unable to symlink {s} -> {s}: {s}", .{ + name_only_path, filename_major_only, @errorName(err), + }); }; } diff --git a/lib/std/Build/InstallArtifactStep.zig b/lib/std/Build/InstallArtifactStep.zig index 377cba301c..c3002c7f54 100644 --- a/lib/std/Build/InstallArtifactStep.zig +++ b/lib/std/Build/InstallArtifactStep.zig @@ -83,7 +83,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { full_dest_path, ); if (self.artifact.isDynamicLibrary() and self.artifact.version != null and self.artifact.target.wantSharedLibSymLinks()) { - try CompileStep.doAtomicSymLinks(src_builder.allocator, full_dest_path, self.artifact.major_only_filename.?, self.artifact.name_only_filename.?); + try CompileStep.doAtomicSymLinks(step, full_dest_path, self.artifact.major_only_filename.?, self.artifact.name_only_filename.?); } if (self.artifact.isDynamicLibrary() and self.artifact.target.isWindows() and self.artifact.emit_implib != .no_emit) { const full_implib_path = dest_builder.getInstallPath(self.dest_dir, self.artifact.out_lib_filename); diff --git a/lib/std/Build/RemoveDirStep.zig b/lib/std/Build/RemoveDirStep.zig index 9f291c7523..a5bf3c3256 100644 --- a/lib/std/Build/RemoveDirStep.zig +++ b/lib/std/Build/RemoveDirStep.zig @@ -1,5 +1,4 @@ const std = @import("../std.zig"); -const log = std.log; const fs = std.fs; const Step = std.Build.Step; const RemoveDirStep = @This(); From a4c35a62454a3970dd44ac8b842be91a46bb896c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 5 Mar 2023 16:11:04 -0700 Subject: [PATCH 201/294] std.Build: audit use of updateFile * remove std.Build.updateFile. I noticed some people use it from build.zig (declare phase) when it is intended only for use in the make phase. - This also was incorrectly reporting errors with std.log. * std.Build.InstallArtifactStep - report better errors on failure - report whether the step was cached or not * std.Build.InstallDirStep: report better error on failure * std.Build.InstallFileStep: report better error on failure --- lib/std/Build.zig | 12 ------ lib/std/Build/CompileStep.zig | 6 +-- lib/std/Build/InstallArtifactStep.zig | 53 ++++++++++++++++++++++----- lib/std/Build/InstallDirStep.zig | 8 +++- lib/std/Build/InstallFileStep.zig | 8 +++- 5 files changed, 60 insertions(+), 27 deletions(-) diff --git a/lib/std/Build.zig b/lib/std/Build.zig index 8f46499fdc..e3b44e965e 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -1235,18 +1235,6 @@ pub fn pushInstalledFile(self: *Build, dir: InstallDir, dest_rel_path: []const u self.installed_files.append(file.dupe(self)) catch @panic("OOM"); } -pub fn updateFile(self: *Build, source_path: []const u8, dest_path: []const u8) !void { - if (self.verbose) { - log.info("cp {s} {s} ", .{ source_path, dest_path }); - } - const cwd = fs.cwd(); - const prev_status = try fs.Dir.updateFile(cwd, source_path, cwd, dest_path, .{}); - if (self.verbose) switch (prev_status) { - .stale => log.info("# installed", .{}), - .fresh => log.info("# up-to-date", .{}), - }; -} - pub fn truncateFile(self: *Build, dest_path: []const u8) !void { if (self.verbose) { log.info("truncate {s}", .{dest_path}); diff --git a/lib/std/Build/CompileStep.zig b/lib/std/Build/CompileStep.zig index 6802f4fb70..895c1a7678 100644 --- a/lib/std/Build/CompileStep.zig +++ b/lib/std/Build/CompileStep.zig @@ -1910,13 +1910,13 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { const build_output_dir = fs.path.dirname(output_bin_path).?; if (self.output_dir) |output_dir| { - var src_dir = try std.fs.cwd().openIterableDir(build_output_dir, .{}); + var src_dir = try fs.cwd().openIterableDir(build_output_dir, .{}); defer src_dir.close(); // Create the output directory if it doesn't exist. - try std.fs.cwd().makePath(output_dir); + try fs.cwd().makePath(output_dir); - var dest_dir = try std.fs.cwd().openDir(output_dir, .{}); + var dest_dir = try fs.cwd().openDir(output_dir, .{}); defer dest_dir.close(); var it = src_dir.iterate(); diff --git a/lib/std/Build/InstallArtifactStep.zig b/lib/std/Build/InstallArtifactStep.zig index c3002c7f54..803998a619 100644 --- a/lib/std/Build/InstallArtifactStep.zig +++ b/lib/std/Build/InstallArtifactStep.zig @@ -3,6 +3,7 @@ const Step = std.Build.Step; const CompileStep = std.Build.CompileStep; const InstallDir = std.Build.InstallDir; const InstallArtifactStep = @This(); +const fs = std.fs; pub const base_id = .install_artifact; @@ -77,25 +78,59 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { const dest_sub_path = if (self.dest_sub_path) |sub_path| sub_path else self.artifact.out_filename; const full_dest_path = dest_builder.getInstallPath(self.dest_dir, dest_sub_path); + const cwd = fs.cwd(); - try src_builder.updateFile( - self.artifact.getOutputSource().getPath(src_builder), - full_dest_path, - ); - if (self.artifact.isDynamicLibrary() and self.artifact.version != null and self.artifact.target.wantSharedLibSymLinks()) { + var all_cached = true; + + { + const full_src_path = self.artifact.getOutputSource().getPath(src_builder); + const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_dest_path, .{}) catch |err| { + return step.fail("unable to update file from '{s}' to '{s}': {s}", .{ + full_src_path, full_dest_path, @errorName(err), + }); + }; + all_cached = all_cached and p == .fresh; + } + + if (self.artifact.isDynamicLibrary() and + self.artifact.version != null and + self.artifact.target.wantSharedLibSymLinks()) + { try CompileStep.doAtomicSymLinks(step, full_dest_path, self.artifact.major_only_filename.?, self.artifact.name_only_filename.?); } - if (self.artifact.isDynamicLibrary() and self.artifact.target.isWindows() and self.artifact.emit_implib != .no_emit) { + if (self.artifact.isDynamicLibrary() and + self.artifact.target.isWindows() and + self.artifact.emit_implib != .no_emit) + { + const full_src_path = self.artifact.getOutputLibSource().getPath(src_builder); const full_implib_path = dest_builder.getInstallPath(self.dest_dir, self.artifact.out_lib_filename); - try src_builder.updateFile(self.artifact.getOutputLibSource().getPath(src_builder), full_implib_path); + const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_implib_path, .{}) catch |err| { + return step.fail("unable to update file from '{s}' to '{s}': {s}", .{ + full_src_path, full_implib_path, @errorName(err), + }); + }; + all_cached = all_cached and p == .fresh; } if (self.pdb_dir) |pdb_dir| { + const full_src_path = self.artifact.getOutputPdbSource().getPath(src_builder); const full_pdb_path = dest_builder.getInstallPath(pdb_dir, self.artifact.out_pdb_filename); - try src_builder.updateFile(self.artifact.getOutputPdbSource().getPath(src_builder), full_pdb_path); + const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_pdb_path, .{}) catch |err| { + return step.fail("unable to update file from '{s}' to '{s}': {s}", .{ + full_src_path, full_pdb_path, @errorName(err), + }); + }; + all_cached = all_cached and p == .fresh; } if (self.h_dir) |h_dir| { + const full_src_path = self.artifact.getOutputHSource().getPath(src_builder); const full_h_path = dest_builder.getInstallPath(h_dir, self.artifact.out_h_filename); - try src_builder.updateFile(self.artifact.getOutputHSource().getPath(src_builder), full_h_path); + const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_h_path, .{}) catch |err| { + return step.fail("unable to update file from '{s}' to '{s}': {s}", .{ + full_src_path, full_h_path, @errorName(err), + }); + }; + all_cached = all_cached and p == .fresh; } self.artifact.installed_path = full_dest_path; + step.result_cached = all_cached; } diff --git a/lib/std/Build/InstallDirStep.zig b/lib/std/Build/InstallDirStep.zig index 2807d641f3..d9ea248913 100644 --- a/lib/std/Build/InstallDirStep.zig +++ b/lib/std/Build/InstallDirStep.zig @@ -89,13 +89,17 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { } } - const prev_status = try fs.Dir.updateFile( + const prev_status = fs.Dir.updateFile( src_builder.build_root.handle, src_sub_path, cwd, dest_path, .{}, - ); + ) catch |err| { + return step.fail("unable to update file from '{}{s}' to '{s}': {s}", .{ + src_builder.build_root, src_sub_path, dest_path, @errorName(err), + }); + }; all_cached = all_cached and prev_status == .fresh; }, else => continue, diff --git a/lib/std/Build/InstallFileStep.zig b/lib/std/Build/InstallFileStep.zig index ed7576f42c..a77fa10b43 100644 --- a/lib/std/Build/InstallFileStep.zig +++ b/lib/std/Build/InstallFileStep.zig @@ -42,5 +42,11 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { const dest_builder = self.dest_builder; const full_src_path = self.source.getPath2(src_builder, step); const full_dest_path = dest_builder.getInstallPath(self.dir, self.dest_rel_path); - try dest_builder.updateFile(full_src_path, full_dest_path); + const cwd = std.fs.cwd(); + const prev = std.fs.Dir.updateFile(cwd, full_src_path, cwd, full_dest_path, .{}) catch |err| { + return step.fail("unable to update file from '{s}' to '{s}': {s}", .{ + full_src_path, full_dest_path, @errorName(err), + }); + }; + step.result_cached = prev == .fresh; } From d3cbbe0b1e38394de5c4ea6efe9372203092002f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 5 Mar 2023 16:16:32 -0700 Subject: [PATCH 202/294] std.Build.Step: no-op steps report cached if all deps cached --- lib/std/Build/Step.zig | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig index 1a7fe24e7c..d146ddb259 100644 --- a/lib/std/Build/Step.zig +++ b/lib/std/Build/Step.zig @@ -144,9 +144,16 @@ pub fn getStackTrace(s: *Step) std.builtin.StackTrace { }; } -fn makeNoOp(self: *Step, prog_node: *std.Progress.Node) anyerror!void { - _ = self; +fn makeNoOp(step: *Step, prog_node: *std.Progress.Node) anyerror!void { _ = prog_node; + + var all_cached = true; + + for (step.dependencies.items) |dep| { + all_cached = all_cached and dep.result_cached; + } + + step.result_cached = all_cached; } pub fn cast(step: *Step, comptime T: type) ?*T { From 6377aad23a51ba6d69a01fafa60001e2238cde0f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 5 Mar 2023 17:48:01 -0700 Subject: [PATCH 203/294] build runner: fix typo in max rss display --- lib/build_runner.zig | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/build_runner.zig b/lib/build_runner.zig index c99d55b783..7a19a6962b 100644 --- a/lib/build_runner.zig +++ b/lib/build_runner.zig @@ -527,10 +527,8 @@ fn printTreeStep( if (s.result_peak_rss != 0) { const rss = s.result_peak_rss; try ttyconf.setColor(stderr, .Dim); - if (rss >= 1000_000_000_000) { - try stderr.writer().print(" MaxRSS:{d}G", .{rss / 1000_000_000_000}); - } else if (rss >= 1000_000_000) { - try stderr.writer().print(" MaxRSS:{d}M", .{rss / 1000_000_000}); + if (rss >= 1000_000_000) { + try stderr.writer().print(" MaxRSS:{d}G", .{rss / 1000_000_000}); } else if (rss >= 1000_000) { try stderr.writer().print(" MaxRSS:{d}M", .{rss / 1000_000}); } else if (rss >= 1000) { From 3b29d00c9826d8053b63fe2bcd86c6d1517fcc51 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 6 Mar 2023 00:19:32 -0700 Subject: [PATCH 204/294] add std.process.totalSystemMemory --- lib/std/os/windows/kernel32.zig | 3 +++ lib/std/process.zig | 35 +++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/lib/std/os/windows/kernel32.zig b/lib/std/os/windows/kernel32.zig index d3bfeaaf2c..1fd8a406d5 100644 --- a/lib/std/os/windows/kernel32.zig +++ b/lib/std/os/windows/kernel32.zig @@ -67,6 +67,7 @@ const RUNTIME_FUNCTION = windows.RUNTIME_FUNCTION; const KNONVOLATILE_CONTEXT_POINTERS = windows.KNONVOLATILE_CONTEXT_POINTERS; const EXCEPTION_ROUTINE = windows.EXCEPTION_ROUTINE; const MODULEENTRY32 = windows.MODULEENTRY32; +const ULONGLONG = windows.ULONGLONG; pub extern "kernel32" fn AddVectoredExceptionHandler(First: c_ulong, Handler: ?VECTORED_EXCEPTION_HANDLER) callconv(WINAPI) ?*anyopaque; pub extern "kernel32" fn RemoveVectoredExceptionHandler(Handle: HANDLE) callconv(WINAPI) c_ulong; @@ -457,3 +458,5 @@ pub extern "kernel32" fn RegOpenKeyExW( samDesired: REGSAM, phkResult: *HKEY, ) callconv(WINAPI) LSTATUS; + +pub extern "kernel32" fn GetPhysicallyInstalledSystemMemory(TotalMemoryInKilobytes: *ULONGLONG) BOOL; diff --git a/lib/std/process.zig b/lib/std/process.zig index eff29e86fa..8652dd1bbc 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -1169,3 +1169,38 @@ pub fn execve( return os.execvpeZ_expandArg0(.no_expand, argv_buf.ptr[0].?, argv_buf.ptr, envp); } + +pub const TotalSystemMemoryError = error{ + UnknownTotalSystemMemory, +}; + +/// Returns the total system memory, in bytes. +pub fn totalSystemMemory() TotalSystemMemoryError!usize { + switch (builtin.os.tag) { + .linux => { + return totalSystemMemoryLinux() catch return error.UnknownTotalSystemMemory; + }, + .windows => { + var kilobytes: std.os.windows.ULONGLONG = undefined; + assert(std.os.windows.kernel32.GetPhysicallyInstalledSystemMemory(&kilobytes) == std.os.windows.TRUE); + return kilobytes * 1024; + }, + else => return error.UnknownTotalSystemMemory, + } +} + +fn totalSystemMemoryLinux() !usize { + var file = try std.fs.openFileAbsoluteZ("/proc/meminfo", .{}); + defer file.close(); + var buf: [50]u8 = undefined; + const amt = try file.read(&buf); + if (amt != 50) return error.Unexpected; + var it = std.mem.tokenize(u8, buf[0..amt], " \n"); + const label = it.next().?; + if (!std.mem.eql(u8, label, "MemTotal:")) return error.Unexpected; + const int_text = it.next() orelse return error.Unexpected; + const units = it.next() orelse return error.Unexpected; + if (!std.mem.eql(u8, units, "kB")) return error.Unexpected; + const kilobytes = try std.fmt.parseInt(usize, int_text, 10); + return kilobytes * 1024; +} From f51413d2cf0bd87079dace7f6481d2a361a19ea6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 6 Mar 2023 00:20:11 -0700 Subject: [PATCH 205/294] zig build: add an OOM-prevention system The problem is that one may execute too many subprocesses concurrently that, together, exceed an RSS value that causes the OOM killer to kill something problematic such as the window manager. Or worse, nothing, and the system freezes. This is a real world problem. For example when building LLVM a simple `ninja install` will bring your system to its knees if you don't know that you should add `-DLLVM_PARALLEL_LINK_JOBS=1`. In particular: compiling the zig std lib tests takes about 2G each, which at 16x at once (8 cores + hyperthreading) is using all 32GB of my RAM, causing the OOM killer to kill my window manager The idea here is that you can annotate steps that might use a high amount of system resources with an upper bound. So for example I could mark the std lib tests as having an upper bound peak RSS of 3 GiB. Then the build system will do 2 things: 1. ulimit the child process, so that it will fail if it would exceed that memory limit. 2. Notice how much system RAM is available and avoid running too many concurrent jobs at once that would total more than that. This implements (1) not with an operating system enforced limit, but by checking the maxrss after a child process exits. However it does implement (2) correctly. The available memory used by the build system defaults to the total system memory, regardless of whether it is used by other processes at the time of spawning the build runner. This value can be overridden with the new --maxrss flag to `zig build`. This mechanism will ensure that the sum total of upper bound RSS memory of concurrent tasks will not exceed this value. This system makes it so that project maintainers can annotate problematic subprocesses, avoiding bug reports from users, who can blissfully execute `zig build` without worrying about the project's internals. Nobody's computer crashes, and the build system uses as much parallelism as possible without risking OOM. Users do not need to unnecessarily resort to -j1 when the build system can figure this out for them. --- lib/build_runner.zig | 189 +++++++++++++++++++++++++++------- lib/std/Build.zig | 12 +++ lib/std/Build/CompileStep.zig | 2 + lib/std/Build/Step.zig | 45 ++++++-- 4 files changed, 207 insertions(+), 41 deletions(-) diff --git a/lib/build_runner.zig b/lib/build_runner.zig index 7a19a6962b..c30fbaea87 100644 --- a/lib/build_runner.zig +++ b/lib/build_runner.zig @@ -84,20 +84,21 @@ pub fn main() !void { ); defer builder.destroy(); + const Color = enum { auto, off, on }; + var targets = ArrayList([]const u8).init(arena); var debug_log_scopes = ArrayList([]const u8).init(arena); var thread_pool_options: std.Thread.Pool.Options = .{ .allocator = arena }; - const stderr_stream = io.getStdErr().writer(); - const stdout_stream = io.getStdOut().writer(); - var install_prefix: ?[]const u8 = null; var dir_list = std.Build.DirList{}; var enable_summary: ?bool = null; - - const Color = enum { auto, off, on }; + var max_rss: usize = 0; var color: Color = .auto; + const stderr_stream = io.getStdErr().writer(); + const stdout_stream = io.getStdOut().writer(); + while (nextArg(args, &arg_idx)) |arg| { if (mem.startsWith(u8, arg, "-D")) { const option_contents = arg[2..]; @@ -147,6 +148,18 @@ pub fn main() !void { usageAndErr(builder, false, stderr_stream); }; builder.sysroot = sysroot; + } else if (mem.eql(u8, arg, "--maxrss")) { + const max_rss_text = nextArg(args, &arg_idx) orelse { + std.debug.print("Expected argument after --sysroot\n\n", .{}); + usageAndErr(builder, false, stderr_stream); + }; + // TODO: support shorthand such as "2GiB", "2GB", or "2G" + max_rss = std.fmt.parseInt(usize, max_rss_text, 10) catch |err| { + std.debug.print("invalid byte size: '{s}': {s}\n", .{ + max_rss_text, @errorName(err), + }); + process.exit(1); + }; } else if (mem.eql(u8, arg, "--search-prefix")) { const search_prefix = nextArg(args, &arg_idx) orelse { std.debug.print("Expected argument after --search-prefix\n\n", .{}); @@ -280,30 +293,55 @@ pub fn main() !void { if (builder.validateUserInputDidItFail()) usageAndErr(builder, true, stderr_stream); + var run: Run = .{ + .max_rss = max_rss, + .max_rss_is_default = false, + .max_rss_mutex = .{}, + .memory_blocked_steps = std.ArrayList(*Step).init(arena), + + .claimed_rss = 0, + .enable_summary = enable_summary, + .ttyconf = ttyconf, + .stderr = stderr, + }; + + if (run.max_rss == 0) { + run.max_rss = process.totalSystemMemory() catch std.math.maxInt(usize); + run.max_rss_is_default = true; + } + runStepNames( arena, builder, targets.items, main_progress_node, thread_pool_options, - ttyconf, - stderr, - enable_summary, + &run, ) catch |err| switch (err) { error.UncleanExit => process.exit(1), else => return err, }; } +const Run = struct { + max_rss: usize, + max_rss_is_default: bool, + max_rss_mutex: std.Thread.Mutex, + memory_blocked_steps: std.ArrayList(*Step), + + claimed_rss: usize, + enable_summary: ?bool, + ttyconf: std.debug.TTY.Config, + stderr: std.fs.File, +}; + fn runStepNames( arena: std.mem.Allocator, b: *std.Build, step_names: []const []const u8, parent_prog_node: *std.Progress.Node, thread_pool_options: std.Thread.Pool.Options, - ttyconf: std.debug.TTY.Config, - stderr: std.fs.File, - enable_summary: ?bool, + run: *Run, ) !void { const gpa = b.allocator; var step_stack: std.AutoArrayHashMapUnmanaged(*Step, void) = .{}; @@ -331,6 +369,26 @@ fn runStepNames( }; } + { + // Check that we have enough memory to complete the build. + var any_problems = false; + for (step_stack.keys()) |s| { + if (s.max_rss == 0) continue; + if (s.max_rss > run.max_rss) { + std.debug.print("{s}{s}: this step declares an upper bound of {d} bytes of memory, exceeding the available {d} bytes of memory\n", .{ + s.owner.dep_prefix, s.name, s.max_rss, run.max_rss, + }); + any_problems = true; + } + } + if (any_problems) { + if (run.max_rss_is_default) { + std.debug.print("note: use --maxrss to override the default", .{}); + } + return error.UncleanExit; + } + } + var thread_pool: std.Thread.Pool = undefined; try thread_pool.init(thread_pool_options); defer thread_pool.deinit(); @@ -353,10 +411,11 @@ fn runStepNames( wait_group.start(); thread_pool.spawn(workerMakeOneStep, .{ - &wait_group, &thread_pool, b, step, &step_prog, ttyconf, + &wait_group, &thread_pool, b, step, &step_prog, run, }) catch @panic("OOM"); } } + assert(run.memory_blocked_steps.items.len == 0); var success_count: usize = 0; var skipped_count: usize = 0; @@ -396,9 +455,12 @@ fn runStepNames( // A proper command line application defaults to silently succeeding. // The user may request verbose mode if they have a different preference. - if (failure_count == 0 and enable_summary != true) return cleanExit(); + if (failure_count == 0 and run.enable_summary != true) return cleanExit(); - if (enable_summary != false) { + const ttyconf = run.ttyconf; + const stderr = run.stderr; + + if (run.enable_summary != false) { const total_count = success_count + failure_count + pending_count + skipped_count; ttyconf.setColor(stderr, .Cyan) catch {}; stderr.writeAll("Build Summary:") catch {}; @@ -407,7 +469,7 @@ fn runStepNames( if (skipped_count > 0) stderr.writer().print("; {d} skipped", .{skipped_count}) catch {}; if (failure_count > 0) stderr.writer().print("; {d} failed", .{failure_count}) catch {}; - if (enable_summary == null) { + if (run.enable_summary == null) { ttyconf.setColor(stderr, .Dim) catch {}; stderr.writeAll(" (disable with -fno-summary)") catch {}; ttyconf.setColor(stderr, .Reset) catch {}; @@ -623,7 +685,7 @@ fn workerMakeOneStep( b: *std.Build, s: *Step, prog_node: *std.Progress.Node, - ttyconf: std.debug.TTY.Config, + run: *Run, ) void { defer wg.finish(); @@ -646,10 +708,32 @@ fn workerMakeOneStep( } } - // Avoid running steps twice. - if (@cmpxchgStrong(Step.State, &s.state, .precheck_done, .running, .SeqCst, .SeqCst) != null) { - // Another worker got the job. - return; + if (s.max_rss != 0) { + run.max_rss_mutex.lock(); + defer run.max_rss_mutex.unlock(); + + // Avoid running steps twice. + if (s.state != .precheck_done) { + // Another worker got the job. + return; + } + + const new_claimed_rss = run.claimed_rss + s.max_rss; + if (new_claimed_rss > run.max_rss) { + // Running this step right now could possibly exceed the allotted RSS. + // Add this step to the queue of memory-blocked steps. + run.memory_blocked_steps.append(s) catch @panic("OOM"); + return; + } + + run.claimed_rss = new_claimed_rss; + s.state = .running; + } else { + // Avoid running steps twice. + if (@cmpxchgStrong(Step.State, &s.state, .precheck_done, .running, .SeqCst, .SeqCst) != null) { + // Another worker got the job. + return; + } } var sub_prog_node = prog_node.start(s.name, 0); @@ -667,7 +751,8 @@ fn workerMakeOneStep( sub_prog_node.context.lock_stderr(); defer sub_prog_node.context.unlock_stderr(); - const stderr = std.io.getStdErr(); + const stderr = run.stderr; + const ttyconf = run.ttyconf; for (s.result_error_msgs.items) |msg| { // Sometimes it feels like you just can't catch a break. Finally, @@ -684,22 +769,55 @@ fn workerMakeOneStep( } } - if (make_result) |_| { - @atomicStore(Step.State, &s.state, .success, .SeqCst); - } else |err| switch (err) { - error.MakeFailed => { - @atomicStore(Step.State, &s.state, .failure, .SeqCst); - return; - }, - error.MakeSkipped => @atomicStore(Step.State, &s.state, .skipped, .SeqCst), + handle_result: { + if (make_result) |_| { + @atomicStore(Step.State, &s.state, .success, .SeqCst); + } else |err| switch (err) { + error.MakeFailed => { + @atomicStore(Step.State, &s.state, .failure, .SeqCst); + break :handle_result; + }, + error.MakeSkipped => @atomicStore(Step.State, &s.state, .skipped, .SeqCst), + } + + // Successful completion of a step, so we queue up its dependants as well. + for (s.dependants.items) |dep| { + wg.start(); + thread_pool.spawn(workerMakeOneStep, .{ + wg, thread_pool, b, dep, prog_node, run, + }) catch @panic("OOM"); + } } - // Successful completion of a step, so we queue up its dependants as well. - for (s.dependants.items) |dep| { - wg.start(); - thread_pool.spawn(workerMakeOneStep, .{ - wg, thread_pool, b, dep, prog_node, ttyconf, - }) catch @panic("OOM"); + // If this is a step that claims resources, we must now queue up other + // steps that are waiting for resources. + if (s.max_rss != 0) { + run.max_rss_mutex.lock(); + defer run.max_rss_mutex.unlock(); + + // Give the memory back to the scheduler. + run.claimed_rss -= s.max_rss; + // Avoid kicking off too many tasks that we already know will not have + // enough resources. + var remaining = run.max_rss - run.claimed_rss; + var i: usize = 0; + var j: usize = 0; + while (j < run.memory_blocked_steps.items.len) : (j += 1) { + const dep = run.memory_blocked_steps.items[j]; + assert(dep.max_rss != 0); + if (dep.max_rss <= remaining) { + remaining -= dep.max_rss; + + wg.start(); + thread_pool.spawn(workerMakeOneStep, .{ + wg, thread_pool, b, dep, prog_node, run, + }) catch @panic("OOM"); + } else { + run.memory_blocked_steps.items[i] = dep; + i += 1; + } + } + run.memory_blocked_steps.shrinkRetainingCapacity(i); } } @@ -770,6 +888,7 @@ fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi \\ --color [auto|off|on] Enable or disable colored error messages \\ --prominent-compile-errors Output compile errors formatted for a human to read \\ -j Limit concurrent jobs (default is to use all CPU cores) + \\ --maxrss Limit memory usage (default is to use available memory) \\ \\Project-Specific Options: \\ diff --git a/lib/std/Build.zig b/lib/std/Build.zig index e3b44e965e..df10f55439 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -453,6 +453,7 @@ pub const ExecutableOptions = struct { target: CrossTarget = .{}, optimize: std.builtin.Mode = .Debug, linkage: ?CompileStep.Linkage = null, + max_rss: usize = 0, }; pub fn addExecutable(b: *Build, options: ExecutableOptions) *CompileStep { @@ -464,6 +465,7 @@ pub fn addExecutable(b: *Build, options: ExecutableOptions) *CompileStep { .optimize = options.optimize, .kind = .exe, .linkage = options.linkage, + .max_rss = options.max_rss, }); } @@ -472,6 +474,7 @@ pub const ObjectOptions = struct { root_source_file: ?FileSource = null, target: CrossTarget, optimize: std.builtin.Mode, + max_rss: usize = 0, }; pub fn addObject(b: *Build, options: ObjectOptions) *CompileStep { @@ -481,6 +484,7 @@ pub fn addObject(b: *Build, options: ObjectOptions) *CompileStep { .target = options.target, .optimize = options.optimize, .kind = .obj, + .max_rss = options.max_rss, }); } @@ -490,6 +494,7 @@ pub const SharedLibraryOptions = struct { version: ?std.builtin.Version = null, target: CrossTarget, optimize: std.builtin.Mode, + max_rss: usize = 0, }; pub fn addSharedLibrary(b: *Build, options: SharedLibraryOptions) *CompileStep { @@ -501,6 +506,7 @@ pub fn addSharedLibrary(b: *Build, options: SharedLibraryOptions) *CompileStep { .version = options.version, .target = options.target, .optimize = options.optimize, + .max_rss = options.max_rss, }); } @@ -510,6 +516,7 @@ pub const StaticLibraryOptions = struct { target: CrossTarget, optimize: std.builtin.Mode, version: ?std.builtin.Version = null, + max_rss: usize = 0, }; pub fn addStaticLibrary(b: *Build, options: StaticLibraryOptions) *CompileStep { @@ -521,6 +528,7 @@ pub fn addStaticLibrary(b: *Build, options: StaticLibraryOptions) *CompileStep { .version = options.version, .target = options.target, .optimize = options.optimize, + .max_rss = options.max_rss, }); } @@ -531,6 +539,7 @@ pub const TestOptions = struct { target: CrossTarget = .{}, optimize: std.builtin.Mode = .Debug, version: ?std.builtin.Version = null, + max_rss: usize = 0, }; pub fn addTest(b: *Build, options: TestOptions) *CompileStep { @@ -540,6 +549,7 @@ pub fn addTest(b: *Build, options: TestOptions) *CompileStep { .root_source_file = options.root_source_file, .target = options.target, .optimize = options.optimize, + .max_rss = options.max_rss, }); } @@ -548,6 +558,7 @@ pub const AssemblyOptions = struct { source_file: FileSource, target: CrossTarget, optimize: std.builtin.Mode, + max_rss: usize = 0, }; pub fn addAssembly(b: *Build, options: AssemblyOptions) *CompileStep { @@ -557,6 +568,7 @@ pub fn addAssembly(b: *Build, options: AssemblyOptions) *CompileStep { .root_source_file = null, .target = options.target, .optimize = options.optimize, + .max_rss = options.max_rss, }); obj_step.addAssemblyFileSource(options.source_file.dupe(b)); return obj_step; diff --git a/lib/std/Build/CompileStep.zig b/lib/std/Build/CompileStep.zig index 895c1a7678..df9abfbc6d 100644 --- a/lib/std/Build/CompileStep.zig +++ b/lib/std/Build/CompileStep.zig @@ -274,6 +274,7 @@ pub const Options = struct { kind: Kind, linkage: ?Linkage = null, version: ?std.builtin.Version = null, + max_rss: usize = 0, }; pub const Kind = enum { @@ -333,6 +334,7 @@ pub fn create(owner: *std.Build, options: Options) *CompileStep { .name = step_name, .owner = owner, .makeFn = make, + .max_rss = options.max_rss, }), .version = options.version, .out_filename = undefined, diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig index d146ddb259..f1edab5881 100644 --- a/lib/std/Build/Step.zig +++ b/lib/std/Build/Step.zig @@ -2,14 +2,32 @@ id: Id, name: []const u8, owner: *Build, makeFn: MakeFn, + dependencies: std.ArrayList(*Step), /// This field is empty during execution of the user's build script, and /// then populated during dependency loop checking in the build runner. dependants: std.ArrayListUnmanaged(*Step), state: State, -/// The return addresss associated with creation of this step that can be useful -/// to print along with debugging messages. -debug_stack_trace: [n_debug_stack_frames]usize, +/// Set this field to declare an upper bound on the amount of bytes of memory it will +/// take to run the step. Zero means no limit. +/// +/// The idea to annotate steps that might use a high amount of RAM with an +/// upper bound. For example, perhaps a particular set of unit tests require 4 +/// GiB of RAM, and those tests will be run under 4 different build +/// configurations at once. This would potentially require 16 GiB of memory on +/// the system if all 4 steps executed simultaneously, which could easily be +/// greater than what is actually available, potentially causing the system to +/// crash when using `zig build` at the default concurrency level. +/// +/// This field causes the build runner to do two things: +/// 1. ulimit child processes, so that they will fail if it would exceed this +/// memory limit. This serves to enforce that this upper bound value is +/// correct. +/// 2. Ensure that the set of concurrent steps at any given time have a total +/// max_rss value that does not exceed the `max_total_rss` value of the build +/// runner. This value is configurable on the command line, and defaults to the +/// total system memory available. +max_rss: usize, result_error_msgs: std.ArrayListUnmanaged([]const u8), result_error_bundle: std.zig.ErrorBundle, @@ -18,6 +36,10 @@ result_duration_ns: ?u64, /// 0 means unavailable or not reported. result_peak_rss: usize, +/// The return addresss associated with creation of this step that can be useful +/// to print along with debugging messages. +debug_stack_trace: [n_debug_stack_frames]usize, + pub const MakeFn = *const fn (self: *Step, prog_node: *std.Progress.Node) anyerror!void; const n_debug_stack_frames = 4; @@ -83,6 +105,7 @@ pub const Options = struct { owner: *Build, makeFn: MakeFn = makeNoOp, first_ret_addr: ?usize = null, + max_rss: usize = 0, }; pub fn init(options: Options) Step { @@ -104,6 +127,7 @@ pub fn init(options: Options) Step { .dependencies = std.ArrayList(*Step).init(arena), .dependants = .{}, .state = .precheck_unstarted, + .max_rss = options.max_rss, .debug_stack_trace = addresses, .result_error_msgs = .{}, .result_error_bundle = std.zig.ErrorBundle.empty, @@ -117,15 +141,24 @@ pub fn init(options: Options) Step { /// have already reported the error. Otherwise, we add a simple error report /// here. pub fn make(s: *Step, prog_node: *std.Progress.Node) error{ MakeFailed, MakeSkipped }!void { - return s.makeFn(s, prog_node) catch |err| switch (err) { + const arena = s.owner.allocator; + + s.makeFn(s, prog_node) catch |err| switch (err) { error.MakeFailed => return error.MakeFailed, error.MakeSkipped => return error.MakeSkipped, else => { - const gpa = s.dependencies.allocator; - s.result_error_msgs.append(gpa, @errorName(err)) catch @panic("OOM"); + s.result_error_msgs.append(arena, @errorName(err)) catch @panic("OOM"); return error.MakeFailed; }, }; + + if (s.max_rss != 0 and s.result_peak_rss > s.max_rss) { + const msg = std.fmt.allocPrint(arena, "memory usage peaked at {d} bytes, exceeding the declared upper bound of {d}", .{ + s.result_peak_rss, s.max_rss, + }) catch @panic("OOM"); + s.result_error_msgs.append(arena, msg) catch @panic("OOM"); + return error.MakeFailed; + } } pub fn dependOn(self: *Step, other: *Step) void { From 7bad6958657c6b4a791c73a3f827f7f848cab762 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 6 Mar 2023 00:27:46 -0700 Subject: [PATCH 206/294] build.zig: annotate std lib tests maxrss --- build.zig | 102 ++++++++++++++++++++++++------------------------- test/tests.zig | 35 ++++++++++------- 2 files changed, 72 insertions(+), 65 deletions(-) diff --git a/build.zig b/build.zig index 40210500d8..a8b06be40c 100644 --- a/build.zig +++ b/build.zig @@ -402,47 +402,45 @@ pub fn build(b: *std.Build) !void { const do_fmt_step = b.step("fmt", "Modify source files in place to have conforming formatting"); do_fmt_step.dependOn(&do_fmt.step); - test_step.dependOn(tests.addPkgTests( - b, - test_filter, - "test/behavior.zig", - "behavior", - "Run the behavior tests", - optimization_modes, - skip_single_threaded, - skip_non_native, - skip_libc, - skip_stage1, - skip_stage2_tests, - )); + test_step.dependOn(tests.addModuleTests(b, .{ + .test_filter = test_filter, + .root_src = "test/behavior.zig", + .name = "behavior", + .desc = "Run the behavior tests", + .optimize_modes = optimization_modes, + .skip_single_threaded = skip_single_threaded, + .skip_non_native = skip_non_native, + .skip_libc = skip_libc, + .skip_stage1 = skip_stage1, + .skip_stage2 = skip_stage2_tests, + .max_rss = 1 * 1024 * 1024 * 1024, + })); - test_step.dependOn(tests.addPkgTests( - b, - test_filter, - "lib/compiler_rt.zig", - "compiler-rt", - "Run the compiler_rt tests", - optimization_modes, - true, // skip_single_threaded - skip_non_native, - true, // skip_libc - skip_stage1, - skip_stage2_tests or true, // TODO get these all passing - )); + test_step.dependOn(tests.addModuleTests(b, .{ + .test_filter = test_filter, + .root_src = "lib/compiler_rt.zig", + .name = "compiler-rt", + .desc = "Run the compiler_rt tests", + .optimize_modes = optimization_modes, + .skip_single_threaded = true, + .skip_non_native = skip_non_native, + .skip_libc = true, + .skip_stage1 = skip_stage1, + .skip_stage2 = true, // TODO get all these passing + })); - test_step.dependOn(tests.addPkgTests( - b, - test_filter, - "lib/c.zig", - "universal-libc", - "Run the universal libc tests", - optimization_modes, - true, // skip_single_threaded - skip_non_native, - true, // skip_libc - skip_stage1, - skip_stage2_tests or true, // TODO get these all passing - )); + test_step.dependOn(tests.addModuleTests(b, .{ + .test_filter = test_filter, + .root_src = "lib/c.zig", + .name = "universal-libc", + .desc = "Run the universal libc tests", + .optimize_modes = optimization_modes, + .skip_single_threaded = true, + .skip_non_native = skip_non_native, + .skip_libc = true, + .skip_stage1 = skip_stage1, + .skip_stage2 = true, // TODO get all these passing + })); test_step.dependOn(tests.addCompareOutputTests(b, test_filter, optimization_modes)); test_step.dependOn(tests.addStandaloneTests( @@ -472,19 +470,19 @@ pub fn build(b: *std.Build) !void { // tests for this feature are disabled until we have the self-hosted compiler available // test_step.dependOn(tests.addGenHTests(b, test_filter)); - test_step.dependOn(tests.addPkgTests( - b, - test_filter, - "lib/std/std.zig", - "std", - "Run the standard library tests", - optimization_modes, - skip_single_threaded, - skip_non_native, - skip_libc, - skip_stage1, - true, // TODO get these all passing - )); + test_step.dependOn(tests.addModuleTests(b, .{ + .test_filter = test_filter, + .root_src = "lib/std/std.zig", + .name = "std", + .desc = "Run the standard library tests", + .optimize_modes = optimization_modes, + .skip_single_threaded = skip_single_threaded, + .skip_non_native = skip_non_native, + .skip_libc = skip_libc, + .skip_stage1 = skip_stage1, + .skip_stage2 = true, // TODO get all these passing + .max_rss = 3 * 1024 * 1024 * 1024, + })); try addWasiUpdateStep(b, version); } diff --git a/test/tests.zig b/test/tests.zig index 494ae0ea41..2d110b28af 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -639,8 +639,7 @@ pub fn addGenHTests(b: *std.Build, test_filter: ?[]const u8) *Step { return cases.step; } -pub fn addPkgTests( - b: *std.Build, +const ModuleTestOptions = struct { test_filter: ?[]const u8, root_src: []const u8, name: []const u8, @@ -651,14 +650,17 @@ pub fn addPkgTests( skip_libc: bool, skip_stage1: bool, skip_stage2: bool, -) *Step { - const step = b.step(b.fmt("test-{s}", .{name}), desc); + max_rss: usize = 0, +}; + +pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step { + const step = b.step(b.fmt("test-{s}", .{options.name}), options.desc); for (test_targets) |test_target| { - if (skip_non_native and !test_target.target.isNative()) + if (options.skip_non_native and !test_target.target.isNative()) continue; - if (skip_libc and test_target.link_libc) + if (options.skip_libc and test_target.link_libc) continue; if (test_target.link_libc and test_target.target.getOs().requiresLibC()) { @@ -666,7 +668,7 @@ pub fn addPkgTests( continue; } - if (skip_single_threaded and test_target.single_threaded) + if (options.skip_single_threaded and test_target.single_threaded) continue; if (test_target.disable_native and @@ -677,12 +679,12 @@ pub fn addPkgTests( } if (test_target.backend) |backend| switch (backend) { - .stage1 => if (skip_stage1) continue, + .stage1 => if (options.skip_stage1) continue, .stage2_llvm => {}, - else => if (skip_stage2) continue, + else => if (options.skip_stage2) continue, }; - const want_this_mode = for (optimize_modes) |m| { + const want_this_mode = for (options.optimize_modes) |m| { if (m == test_target.optimize_mode) break true; } else false; if (!want_this_mode) continue; @@ -696,15 +698,22 @@ pub fn addPkgTests( const triple_prefix = test_target.target.zigTriple(b.allocator) catch unreachable; + // wasm32-wasi builds need more RAM, idk why + const max_rss = if (test_target.target.getOs().tag == .wasi) + options.max_rss * 2 + else + options.max_rss; + const these_tests = b.addTest(.{ - .root_source_file = .{ .path = root_src }, + .root_source_file = .{ .path = options.root_src }, .optimize = test_target.optimize_mode, .target = test_target.target, + .max_rss = max_rss, }); const single_threaded_txt = if (test_target.single_threaded) "single" else "multi"; const backend_txt = if (test_target.backend) |backend| @tagName(backend) else "default"; these_tests.setNamePrefix(b.fmt("{s}-{s}-{s}-{s}-{s}-{s} ", .{ - name, + options.name, triple_prefix, @tagName(test_target.optimize_mode), libc_prefix, @@ -712,7 +721,7 @@ pub fn addPkgTests( backend_txt, })); these_tests.single_threaded = test_target.single_threaded; - these_tests.setFilter(test_filter); + these_tests.setFilter(options.test_filter); if (test_target.link_libc) { these_tests.linkSystemLibrary("c"); } From a24af8e4005d00cf76755635eb6c661d9c260758 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 6 Mar 2023 19:10:37 -0700 Subject: [PATCH 207/294] re-integrate stack trace tests with the new std.Build API * RunStep: ability to set stdin * RunStep: ability to capture stdout and stderr as a FileSource * RunStep: add setName method * RunStep: hash the stdio checks --- build.zig | 44 +- lib/std/Build/RunStep.zig | 246 ++++++++--- lib/std/Build/WriteFileStep.zig | 14 +- test/src/StackTrace.zig | 105 +++++ test/src/Standalone.zig | 141 ++++++ test/src/check-stack-trace.zig | 79 ++++ test/tests.zig | 735 ++++---------------------------- 7 files changed, 637 insertions(+), 727 deletions(-) create mode 100644 test/src/StackTrace.zig create mode 100644 test/src/Standalone.zig create mode 100644 test/src/check-stack-trace.zig diff --git a/build.zig b/build.zig index a8b06be40c..1f1b119ed3 100644 --- a/build.zig +++ b/build.zig @@ -442,33 +442,33 @@ pub fn build(b: *std.Build) !void { .skip_stage2 = true, // TODO get all these passing })); - test_step.dependOn(tests.addCompareOutputTests(b, test_filter, optimization_modes)); - test_step.dependOn(tests.addStandaloneTests( - b, - test_filter, - optimization_modes, - skip_non_native, - enable_macos_sdk, - target, - skip_stage2_tests, - b.enable_darling, - b.enable_qemu, - b.enable_rosetta, - b.enable_wasmtime, - b.enable_wine, - enable_symlinks_windows, - )); - test_step.dependOn(tests.addCAbiTests(b, skip_non_native, skip_release)); - test_step.dependOn(tests.addLinkTests(b, test_filter, optimization_modes, enable_macos_sdk, skip_stage2_tests, enable_symlinks_windows)); + _ = enable_symlinks_windows; + _ = enable_macos_sdk; + //test_step.dependOn(tests.addCompareOutputTests(b, test_filter, optimization_modes)); + //test_step.dependOn(tests.addStandaloneTests( + // b, + // test_filter, + // optimization_modes, + // skip_non_native, + // enable_macos_sdk, + // target, + // skip_stage2_tests, + // b.enable_darling, + // b.enable_qemu, + // b.enable_rosetta, + // b.enable_wasmtime, + // b.enable_wine, + // enable_symlinks_windows, + //)); + //test_step.dependOn(tests.addCAbiTests(b, skip_non_native, skip_release)); + //test_step.dependOn(tests.addLinkTests(b, test_filter, optimization_modes, enable_macos_sdk, skip_stage2_tests, enable_symlinks_windows)); test_step.dependOn(tests.addStackTraceTests(b, test_filter, optimization_modes)); - test_step.dependOn(tests.addCliTests(b, test_filter, optimization_modes)); - test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, optimization_modes)); + //test_step.dependOn(tests.addCliTests(b, test_filter, optimization_modes)); + //test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, optimization_modes)); test_step.dependOn(tests.addTranslateCTests(b, test_filter)); if (!skip_run_translated_c) { test_step.dependOn(tests.addRunTranslatedCTests(b, test_filter, target)); } - // tests for this feature are disabled until we have the self-hosted compiler available - // test_step.dependOn(tests.addGenHTests(b, test_filter)); test_step.dependOn(tests.addModuleTests(b, .{ .test_filter = test_filter, diff --git a/lib/std/Build/RunStep.zig b/lib/std/Build/RunStep.zig index d671ff7ae8..95e37f230a 100644 --- a/lib/std/Build/RunStep.zig +++ b/lib/std/Build/RunStep.zig @@ -38,6 +38,8 @@ env_map: ?*EnvMap, /// be skipped if all output files are up-to-date and input files are /// unchanged. stdio: StdIo = .infer_from_args, +/// This field must be `null` if stdio is `inherit`. +stdin: ?[]const u8 = null, /// Additional file paths relative to build.zig that, when modified, indicate /// that the RunStep should be re-executed. @@ -65,6 +67,9 @@ skip_foreign_checks: bool = false, /// the step fails. max_stdio_size: usize = 10 * 1024 * 1024, +captured_stdout: ?*Output = null, +captured_stderr: ?*Output = null, + pub const StdIo = union(enum) { /// Whether the RunStep has side-effects will be determined by whether or not one /// of the args is an output file (added with `addOutputFileArg`). @@ -99,12 +104,12 @@ pub const Arg = union(enum) { artifact: *CompileStep, file_source: std.Build.FileSource, bytes: []u8, - output: Output, + output: *Output, +}; - pub const Output = struct { - generated_file: *std.Build.GeneratedFile, - basename: []const u8, - }; +pub const Output = struct { + generated_file: std.Build.GeneratedFile, + basename: []const u8, }; pub fn create(owner: *std.Build, name: []const u8) *RunStep { @@ -119,12 +124,15 @@ pub fn create(owner: *std.Build, name: []const u8) *RunStep { .argv = ArrayList(Arg).init(owner.allocator), .cwd = null, .env_map = null, - .rename_step_with_output_arg = true, - .max_stdio_size = 10 * 1024 * 1024, }; return self; } +pub fn setName(self: *RunStep, name: []const u8) void { + self.step.name = name; + self.rename_step_with_output_arg = false; +} + pub fn addArtifactArg(self: *RunStep, artifact: *CompileStep) void { self.argv.append(Arg{ .artifact = artifact }) catch @panic("OOM"); self.step.dependOn(&artifact.step); @@ -135,19 +143,19 @@ pub fn addArtifactArg(self: *RunStep, artifact: *CompileStep) void { /// throughout the build system. pub fn addOutputFileArg(rs: *RunStep, basename: []const u8) std.Build.FileSource { const b = rs.step.owner; - const generated_file = b.allocator.create(std.Build.GeneratedFile) catch @panic("OOM"); - generated_file.* = .{ .step = &rs.step }; - rs.argv.append(.{ .output = .{ - .generated_file = generated_file, - .basename = b.dupe(basename), - } }) catch @panic("OOM"); + + const output = b.allocator.create(Output) catch @panic("OOM"); + output.* = .{ + .basename = basename, + .generated_file = .{ .step = &rs.step }, + }; + rs.argv.append(.{ .output = output }) catch @panic("OOM"); if (rs.rename_step_with_output_arg) { - rs.rename_step_with_output_arg = false; - rs.step.name = b.fmt("{s} ({s})", .{ rs.step.name, basename }); + rs.setName(b.fmt("{s} ({s})", .{ rs.step.name, basename })); } - return .{ .generated = generated_file }; + return .{ .generated = &output.generated_file }; } pub fn addFileSourceArg(self: *RunStep, file_source: std.Build.FileSource) void { @@ -259,6 +267,34 @@ pub fn addCheck(self: *RunStep, new_check: StdIo.Check) void { } } +pub fn captureStdErr(self: *RunStep) std.Build.FileSource { + assert(self.stdio != .inherit); + + if (self.captured_stderr) |output| return .{ .generated = &output.generated_file }; + + const output = self.step.owner.allocator.create(Output) catch @panic("OOM"); + output.* = .{ + .basename = "stderr", + .generated_file = .{ .step = &self.step }, + }; + self.captured_stderr = output; + return .{ .generated = &output.generated_file }; +} + +pub fn captureStdOut(self: *RunStep) *std.Build.GeneratedFile { + assert(self.stdio != .inherit); + + if (self.captured_stdout) |output| return .{ .generated = &output.generated_file }; + + const output = self.step.owner.allocator.create(Output) catch @panic("OOM"); + output.* = .{ + .basename = "stdout", + .generated_file = .{ .step = &self.step }, + }; + self.captured_stdout = output; + return .{ .generated = &output.generated_file }; +} + /// Returns whether the RunStep has side effects *other than* updating the output arguments. fn hasSideEffects(self: RunStep) bool { return switch (self.stdio) { @@ -269,6 +305,8 @@ fn hasSideEffects(self: RunStep) bool { } fn hasAnyOutputArgs(self: RunStep) bool { + if (self.captured_stdout != null) return true; + if (self.captured_stderr != null) return true; for (self.argv.items) |arg| switch (arg) { .output => return true, else => continue, @@ -318,7 +356,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { var argv_list = ArrayList([]const u8).init(arena); var output_placeholders = ArrayList(struct { index: usize, - output: Arg.Output, + output: *Output, }).init(arena); var man = b.cache.obtain(); @@ -361,46 +399,68 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { } } - if (!has_side_effects) { - for (self.extra_file_dependencies) |file_path| { - _ = try man.addFile(b.pathFromRoot(file_path), null); - } + if (self.captured_stdout) |output| { + man.hash.addBytes(output.basename); + } - if (try step.cacheHit(&man)) { - // cache hit, skip running command - const digest = man.final(); - for (output_placeholders.items) |placeholder| { - placeholder.output.generated_file.path = try b.cache_root.join( - arena, - &.{ "o", &digest, placeholder.output.basename }, - ); - } - step.result_cached = true; - return; - } + if (self.captured_stderr) |output| { + man.hash.addBytes(output.basename); + } + hashStdIo(&man.hash, self.stdio); + + if (has_side_effects) { + try runCommand(self, argv_list.items, has_side_effects, null); + return; + } + + for (self.extra_file_dependencies) |file_path| { + _ = try man.addFile(b.pathFromRoot(file_path), null); + } + + if (try step.cacheHit(&man)) { + // cache hit, skip running command const digest = man.final(); - for (output_placeholders.items) |placeholder| { - const output_components = .{ "o", &digest, placeholder.output.basename }; - const output_sub_path = try fs.path.join(arena, &output_components); - const output_sub_dir_path = fs.path.dirname(output_sub_path).?; - b.cache_root.handle.makePath(output_sub_dir_path) catch |err| { - return step.fail("unable to make path '{}{s}': {s}", .{ - b.cache_root, output_sub_dir_path, @errorName(err), - }); - }; - const output_path = try b.cache_root.join(arena, &output_components); - placeholder.output.generated_file.path = output_path; - argv_list.items[placeholder.index] = output_path; + placeholder.output.generated_file.path = try b.cache_root.join(arena, &.{ + "o", &digest, placeholder.output.basename, + }); } + + if (self.captured_stdout) |output| { + output.generated_file.path = try b.cache_root.join(arena, &.{ + "o", &digest, output.basename, + }); + } + + if (self.captured_stderr) |output| { + output.generated_file.path = try b.cache_root.join(arena, &.{ + "o", &digest, output.basename, + }); + } + + step.result_cached = true; + return; } - try runCommand(self, argv_list.items, has_side_effects); + const digest = man.final(); - if (!has_side_effects) { - try man.writeManifest(); + for (output_placeholders.items) |placeholder| { + const output_components = .{ "o", &digest, placeholder.output.basename }; + const output_sub_path = try fs.path.join(arena, &output_components); + const output_sub_dir_path = fs.path.dirname(output_sub_path).?; + b.cache_root.handle.makePath(output_sub_dir_path) catch |err| { + return step.fail("unable to make path '{}{s}': {s}", .{ + b.cache_root, output_sub_dir_path, @errorName(err), + }); + }; + const output_path = try b.cache_root.join(arena, &output_components); + placeholder.output.generated_file.path = output_path; + argv_list.items[placeholder.index] = output_path; } + + try runCommand(self, argv_list.items, has_side_effects, &digest); + try man.writeManifest(); } fn formatTerm( @@ -448,7 +508,12 @@ fn termMatches(expected: ?std.process.Child.Term, actual: std.process.Child.Term }; } -fn runCommand(self: *RunStep, argv: []const []const u8, has_side_effects: bool) !void { +fn runCommand( + self: *RunStep, + argv: []const []const u8, + has_side_effects: bool, + digest: ?*const [std.Build.Cache.hex_digest_len]u8, +) !void { const step = &self.step; const b = step.owner; const arena = b.allocator; @@ -584,6 +649,46 @@ fn runCommand(self: *RunStep, argv: []const []const u8, has_side_effects: bool) step.result_duration_ns = result.elapsed_ns; step.result_peak_rss = result.peak_rss; + // Capture stdout and stderr to GeneratedFile objects. + const Stream = struct { + captured: ?*Output, + is_null: bool, + bytes: []const u8, + }; + for ([_]Stream{ + .{ + .captured = self.captured_stdout, + .is_null = result.stdout_null, + .bytes = result.stdout, + }, + .{ + .captured = self.captured_stderr, + .is_null = result.stderr_null, + .bytes = result.stderr, + }, + }) |stream| { + if (stream.captured) |output| { + assert(!stream.is_null); + + const output_components = .{ "o", digest.?, output.basename }; + const output_path = try b.cache_root.join(arena, &output_components); + output.generated_file.path = output_path; + + const sub_path = try fs.path.join(arena, &output_components); + const sub_path_dirname = fs.path.dirname(sub_path).?; + b.cache_root.handle.makePath(sub_path_dirname) catch |err| { + return step.fail("unable to make path '{}{s}': {s}", .{ + b.cache_root, sub_path_dirname, @errorName(err), + }); + }; + b.cache_root.handle.writeFile(sub_path, stream.bytes) catch |err| { + return step.fail("unable to write file '{}{s}': {s}", .{ + b.cache_root, sub_path, @errorName(err), + }); + }; + } + } + switch (self.stdio) { .check => |checks| for (checks.items) |check| switch (check) { .expect_stderr_exact => |expected_bytes| { @@ -705,7 +810,7 @@ fn spawnChildAndCollect( child.request_resource_usage_statistics = true; child.stdin_behavior = switch (self.stdio) { - .infer_from_args => if (has_side_effects) .Inherit else .Ignore, + .infer_from_args => if (has_side_effects) .Inherit else .Close, .inherit => .Inherit, .check => .Close, }; @@ -719,12 +824,26 @@ fn spawnChildAndCollect( .inherit => .Inherit, .check => .Pipe, }; + if (self.captured_stdout != null) child.stdout_behavior = .Pipe; + if (self.captured_stderr != null) child.stderr_behavior = .Pipe; + if (self.stdin != null) { + assert(child.stdin_behavior != .Inherit); + child.stdin_behavior = .Pipe; + } child.spawn() catch |err| return self.step.fail("unable to spawn {s}: {s}", .{ argv[0], @errorName(err), }); var timer = try std.time.Timer.start(); + if (self.stdin) |stdin| { + child.stdin.?.writeAll(stdin) catch |err| { + return self.step.fail("unable to write stdin: {s}", .{@errorName(err)}); + }; + child.stdin.?.close(); + child.stdin = null; + } + // These are not optionals, as a workaround for // https://github.com/ziglang/zig/issues/14783 var stdout_bytes: []const u8 = undefined; @@ -761,7 +880,8 @@ fn spawnChildAndCollect( } if (!stderr_null and stderr_bytes.len > 0) { - const stderr_is_diagnostic = switch (self.stdio) { + // Treat stderr as an error message. + const stderr_is_diagnostic = self.captured_stderr == null and switch (self.stdio) { .check => |checks| !checksContainStderr(checks.items), else => true, }; @@ -829,3 +949,27 @@ fn failForeign( }, } } + +fn hashStdIo(hh: *std.Build.Cache.HashHelper, stdio: StdIo) void { + switch (stdio) { + .infer_from_args, .inherit => {}, + .check => |checks| for (checks.items) |check| { + hh.add(@as(std.meta.Tag(StdIo.Check), check)); + switch (check) { + .expect_stderr_exact, + .expect_stderr_match, + .expect_stdout_exact, + .expect_stdout_match, + => |s| hh.addBytes(s), + + .expect_term => |term| { + hh.add(@as(std.meta.Tag(std.process.Child.Term), term)); + switch (term) { + .Exited => |x| hh.add(x), + .Signal, .Stopped, .Unknown => |x| hh.add(x), + } + }, + } + }, + } +} diff --git a/lib/std/Build/WriteFileStep.zig b/lib/std/Build/WriteFileStep.zig index 1109cf5426..64025ce0fe 100644 --- a/lib/std/Build/WriteFileStep.zig +++ b/lib/std/Build/WriteFileStep.zig @@ -189,10 +189,9 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { if (try step.cacheHit(&man)) { const digest = man.final(); for (wf.files.items) |file| { - file.generated_file.path = try b.cache_root.join( - b.allocator, - &.{ "o", &digest, file.sub_path }, - ); + file.generated_file.path = try b.cache_root.join(b.allocator, &.{ + "o", &digest, file.sub_path, + }); } return; } @@ -249,10 +248,9 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { }, } - file.generated_file.path = try b.cache_root.join( - b.allocator, - &.{ cache_path, file.sub_path }, - ); + file.generated_file.path = try b.cache_root.join(b.allocator, &.{ + cache_path, file.sub_path, + }); } try man.writeManifest(); diff --git a/test/src/StackTrace.zig b/test/src/StackTrace.zig new file mode 100644 index 0000000000..5709b9d29e --- /dev/null +++ b/test/src/StackTrace.zig @@ -0,0 +1,105 @@ +b: *std.Build, +step: *Step, +test_index: usize, +test_filter: ?[]const u8, +optimize_modes: []const OptimizeMode, +check_exe: *std.Build.CompileStep, + +const Expect = [@typeInfo(OptimizeMode).Enum.fields.len][]const u8; + +pub fn addCase(self: *StackTrace, config: anytype) void { + if (@hasField(@TypeOf(config), "exclude")) { + if (config.exclude.exclude()) return; + } + if (@hasField(@TypeOf(config), "exclude_arch")) { + const exclude_arch: []const std.Target.Cpu.Arch = &config.exclude_arch; + for (exclude_arch) |arch| if (arch == builtin.cpu.arch) return; + } + if (@hasField(@TypeOf(config), "exclude_os")) { + const exclude_os: []const std.Target.Os.Tag = &config.exclude_os; + for (exclude_os) |os| if (os == builtin.os.tag) return; + } + for (self.optimize_modes) |optimize_mode| { + switch (optimize_mode) { + .Debug => { + if (@hasField(@TypeOf(config), "Debug")) { + self.addExpect(config.name, config.source, optimize_mode, config.Debug); + } + }, + .ReleaseSafe => { + if (@hasField(@TypeOf(config), "ReleaseSafe")) { + self.addExpect(config.name, config.source, optimize_mode, config.ReleaseSafe); + } + }, + .ReleaseFast => { + if (@hasField(@TypeOf(config), "ReleaseFast")) { + self.addExpect(config.name, config.source, optimize_mode, config.ReleaseFast); + } + }, + .ReleaseSmall => { + if (@hasField(@TypeOf(config), "ReleaseSmall")) { + self.addExpect(config.name, config.source, optimize_mode, config.ReleaseSmall); + } + }, + } + } +} + +fn addExpect( + self: *StackTrace, + name: []const u8, + source: []const u8, + optimize_mode: OptimizeMode, + mode_config: anytype, +) void { + if (@hasField(@TypeOf(mode_config), "exclude")) { + if (mode_config.exclude.exclude()) return; + } + if (@hasField(@TypeOf(mode_config), "exclude_arch")) { + const exclude_arch: []const std.Target.Cpu.Arch = &mode_config.exclude_arch; + for (exclude_arch) |arch| if (arch == builtin.cpu.arch) return; + } + if (@hasField(@TypeOf(mode_config), "exclude_os")) { + const exclude_os: []const std.Target.Os.Tag = &mode_config.exclude_os; + for (exclude_os) |os| if (os == builtin.os.tag) return; + } + + const b = self.b; + const annotated_case_name = fmt.allocPrint(b.allocator, "check {s} ({s})", .{ + name, @tagName(optimize_mode), + }) catch @panic("OOM"); + if (self.test_filter) |filter| { + if (mem.indexOf(u8, annotated_case_name, filter) == null) return; + } + + const src_basename = "source.zig"; + const write_src = b.addWriteFile(src_basename, source); + const exe = b.addExecutable(.{ + .name = "test", + .root_source_file = write_src.getFileSource(src_basename).?, + .optimize = optimize_mode, + .target = .{}, + }); + + const run = b.addRunArtifact(exe); + run.expectExitCode(1); + run.expectStdOutEqual(""); + + const check_run = b.addRunArtifact(self.check_exe); + check_run.setName(annotated_case_name); + check_run.addFileSourceArg(run.captureStdErr()); + check_run.addArgs(&.{ + @tagName(optimize_mode), + }); + check_run.expectStdOutEqual(mode_config.expect); + + self.step.dependOn(&check_run.step); +} + +const StackTrace = @This(); +const std = @import("std"); +const builtin = @import("builtin"); +const Step = std.Build.Step; +const OptimizeMode = std.builtin.OptimizeMode; +const fmt = std.fmt; +const mem = std.mem; diff --git a/test/src/Standalone.zig b/test/src/Standalone.zig new file mode 100644 index 0000000000..c07bb511c5 --- /dev/null +++ b/test/src/Standalone.zig @@ -0,0 +1,141 @@ +b: *std.Build, +step: *Step, +test_index: usize, +test_filter: ?[]const u8, +optimize_modes: []const OptimizeMode, +skip_non_native: bool, +enable_macos_sdk: bool, +target: std.zig.CrossTarget, +omit_stage2: bool, +enable_darling: bool = false, +enable_qemu: bool = false, +enable_rosetta: bool = false, +enable_wasmtime: bool = false, +enable_wine: bool = false, +enable_symlinks_windows: bool, + +pub fn addC(self: *Standalone, root_src: []const u8) void { + self.addAllArgs(root_src, true); +} + +pub fn add(self: *Standalone, root_src: []const u8) void { + self.addAllArgs(root_src, false); +} + +pub fn addBuildFile(self: *Standalone, build_file: []const u8, features: struct { + build_modes: bool = false, + cross_targets: bool = false, + requires_macos_sdk: bool = false, + requires_stage2: bool = false, + use_emulation: bool = false, + requires_symlinks: bool = false, + extra_argv: []const []const u8 = &.{}, +}) void { + const b = self.b; + + if (features.requires_macos_sdk and !self.enable_macos_sdk) return; + if (features.requires_stage2 and self.omit_stage2) return; + if (features.requires_symlinks and !self.enable_symlinks_windows and builtin.os.tag == .windows) return; + + const annotated_case_name = b.fmt("build {s}", .{build_file}); + if (self.test_filter) |filter| { + if (mem.indexOf(u8, annotated_case_name, filter) == null) return; + } + + var zig_args = ArrayList([]const u8).init(b.allocator); + const rel_zig_exe = fs.path.relative(b.allocator, b.build_root.path orelse ".", b.zig_exe) catch unreachable; + zig_args.append(rel_zig_exe) catch unreachable; + zig_args.append("build") catch unreachable; + + // TODO: fix the various non-concurrency-safe issues in zig's standalone tests, + // and then remove this! + zig_args.append("-j1") catch @panic("OOM"); + + zig_args.append("--build-file") catch unreachable; + zig_args.append(b.pathFromRoot(build_file)) catch unreachable; + + zig_args.appendSlice(features.extra_argv) catch unreachable; + + zig_args.append("test") catch unreachable; + + if (b.verbose) { + zig_args.append("--verbose") catch unreachable; + } + + if (features.cross_targets and !self.target.isNative()) { + const target_triple = self.target.zigTriple(b.allocator) catch unreachable; + const target_arg = fmt.allocPrint(b.allocator, "-Dtarget={s}", .{target_triple}) catch unreachable; + zig_args.append(target_arg) catch unreachable; + } + + if (features.use_emulation) { + if (self.enable_darling) { + zig_args.append("-fdarling") catch unreachable; + } + if (self.enable_qemu) { + zig_args.append("-fqemu") catch unreachable; + } + if (self.enable_rosetta) { + zig_args.append("-frosetta") catch unreachable; + } + if (self.enable_wasmtime) { + zig_args.append("-fwasmtime") catch unreachable; + } + if (self.enable_wine) { + zig_args.append("-fwine") catch unreachable; + } + } + + const optimize_modes = if (features.build_modes) self.optimize_modes else &[1]OptimizeMode{.Debug}; + for (optimize_modes) |optimize_mode| { + const arg = switch (optimize_mode) { + .Debug => "", + .ReleaseFast => "-Doptimize=ReleaseFast", + .ReleaseSafe => "-Doptimize=ReleaseSafe", + .ReleaseSmall => "-Doptimize=ReleaseSmall", + }; + const zig_args_base_len = zig_args.items.len; + if (arg.len > 0) + zig_args.append(arg) catch unreachable; + defer zig_args.resize(zig_args_base_len) catch unreachable; + + const run_cmd = b.addSystemCommand(zig_args.items); + self.step.dependOn(&run_cmd.step); + } +} + +pub fn addAllArgs(self: *Standalone, root_src: []const u8, link_libc: bool) void { + const b = self.b; + + for (self.optimize_modes) |optimize| { + const annotated_case_name = fmt.allocPrint(self.b.allocator, "build {s} ({s})", .{ + root_src, + @tagName(optimize), + }) catch unreachable; + if (self.test_filter) |filter| { + if (mem.indexOf(u8, annotated_case_name, filter) == null) continue; + } + + const exe = b.addExecutable(.{ + .name = "test", + .root_source_file = .{ .path = root_src }, + .optimize = optimize, + .target = .{}, + }); + if (link_libc) { + exe.linkSystemLibrary("c"); + } + + self.step.dependOn(&exe.step); + } +} + +const Standalone = @This(); +const std = @import("std"); +const builtin = @import("builtin"); +const Step = std.Build.Step; +const OptimizeMode = std.builtin.OptimizeMode; +const fmt = std.fmt; +const mem = std.mem; +const ArrayList = std.ArrayList; +const fs = std.fs; diff --git a/test/src/check-stack-trace.zig b/test/src/check-stack-trace.zig new file mode 100644 index 0000000000..bb1db55076 --- /dev/null +++ b/test/src/check-stack-trace.zig @@ -0,0 +1,79 @@ +const builtin = @import("builtin"); +const std = @import("std"); +const mem = std.mem; +const fs = std.fs; + +pub fn main() !void { + var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer arena_instance.deinit(); + const arena = arena_instance.allocator(); + + const args = try std.process.argsAlloc(arena); + + const input_path = args[1]; + const optimize_mode_text = args[2]; + + const input_bytes = try std.fs.cwd().readFileAlloc(arena, input_path, 5 * 1024 * 1024); + const optimize_mode = std.meta.stringToEnum(std.builtin.OptimizeMode, optimize_mode_text).?; + + var stderr = input_bytes; + + // process result + // - keep only basename of source file path + // - replace address with symbolic string + // - replace function name with symbolic string when optimize_mode != .Debug + // - skip empty lines + const got: []const u8 = got_result: { + var buf = std.ArrayList(u8).init(arena); + defer buf.deinit(); + if (stderr.len != 0 and stderr[stderr.len - 1] == '\n') stderr = stderr[0 .. stderr.len - 1]; + var it = mem.split(u8, stderr, "\n"); + process_lines: while (it.next()) |line| { + if (line.len == 0) continue; + + // offset search past `[drive]:` on windows + var pos: usize = if (builtin.os.tag == .windows) 2 else 0; + // locate delims/anchor + const delims = [_][]const u8{ ":", ":", ":", " in ", "(", ")" }; + var marks = [_]usize{0} ** delims.len; + for (delims, 0..) |delim, i| { + marks[i] = mem.indexOfPos(u8, line, pos, delim) orelse { + // unexpected pattern: emit raw line and cont + try buf.appendSlice(line); + try buf.appendSlice("\n"); + continue :process_lines; + }; + pos = marks[i] + delim.len; + } + // locate source basename + pos = mem.lastIndexOfScalar(u8, line[0..marks[0]], fs.path.sep) orelse { + // unexpected pattern: emit raw line and cont + try buf.appendSlice(line); + try buf.appendSlice("\n"); + continue :process_lines; + }; + // end processing if source basename changes + if (!mem.eql(u8, "source.zig", line[pos + 1 .. marks[0]])) break; + // emit substituted line + try buf.appendSlice(line[pos + 1 .. marks[2] + delims[2].len]); + try buf.appendSlice(" [address]"); + if (optimize_mode == .Debug) { + // On certain platforms (windows) or possibly depending on how we choose to link main + // the object file extension may be present so we simply strip any extension. + if (mem.indexOfScalar(u8, line[marks[4]..marks[5]], '.')) |idot| { + try buf.appendSlice(line[marks[3] .. marks[4] + idot]); + try buf.appendSlice(line[marks[5]..]); + } else { + try buf.appendSlice(line[marks[3]..]); + } + } else { + try buf.appendSlice(line[marks[3] .. marks[3] + delims[3].len]); + try buf.appendSlice("[function]"); + } + try buf.appendSlice("\n"); + } + break :got_result try buf.toOwnedSlice(); + }; + + try std.io.getStdOut().writeAll(got); +} diff --git a/test/tests.zig b/test/tests.zig index 2d110b28af..1a80d7de8d 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -20,13 +20,14 @@ const stack_traces = @import("stack_traces.zig"); const assemble_and_link = @import("assemble_and_link.zig"); const translate_c = @import("translate_c.zig"); const run_translated_c = @import("run_translated_c.zig"); -const gen_h = @import("gen_h.zig"); const link = @import("link.zig"); // Implementations pub const TranslateCContext = @import("src/translate_c.zig").TranslateCContext; pub const RunTranslatedCContext = @import("src/run_translated_c.zig").RunTranslatedCContext; pub const CompareOutputContext = @import("src/compare_output.zig").CompareOutputContext; +pub const StackTracesContext = @import("src/StackTrace.zig"); +pub const StandaloneContext = @import("src/Standalone.zig"); const TestTarget = struct { target: CrossTarget = @as(CrossTarget, .{}), @@ -460,10 +461,71 @@ const test_targets = blk: { }; }; -const max_stdout_size = 1 * 1024 * 1024; // 1 MB +const c_abi_targets = [_]CrossTarget{ + .{}, + .{ + .cpu_arch = .x86_64, + .os_tag = .linux, + .abi = .musl, + }, + .{ + .cpu_arch = .x86, + .os_tag = .linux, + .abi = .musl, + }, + .{ + .cpu_arch = .aarch64, + .os_tag = .linux, + .abi = .musl, + }, + .{ + .cpu_arch = .arm, + .os_tag = .linux, + .abi = .musleabihf, + }, + .{ + .cpu_arch = .mips, + .os_tag = .linux, + .abi = .musl, + }, + .{ + .cpu_arch = .riscv64, + .os_tag = .linux, + .abi = .musl, + }, + .{ + .cpu_arch = .wasm32, + .os_tag = .wasi, + .abi = .musl, + }, + .{ + .cpu_arch = .powerpc, + .os_tag = .linux, + .abi = .musl, + }, + .{ + .cpu_arch = .powerpc64le, + .os_tag = .linux, + .abi = .musl, + }, + .{ + .cpu_arch = .x86, + .os_tag = .windows, + .abi = .gnu, + }, + .{ + .cpu_arch = .x86_64, + .os_tag = .windows, + .abi = .gnu, + }, +}; -pub fn addCompareOutputTests(b: *std.Build, test_filter: ?[]const u8, optimize_modes: []const OptimizeMode) *Step { - const cases = b.allocator.create(CompareOutputContext) catch unreachable; +pub fn addCompareOutputTests( + b: *std.Build, + test_filter: ?[]const u8, + optimize_modes: []const OptimizeMode, +) *Step { + const cases = b.allocator.create(CompareOutputContext) catch @panic("OOM"); cases.* = CompareOutputContext{ .b = b, .step = b.step("test-compare-output", "Run the compare output tests"), @@ -477,14 +539,26 @@ pub fn addCompareOutputTests(b: *std.Build, test_filter: ?[]const u8, optimize_m return cases.step; } -pub fn addStackTraceTests(b: *std.Build, test_filter: ?[]const u8, optimize_modes: []const OptimizeMode) *Step { - const cases = b.allocator.create(StackTracesContext) catch unreachable; - cases.* = StackTracesContext{ +pub fn addStackTraceTests( + b: *std.Build, + test_filter: ?[]const u8, + optimize_modes: []const OptimizeMode, +) *Step { + const check_exe = b.addExecutable(.{ + .name = "check-stack-trace", + .root_source_file = .{ .path = "test/src/check-stack-trace.zig" }, + .target = .{}, + .optimize = .Debug, + }); + + const cases = b.allocator.create(StackTracesContext) catch @panic("OOM"); + cases.* = .{ .b = b, .step = b.step("test-stack-traces", "Run the stack trace tests"), .test_index = 0, .test_filter = test_filter, .optimize_modes = optimize_modes, + .check_exe = check_exe, }; stack_traces.addCases(cases); @@ -507,7 +581,7 @@ pub fn addStandaloneTests( enable_wine: bool, enable_symlinks_windows: bool, ) *Step { - const cases = b.allocator.create(StandaloneContext) catch unreachable; + const cases = b.allocator.create(StandaloneContext) catch @panic("OOM"); cases.* = StandaloneContext{ .b = b, .step = b.step("test-standalone", "Run the standalone tests"), @@ -539,7 +613,7 @@ pub fn addLinkTests( omit_stage2: bool, enable_symlinks_windows: bool, ) *Step { - const cases = b.allocator.create(StandaloneContext) catch unreachable; + const cases = b.allocator.create(StandaloneContext) catch @panic("OOM"); cases.* = StandaloneContext{ .b = b, .step = b.step("test-link", "Run the linker tests"), @@ -569,7 +643,7 @@ pub fn addCliTests(b: *std.Build, test_filter: ?[]const u8, optimize_modes: []co }); const run_cmd = exe.run(); run_cmd.addArgs(&[_][]const u8{ - fs.realpathAlloc(b.allocator, b.zig_exe) catch unreachable, + fs.realpathAlloc(b.allocator, b.zig_exe) catch @panic("OOM"), b.pathFromRoot(b.cache_root.path orelse "."), }); @@ -578,7 +652,7 @@ pub fn addCliTests(b: *std.Build, test_filter: ?[]const u8, optimize_modes: []co } pub fn addAssembleAndLinkTests(b: *std.Build, test_filter: ?[]const u8, optimize_modes: []const OptimizeMode) *Step { - const cases = b.allocator.create(CompareOutputContext) catch unreachable; + const cases = b.allocator.create(CompareOutputContext) catch @panic("OOM"); cases.* = CompareOutputContext{ .b = b, .step = b.step("test-asm-link", "Run the assemble and link tests"), @@ -593,7 +667,7 @@ pub fn addAssembleAndLinkTests(b: *std.Build, test_filter: ?[]const u8, optimize } pub fn addTranslateCTests(b: *std.Build, test_filter: ?[]const u8) *Step { - const cases = b.allocator.create(TranslateCContext) catch unreachable; + const cases = b.allocator.create(TranslateCContext) catch @panic("OOM"); cases.* = TranslateCContext{ .b = b, .step = b.step("test-translate-c", "Run the C translation tests"), @@ -611,7 +685,7 @@ pub fn addRunTranslatedCTests( test_filter: ?[]const u8, target: std.zig.CrossTarget, ) *Step { - const cases = b.allocator.create(RunTranslatedCContext) catch unreachable; + const cases = b.allocator.create(RunTranslatedCContext) catch @panic("OOM"); cases.* = .{ .b = b, .step = b.step("test-run-translated-c", "Run the Run-Translated-C tests"), @@ -625,20 +699,6 @@ pub fn addRunTranslatedCTests( return cases.step; } -pub fn addGenHTests(b: *std.Build, test_filter: ?[]const u8) *Step { - const cases = b.allocator.create(GenHContext) catch unreachable; - cases.* = GenHContext{ - .b = b, - .step = b.step("test-gen-h", "Run the C header file generation tests"), - .test_index = 0, - .test_filter = test_filter, - }; - - gen_h.addCases(cases); - - return cases.step; -} - const ModuleTestOptions = struct { test_filter: ?[]const u8, root_src: []const u8, @@ -696,7 +756,7 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step { else "bare"; - const triple_prefix = test_target.target.zigTriple(b.allocator) catch unreachable; + const triple_prefix = test_target.target.zigTriple(b.allocator) catch @panic("OOM"); // wasm32-wasi builds need more RAM, idk why const max_rss = if (test_target.target.getOs().tag == .wasi) @@ -750,623 +810,6 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step { return step; } -pub const StackTracesContext = struct { - b: *std.Build, - step: *Step, - test_index: usize, - test_filter: ?[]const u8, - optimize_modes: []const OptimizeMode, - - const Expect = [@typeInfo(OptimizeMode).Enum.fields.len][]const u8; - - pub fn addCase(self: *StackTracesContext, config: anytype) void { - if (@hasField(@TypeOf(config), "exclude")) { - if (config.exclude.exclude()) return; - } - if (@hasField(@TypeOf(config), "exclude_arch")) { - const exclude_arch: []const std.Target.Cpu.Arch = &config.exclude_arch; - for (exclude_arch) |arch| if (arch == builtin.cpu.arch) return; - } - if (@hasField(@TypeOf(config), "exclude_os")) { - const exclude_os: []const std.Target.Os.Tag = &config.exclude_os; - for (exclude_os) |os| if (os == builtin.os.tag) return; - } - for (self.optimize_modes) |optimize_mode| { - switch (optimize_mode) { - .Debug => { - if (@hasField(@TypeOf(config), "Debug")) { - self.addExpect(config.name, config.source, optimize_mode, config.Debug); - } - }, - .ReleaseSafe => { - if (@hasField(@TypeOf(config), "ReleaseSafe")) { - self.addExpect(config.name, config.source, optimize_mode, config.ReleaseSafe); - } - }, - .ReleaseFast => { - if (@hasField(@TypeOf(config), "ReleaseFast")) { - self.addExpect(config.name, config.source, optimize_mode, config.ReleaseFast); - } - }, - .ReleaseSmall => { - if (@hasField(@TypeOf(config), "ReleaseSmall")) { - self.addExpect(config.name, config.source, optimize_mode, config.ReleaseSmall); - } - }, - } - } - } - - fn addExpect( - self: *StackTracesContext, - name: []const u8, - source: []const u8, - optimize_mode: OptimizeMode, - mode_config: anytype, - ) void { - if (@hasField(@TypeOf(mode_config), "exclude")) { - if (mode_config.exclude.exclude()) return; - } - if (@hasField(@TypeOf(mode_config), "exclude_arch")) { - const exclude_arch: []const std.Target.Cpu.Arch = &mode_config.exclude_arch; - for (exclude_arch) |arch| if (arch == builtin.cpu.arch) return; - } - if (@hasField(@TypeOf(mode_config), "exclude_os")) { - const exclude_os: []const std.Target.Os.Tag = &mode_config.exclude_os; - for (exclude_os) |os| if (os == builtin.os.tag) return; - } - - const annotated_case_name = fmt.allocPrint(self.b.allocator, "{s} {s} ({s})", .{ - "stack-trace", - name, - @tagName(optimize_mode), - }) catch unreachable; - if (self.test_filter) |filter| { - if (mem.indexOf(u8, annotated_case_name, filter) == null) return; - } - - const b = self.b; - const src_basename = "source.zig"; - const write_src = b.addWriteFile(src_basename, source); - const exe = b.addExecutable(.{ - .name = "test", - .root_source_file = write_src.getFileSource(src_basename).?, - .optimize = optimize_mode, - .target = .{}, - }); - - const run_and_compare = RunAndCompareStep.create( - self, - exe, - annotated_case_name, - optimize_mode, - mode_config.expect, - ); - - self.step.dependOn(&run_and_compare.step); - } - - const RunAndCompareStep = struct { - pub const base_id = .custom; - - step: Step, - context: *StackTracesContext, - exe: *CompileStep, - name: []const u8, - optimize_mode: OptimizeMode, - expect_output: []const u8, - test_index: usize, - - pub fn create( - context: *StackTracesContext, - exe: *CompileStep, - name: []const u8, - optimize_mode: OptimizeMode, - expect_output: []const u8, - ) *RunAndCompareStep { - const allocator = context.b.allocator; - const ptr = allocator.create(RunAndCompareStep) catch unreachable; - ptr.* = RunAndCompareStep{ - .step = Step.init(.{ - .id = .custom, - .name = "StackTraceCompareOutputStep", - .makeFn = make, - .owner = context.b, - }), - .context = context, - .exe = exe, - .name = name, - .optimize_mode = optimize_mode, - .expect_output = expect_output, - .test_index = context.test_index, - }; - ptr.step.dependOn(&exe.step); - context.test_index += 1; - return ptr; - } - - fn make(step: *Step, prog_node: *std.Progress.Node) !void { - _ = prog_node; - const self = @fieldParentPtr(RunAndCompareStep, "step", step); - const b = self.context.b; - - const full_exe_path = self.exe.getOutputSource().getPath(b); - var args = ArrayList([]const u8).init(b.allocator); - defer args.deinit(); - args.append(full_exe_path) catch unreachable; - - std.debug.print("Test {d}/{d} {s}...", .{ self.test_index + 1, self.context.test_index, self.name }); - - if (!std.process.can_spawn) { - const cmd = try std.mem.join(b.allocator, " ", args.items); - std.debug.print("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{ @tagName(builtin.os.tag), cmd }); - b.allocator.free(cmd); - return ExecError.ExecNotSupported; - } - - var child = std.ChildProcess.init(args.items, b.allocator); - child.stdin_behavior = .Ignore; - child.stdout_behavior = .Pipe; - child.stderr_behavior = .Pipe; - child.env_map = b.env_map; - - if (b.verbose) { - printInvocation(args.items); - } - child.spawn() catch |err| debug.panic("Unable to spawn {s}: {s}\n", .{ full_exe_path, @errorName(err) }); - - const stdout = child.stdout.?.reader().readAllAlloc(b.allocator, max_stdout_size) catch unreachable; - defer b.allocator.free(stdout); - const stderrFull = child.stderr.?.reader().readAllAlloc(b.allocator, max_stdout_size) catch unreachable; - defer b.allocator.free(stderrFull); - var stderr = stderrFull; - - const term = child.wait() catch |err| { - debug.panic("Unable to spawn {s}: {s}\n", .{ full_exe_path, @errorName(err) }); - }; - - switch (term) { - .Exited => |code| { - const expect_code: u32 = 1; - if (code != expect_code) { - std.debug.print("Process {s} exited with error code {d} but expected code {d}\n", .{ - full_exe_path, - code, - expect_code, - }); - printInvocation(args.items); - return error.TestFailed; - } - }, - .Signal => |signum| { - std.debug.print("Process {s} terminated on signal {d}\n", .{ full_exe_path, signum }); - printInvocation(args.items); - return error.TestFailed; - }, - .Stopped => |signum| { - std.debug.print("Process {s} stopped on signal {d}\n", .{ full_exe_path, signum }); - printInvocation(args.items); - return error.TestFailed; - }, - .Unknown => |code| { - std.debug.print("Process {s} terminated unexpectedly with error code {d}\n", .{ full_exe_path, code }); - printInvocation(args.items); - return error.TestFailed; - }, - } - - // process result - // - keep only basename of source file path - // - replace address with symbolic string - // - replace function name with symbolic string when optimize_mode != .Debug - // - skip empty lines - const got: []const u8 = got_result: { - var buf = ArrayList(u8).init(b.allocator); - defer buf.deinit(); - if (stderr.len != 0 and stderr[stderr.len - 1] == '\n') stderr = stderr[0 .. stderr.len - 1]; - var it = mem.split(u8, stderr, "\n"); - process_lines: while (it.next()) |line| { - if (line.len == 0) continue; - - // offset search past `[drive]:` on windows - var pos: usize = if (builtin.os.tag == .windows) 2 else 0; - // locate delims/anchor - const delims = [_][]const u8{ ":", ":", ":", " in ", "(", ")" }; - var marks = [_]usize{0} ** delims.len; - for (delims, 0..) |delim, i| { - marks[i] = mem.indexOfPos(u8, line, pos, delim) orelse { - // unexpected pattern: emit raw line and cont - try buf.appendSlice(line); - try buf.appendSlice("\n"); - continue :process_lines; - }; - pos = marks[i] + delim.len; - } - // locate source basename - pos = mem.lastIndexOfScalar(u8, line[0..marks[0]], fs.path.sep) orelse { - // unexpected pattern: emit raw line and cont - try buf.appendSlice(line); - try buf.appendSlice("\n"); - continue :process_lines; - }; - // end processing if source basename changes - if (!mem.eql(u8, "source.zig", line[pos + 1 .. marks[0]])) break; - // emit substituted line - try buf.appendSlice(line[pos + 1 .. marks[2] + delims[2].len]); - try buf.appendSlice(" [address]"); - if (self.optimize_mode == .Debug) { - // On certain platforms (windows) or possibly depending on how we choose to link main - // the object file extension may be present so we simply strip any extension. - if (mem.indexOfScalar(u8, line[marks[4]..marks[5]], '.')) |idot| { - try buf.appendSlice(line[marks[3] .. marks[4] + idot]); - try buf.appendSlice(line[marks[5]..]); - } else { - try buf.appendSlice(line[marks[3]..]); - } - } else { - try buf.appendSlice(line[marks[3] .. marks[3] + delims[3].len]); - try buf.appendSlice("[function]"); - } - try buf.appendSlice("\n"); - } - break :got_result try buf.toOwnedSlice(); - }; - - if (!mem.eql(u8, self.expect_output, got)) { - std.debug.print( - \\ - \\========= Expected this output: ========= - \\{s} - \\================================================ - \\{s} - \\ - , .{ self.expect_output, got }); - return error.TestFailed; - } - std.debug.print("OK\n", .{}); - } - }; -}; - -pub const StandaloneContext = struct { - b: *std.Build, - step: *Step, - test_index: usize, - test_filter: ?[]const u8, - optimize_modes: []const OptimizeMode, - skip_non_native: bool, - enable_macos_sdk: bool, - target: std.zig.CrossTarget, - omit_stage2: bool, - enable_darling: bool = false, - enable_qemu: bool = false, - enable_rosetta: bool = false, - enable_wasmtime: bool = false, - enable_wine: bool = false, - enable_symlinks_windows: bool, - - pub fn addC(self: *StandaloneContext, root_src: []const u8) void { - self.addAllArgs(root_src, true); - } - - pub fn add(self: *StandaloneContext, root_src: []const u8) void { - self.addAllArgs(root_src, false); - } - - pub fn addBuildFile(self: *StandaloneContext, build_file: []const u8, features: struct { - build_modes: bool = false, - cross_targets: bool = false, - requires_macos_sdk: bool = false, - requires_stage2: bool = false, - use_emulation: bool = false, - requires_symlinks: bool = false, - extra_argv: []const []const u8 = &.{}, - }) void { - const b = self.b; - - if (features.requires_macos_sdk and !self.enable_macos_sdk) return; - if (features.requires_stage2 and self.omit_stage2) return; - if (features.requires_symlinks and !self.enable_symlinks_windows and builtin.os.tag == .windows) return; - - const annotated_case_name = b.fmt("build {s}", .{build_file}); - if (self.test_filter) |filter| { - if (mem.indexOf(u8, annotated_case_name, filter) == null) return; - } - - var zig_args = ArrayList([]const u8).init(b.allocator); - const rel_zig_exe = fs.path.relative(b.allocator, b.build_root.path orelse ".", b.zig_exe) catch unreachable; - zig_args.append(rel_zig_exe) catch unreachable; - zig_args.append("build") catch unreachable; - - // TODO: fix the various non-concurrency-safe issues in zig's standalone tests, - // and then remove this! - zig_args.append("-j1") catch @panic("OOM"); - - zig_args.append("--build-file") catch unreachable; - zig_args.append(b.pathFromRoot(build_file)) catch unreachable; - - zig_args.appendSlice(features.extra_argv) catch unreachable; - - zig_args.append("test") catch unreachable; - - if (b.verbose) { - zig_args.append("--verbose") catch unreachable; - } - - if (features.cross_targets and !self.target.isNative()) { - const target_triple = self.target.zigTriple(b.allocator) catch unreachable; - const target_arg = fmt.allocPrint(b.allocator, "-Dtarget={s}", .{target_triple}) catch unreachable; - zig_args.append(target_arg) catch unreachable; - } - - if (features.use_emulation) { - if (self.enable_darling) { - zig_args.append("-fdarling") catch unreachable; - } - if (self.enable_qemu) { - zig_args.append("-fqemu") catch unreachable; - } - if (self.enable_rosetta) { - zig_args.append("-frosetta") catch unreachable; - } - if (self.enable_wasmtime) { - zig_args.append("-fwasmtime") catch unreachable; - } - if (self.enable_wine) { - zig_args.append("-fwine") catch unreachable; - } - } - - const optimize_modes = if (features.build_modes) self.optimize_modes else &[1]OptimizeMode{.Debug}; - for (optimize_modes) |optimize_mode| { - const arg = switch (optimize_mode) { - .Debug => "", - .ReleaseFast => "-Doptimize=ReleaseFast", - .ReleaseSafe => "-Doptimize=ReleaseSafe", - .ReleaseSmall => "-Doptimize=ReleaseSmall", - }; - const zig_args_base_len = zig_args.items.len; - if (arg.len > 0) - zig_args.append(arg) catch unreachable; - defer zig_args.resize(zig_args_base_len) catch unreachable; - - const run_cmd = b.addSystemCommand(zig_args.items); - self.step.dependOn(&run_cmd.step); - } - } - - pub fn addAllArgs(self: *StandaloneContext, root_src: []const u8, link_libc: bool) void { - const b = self.b; - - for (self.optimize_modes) |optimize| { - const annotated_case_name = fmt.allocPrint(self.b.allocator, "build {s} ({s})", .{ - root_src, - @tagName(optimize), - }) catch unreachable; - if (self.test_filter) |filter| { - if (mem.indexOf(u8, annotated_case_name, filter) == null) continue; - } - - const exe = b.addExecutable(.{ - .name = "test", - .root_source_file = .{ .path = root_src }, - .optimize = optimize, - .target = .{}, - }); - if (link_libc) { - exe.linkSystemLibrary("c"); - } - - self.step.dependOn(&exe.step); - } - } -}; - -pub const GenHContext = struct { - b: *std.Build, - step: *Step, - test_index: usize, - test_filter: ?[]const u8, - - const TestCase = struct { - name: []const u8, - sources: ArrayList(SourceFile), - expected_lines: ArrayList([]const u8), - - const SourceFile = struct { - filename: []const u8, - source: []const u8, - }; - - pub fn addSourceFile(self: *TestCase, filename: []const u8, source: []const u8) void { - self.sources.append(SourceFile{ - .filename = filename, - .source = source, - }) catch unreachable; - } - - pub fn addExpectedLine(self: *TestCase, text: []const u8) void { - self.expected_lines.append(text) catch unreachable; - } - }; - - const GenHCmpOutputStep = struct { - step: Step, - context: *GenHContext, - obj: *CompileStep, - name: []const u8, - test_index: usize, - case: *const TestCase, - - pub fn create( - context: *GenHContext, - obj: *CompileStep, - name: []const u8, - case: *const TestCase, - ) *GenHCmpOutputStep { - const allocator = context.b.allocator; - const ptr = allocator.create(GenHCmpOutputStep) catch unreachable; - ptr.* = GenHCmpOutputStep{ - .step = Step.init(.{ - .id = .custom, - .name = "ParseCCmpOutput", - .owner = context.b, - .makeFn = make, - }), - .context = context, - .obj = obj, - .name = name, - .test_index = context.test_index, - .case = case, - }; - ptr.step.dependOn(&obj.step); - context.test_index += 1; - return ptr; - } - - fn make(step: *Step, prog_node: *std.Progress.Node) !void { - _ = prog_node; - const self = @fieldParentPtr(GenHCmpOutputStep, "step", step); - const b = self.context.b; - - std.debug.print("Test {d}/{d} {s}...", .{ self.test_index + 1, self.context.test_index, self.name }); - - const full_h_path = self.obj.getOutputHPath(); - const actual_h = try io.readFileAlloc(b.allocator, full_h_path); - - for (self.case.expected_lines.items) |expected_line| { - if (mem.indexOf(u8, actual_h, expected_line) == null) { - std.debug.print( - \\ - \\========= Expected this output: ================ - \\{s} - \\========= But found: =========================== - \\{s} - \\ - , .{ expected_line, actual_h }); - return error.TestFailed; - } - } - std.debug.print("OK\n", .{}); - } - }; - - pub fn create( - self: *GenHContext, - filename: []const u8, - name: []const u8, - source: []const u8, - expected_lines: []const []const u8, - ) *TestCase { - const tc = self.b.allocator.create(TestCase) catch unreachable; - tc.* = TestCase{ - .name = name, - .sources = ArrayList(TestCase.SourceFile).init(self.b.allocator), - .expected_lines = ArrayList([]const u8).init(self.b.allocator), - }; - - tc.addSourceFile(filename, source); - var arg_i: usize = 0; - while (arg_i < expected_lines.len) : (arg_i += 1) { - tc.addExpectedLine(expected_lines[arg_i]); - } - return tc; - } - - pub fn add(self: *GenHContext, name: []const u8, source: []const u8, expected_lines: []const []const u8) void { - const tc = self.create("test.zig", name, source, expected_lines); - self.addCase(tc); - } - - pub fn addCase(self: *GenHContext, case: *const TestCase) void { - const b = self.b; - - const optimize_mode = std.builtin.OptimizeMode.Debug; - const annotated_case_name = fmt.allocPrint(self.b.allocator, "gen-h {s} ({s})", .{ case.name, @tagName(optimize_mode) }) catch unreachable; - if (self.test_filter) |filter| { - if (mem.indexOf(u8, annotated_case_name, filter) == null) return; - } - - const write_src = b.addWriteFiles(); - for (case.sources.items) |src_file| { - write_src.add(src_file.filename, src_file.source); - } - - const obj = b.addObjectFromWriteFileStep("test", write_src, case.sources.items[0].filename); - obj.setBuildMode(optimize_mode); - - const cmp_h = GenHCmpOutputStep.create(self, obj, annotated_case_name, case); - - self.step.dependOn(&cmp_h.step); - } -}; - -fn printInvocation(args: []const []const u8) void { - for (args) |arg| { - std.debug.print("{s} ", .{arg}); - } - std.debug.print("\n", .{}); -} - -const c_abi_targets = [_]CrossTarget{ - .{}, - .{ - .cpu_arch = .x86_64, - .os_tag = .linux, - .abi = .musl, - }, - .{ - .cpu_arch = .x86, - .os_tag = .linux, - .abi = .musl, - }, - .{ - .cpu_arch = .aarch64, - .os_tag = .linux, - .abi = .musl, - }, - .{ - .cpu_arch = .arm, - .os_tag = .linux, - .abi = .musleabihf, - }, - .{ - .cpu_arch = .mips, - .os_tag = .linux, - .abi = .musl, - }, - .{ - .cpu_arch = .riscv64, - .os_tag = .linux, - .abi = .musl, - }, - .{ - .cpu_arch = .wasm32, - .os_tag = .wasi, - .abi = .musl, - }, - .{ - .cpu_arch = .powerpc, - .os_tag = .linux, - .abi = .musl, - }, - .{ - .cpu_arch = .powerpc64le, - .os_tag = .linux, - .abi = .musl, - }, - .{ - .cpu_arch = .x86, - .os_tag = .windows, - .abi = .gnu, - }, - .{ - .cpu_arch = .x86_64, - .os_tag = .windows, - .abi = .gnu, - }, -}; - pub fn addCAbiTests(b: *std.Build, skip_non_native: bool, skip_release: bool) *Step { const step = b.step("test-c-abi", "Run the C ABI tests"); @@ -1395,7 +838,7 @@ pub fn addCAbiTests(b: *std.Build, skip_non_native: bool, skip_release: bool) *S test_step.want_lto = false; } - const triple_prefix = c_abi_target.zigTriple(b.allocator) catch unreachable; + const triple_prefix = c_abi_target.zigTriple(b.allocator) catch @panic("OOM"); test_step.setNamePrefix(b.fmt("{s}-{s}-{s} ", .{ "test-c-abi", triple_prefix, From e897637d8d25f5b6b118356d2355da7c9148d8cb Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 6 Mar 2023 19:19:48 -0700 Subject: [PATCH 208/294] re-enable compare-output test cases --- build.zig | 2 +- lib/build_runner.zig | 4 +- test/src/CompareOutput.zig | 176 ++++++++++++++++++++++++++++++++++++ test/src/compare_output.zig | 175 ----------------------------------- test/tests.zig | 2 +- 5 files changed, 180 insertions(+), 179 deletions(-) create mode 100644 test/src/CompareOutput.zig delete mode 100644 test/src/compare_output.zig diff --git a/build.zig b/build.zig index 1f1b119ed3..40dc823eb7 100644 --- a/build.zig +++ b/build.zig @@ -444,7 +444,7 @@ pub fn build(b: *std.Build) !void { _ = enable_symlinks_windows; _ = enable_macos_sdk; - //test_step.dependOn(tests.addCompareOutputTests(b, test_filter, optimization_modes)); + test_step.dependOn(tests.addCompareOutputTests(b, test_filter, optimization_modes)); //test_step.dependOn(tests.addStandaloneTests( // b, // test_filter, diff --git a/lib/build_runner.zig b/lib/build_runner.zig index c30fbaea87..bb04b3e132 100644 --- a/lib/build_runner.zig +++ b/lib/build_runner.zig @@ -887,6 +887,8 @@ fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi \\ --verbose Print commands before executing them \\ --color [auto|off|on] Enable or disable colored error messages \\ --prominent-compile-errors Output compile errors formatted for a human to read + \\ -fsummary Print the build summary, even on success + \\ -fno-summary Omit the build summary, even on failure \\ -j Limit concurrent jobs (default is to use all CPU cores) \\ --maxrss Limit memory usage (default is to use available memory) \\ @@ -920,8 +922,6 @@ fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi \\Advanced Options: \\ -freference-trace[=num] How many lines of reference trace should be shown per compile error \\ -fno-reference-trace Disable reference trace - \\ -fsummary Print the build summary, even on success - \\ -fno-summary Omit the build summary, even on failure \\ --build-file [file] Override path to build.zig \\ --cache-dir [path] Override path to local Zig cache directory \\ --global-cache-dir [path] Override path to global Zig cache directory diff --git a/test/src/CompareOutput.zig b/test/src/CompareOutput.zig new file mode 100644 index 0000000000..68cf942684 --- /dev/null +++ b/test/src/CompareOutput.zig @@ -0,0 +1,176 @@ +//! This is the implementation of the test harness. +//! For the actual test cases, see test/compare_output.zig. + +b: *std.Build, +step: *std.Build.Step, +test_index: usize, +test_filter: ?[]const u8, +optimize_modes: []const OptimizeMode, + +const Special = enum { + None, + Asm, + RuntimeSafety, +}; + +const TestCase = struct { + name: []const u8, + sources: ArrayList(SourceFile), + expected_output: []const u8, + link_libc: bool, + special: Special, + cli_args: []const []const u8, + + const SourceFile = struct { + filename: []const u8, + source: []const u8, + }; + + pub fn addSourceFile(self: *TestCase, filename: []const u8, source: []const u8) void { + self.sources.append(SourceFile{ + .filename = filename, + .source = source, + }) catch @panic("OOM"); + } + + pub fn setCommandLineArgs(self: *TestCase, args: []const []const u8) void { + self.cli_args = args; + } +}; + +pub fn createExtra(self: *CompareOutput, name: []const u8, source: []const u8, expected_output: []const u8, special: Special) TestCase { + var tc = TestCase{ + .name = name, + .sources = ArrayList(TestCase.SourceFile).init(self.b.allocator), + .expected_output = expected_output, + .link_libc = false, + .special = special, + .cli_args = &[_][]const u8{}, + }; + const root_src_name = if (special == Special.Asm) "source.s" else "source.zig"; + tc.addSourceFile(root_src_name, source); + return tc; +} + +pub fn create(self: *CompareOutput, name: []const u8, source: []const u8, expected_output: []const u8) TestCase { + return createExtra(self, name, source, expected_output, Special.None); +} + +pub fn addC(self: *CompareOutput, name: []const u8, source: []const u8, expected_output: []const u8) void { + var tc = self.create(name, source, expected_output); + tc.link_libc = true; + self.addCase(tc); +} + +pub fn add(self: *CompareOutput, name: []const u8, source: []const u8, expected_output: []const u8) void { + const tc = self.create(name, source, expected_output); + self.addCase(tc); +} + +pub fn addAsm(self: *CompareOutput, name: []const u8, source: []const u8, expected_output: []const u8) void { + const tc = self.createExtra(name, source, expected_output, Special.Asm); + self.addCase(tc); +} + +pub fn addRuntimeSafety(self: *CompareOutput, name: []const u8, source: []const u8) void { + const tc = self.createExtra(name, source, undefined, Special.RuntimeSafety); + self.addCase(tc); +} + +pub fn addCase(self: *CompareOutput, case: TestCase) void { + const b = self.b; + + const write_src = b.addWriteFiles(); + for (case.sources.items) |src_file| { + write_src.add(src_file.filename, src_file.source); + } + + switch (case.special) { + Special.Asm => { + const annotated_case_name = fmt.allocPrint(self.b.allocator, "run assemble-and-link {s}", .{ + case.name, + }) catch @panic("OOM"); + if (self.test_filter) |filter| { + if (mem.indexOf(u8, annotated_case_name, filter) == null) return; + } + + const exe = b.addExecutable(.{ + .name = "test", + .target = .{}, + .optimize = .Debug, + }); + exe.addAssemblyFileSource(write_src.getFileSource(case.sources.items[0].filename).?); + + const run = exe.run(); + run.setName(annotated_case_name); + run.addArgs(case.cli_args); + run.expectStdErrEqual(""); + run.expectStdOutEqual(case.expected_output); + + self.step.dependOn(&run.step); + }, + Special.None => { + for (self.optimize_modes) |optimize| { + const annotated_case_name = fmt.allocPrint(self.b.allocator, "run compare-output {s} ({s})", .{ + case.name, @tagName(optimize), + }) catch @panic("OOM"); + if (self.test_filter) |filter| { + if (mem.indexOf(u8, annotated_case_name, filter) == null) continue; + } + + const basename = case.sources.items[0].filename; + const exe = b.addExecutable(.{ + .name = "test", + .root_source_file = write_src.getFileSource(basename).?, + .optimize = optimize, + .target = .{}, + }); + if (case.link_libc) { + exe.linkSystemLibrary("c"); + } + + const run = exe.run(); + run.setName(annotated_case_name); + run.addArgs(case.cli_args); + run.expectStdErrEqual(""); + run.expectStdOutEqual(case.expected_output); + + self.step.dependOn(&run.step); + } + }, + Special.RuntimeSafety => { + // TODO iterate over self.optimize_modes and test this in both + // debug and release safe mode + const annotated_case_name = fmt.allocPrint(self.b.allocator, "run safety {s}", .{case.name}) catch @panic("OOM"); + if (self.test_filter) |filter| { + if (mem.indexOf(u8, annotated_case_name, filter) == null) return; + } + + const basename = case.sources.items[0].filename; + const exe = b.addExecutable(.{ + .name = "test", + .root_source_file = write_src.getFileSource(basename).?, + .target = .{}, + .optimize = .Debug, + }); + if (case.link_libc) { + exe.linkSystemLibrary("c"); + } + + const run = exe.run(); + run.setName(annotated_case_name); + run.addArgs(case.cli_args); + run.expectExitCode(126); + + self.step.dependOn(&run.step); + }, + } +} + +const CompareOutput = @This(); +const std = @import("std"); +const ArrayList = std.ArrayList; +const fmt = std.fmt; +const mem = std.mem; +const fs = std.fs; +const OptimizeMode = std.builtin.OptimizeMode; diff --git a/test/src/compare_output.zig b/test/src/compare_output.zig deleted file mode 100644 index 20bd62d8e2..0000000000 --- a/test/src/compare_output.zig +++ /dev/null @@ -1,175 +0,0 @@ -// This is the implementation of the test harness. -// For the actual test cases, see test/compare_output.zig. -const std = @import("std"); -const ArrayList = std.ArrayList; -const fmt = std.fmt; -const mem = std.mem; -const fs = std.fs; -const OptimizeMode = std.builtin.OptimizeMode; - -pub const CompareOutputContext = struct { - b: *std.Build, - step: *std.Build.Step, - test_index: usize, - test_filter: ?[]const u8, - optimize_modes: []const OptimizeMode, - - const Special = enum { - None, - Asm, - RuntimeSafety, - }; - - const TestCase = struct { - name: []const u8, - sources: ArrayList(SourceFile), - expected_output: []const u8, - link_libc: bool, - special: Special, - cli_args: []const []const u8, - - const SourceFile = struct { - filename: []const u8, - source: []const u8, - }; - - pub fn addSourceFile(self: *TestCase, filename: []const u8, source: []const u8) void { - self.sources.append(SourceFile{ - .filename = filename, - .source = source, - }) catch unreachable; - } - - pub fn setCommandLineArgs(self: *TestCase, args: []const []const u8) void { - self.cli_args = args; - } - }; - - pub fn createExtra(self: *CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8, special: Special) TestCase { - var tc = TestCase{ - .name = name, - .sources = ArrayList(TestCase.SourceFile).init(self.b.allocator), - .expected_output = expected_output, - .link_libc = false, - .special = special, - .cli_args = &[_][]const u8{}, - }; - const root_src_name = if (special == Special.Asm) "source.s" else "source.zig"; - tc.addSourceFile(root_src_name, source); - return tc; - } - - pub fn create(self: *CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) TestCase { - return createExtra(self, name, source, expected_output, Special.None); - } - - pub fn addC(self: *CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) void { - var tc = self.create(name, source, expected_output); - tc.link_libc = true; - self.addCase(tc); - } - - pub fn add(self: *CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) void { - const tc = self.create(name, source, expected_output); - self.addCase(tc); - } - - pub fn addAsm(self: *CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) void { - const tc = self.createExtra(name, source, expected_output, Special.Asm); - self.addCase(tc); - } - - pub fn addRuntimeSafety(self: *CompareOutputContext, name: []const u8, source: []const u8) void { - const tc = self.createExtra(name, source, undefined, Special.RuntimeSafety); - self.addCase(tc); - } - - pub fn addCase(self: *CompareOutputContext, case: TestCase) void { - const b = self.b; - - const write_src = b.addWriteFiles(); - for (case.sources.items) |src_file| { - write_src.add(src_file.filename, src_file.source); - } - - switch (case.special) { - Special.Asm => { - const annotated_case_name = fmt.allocPrint(self.b.allocator, "assemble-and-link {s}", .{ - case.name, - }) catch unreachable; - if (self.test_filter) |filter| { - if (mem.indexOf(u8, annotated_case_name, filter) == null) return; - } - - const exe = b.addExecutable(.{ - .name = "test", - .target = .{}, - .optimize = .Debug, - }); - exe.addAssemblyFileSource(write_src.getFileSource(case.sources.items[0].filename).?); - - const run = exe.run(); - run.addArgs(case.cli_args); - run.expectStdErrEqual(""); - run.expectStdOutEqual(case.expected_output); - - self.step.dependOn(&run.step); - }, - Special.None => { - for (self.optimize_modes) |optimize| { - const annotated_case_name = fmt.allocPrint(self.b.allocator, "{s} {s} ({s})", .{ - "compare-output", - case.name, - @tagName(optimize), - }) catch unreachable; - if (self.test_filter) |filter| { - if (mem.indexOf(u8, annotated_case_name, filter) == null) continue; - } - - const basename = case.sources.items[0].filename; - const exe = b.addExecutable(.{ - .name = "test", - .root_source_file = write_src.getFileSource(basename).?, - .optimize = optimize, - .target = .{}, - }); - if (case.link_libc) { - exe.linkSystemLibrary("c"); - } - - const run = exe.run(); - run.addArgs(case.cli_args); - run.expectStdErrEqual(""); - run.expectStdOutEqual(case.expected_output); - - self.step.dependOn(&run.step); - } - }, - Special.RuntimeSafety => { - // TODO iterate over self.optimize_modes and test this in both - // debug and release safe mode - const annotated_case_name = fmt.allocPrint(self.b.allocator, "safety {s}", .{case.name}) catch unreachable; - if (self.test_filter) |filter| { - if (mem.indexOf(u8, annotated_case_name, filter) == null) return; - } - - const basename = case.sources.items[0].filename; - const exe = b.addExecutable(.{ - .name = "test", - .root_source_file = write_src.getFileSource(basename).?, - .target = .{}, - .optimize = .Debug, - }); - if (case.link_libc) { - exe.linkSystemLibrary("c"); - } - - const run = exe.run(); - run.addArgs(case.cli_args); - run.expectExitCode(126); - - self.step.dependOn(&run.step); - }, - } - } -}; diff --git a/test/tests.zig b/test/tests.zig index 1a80d7de8d..e21e652cba 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -25,7 +25,7 @@ const link = @import("link.zig"); // Implementations pub const TranslateCContext = @import("src/translate_c.zig").TranslateCContext; pub const RunTranslatedCContext = @import("src/run_translated_c.zig").RunTranslatedCContext; -pub const CompareOutputContext = @import("src/compare_output.zig").CompareOutputContext; +pub const CompareOutputContext = @import("src/CompareOutput.zig"); pub const StackTracesContext = @import("src/StackTrace.zig"); pub const StandaloneContext = @import("src/Standalone.zig"); From 0b8736f5ed6e7ae08e8ef84beb03351a95daabdd Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 6 Mar 2023 22:44:36 -0700 Subject: [PATCH 209/294] re-enable CLI tests CLI tests are now ported over to the new std.Build API and thus work properly with concurrency. * add `std.Build.addCheckFile` for creating a `std.Build.CheckFileStep`. * add `std.Build.makeTempPath`. This function is intended to be called in the `configure` phase only. It returns an absolute directory path, which is potentially going to be a source of API breakage in the future, so keep that in mind when using this function. * add `std.Build.CheckFileStep.setName`. * `std.Build.CheckFileStep`: better error message when reading the input file fails. * `std.Build.RunStep`: add a `has_side_effects` flag for when you need to override the autodetection. * `std.Build.RunStep`: add the ability to obtain a FileSource for the directory that contains the written files. * `std.Build.WriteFileStep`: add a way to write bytes to an arbitrary path - absolute or relative to the package root. Be careful with this because it updates source files. This should not be used as part of the normal build process, but as a utility occasionally run by a developer with intent to modify source files and then commit those changes to version control. A file added this way is not available with `getFileSource`. --- build.zig | 2 +- lib/std/Build.zig | 44 ++++++- lib/std/Build/CheckFileStep.zig | 20 +++- lib/std/Build/RunStep.zig | 39 ++++++- lib/std/Build/TranslateCStep.zig | 6 +- lib/std/Build/WriteFileStep.zig | 31 ++++- test/cli.zig | 195 ------------------------------- test/tests.zig | 194 ++++++++++++++++++++++++++++-- 8 files changed, 309 insertions(+), 222 deletions(-) delete mode 100644 test/cli.zig diff --git a/build.zig b/build.zig index 40dc823eb7..7c22016bb7 100644 --- a/build.zig +++ b/build.zig @@ -463,7 +463,7 @@ pub fn build(b: *std.Build) !void { //test_step.dependOn(tests.addCAbiTests(b, skip_non_native, skip_release)); //test_step.dependOn(tests.addLinkTests(b, test_filter, optimization_modes, enable_macos_sdk, skip_stage2_tests, enable_symlinks_windows)); test_step.dependOn(tests.addStackTraceTests(b, test_filter, optimization_modes)); - //test_step.dependOn(tests.addCliTests(b, test_filter, optimization_modes)); + test_step.dependOn(tests.addCliTests(b, test_filter, optimization_modes)); //test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, optimization_modes)); test_step.dependOn(tests.addTranslateCTests(b, test_filter)); if (!skip_run_translated_c) { diff --git a/lib/std/Build.zig b/lib/std/Build.zig index df10f55439..68e80435f8 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -699,10 +699,8 @@ pub fn addWriteFile(self: *Build, file_path: []const u8, data: []const u8) *Writ return write_file_step; } -pub fn addWriteFiles(self: *Build) *WriteFileStep { - const write_file_step = self.allocator.create(WriteFileStep) catch @panic("OOM"); - write_file_step.* = WriteFileStep.init(self); - return write_file_step; +pub fn addWriteFiles(b: *Build) *WriteFileStep { + return WriteFileStep.create(b); } pub fn addRemoveDirTree(self: *Build, dir_path: []const u8) *RemoveDirStep { @@ -1239,6 +1237,14 @@ pub fn addInstallDirectory(self: *Build, options: InstallDirectoryOptions) *Inst return install_step; } +pub fn addCheckFile( + b: *Build, + file_source: FileSource, + options: CheckFileStep.Options, +) *CheckFileStep { + return CheckFileStep.create(b, file_source, options); +} + pub fn pushInstalledFile(self: *Build, dir: InstallDir, dest_rel_path: []const u8) void { const file = InstalledFile{ .dir = dir, @@ -1713,6 +1719,36 @@ pub fn serializeCpu(allocator: Allocator, cpu: std.Target.Cpu) ![]const u8 { } } +/// This function is intended to be called in the `configure` phase only. +/// It returns an absolute directory path, which is potentially going to be a +/// source of API breakage in the future, so keep that in mind when using this +/// function. +pub fn makeTempPath(b: *Build) []const u8 { + const rand_int = std.crypto.random.int(u64); + const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ hex64(rand_int); + const result_path = b.cache_root.join(b.allocator, &.{tmp_dir_sub_path}) catch @panic("OOM"); + fs.cwd().makePath(result_path) catch |err| { + std.debug.print("unable to make tmp path '{s}': {s}\n", .{ + result_path, @errorName(err), + }); + }; + return result_path; +} + +/// There are a few copies of this function in miscellaneous places. Would be nice to find +/// a home for them. +fn hex64(x: u64) [16]u8 { + const hex_charset = "0123456789abcdef"; + var result: [16]u8 = undefined; + var i: usize = 0; + while (i < 8) : (i += 1) { + const byte = @truncate(u8, x >> @intCast(u6, 8 * i)); + result[i * 2 + 0] = hex_charset[byte >> 4]; + result[i * 2 + 1] = hex_charset[byte & 15]; + } + return result; +} + test { _ = CheckFileStep; _ = CheckObjectStep; diff --git a/lib/std/Build/CheckFileStep.zig b/lib/std/Build/CheckFileStep.zig index 03b23d0b03..a65810681b 100644 --- a/lib/std/Build/CheckFileStep.zig +++ b/lib/std/Build/CheckFileStep.zig @@ -12,13 +12,17 @@ expected_matches: []const []const u8, source: std.Build.FileSource, max_bytes: usize = 20 * 1024 * 1024, +pub const Options = struct { + expected_matches: []const []const u8, +}; + pub fn create( owner: *std.Build, source: std.Build.FileSource, - expected_matches: []const []const u8, + options: Options, ) *CheckFileStep { const self = owner.allocator.create(CheckFileStep) catch @panic("OOM"); - self.* = CheckFileStep{ + self.* = .{ .step = Step.init(.{ .id = .check_file, .name = "CheckFile", @@ -26,19 +30,27 @@ pub fn create( .makeFn = make, }), .source = source.dupe(owner), - .expected_matches = owner.dupeStrings(expected_matches), + .expected_matches = owner.dupeStrings(options.expected_matches), }; self.source.addStepDependencies(&self.step); return self; } +pub fn setName(self: *CheckFileStep, name: []const u8) void { + self.step.name = name; +} + fn make(step: *Step, prog_node: *std.Progress.Node) !void { _ = prog_node; const b = step.owner; const self = @fieldParentPtr(CheckFileStep, "step", step); const src_path = self.source.getPath(b); - const contents = try fs.cwd().readFileAlloc(b.allocator, src_path, self.max_bytes); + const contents = fs.cwd().readFileAlloc(b.allocator, src_path, self.max_bytes) catch |err| { + return step.fail("unable to read '{s}': {s}", .{ + src_path, @errorName(err), + }); + }; for (self.expected_matches) |expected_match| { if (mem.indexOf(u8, contents, expected_match) == null) { diff --git a/lib/std/Build/RunStep.zig b/lib/std/Build/RunStep.zig index 95e37f230a..9ab9972c2e 100644 --- a/lib/std/Build/RunStep.zig +++ b/lib/std/Build/RunStep.zig @@ -70,6 +70,8 @@ max_stdio_size: usize = 10 * 1024 * 1024, captured_stdout: ?*Output = null, captured_stderr: ?*Output = null, +has_side_effects: bool = false, + pub const StdIo = union(enum) { /// Whether the RunStep has side-effects will be determined by whether or not one /// of the args is an output file (added with `addOutputFileArg`). @@ -103,12 +105,14 @@ pub const StdIo = union(enum) { pub const Arg = union(enum) { artifact: *CompileStep, file_source: std.Build.FileSource, + directory_source: std.Build.FileSource, bytes: []u8, output: *Output, }; pub const Output = struct { generated_file: std.Build.GeneratedFile, + prefix: []const u8, basename: []const u8, }; @@ -142,10 +146,19 @@ pub fn addArtifactArg(self: *RunStep, artifact: *CompileStep) void { /// run, and returns a FileSource which can be used as inputs to other APIs /// throughout the build system. pub fn addOutputFileArg(rs: *RunStep, basename: []const u8) std.Build.FileSource { + return addPrefixedOutputFileArg(rs, "", basename); +} + +pub fn addPrefixedOutputFileArg( + rs: *RunStep, + prefix: []const u8, + basename: []const u8, +) std.Build.FileSource { const b = rs.step.owner; const output = b.allocator.create(Output) catch @panic("OOM"); output.* = .{ + .prefix = prefix, .basename = basename, .generated_file = .{ .step = &rs.step }, }; @@ -159,14 +172,21 @@ pub fn addOutputFileArg(rs: *RunStep, basename: []const u8) std.Build.FileSource } pub fn addFileSourceArg(self: *RunStep, file_source: std.Build.FileSource) void { - self.argv.append(Arg{ + self.argv.append(.{ .file_source = file_source.dupe(self.step.owner), }) catch @panic("OOM"); file_source.addStepDependencies(&self.step); } +pub fn addDirectorySourceArg(self: *RunStep, directory_source: std.Build.FileSource) void { + self.argv.append(.{ + .directory_source = directory_source.dupe(self.step.owner), + }) catch @panic("OOM"); + directory_source.addStepDependencies(&self.step); +} + pub fn addArg(self: *RunStep, arg: []const u8) void { - self.argv.append(Arg{ .bytes = self.step.owner.dupe(arg) }) catch @panic("OOM"); + self.argv.append(.{ .bytes = self.step.owner.dupe(arg) }) catch @panic("OOM"); } pub fn addArgs(self: *RunStep, args: []const []const u8) void { @@ -274,6 +294,7 @@ pub fn captureStdErr(self: *RunStep) std.Build.FileSource { const output = self.step.owner.allocator.create(Output) catch @panic("OOM"); output.* = .{ + .prefix = "", .basename = "stderr", .generated_file = .{ .step = &self.step }, }; @@ -288,6 +309,7 @@ pub fn captureStdOut(self: *RunStep) *std.Build.GeneratedFile { const output = self.step.owner.allocator.create(Output) catch @panic("OOM"); output.* = .{ + .prefix = "", .basename = "stdout", .generated_file = .{ .step = &self.step }, }; @@ -297,6 +319,7 @@ pub fn captureStdOut(self: *RunStep) *std.Build.GeneratedFile { /// Returns whether the RunStep has side effects *other than* updating the output arguments. fn hasSideEffects(self: RunStep) bool { + if (self.has_side_effects) return true; return switch (self.stdio) { .infer_from_args => !self.hasAnyOutputArgs(), .inherit => true, @@ -373,6 +396,11 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { try argv_list.append(file_path); _ = try man.addFile(file_path, null); }, + .directory_source => |file| { + const file_path = file.getPath(b); + try argv_list.append(file_path); + man.hash.addBytes(file_path); + }, .artifact => |artifact| { if (artifact.target.isWindows()) { // On Windows we don't have rpaths so we have to add .dll search paths to PATH @@ -386,6 +414,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { _ = try man.addFile(file_path, null); }, .output => |output| { + man.hash.addBytes(output.prefix); man.hash.addBytes(output.basename); // Add a placeholder into the argument list because we need the // manifest hash to be updated with all arguments before the @@ -456,7 +485,11 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { }; const output_path = try b.cache_root.join(arena, &output_components); placeholder.output.generated_file.path = output_path; - argv_list.items[placeholder.index] = output_path; + const cli_arg = if (placeholder.output.prefix.len == 0) + output_path + else + b.fmt("{s}{s}", .{ placeholder.output.prefix, output_path }); + argv_list.items[placeholder.index] = cli_arg; } try runCommand(self, argv_list.items, has_side_effects, &digest); diff --git a/lib/std/Build/TranslateCStep.zig b/lib/std/Build/TranslateCStep.zig index dbb93d8c61..0cfd5d85a8 100644 --- a/lib/std/Build/TranslateCStep.zig +++ b/lib/std/Build/TranslateCStep.zig @@ -72,7 +72,11 @@ pub fn addIncludeDir(self: *TranslateCStep, include_dir: []const u8) void { } pub fn addCheckFile(self: *TranslateCStep, expected_matches: []const []const u8) *CheckFileStep { - return CheckFileStep.create(self.step.owner, .{ .generated = &self.output_file }, self.step.owner.dupeStrings(expected_matches)); + return CheckFileStep.create( + self.step.owner, + .{ .generated = &self.output_file }, + .{ .expected_matches = expected_matches }, + ); } /// If the value is omitted, it is set to 1. diff --git a/lib/std/Build/WriteFileStep.zig b/lib/std/Build/WriteFileStep.zig index 64025ce0fe..e6ceb4777c 100644 --- a/lib/std/Build/WriteFileStep.zig +++ b/lib/std/Build/WriteFileStep.zig @@ -14,6 +14,7 @@ step: Step, /// GeneratedFile field. files: std.ArrayListUnmanaged(*File), output_source_files: std.ArrayListUnmanaged(OutputSourceFile), +generated_directory: std.Build.GeneratedFile, pub const base_id = .write_file; @@ -33,8 +34,9 @@ pub const Contents = union(enum) { copy: std.Build.FileSource, }; -pub fn init(owner: *std.Build) WriteFileStep { - return .{ +pub fn create(owner: *std.Build) *WriteFileStep { + const wf = owner.allocator.create(WriteFileStep) catch @panic("OOM"); + wf.* = .{ .step = Step.init(.{ .id = .write_file, .name = "WriteFile", @@ -43,7 +45,9 @@ pub fn init(owner: *std.Build) WriteFileStep { }), .files = .{}, .output_source_files = .{}, + .generated_directory = .{ .step = &wf.step }, }; + return wf; } pub fn add(wf: *WriteFileStep, sub_path: []const u8, bytes: []const u8) void { @@ -95,6 +99,20 @@ pub fn addCopyFileToSource(wf: *WriteFileStep, source: std.Build.FileSource, sub }) catch @panic("OOM"); } +/// A path relative to the package root. +/// Be careful with this because it updates source files. This should not be +/// used as part of the normal build process, but as a utility occasionally +/// run by a developer with intent to modify source files and then commit +/// those changes to version control. +/// A file added this way is not available with `getFileSource`. +pub fn addBytesToSource(wf: *WriteFileStep, bytes: []const u8, sub_path: []const u8) void { + const b = wf.step.owner; + wf.output_source_files.append(b.allocator, .{ + .contents = .{ .bytes = bytes }, + .sub_path = sub_path, + }) catch @panic("OOM"); +} + /// Gets a file source for the given sub_path. If the file does not exist, returns `null`. pub fn getFileSource(wf: *WriteFileStep, sub_path: []const u8) ?std.Build.FileSource { for (wf.files.items) |file| { @@ -105,6 +123,12 @@ pub fn getFileSource(wf: *WriteFileStep, sub_path: []const u8) ?std.Build.FileSo return null; } +/// Returns a `FileSource` representing the base directory that contains all the +/// files from this `WriteFileStep`. +pub fn getDirectorySource(wf: *WriteFileStep) std.Build.FileSource { + return .{ .generated = &wf.generated_directory }; +} + fn maybeUpdateName(wf: *WriteFileStep) void { if (wf.files.items.len == 1) { // First time adding a file; update name. @@ -193,12 +217,15 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { "o", &digest, file.sub_path, }); } + wf.generated_directory.path = try b.cache_root.join(b.allocator, &.{ "o", &digest }); return; } const digest = man.final(); const cache_path = "o" ++ fs.path.sep_str ++ digest; + wf.generated_directory.path = try b.cache_root.join(b.allocator, &.{ "o", &digest }); + var cache_dir = b.cache_root.handle.makeOpenPath(cache_path, .{}) catch |err| { return step.fail("unable to make path '{}{s}': {s}", .{ b.cache_root, cache_path, @errorName(err), diff --git a/test/cli.zig b/test/cli.zig deleted file mode 100644 index 57f26f73d7..0000000000 --- a/test/cli.zig +++ /dev/null @@ -1,195 +0,0 @@ -const std = @import("std"); -const builtin = @import("builtin"); -const testing = std.testing; -const process = std.process; -const fs = std.fs; -const ChildProcess = std.ChildProcess; - -var a: std.mem.Allocator = undefined; - -pub fn main() !void { - var gpa = std.heap.GeneralPurposeAllocator(.{}){}; - defer _ = gpa.deinit(); - var arena = std.heap.ArenaAllocator.init(gpa.allocator()); - defer arena.deinit(); - - a = arena.allocator(); - var arg_it = try process.argsWithAllocator(a); - - // skip my own exe name - _ = arg_it.skip(); - - const zig_exe_rel = arg_it.next() orelse { - std.debug.print("Expected first argument to be path to zig compiler\n", .{}); - return error.InvalidArgs; - }; - const cache_root = arg_it.next() orelse { - std.debug.print("Expected second argument to be cache root directory path\n", .{}); - return error.InvalidArgs; - }; - const zig_exe = try fs.path.resolve(a, &[_][]const u8{zig_exe_rel}); - - const dir_path = try fs.path.join(a, &[_][]const u8{ cache_root, "clitest" }); - defer fs.cwd().deleteTree(dir_path) catch {}; - - const TestFn = fn ([]const u8, []const u8) anyerror!void; - const Test = struct { - func: TestFn, - name: []const u8, - }; - const tests = [_]Test{ - .{ .func = testZigInitLib, .name = "zig init-lib" }, - .{ .func = testZigInitExe, .name = "zig init-exe" }, - .{ .func = testGodboltApi, .name = "godbolt API" }, - .{ .func = testMissingOutputPath, .name = "missing output path" }, - .{ .func = testZigFmt, .name = "zig fmt" }, - }; - inline for (tests) |t| { - try fs.cwd().deleteTree(dir_path); - try fs.cwd().makeDir(dir_path); - t.func(zig_exe, dir_path) catch |err| { - std.debug.print("test '{s}' failed: {s}\n", .{ - t.name, @errorName(err), - }); - return err; - }; - } -} - -fn printCmd(cwd: []const u8, argv: []const []const u8) void { - std.debug.print("cd {s} && ", .{cwd}); - for (argv) |arg| { - std.debug.print("{s} ", .{arg}); - } - std.debug.print("\n", .{}); -} - -fn exec(cwd: []const u8, expect_0: bool, argv: []const []const u8) !ChildProcess.ExecResult { - const max_output_size = 100 * 1024; - const result = ChildProcess.exec(.{ - .allocator = a, - .argv = argv, - .cwd = cwd, - .max_output_bytes = max_output_size, - }) catch |err| { - std.debug.print("The following command failed:\n", .{}); - printCmd(cwd, argv); - return err; - }; - switch (result.term) { - .Exited => |code| { - if ((code != 0) == expect_0) { - std.debug.print("The following command exited with error code {}:\n", .{code}); - printCmd(cwd, argv); - std.debug.print("stderr:\n{s}\n", .{result.stderr}); - return error.CommandFailed; - } - }, - else => { - std.debug.print("The following command terminated unexpectedly:\n", .{}); - printCmd(cwd, argv); - std.debug.print("stderr:\n{s}\n", .{result.stderr}); - return error.CommandFailed; - }, - } - return result; -} - -fn testZigInitLib(zig_exe: []const u8, dir_path: []const u8) !void { - _ = try exec(dir_path, true, &[_][]const u8{ zig_exe, "init-lib" }); - const test_result = try exec(dir_path, true, &[_][]const u8{ zig_exe, "build", "test" }); - try testing.expectStringEndsWith(test_result.stderr, "All 1 tests passed.\n"); -} - -fn testZigInitExe(zig_exe: []const u8, dir_path: []const u8) !void { - _ = try exec(dir_path, true, &[_][]const u8{ zig_exe, "init-exe" }); - const run_result = try exec(dir_path, true, &[_][]const u8{ zig_exe, "build", "run" }); - try testing.expectEqualStrings("All your codebase are belong to us.\n", run_result.stderr); - try testing.expectEqualStrings("Run `zig build test` to run the tests.\n", run_result.stdout); -} - -fn testGodboltApi(zig_exe: []const u8, dir_path: []const u8) anyerror!void { - if (builtin.os.tag != .linux or builtin.cpu.arch != .x86_64) return; - - const example_zig_path = try fs.path.join(a, &[_][]const u8{ dir_path, "example.zig" }); - const example_s_path = try fs.path.join(a, &[_][]const u8{ dir_path, "example.s" }); - - try fs.cwd().writeFile(example_zig_path, - \\// Type your code here, or load an example. - \\export fn square(num: i32) i32 { - \\ return num * num; - \\} - \\extern fn zig_panic() noreturn; - \\pub fn panic(msg: []const u8, error_return_trace: ?*@import("std").builtin.StackTrace, _: ?usize) noreturn { - \\ _ = msg; - \\ _ = error_return_trace; - \\ zig_panic(); - \\} - ); - - var args = std.ArrayList([]const u8).init(a); - try args.appendSlice(&[_][]const u8{ - zig_exe, "build-obj", - "--cache-dir", dir_path, - "--name", "example", - "-fno-emit-bin", "-fno-emit-h", - "-fstrip", "-OReleaseFast", - example_zig_path, - }); - - const emit_asm_arg = try std.fmt.allocPrint(a, "-femit-asm={s}", .{example_s_path}); - try args.append(emit_asm_arg); - - _ = try exec(dir_path, true, args.items); - - const out_asm = try std.fs.cwd().readFileAlloc(a, example_s_path, std.math.maxInt(usize)); - try testing.expect(std.mem.indexOf(u8, out_asm, "square:") != null); - try testing.expect(std.mem.indexOf(u8, out_asm, "mov\teax, edi") != null); - try testing.expect(std.mem.indexOf(u8, out_asm, "imul\teax, edi") != null); -} - -fn testMissingOutputPath(zig_exe: []const u8, dir_path: []const u8) !void { - _ = try exec(dir_path, true, &[_][]const u8{ zig_exe, "init-exe" }); - const output_path = try fs.path.join(a, &[_][]const u8{ "does", "not", "exist", "foo.exe" }); - const output_arg = try std.fmt.allocPrint(a, "-femit-bin={s}", .{output_path}); - const source_path = try fs.path.join(a, &[_][]const u8{ "src", "main.zig" }); - const result = try exec(dir_path, false, &[_][]const u8{ zig_exe, "build-exe", source_path, output_arg }); - const s = std.fs.path.sep_str; - const expected: []const u8 = "error: unable to open output directory 'does" ++ s ++ "not" ++ s ++ "exist': FileNotFound\n"; - try testing.expectEqualStrings(expected, result.stderr); -} - -fn testZigFmt(zig_exe: []const u8, dir_path: []const u8) !void { - _ = try exec(dir_path, true, &[_][]const u8{ zig_exe, "init-exe" }); - - const unformatted_code = " // no reason for indent"; - - const fmt1_zig_path = try fs.path.join(a, &[_][]const u8{ dir_path, "fmt1.zig" }); - try fs.cwd().writeFile(fmt1_zig_path, unformatted_code); - - const run_result1 = try exec(dir_path, true, &[_][]const u8{ zig_exe, "fmt", fmt1_zig_path }); - // stderr should be file path + \n - try testing.expect(std.mem.startsWith(u8, run_result1.stdout, fmt1_zig_path)); - try testing.expect(run_result1.stdout.len == fmt1_zig_path.len + 1 and run_result1.stdout[run_result1.stdout.len - 1] == '\n'); - - const fmt2_zig_path = try fs.path.join(a, &[_][]const u8{ dir_path, "fmt2.zig" }); - try fs.cwd().writeFile(fmt2_zig_path, unformatted_code); - - const run_result2 = try exec(dir_path, true, &[_][]const u8{ zig_exe, "fmt", dir_path }); - // running it on the dir, only the new file should be changed - try testing.expect(std.mem.startsWith(u8, run_result2.stdout, fmt2_zig_path)); - try testing.expect(run_result2.stdout.len == fmt2_zig_path.len + 1 and run_result2.stdout[run_result2.stdout.len - 1] == '\n'); - - const run_result3 = try exec(dir_path, true, &[_][]const u8{ zig_exe, "fmt", dir_path }); - // both files have been formatted, nothing should change now - try testing.expect(run_result3.stdout.len == 0); - - // Check UTF-16 decoding - const fmt4_zig_path = try fs.path.join(a, &[_][]const u8{ dir_path, "fmt4.zig" }); - var unformatted_code_utf16 = "\xff\xfe \x00 \x00 \x00 \x00/\x00/\x00 \x00n\x00o\x00 \x00r\x00e\x00a\x00s\x00o\x00n\x00"; - try fs.cwd().writeFile(fmt4_zig_path, unformatted_code_utf16); - - const run_result4 = try exec(dir_path, true, &[_][]const u8{ zig_exe, "fmt", dir_path }); - try testing.expect(std.mem.startsWith(u8, run_result4.stdout, fmt4_zig_path)); - try testing.expect(run_result4.stdout.len == fmt4_zig_path.len + 1 and run_result4.stdout[run_result4.stdout.len - 1] == '\n'); -} diff --git a/test/tests.zig b/test/tests.zig index e21e652cba..665ad023fd 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -635,19 +635,189 @@ pub fn addCliTests(b: *std.Build, test_filter: ?[]const u8, optimize_modes: []co _ = optimize_modes; const step = b.step("test-cli", "Test the command line interface"); - const exe = b.addExecutable(.{ - .name = "test-cli", - .root_source_file = .{ .path = "test/cli.zig" }, - .target = .{}, - .optimize = .Debug, - }); - const run_cmd = exe.run(); - run_cmd.addArgs(&[_][]const u8{ - fs.realpathAlloc(b.allocator, b.zig_exe) catch @panic("OOM"), - b.pathFromRoot(b.cache_root.path orelse "."), - }); + { + // Test `zig init-lib`. + const tmp_path = b.makeTempPath(); + const init_lib = b.addSystemCommand(&.{ b.zig_exe, "init-lib" }); + init_lib.cwd = tmp_path; + init_lib.setName("zig init-lib"); + init_lib.expectStdOutEqual(""); + init_lib.expectStdErrEqual( + \\info: Created build.zig + \\info: Created src/main.zig + \\info: Next, try `zig build --help` or `zig build test` + \\ + ); + + const run_test = b.addSystemCommand(&.{ b.zig_exe, "build", "test" }); + run_test.cwd = tmp_path; + run_test.setName("zig build test"); + run_test.expectStdOutEqual(""); + run_test.step.dependOn(&init_lib.step); + + const cleanup = b.addRemoveDirTree(tmp_path); + cleanup.step.dependOn(&run_test.step); + + step.dependOn(&cleanup.step); + } + + { + // Test `zig init-exe`. + const tmp_path = b.makeTempPath(); + const init_exe = b.addSystemCommand(&.{ b.zig_exe, "init-exe" }); + init_exe.cwd = tmp_path; + init_exe.setName("zig init-exe"); + init_exe.expectStdOutEqual(""); + init_exe.expectStdErrEqual( + \\info: Created build.zig + \\info: Created src/main.zig + \\info: Next, try `zig build --help` or `zig build run` + \\ + ); + + // Test missing output path. + const s = std.fs.path.sep_str; + const bad_out_arg = "-femit-bin=does" ++ s ++ "not" ++ s ++ "exist" ++ s ++ "foo.exe"; + const ok_src_arg = "src" ++ s ++ "main.zig"; + const expected = "error: unable to open output directory 'does" ++ s ++ "not" ++ s ++ "exist': FileNotFound\n"; + const run_bad = b.addSystemCommand(&.{ b.zig_exe, "build-exe", ok_src_arg, bad_out_arg }); + run_bad.setName("zig build-exe error message for bad -femit-bin arg"); + run_bad.expectExitCode(1); + run_bad.expectStdErrEqual(expected); + run_bad.expectStdOutEqual(""); + run_bad.step.dependOn(&init_exe.step); + + const run_test = b.addSystemCommand(&.{ b.zig_exe, "build", "test" }); + run_test.cwd = tmp_path; + run_test.setName("zig build test"); + run_test.expectStdOutEqual(""); + run_test.step.dependOn(&init_exe.step); + + const run_run = b.addSystemCommand(&.{ b.zig_exe, "build", "run" }); + run_run.cwd = tmp_path; + run_run.setName("zig build run"); + run_run.expectStdOutEqual("Run `zig build test` to run the tests.\n"); + run_run.expectStdErrEqual("All your codebase are belong to us.\n"); + run_run.step.dependOn(&init_exe.step); + + const cleanup = b.addRemoveDirTree(tmp_path); + cleanup.step.dependOn(&run_test.step); + cleanup.step.dependOn(&run_run.step); + cleanup.step.dependOn(&run_bad.step); + + step.dependOn(&cleanup.step); + } + + // Test Godbolt API + if (builtin.os.tag == .linux and builtin.cpu.arch == .x86_64) { + const tmp_path = b.makeTempPath(); + + const writefile = b.addWriteFile("example.zig", + \\// Type your code here, or load an example. + \\export fn square(num: i32) i32 { + \\ return num * num; + \\} + \\extern fn zig_panic() noreturn; + \\pub fn panic(msg: []const u8, error_return_trace: ?*@import("std").builtin.StackTrace, _: ?usize) noreturn { + \\ _ = msg; + \\ _ = error_return_trace; + \\ zig_panic(); + \\} + ); + + // This is intended to be the exact CLI usage used by godbolt.org. + const run = b.addSystemCommand(&.{ + b.zig_exe, "build-obj", + "--cache-dir", tmp_path, + "--name", "example", + "-fno-emit-bin", "-fno-emit-h", + "-fstrip", "-OReleaseFast", + }); + run.addFileSourceArg(writefile.getFileSource("example.zig").?); + const example_s = run.addPrefixedOutputFileArg("-femit-asm=", "example.s"); + + const checkfile = b.addCheckFile(example_s, .{ + .expected_matches = &.{ + "square:", + "mov\teax, edi", + "imul\teax, edi", + }, + }); + checkfile.setName("check godbolt.org CLI usage generating valid asm"); + + const cleanup = b.addRemoveDirTree(tmp_path); + cleanup.step.dependOn(&checkfile.step); + + step.dependOn(&cleanup.step); + } + + { + // Test `zig fmt`. + // This test must use a temporary directory rather than a cache + // directory because this test will be mutating the files. The cache + // system relies on cache directories being mutated only by their + // owners. + const tmp_path = b.makeTempPath(); + const unformatted_code = " // no reason for indent"; + const s = std.fs.path.sep_str; + + var dir = fs.cwd().openDir(tmp_path, .{}) catch @panic("unhandled"); + defer dir.close(); + dir.writeFile("fmt1.zig", unformatted_code) catch @panic("unhandled"); + dir.writeFile("fmt2.zig", unformatted_code) catch @panic("unhandled"); + + // Test zig fmt affecting only the appropriate files. + const run1 = b.addSystemCommand(&.{ b.zig_exe, "fmt", "fmt1.zig" }); + run1.setName("run zig fmt one file"); + run1.cwd = tmp_path; + run1.has_side_effects = true; + // stdout should be file path + \n + run1.expectStdOutEqual("fmt1.zig\n"); + + // running it on the dir, only the new file should be changed + const run2 = b.addSystemCommand(&.{ b.zig_exe, "fmt", "." }); + run2.setName("run zig fmt the directory"); + run2.cwd = tmp_path; + run2.has_side_effects = true; + run2.expectStdOutEqual("." ++ s ++ "fmt2.zig\n"); + run2.step.dependOn(&run1.step); + + // both files have been formatted, nothing should change now + const run3 = b.addSystemCommand(&.{ b.zig_exe, "fmt", "." }); + run3.setName("run zig fmt with nothing to do"); + run3.cwd = tmp_path; + run3.has_side_effects = true; + run3.expectStdOutEqual(""); + run3.step.dependOn(&run2.step); + + const unformatted_code_utf16 = "\xff\xfe \x00 \x00 \x00 \x00/\x00/\x00 \x00n\x00o\x00 \x00r\x00e\x00a\x00s\x00o\x00n\x00"; + const fmt4_path = fs.path.join(b.allocator, &.{ tmp_path, "fmt4.zig" }) catch @panic("OOM"); + const write4 = b.addWriteFiles(); + write4.addBytesToSource(unformatted_code_utf16, fmt4_path); + write4.step.dependOn(&run3.step); + + // Test `zig fmt` handling UTF-16 decoding. + const run4 = b.addSystemCommand(&.{ b.zig_exe, "fmt", "." }); + run4.setName("run zig fmt convert UTF-16 to UTF-8"); + run4.cwd = tmp_path; + run4.has_side_effects = true; + run4.expectStdOutEqual("." ++ s ++ "fmt4.zig\n"); + run4.step.dependOn(&write4.step); + + // TODO change this to an exact match + const check4 = b.addCheckFile(.{ .path = fmt4_path }, .{ + .expected_matches = &.{ + "// no reason", + }, + }); + check4.step.dependOn(&run4.step); + + const cleanup = b.addRemoveDirTree(tmp_path); + cleanup.step.dependOn(&check4.step); + + step.dependOn(&cleanup.step); + } - step.dependOn(&run_cmd.step); return step; } From 8b871ae27584c2433683ca064dbd3e7127d2a184 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 6 Mar 2023 22:57:53 -0700 Subject: [PATCH 210/294] re-enable C ABI tests These were mostly already using the correct build API. I cleaned up the code a bit and unconditionally disabled LTO for these tests since that actually tests the intended behavior better. --- build.zig | 2 +- test/tests.zig | 56 +++++++++++++++++++++++++------------------------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/build.zig b/build.zig index 7c22016bb7..b97ea3c24c 100644 --- a/build.zig +++ b/build.zig @@ -460,7 +460,7 @@ pub fn build(b: *std.Build) !void { // b.enable_wine, // enable_symlinks_windows, //)); - //test_step.dependOn(tests.addCAbiTests(b, skip_non_native, skip_release)); + test_step.dependOn(tests.addCAbiTests(b, skip_non_native, skip_release)); //test_step.dependOn(tests.addLinkTests(b, test_filter, optimization_modes, enable_macos_sdk, skip_stage2_tests, enable_symlinks_windows)); test_step.dependOn(tests.addStackTraceTests(b, test_filter, optimization_modes)); test_step.dependOn(tests.addCliTests(b, test_filter, optimization_modes)); diff --git a/test/tests.zig b/test/tests.zig index 665ad023fd..1e99e5c2a8 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -985,37 +985,37 @@ pub fn addCAbiTests(b: *std.Build, skip_non_native: bool, skip_release: bool) *S const optimize_modes: [2]OptimizeMode = .{ .Debug, .ReleaseFast }; - for (optimize_modes[0 .. @as(u8, 1) + @boolToInt(!skip_release)]) |optimize_mode| for (c_abi_targets) |c_abi_target| { - if (skip_non_native and !c_abi_target.isNative()) - continue; + for (optimize_modes) |optimize_mode| { + if (optimize_mode != .Debug and skip_release) continue; - const test_step = b.addTest(.{ - .root_source_file = .{ .path = "test/c_abi/main.zig" }, - .optimize = optimize_mode, - .target = c_abi_target, - }); - if (c_abi_target.abi != null and c_abi_target.abi.?.isMusl()) { - // TODO NativeTargetInfo insists on dynamically linking musl - // for some reason? - test_step.target_info.dynamic_linker.max_byte = null; - } - test_step.linkLibC(); - test_step.addCSourceFile("test/c_abi/cfuncs.c", &.{"-std=c99"}); + for (c_abi_targets) |c_abi_target| { + if (skip_non_native and !c_abi_target.isNative()) continue; - if (c_abi_target.isWindows() and (c_abi_target.getCpuArch() == .x86 or builtin.target.os.tag == .linux)) { - // LTO currently incorrectly strips stdcall name-mangled functions - // LLD crashes in LTO here when cross compiling for windows on linux + const test_step = b.addTest(.{ + .root_source_file = .{ .path = "test/c_abi/main.zig" }, + .optimize = optimize_mode, + .target = c_abi_target, + }); + if (c_abi_target.abi != null and c_abi_target.abi.?.isMusl()) { + // TODO NativeTargetInfo insists on dynamically linking musl + // for some reason? + test_step.target_info.dynamic_linker.max_byte = null; + } + test_step.linkLibC(); + test_step.addCSourceFile("test/c_abi/cfuncs.c", &.{"-std=c99"}); + // This test is intentionally trying to check if the external ABI is + // done properly. LTO would be a hindrance to this. test_step.want_lto = false; + + const triple_prefix = c_abi_target.zigTriple(b.allocator) catch @panic("OOM"); + test_step.setNamePrefix(b.fmt("{s}-{s}-{s} ", .{ + "test-c-abi", + triple_prefix, + @tagName(optimize_mode), + })); + + step.dependOn(&test_step.step); } - - const triple_prefix = c_abi_target.zigTriple(b.allocator) catch @panic("OOM"); - test_step.setNamePrefix(b.fmt("{s}-{s}-{s} ", .{ - "test-c-abi", - triple_prefix, - @tagName(optimize_mode), - })); - - step.dependOn(&test_step.step); - }; + } return step; } From 263aaf0e66a1e2cdf5cebdbfc5e2761dc5a67181 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 6 Mar 2023 23:00:32 -0700 Subject: [PATCH 211/294] re-enable asm-and-link tests These already looked pretty good. I deleted two unnecessary calls to expectStdErrEqual. --- build.zig | 2 +- test/src/CompareOutput.zig | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/build.zig b/build.zig index b97ea3c24c..b494a4ad7a 100644 --- a/build.zig +++ b/build.zig @@ -464,7 +464,7 @@ pub fn build(b: *std.Build) !void { //test_step.dependOn(tests.addLinkTests(b, test_filter, optimization_modes, enable_macos_sdk, skip_stage2_tests, enable_symlinks_windows)); test_step.dependOn(tests.addStackTraceTests(b, test_filter, optimization_modes)); test_step.dependOn(tests.addCliTests(b, test_filter, optimization_modes)); - //test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, optimization_modes)); + test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, optimization_modes)); test_step.dependOn(tests.addTranslateCTests(b, test_filter)); if (!skip_run_translated_c) { test_step.dependOn(tests.addRunTranslatedCTests(b, test_filter, target)); diff --git a/test/src/CompareOutput.zig b/test/src/CompareOutput.zig index 68cf942684..854bd11f9c 100644 --- a/test/src/CompareOutput.zig +++ b/test/src/CompareOutput.zig @@ -104,7 +104,6 @@ pub fn addCase(self: *CompareOutput, case: TestCase) void { const run = exe.run(); run.setName(annotated_case_name); run.addArgs(case.cli_args); - run.expectStdErrEqual(""); run.expectStdOutEqual(case.expected_output); self.step.dependOn(&run.step); @@ -132,7 +131,6 @@ pub fn addCase(self: *CompareOutput, case: TestCase) void { const run = exe.run(); run.setName(annotated_case_name); run.addArgs(case.cli_args); - run.expectStdErrEqual(""); run.expectStdOutEqual(case.expected_output); self.step.dependOn(&run.step); From 8510f9e2bc1c1da6b3d12a07ae613cb3ca888d63 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 7 Mar 2023 00:38:59 -0700 Subject: [PATCH 212/294] std.Build: add addAnonymousDependency This is for bypassing the package manager and directly depending on another package via the build system. For this to work the anonymous package must be found on the file system relative to the current package's build.zig. --- lib/std/Build.zig | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/lib/std/Build.zig b/lib/std/Build.zig index 68e80435f8..c091079b38 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -1457,7 +1457,26 @@ pub fn dependency(b: *Build, name: []const u8, args: anytype) *Dependency { process.exit(1); } -fn dependencyInner( +pub fn anonymousDependency( + b: *Build, + /// The path to the directory containing the dependency's build.zig file, + /// relative to the current package's build.zig. + relative_build_root: []const u8, + /// A direct `@import` of the build.zig of the dependency. + comptime build_zig: type, + args: anytype, +) *Dependency { + const arena = b.allocator; + const build_root = b.build_root.join(arena, &.{relative_build_root}) catch @panic("OOM"); + const name = arena.dupe(u8, relative_build_root) catch @panic("OOM"); + for (name) |*byte| switch (byte.*) { + '/', '\\' => byte.* = '.', + else => continue, + }; + return dependencyInner(b, name, build_root, build_zig, args); +} + +pub fn dependencyInner( b: *Build, name: []const u8, build_root_string: []const u8, From 3b069907305497af5ef6cdb1d6adddd05636aa51 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 7 Mar 2023 00:40:32 -0700 Subject: [PATCH 213/294] std.Build.CompileStep: tweak the default step name --- lib/std/Build/CompileStep.zig | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/std/Build/CompileStep.zig b/lib/std/Build/CompileStep.zig index df9abfbc6d..9b9fc02e74 100644 --- a/lib/std/Build/CompileStep.zig +++ b/lib/std/Build/CompileStep.zig @@ -310,8 +310,20 @@ pub fn create(owner: *std.Build, options: Options) *CompileStep { panic("invalid name: '{s}'. It looks like a file path, but it is supposed to be the library or application name.", .{name}); } - const step_name = owner.fmt("compile {s} {s} {s}", .{ - name, + // Avoid the common case of the step name looking like "zig test test". + const name_adjusted = if (options.kind == .@"test" and mem.eql(u8, name, "test")) + "" + else + owner.fmt("{s} ", .{name}); + + const step_name = owner.fmt("{s} {s}{s} {s}", .{ + switch (options.kind) { + .exe => "zig build-exe", + .lib => "zig build-lib", + .obj => "zig build-obj", + .test_exe, .@"test" => "zig test", + }, + name_adjusted, @tagName(options.optimize), options.target.zigTriple(owner.allocator) catch @panic("OOM"), }); From e122cd6312606068eb14987ed4d7527341484d26 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 7 Mar 2023 00:40:55 -0700 Subject: [PATCH 214/294] new linker test harness It's simpler and it takes advantage of `std.Build.addAnonymousDependency`, which has a number of benefits, including concurrenc and preventing extra zig-cache and zig-out directories being created. 4 tests are ported over as an example. --- build.zig | 9 +- test/link.zig | 429 +++++++++--------- test/link/bss/build.zig | 6 +- test/link/bss/main.zig | 2 +- test/link/common_symbols/build.zig | 11 +- test/link/common_symbols_alignment/build.zig | 15 +- .../interdependent_static_c_libs/build.zig | 17 +- test/link/static_lib_as_system_lib/build.zig | 19 +- test/tests.zig | 58 +-- 9 files changed, 287 insertions(+), 279 deletions(-) diff --git a/build.zig b/build.zig index b494a4ad7a..83aabb6c41 100644 --- a/build.zig +++ b/build.zig @@ -442,8 +442,6 @@ pub fn build(b: *std.Build) !void { .skip_stage2 = true, // TODO get all these passing })); - _ = enable_symlinks_windows; - _ = enable_macos_sdk; test_step.dependOn(tests.addCompareOutputTests(b, test_filter, optimization_modes)); //test_step.dependOn(tests.addStandaloneTests( // b, @@ -453,15 +451,10 @@ pub fn build(b: *std.Build) !void { // enable_macos_sdk, // target, // skip_stage2_tests, - // b.enable_darling, - // b.enable_qemu, - // b.enable_rosetta, - // b.enable_wasmtime, - // b.enable_wine, // enable_symlinks_windows, //)); test_step.dependOn(tests.addCAbiTests(b, skip_non_native, skip_release)); - //test_step.dependOn(tests.addLinkTests(b, test_filter, optimization_modes, enable_macos_sdk, skip_stage2_tests, enable_symlinks_windows)); + test_step.dependOn(tests.addLinkTests(b, test_filter, optimization_modes, enable_macos_sdk, skip_stage2_tests, enable_symlinks_windows)); test_step.dependOn(tests.addStackTraceTests(b, test_filter, optimization_modes)); test_step.dependOn(tests.addCliTests(b, test_filter, optimization_modes)); test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, optimization_modes)); diff --git a/test/link.zig b/test/link.zig index c787e8b1ae..d6d42a53e8 100644 --- a/test/link.zig +++ b/test/link.zig @@ -1,213 +1,220 @@ +pub const Case = struct { + build_root: []const u8, + import: type, +}; + +pub const cases = [_]Case{ + .{ + .build_root = "test/link/bss", + .import = @import("link/bss/build.zig"), + }, + .{ + .build_root = "test/link/common_symbols", + .import = @import("link/common_symbols/build.zig"), + }, + .{ + .build_root = "test/link/common_symbols_alignment", + .import = @import("link/common_symbols_alignment/build.zig"), + }, + .{ + .build_root = "test/link/interdependent_static_c_libs", + .import = @import("link/interdependent_static_c_libs/build.zig"), + }, + //.{ + // .build_root = "test/link/static_lib_as_system_lib", + // .import = @import("link/static_lib_as_system_lib/build.zig"), + //}, +}; + +//pub fn addCases(cases: *Standalone) void { +// addWasmCases(cases); +// addMachOCases(cases); +//} +// +//fn addWasmCases(cases: *Standalone) void { +// cases.addBuildFile("test/link/wasm/archive/build.zig", .{ +// .build_modes = true, +// .requires_stage2 = true, +// }); +// +// cases.addBuildFile("test/link/wasm/basic-features/build.zig", .{ +// .requires_stage2 = true, +// }); +// +// cases.addBuildFile("test/link/wasm/bss/build.zig", .{ +// .build_modes = false, +// .requires_stage2 = true, +// }); +// +// cases.addBuildFile("test/link/wasm/export/build.zig", .{ +// .build_modes = true, +// .requires_stage2 = true, +// }); +// +// // TODO: Fix open handle in wasm-linker refraining rename from working on Windows. +// if (builtin.os.tag != .windows) { +// cases.addBuildFile("test/link/wasm/export-data/build.zig", .{}); +// } +// +// cases.addBuildFile("test/link/wasm/extern/build.zig", .{ +// .build_modes = true, +// .requires_stage2 = true, +// .use_emulation = true, +// }); +// +// cases.addBuildFile("test/link/wasm/extern-mangle/build.zig", .{ +// .build_modes = true, +// .requires_stage2 = true, +// }); +// +// cases.addBuildFile("test/link/wasm/function-table/build.zig", .{ +// .build_modes = true, +// .requires_stage2 = true, +// }); +// +// cases.addBuildFile("test/link/wasm/infer-features/build.zig", .{ +// .requires_stage2 = true, +// }); +// +// cases.addBuildFile("test/link/wasm/producers/build.zig", .{ +// .build_modes = true, +// .requires_stage2 = true, +// }); +// +// cases.addBuildFile("test/link/wasm/segments/build.zig", .{ +// .build_modes = true, +// .requires_stage2 = true, +// }); +// +// cases.addBuildFile("test/link/wasm/stack_pointer/build.zig", .{ +// .build_modes = true, +// .requires_stage2 = true, +// }); +// +// cases.addBuildFile("test/link/wasm/type/build.zig", .{ +// .build_modes = true, +// .requires_stage2 = true, +// }); +//} +// +//fn addMachOCases(cases: *tests.StandaloneContext) void { +// cases.addBuildFile("test/link/macho/bugs/13056/build.zig", .{ +// .build_modes = true, +// .requires_macos_sdk = true, +// .requires_symlinks = true, +// }); +// +// cases.addBuildFile("test/link/macho/bugs/13457/build.zig", .{ +// .build_modes = true, +// .requires_symlinks = true, +// }); +// +// cases.addBuildFile("test/link/macho/dead_strip/build.zig", .{ +// .build_modes = false, +// .requires_symlinks = true, +// }); +// +// cases.addBuildFile("test/link/macho/dead_strip_dylibs/build.zig", .{ +// .build_modes = true, +// .requires_macos_sdk = true, +// .requires_symlinks = true, +// }); +// +// cases.addBuildFile("test/link/macho/dylib/build.zig", .{ +// .build_modes = true, +// .requires_symlinks = true, +// }); +// +// cases.addBuildFile("test/link/macho/empty/build.zig", .{ +// .build_modes = true, +// .requires_symlinks = true, +// }); +// +// cases.addBuildFile("test/link/macho/entry/build.zig", .{ +// .build_modes = true, +// .requires_symlinks = true, +// }); +// +// cases.addBuildFile("test/link/macho/headerpad/build.zig", .{ +// .build_modes = true, +// .requires_macos_sdk = true, +// .requires_symlinks = true, +// }); +// +// cases.addBuildFile("test/link/macho/linksection/build.zig", .{ +// .build_modes = true, +// .requires_symlinks = true, +// }); +// +// cases.addBuildFile("test/link/macho/needed_framework/build.zig", .{ +// .build_modes = true, +// .requires_macos_sdk = true, +// .requires_symlinks = true, +// }); +// +// cases.addBuildFile("test/link/macho/needed_library/build.zig", .{ +// .build_modes = true, +// .requires_symlinks = true, +// }); +// +// cases.addBuildFile("test/link/macho/objc/build.zig", .{ +// .build_modes = true, +// .requires_macos_sdk = true, +// .requires_symlinks = true, +// }); +// +// cases.addBuildFile("test/link/macho/objcpp/build.zig", .{ +// .build_modes = true, +// .requires_macos_sdk = true, +// .requires_symlinks = true, +// }); +// +// cases.addBuildFile("test/link/macho/pagezero/build.zig", .{ +// .build_modes = false, +// .requires_symlinks = true, +// }); +// +// cases.addBuildFile("test/link/macho/search_strategy/build.zig", .{ +// .build_modes = true, +// .requires_symlinks = true, +// }); +// +// cases.addBuildFile("test/link/macho/stack_size/build.zig", .{ +// .build_modes = true, +// .requires_symlinks = true, +// }); +// +// cases.addBuildFile("test/link/macho/strict_validation/build.zig", .{ +// .build_modes = true, +// .requires_symlinks = true, +// }); +// +// cases.addBuildFile("test/link/macho/tls/build.zig", .{ +// .build_modes = true, +// .requires_symlinks = true, +// }); +// +// cases.addBuildFile("test/link/macho/unwind_info/build.zig", .{ +// .build_modes = true, +// .requires_symlinks = true, +// }); +// +// cases.addBuildFile("test/link/macho/uuid/build.zig", .{ +// .build_modes = false, +// .requires_symlinks = true, +// }); +// +// cases.addBuildFile("test/link/macho/weak_library/build.zig", .{ +// .build_modes = true, +// .requires_symlinks = true, +// }); +// +// cases.addBuildFile("test/link/macho/weak_framework/build.zig", .{ +// .build_modes = true, +// .requires_macos_sdk = true, +// .requires_symlinks = true, +// }); +//} + const std = @import("std"); const builtin = @import("builtin"); -const tests = @import("tests.zig"); - -pub fn addCases(cases: *tests.StandaloneContext) void { - cases.addBuildFile("test/link/bss/build.zig", .{ - .build_modes = false, // we only guarantee zerofill for undefined in Debug - }); - - cases.addBuildFile("test/link/common_symbols/build.zig", .{ - .build_modes = true, - }); - - cases.addBuildFile("test/link/common_symbols_alignment/build.zig", .{ - .build_modes = true, - }); - - cases.addBuildFile("test/link/interdependent_static_c_libs/build.zig", .{ - .build_modes = true, - }); - - cases.addBuildFile("test/link/static_lib_as_system_lib/build.zig", .{ - .build_modes = true, - }); - - addWasmCases(cases); - addMachOCases(cases); -} - -fn addWasmCases(cases: *tests.StandaloneContext) void { - cases.addBuildFile("test/link/wasm/archive/build.zig", .{ - .build_modes = true, - .requires_stage2 = true, - }); - - cases.addBuildFile("test/link/wasm/basic-features/build.zig", .{ - .requires_stage2 = true, - }); - - cases.addBuildFile("test/link/wasm/bss/build.zig", .{ - .build_modes = false, - .requires_stage2 = true, - }); - - cases.addBuildFile("test/link/wasm/export/build.zig", .{ - .build_modes = true, - .requires_stage2 = true, - }); - - // TODO: Fix open handle in wasm-linker refraining rename from working on Windows. - if (builtin.os.tag != .windows) { - cases.addBuildFile("test/link/wasm/export-data/build.zig", .{}); - } - - cases.addBuildFile("test/link/wasm/extern/build.zig", .{ - .build_modes = true, - .requires_stage2 = true, - .use_emulation = true, - }); - - cases.addBuildFile("test/link/wasm/extern-mangle/build.zig", .{ - .build_modes = true, - .requires_stage2 = true, - }); - - cases.addBuildFile("test/link/wasm/function-table/build.zig", .{ - .build_modes = true, - .requires_stage2 = true, - }); - - cases.addBuildFile("test/link/wasm/infer-features/build.zig", .{ - .requires_stage2 = true, - }); - - cases.addBuildFile("test/link/wasm/producers/build.zig", .{ - .build_modes = true, - .requires_stage2 = true, - }); - - cases.addBuildFile("test/link/wasm/segments/build.zig", .{ - .build_modes = true, - .requires_stage2 = true, - }); - - cases.addBuildFile("test/link/wasm/stack_pointer/build.zig", .{ - .build_modes = true, - .requires_stage2 = true, - }); - - cases.addBuildFile("test/link/wasm/type/build.zig", .{ - .build_modes = true, - .requires_stage2 = true, - }); -} - -fn addMachOCases(cases: *tests.StandaloneContext) void { - cases.addBuildFile("test/link/macho/bugs/13056/build.zig", .{ - .build_modes = true, - .requires_macos_sdk = true, - .requires_symlinks = true, - }); - - cases.addBuildFile("test/link/macho/bugs/13457/build.zig", .{ - .build_modes = true, - .requires_symlinks = true, - }); - - cases.addBuildFile("test/link/macho/dead_strip/build.zig", .{ - .build_modes = false, - .requires_symlinks = true, - }); - - cases.addBuildFile("test/link/macho/dead_strip_dylibs/build.zig", .{ - .build_modes = true, - .requires_macos_sdk = true, - .requires_symlinks = true, - }); - - cases.addBuildFile("test/link/macho/dylib/build.zig", .{ - .build_modes = true, - .requires_symlinks = true, - }); - - cases.addBuildFile("test/link/macho/empty/build.zig", .{ - .build_modes = true, - .requires_symlinks = true, - }); - - cases.addBuildFile("test/link/macho/entry/build.zig", .{ - .build_modes = true, - .requires_symlinks = true, - }); - - cases.addBuildFile("test/link/macho/headerpad/build.zig", .{ - .build_modes = true, - .requires_macos_sdk = true, - .requires_symlinks = true, - }); - - cases.addBuildFile("test/link/macho/linksection/build.zig", .{ - .build_modes = true, - .requires_symlinks = true, - }); - - cases.addBuildFile("test/link/macho/needed_framework/build.zig", .{ - .build_modes = true, - .requires_macos_sdk = true, - .requires_symlinks = true, - }); - - cases.addBuildFile("test/link/macho/needed_library/build.zig", .{ - .build_modes = true, - .requires_symlinks = true, - }); - - cases.addBuildFile("test/link/macho/objc/build.zig", .{ - .build_modes = true, - .requires_macos_sdk = true, - .requires_symlinks = true, - }); - - cases.addBuildFile("test/link/macho/objcpp/build.zig", .{ - .build_modes = true, - .requires_macos_sdk = true, - .requires_symlinks = true, - }); - - cases.addBuildFile("test/link/macho/pagezero/build.zig", .{ - .build_modes = false, - .requires_symlinks = true, - }); - - cases.addBuildFile("test/link/macho/search_strategy/build.zig", .{ - .build_modes = true, - .requires_symlinks = true, - }); - - cases.addBuildFile("test/link/macho/stack_size/build.zig", .{ - .build_modes = true, - .requires_symlinks = true, - }); - - cases.addBuildFile("test/link/macho/strict_validation/build.zig", .{ - .build_modes = true, - .requires_symlinks = true, - }); - - cases.addBuildFile("test/link/macho/tls/build.zig", .{ - .build_modes = true, - .requires_symlinks = true, - }); - - cases.addBuildFile("test/link/macho/unwind_info/build.zig", .{ - .build_modes = true, - .requires_symlinks = true, - }); - - cases.addBuildFile("test/link/macho/uuid/build.zig", .{ - .build_modes = false, - .requires_symlinks = true, - }); - - cases.addBuildFile("test/link/macho/weak_library/build.zig", .{ - .build_modes = true, - .requires_symlinks = true, - }); - - cases.addBuildFile("test/link/macho/weak_framework/build.zig", .{ - .build_modes = true, - .requires_macos_sdk = true, - .requires_symlinks = true, - }); -} diff --git a/test/link/bss/build.zig b/test/link/bss/build.zig index 0df9c1d323..86600b58f5 100644 --- a/test/link/bss/build.zig +++ b/test/link/bss/build.zig @@ -1,17 +1,17 @@ const std = @import("std"); pub fn build(b: *std.Build) void { - const optimize = b.standardOptimizeOption(.{}); const test_step = b.step("test", "Test"); + b.default_step = test_step; const exe = b.addExecutable(.{ .name = "bss", .root_source_file = .{ .path = "main.zig" }, - .optimize = optimize, + .optimize = .Debug, }); - b.default_step.dependOn(&exe.step); const run = exe.run(); run.expectStdOutEqual("0, 1, 0\n"); + test_step.dependOn(&run.step); } diff --git a/test/link/bss/main.zig b/test/link/bss/main.zig index c901f0bb27..d2ecffe982 100644 --- a/test/link/bss/main.zig +++ b/test/link/bss/main.zig @@ -1,7 +1,7 @@ const std = @import("std"); // Stress test zerofill layout -var buffer: [0x1000000]u64 = undefined; +var buffer: [0x1000000]u64 = [1]u64{0} ** 0x1000000; pub fn main() anyerror!void { buffer[0x10] = 1; diff --git a/test/link/common_symbols/build.zig b/test/link/common_symbols/build.zig index ee9dd94ebd..e3c302f0f7 100644 --- a/test/link/common_symbols/build.zig +++ b/test/link/common_symbols/build.zig @@ -1,8 +1,16 @@ const std = @import("std"); pub fn build(b: *std.Build) void { - const optimize = b.standardOptimizeOption(.{}); + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + add(b, test_step, .Debug); + add(b, test_step, .ReleaseFast); + add(b, test_step, .ReleaseSmall); + add(b, test_step, .ReleaseSafe); +} + +fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { const lib_a = b.addStaticLibrary(.{ .name = "a", .optimize = optimize, @@ -16,6 +24,5 @@ pub fn build(b: *std.Build) void { }); test_exe.linkLibrary(lib_a); - const test_step = b.step("test", "Test it"); test_step.dependOn(&test_exe.step); } diff --git a/test/link/common_symbols_alignment/build.zig b/test/link/common_symbols_alignment/build.zig index f6efdc784b..7d1d813447 100644 --- a/test/link/common_symbols_alignment/build.zig +++ b/test/link/common_symbols_alignment/build.zig @@ -1,23 +1,28 @@ const std = @import("std"); pub fn build(b: *std.Build) void { - const optimize = b.standardOptimizeOption(.{}); - const target = b.standardTargetOptions(.{}); + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + add(b, test_step, .Debug); + add(b, test_step, .ReleaseFast); + add(b, test_step, .ReleaseSmall); + add(b, test_step, .ReleaseSafe); +} + +fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { const lib_a = b.addStaticLibrary(.{ .name = "a", .optimize = optimize, - .target = target, + .target = .{}, }); lib_a.addCSourceFiles(&.{"a.c"}, &.{"-fcommon"}); const test_exe = b.addTest(.{ .root_source_file = .{ .path = "main.zig" }, .optimize = optimize, - .target = target, }); test_exe.linkLibrary(lib_a); - const test_step = b.step("test", "Test it"); test_step.dependOn(&test_exe.step); } diff --git a/test/link/interdependent_static_c_libs/build.zig b/test/link/interdependent_static_c_libs/build.zig index d8962a8e08..c4118c1ca4 100644 --- a/test/link/interdependent_static_c_libs/build.zig +++ b/test/link/interdependent_static_c_libs/build.zig @@ -1,13 +1,20 @@ const std = @import("std"); pub fn build(b: *std.Build) void { - const optimize = b.standardOptimizeOption(.{}); - const target = b.standardTargetOptions(.{}); + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + add(b, test_step, .Debug); + add(b, test_step, .ReleaseFast); + add(b, test_step, .ReleaseSmall); + add(b, test_step, .ReleaseSafe); +} + +fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { const lib_a = b.addStaticLibrary(.{ .name = "a", .optimize = optimize, - .target = target, + .target = .{}, }); lib_a.addCSourceFile("a.c", &[_][]const u8{}); lib_a.addIncludePath("."); @@ -15,7 +22,7 @@ pub fn build(b: *std.Build) void { const lib_b = b.addStaticLibrary(.{ .name = "b", .optimize = optimize, - .target = target, + .target = .{}, }); lib_b.addCSourceFile("b.c", &[_][]const u8{}); lib_b.addIncludePath("."); @@ -23,12 +30,10 @@ pub fn build(b: *std.Build) void { const test_exe = b.addTest(.{ .root_source_file = .{ .path = "main.zig" }, .optimize = optimize, - .target = target, }); test_exe.linkLibrary(lib_a); test_exe.linkLibrary(lib_b); test_exe.addIncludePath("."); - const test_step = b.step("test", "Test it"); test_step.dependOn(&test_exe.step); } diff --git a/test/link/static_lib_as_system_lib/build.zig b/test/link/static_lib_as_system_lib/build.zig index b6cf32d711..1957d2e134 100644 --- a/test/link/static_lib_as_system_lib/build.zig +++ b/test/link/static_lib_as_system_lib/build.zig @@ -1,13 +1,20 @@ const std = @import("std"); pub fn build(b: *std.Build) void { - const optimize = b.standardOptimizeOption(.{}); - const target = b.standardTargetOptions(.{}); + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + add(b, test_step, .Debug); + add(b, test_step, .ReleaseFast); + add(b, test_step, .ReleaseSmall); + add(b, test_step, .ReleaseSafe); +} + +fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { const lib_a = b.addStaticLibrary(.{ .name = "a", .optimize = optimize, - .target = target, + .target = .{}, }); lib_a.addCSourceFile("a.c", &[_][]const u8{}); lib_a.addIncludePath("."); @@ -16,14 +23,12 @@ pub fn build(b: *std.Build) void { const test_exe = b.addTest(.{ .root_source_file = .{ .path = "main.zig" }, .optimize = optimize, - .target = target, + .target = .{}, }); test_exe.linkSystemLibrary("a"); // force linking liba.a as -la test_exe.addSystemIncludePath("."); - const search_path = std.fs.path.join(b.allocator, &[_][]const u8{ b.install_path, "lib" }) catch unreachable; + const search_path = std.fs.path.join(b.allocator, &[_][]const u8{ b.install_path, "lib" }) catch @panic("OOM"); test_exe.addLibraryPath(search_path); - const test_step = b.step("test", "Test it"); - test_step.dependOn(b.getInstallStep()); test_step.dependOn(&test_exe.step); } diff --git a/test/tests.zig b/test/tests.zig index 1e99e5c2a8..d5ee83b447 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -1,16 +1,9 @@ const std = @import("std"); const builtin = @import("builtin"); -const debug = std.debug; +const assert = std.debug.assert; const CrossTarget = std.zig.CrossTarget; -const io = std.io; -const fs = std.fs; const mem = std.mem; -const fmt = std.fmt; -const ArrayList = std.ArrayList; const OptimizeMode = std.builtin.OptimizeMode; -const CompileStep = std.Build.CompileStep; -const Allocator = mem.Allocator; -const ExecError = std.Build.ExecError; const Step = std.Build.Step; // Cases @@ -574,15 +567,10 @@ pub fn addStandaloneTests( enable_macos_sdk: bool, target: std.zig.CrossTarget, omit_stage2: bool, - enable_darling: bool, - enable_qemu: bool, - enable_rosetta: bool, - enable_wasmtime: bool, - enable_wine: bool, enable_symlinks_windows: bool, ) *Step { const cases = b.allocator.create(StandaloneContext) catch @panic("OOM"); - cases.* = StandaloneContext{ + cases.* = .{ .b = b, .step = b.step("test-standalone", "Run the standalone tests"), .test_index = 0, @@ -592,11 +580,6 @@ pub fn addStandaloneTests( .enable_macos_sdk = enable_macos_sdk, .target = target, .omit_stage2 = omit_stage2, - .enable_darling = enable_darling, - .enable_qemu = enable_qemu, - .enable_rosetta = enable_rosetta, - .enable_wasmtime = enable_wasmtime, - .enable_wine = enable_wine, .enable_symlinks_windows = enable_symlinks_windows, }; @@ -613,21 +596,24 @@ pub fn addLinkTests( omit_stage2: bool, enable_symlinks_windows: bool, ) *Step { - const cases = b.allocator.create(StandaloneContext) catch @panic("OOM"); - cases.* = StandaloneContext{ - .b = b, - .step = b.step("test-link", "Run the linker tests"), - .test_index = 0, - .test_filter = test_filter, - .optimize_modes = optimize_modes, - .skip_non_native = true, - .enable_macos_sdk = enable_macos_sdk, - .target = .{}, - .omit_stage2 = omit_stage2, - .enable_symlinks_windows = enable_symlinks_windows, - }; - link.addCases(cases); - return cases.step; + _ = test_filter; + _ = optimize_modes; + _ = enable_macos_sdk; + _ = omit_stage2; + _ = enable_symlinks_windows; + + const step = b.step("test-link", "Run the linker tests"); + + inline for (link.cases) |link_test| { + const dep = b.anonymousDependency(link_test.build_root, link_test.import, .{}); + const dep_step = dep.builder.default_step; + assert(mem.startsWith(u8, dep.builder.dep_prefix, "test.")); + const dep_prefix_adjusted = dep.builder.dep_prefix["test.".len..]; + dep_step.name = b.fmt("{s}{s}", .{ dep_prefix_adjusted, dep_step.name }); + step.dependOn(dep_step); + } + + return step; } pub fn addCliTests(b: *std.Build, test_filter: ?[]const u8, optimize_modes: []const OptimizeMode) *Step { @@ -761,7 +747,7 @@ pub fn addCliTests(b: *std.Build, test_filter: ?[]const u8, optimize_modes: []co const unformatted_code = " // no reason for indent"; const s = std.fs.path.sep_str; - var dir = fs.cwd().openDir(tmp_path, .{}) catch @panic("unhandled"); + var dir = std.fs.cwd().openDir(tmp_path, .{}) catch @panic("unhandled"); defer dir.close(); dir.writeFile("fmt1.zig", unformatted_code) catch @panic("unhandled"); dir.writeFile("fmt2.zig", unformatted_code) catch @panic("unhandled"); @@ -791,7 +777,7 @@ pub fn addCliTests(b: *std.Build, test_filter: ?[]const u8, optimize_modes: []co run3.step.dependOn(&run2.step); const unformatted_code_utf16 = "\xff\xfe \x00 \x00 \x00 \x00/\x00/\x00 \x00n\x00o\x00 \x00r\x00e\x00a\x00s\x00o\x00n\x00"; - const fmt4_path = fs.path.join(b.allocator, &.{ tmp_path, "fmt4.zig" }) catch @panic("OOM"); + const fmt4_path = std.fs.path.join(b.allocator, &.{ tmp_path, "fmt4.zig" }) catch @panic("OOM"); const write4 = b.addWriteFiles(); write4.addBytesToSource(unformatted_code_utf16, fmt4_path); write4.step.dependOn(&run3.step); From 4efeeaac881c5583321e8b385e90f004e99bc3d1 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 7 Mar 2023 00:44:10 -0700 Subject: [PATCH 215/294] delete link test "static_lib_as_system_lib" I disagree with this behavior and will be reverting the changes corresponding to this test case. Also this test case unnecessarily uses a .c file when a .zig file would be preferred, and has a problematic dependency on the install step, preventing this test case from playing nicely with the cache. --- test/link.zig | 4 --- test/link/static_lib_as_system_lib/a.c | 4 --- test/link/static_lib_as_system_lib/a.h | 2 -- test/link/static_lib_as_system_lib/build.zig | 34 -------------------- test/link/static_lib_as_system_lib/main.zig | 8 ----- 5 files changed, 52 deletions(-) delete mode 100644 test/link/static_lib_as_system_lib/a.c delete mode 100644 test/link/static_lib_as_system_lib/a.h delete mode 100644 test/link/static_lib_as_system_lib/build.zig delete mode 100644 test/link/static_lib_as_system_lib/main.zig diff --git a/test/link.zig b/test/link.zig index d6d42a53e8..34862e0286 100644 --- a/test/link.zig +++ b/test/link.zig @@ -20,10 +20,6 @@ pub const cases = [_]Case{ .build_root = "test/link/interdependent_static_c_libs", .import = @import("link/interdependent_static_c_libs/build.zig"), }, - //.{ - // .build_root = "test/link/static_lib_as_system_lib", - // .import = @import("link/static_lib_as_system_lib/build.zig"), - //}, }; //pub fn addCases(cases: *Standalone) void { diff --git a/test/link/static_lib_as_system_lib/a.c b/test/link/static_lib_as_system_lib/a.c deleted file mode 100644 index ee9da97a3a..0000000000 --- a/test/link/static_lib_as_system_lib/a.c +++ /dev/null @@ -1,4 +0,0 @@ -#include "a.h" -int32_t add(int32_t a, int32_t b) { - return a + b; -} diff --git a/test/link/static_lib_as_system_lib/a.h b/test/link/static_lib_as_system_lib/a.h deleted file mode 100644 index 7b45d54d56..0000000000 --- a/test/link/static_lib_as_system_lib/a.h +++ /dev/null @@ -1,2 +0,0 @@ -#include -int32_t add(int32_t a, int32_t b); diff --git a/test/link/static_lib_as_system_lib/build.zig b/test/link/static_lib_as_system_lib/build.zig deleted file mode 100644 index 1957d2e134..0000000000 --- a/test/link/static_lib_as_system_lib/build.zig +++ /dev/null @@ -1,34 +0,0 @@ -const std = @import("std"); - -pub fn build(b: *std.Build) void { - const test_step = b.step("test", "Test it"); - b.default_step = test_step; - - add(b, test_step, .Debug); - add(b, test_step, .ReleaseFast); - add(b, test_step, .ReleaseSmall); - add(b, test_step, .ReleaseSafe); -} - -fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { - const lib_a = b.addStaticLibrary(.{ - .name = "a", - .optimize = optimize, - .target = .{}, - }); - lib_a.addCSourceFile("a.c", &[_][]const u8{}); - lib_a.addIncludePath("."); - lib_a.install(); - - const test_exe = b.addTest(.{ - .root_source_file = .{ .path = "main.zig" }, - .optimize = optimize, - .target = .{}, - }); - test_exe.linkSystemLibrary("a"); // force linking liba.a as -la - test_exe.addSystemIncludePath("."); - const search_path = std.fs.path.join(b.allocator, &[_][]const u8{ b.install_path, "lib" }) catch @panic("OOM"); - test_exe.addLibraryPath(search_path); - - test_step.dependOn(&test_exe.step); -} diff --git a/test/link/static_lib_as_system_lib/main.zig b/test/link/static_lib_as_system_lib/main.zig deleted file mode 100644 index 0b9c46217f..0000000000 --- a/test/link/static_lib_as_system_lib/main.zig +++ /dev/null @@ -1,8 +0,0 @@ -const std = @import("std"); -const expect = std.testing.expect; -const c = @cImport(@cInclude("a.h")); - -test "import C add" { - const result = c.add(2, 1); - try expect(result == 3); -} From f558c835a41052b7609f0f0b27c5bbc4fe6b024d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 7 Mar 2023 16:59:53 -0700 Subject: [PATCH 216/294] std.Build.CheckObjectStep: better error message when reading the file fails --- lib/std/Build/CheckObjectStep.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/std/Build/CheckObjectStep.zig b/lib/std/Build/CheckObjectStep.zig index 2a58850fab..7cac2d04ec 100644 --- a/lib/std/Build/CheckObjectStep.zig +++ b/lib/std/Build/CheckObjectStep.zig @@ -314,14 +314,14 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { const self = @fieldParentPtr(CheckObjectStep, "step", step); const src_path = self.source.getPath(b); - const contents = try fs.cwd().readFileAllocOptions( + const contents = fs.cwd().readFileAllocOptions( gpa, src_path, self.max_bytes, null, @alignOf(u64), null, - ); + ) catch |err| return step.fail("unable to read '{s}': {s}", .{ src_path, @errorName(err) }); const output = switch (self.obj_format) { .macho => try MachODumper.parseAndDump(step, contents, .{ From cdf0a2af58ccd4ca4250435f258b35735e953aaf Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 7 Mar 2023 17:00:35 -0700 Subject: [PATCH 217/294] re-enable wasm linker tests --- test/link.zig | 362 +++++++++++------------- test/link/wasm/archive/build.zig | 17 +- test/link/wasm/basic-features/build.zig | 5 +- test/link/wasm/bss/build.zig | 7 +- test/link/wasm/export-data/build.zig | 7 +- test/link/wasm/export/build.zig | 15 +- test/link/wasm/extern-mangle/build.zig | 14 +- test/link/wasm/extern/build.zig | 15 +- test/link/wasm/function-table/build.zig | 19 +- test/link/wasm/infer-features/build.zig | 9 +- test/link/wasm/producers/build.zig | 21 +- test/link/wasm/segments/build.zig | 17 +- test/link/wasm/stack_pointer/build.zig | 17 +- test/link/wasm/type/build.zig | 17 +- test/tests.zig | 17 +- 15 files changed, 316 insertions(+), 243 deletions(-) diff --git a/test/link.zig b/test/link.zig index 34862e0286..ae970beaba 100644 --- a/test/link.zig +++ b/test/link.zig @@ -20,197 +20,179 @@ pub const cases = [_]Case{ .build_root = "test/link/interdependent_static_c_libs", .import = @import("link/interdependent_static_c_libs/build.zig"), }, -}; -//pub fn addCases(cases: *Standalone) void { -// addWasmCases(cases); -// addMachOCases(cases); -//} -// -//fn addWasmCases(cases: *Standalone) void { -// cases.addBuildFile("test/link/wasm/archive/build.zig", .{ -// .build_modes = true, -// .requires_stage2 = true, -// }); -// -// cases.addBuildFile("test/link/wasm/basic-features/build.zig", .{ -// .requires_stage2 = true, -// }); -// -// cases.addBuildFile("test/link/wasm/bss/build.zig", .{ -// .build_modes = false, -// .requires_stage2 = true, -// }); -// -// cases.addBuildFile("test/link/wasm/export/build.zig", .{ -// .build_modes = true, -// .requires_stage2 = true, -// }); -// -// // TODO: Fix open handle in wasm-linker refraining rename from working on Windows. -// if (builtin.os.tag != .windows) { -// cases.addBuildFile("test/link/wasm/export-data/build.zig", .{}); -// } -// -// cases.addBuildFile("test/link/wasm/extern/build.zig", .{ -// .build_modes = true, -// .requires_stage2 = true, -// .use_emulation = true, -// }); -// -// cases.addBuildFile("test/link/wasm/extern-mangle/build.zig", .{ -// .build_modes = true, -// .requires_stage2 = true, -// }); -// -// cases.addBuildFile("test/link/wasm/function-table/build.zig", .{ -// .build_modes = true, -// .requires_stage2 = true, -// }); -// -// cases.addBuildFile("test/link/wasm/infer-features/build.zig", .{ -// .requires_stage2 = true, -// }); -// -// cases.addBuildFile("test/link/wasm/producers/build.zig", .{ -// .build_modes = true, -// .requires_stage2 = true, -// }); -// -// cases.addBuildFile("test/link/wasm/segments/build.zig", .{ -// .build_modes = true, -// .requires_stage2 = true, -// }); -// -// cases.addBuildFile("test/link/wasm/stack_pointer/build.zig", .{ -// .build_modes = true, -// .requires_stage2 = true, -// }); -// -// cases.addBuildFile("test/link/wasm/type/build.zig", .{ -// .build_modes = true, -// .requires_stage2 = true, -// }); -//} -// -//fn addMachOCases(cases: *tests.StandaloneContext) void { -// cases.addBuildFile("test/link/macho/bugs/13056/build.zig", .{ -// .build_modes = true, -// .requires_macos_sdk = true, -// .requires_symlinks = true, -// }); -// -// cases.addBuildFile("test/link/macho/bugs/13457/build.zig", .{ -// .build_modes = true, -// .requires_symlinks = true, -// }); -// -// cases.addBuildFile("test/link/macho/dead_strip/build.zig", .{ -// .build_modes = false, -// .requires_symlinks = true, -// }); -// -// cases.addBuildFile("test/link/macho/dead_strip_dylibs/build.zig", .{ -// .build_modes = true, -// .requires_macos_sdk = true, -// .requires_symlinks = true, -// }); -// -// cases.addBuildFile("test/link/macho/dylib/build.zig", .{ -// .build_modes = true, -// .requires_symlinks = true, -// }); -// -// cases.addBuildFile("test/link/macho/empty/build.zig", .{ -// .build_modes = true, -// .requires_symlinks = true, -// }); -// -// cases.addBuildFile("test/link/macho/entry/build.zig", .{ -// .build_modes = true, -// .requires_symlinks = true, -// }); -// -// cases.addBuildFile("test/link/macho/headerpad/build.zig", .{ -// .build_modes = true, -// .requires_macos_sdk = true, -// .requires_symlinks = true, -// }); -// -// cases.addBuildFile("test/link/macho/linksection/build.zig", .{ -// .build_modes = true, -// .requires_symlinks = true, -// }); -// -// cases.addBuildFile("test/link/macho/needed_framework/build.zig", .{ -// .build_modes = true, -// .requires_macos_sdk = true, -// .requires_symlinks = true, -// }); -// -// cases.addBuildFile("test/link/macho/needed_library/build.zig", .{ -// .build_modes = true, -// .requires_symlinks = true, -// }); -// -// cases.addBuildFile("test/link/macho/objc/build.zig", .{ -// .build_modes = true, -// .requires_macos_sdk = true, -// .requires_symlinks = true, -// }); -// -// cases.addBuildFile("test/link/macho/objcpp/build.zig", .{ -// .build_modes = true, -// .requires_macos_sdk = true, -// .requires_symlinks = true, -// }); -// -// cases.addBuildFile("test/link/macho/pagezero/build.zig", .{ -// .build_modes = false, -// .requires_symlinks = true, -// }); -// -// cases.addBuildFile("test/link/macho/search_strategy/build.zig", .{ -// .build_modes = true, -// .requires_symlinks = true, -// }); -// -// cases.addBuildFile("test/link/macho/stack_size/build.zig", .{ -// .build_modes = true, -// .requires_symlinks = true, -// }); -// -// cases.addBuildFile("test/link/macho/strict_validation/build.zig", .{ -// .build_modes = true, -// .requires_symlinks = true, -// }); -// -// cases.addBuildFile("test/link/macho/tls/build.zig", .{ -// .build_modes = true, -// .requires_symlinks = true, -// }); -// -// cases.addBuildFile("test/link/macho/unwind_info/build.zig", .{ -// .build_modes = true, -// .requires_symlinks = true, -// }); -// -// cases.addBuildFile("test/link/macho/uuid/build.zig", .{ -// .build_modes = false, -// .requires_symlinks = true, -// }); -// -// cases.addBuildFile("test/link/macho/weak_library/build.zig", .{ -// .build_modes = true, -// .requires_symlinks = true, -// }); -// -// cases.addBuildFile("test/link/macho/weak_framework/build.zig", .{ -// .build_modes = true, -// .requires_macos_sdk = true, -// .requires_symlinks = true, -// }); -//} + // WASM Cases + .{ + .build_root = "test/link/wasm/archive", + .import = @import("link/wasm/archive/build.zig"), + }, + .{ + .build_root = "test/link/wasm/basic-features", + .import = @import("link/wasm/basic-features/build.zig"), + }, + .{ + .build_root = "test/link/wasm/bss", + .import = @import("link/wasm/bss/build.zig"), + }, + .{ + .build_root = "test/link/wasm/export", + .import = @import("link/wasm/export/build.zig"), + }, + .{ + .build_root = "test/link/wasm/export-data", + .import = @import("link/wasm/export-data/build.zig"), + }, + .{ + .build_root = "test/link/wasm/extern", + .import = @import("link/wasm/extern/build.zig"), + }, + .{ + .build_root = "test/link/wasm/extern-mangle", + .import = @import("link/wasm/extern-mangle/build.zig"), + }, + .{ + .build_root = "test/link/wasm/function-table", + .import = @import("link/wasm/function-table/build.zig"), + }, + .{ + .build_root = "test/link/wasm/infer-features", + .import = @import("link/wasm/infer-features/build.zig"), + }, + .{ + .build_root = "test/link/wasm/producers", + .import = @import("link/wasm/producers/build.zig"), + }, + .{ + .build_root = "test/link/wasm/segments", + .import = @import("link/wasm/segments/build.zig"), + }, + .{ + .build_root = "test/link/wasm/stack_pointer", + .import = @import("link/wasm/stack_pointer/build.zig"), + }, + .{ + .build_root = "test/link/wasm/type", + .import = @import("link/wasm/type/build.zig"), + }, + + // Mach-O Cases + // cases.addBuildFile("test/link/macho/bugs/13056/build.zig", .{ + // .build_modes = true, + // .requires_macos_sdk = true, + // .requires_symlinks = true, + // }); + // + // cases.addBuildFile("test/link/macho/bugs/13457/build.zig", .{ + // .build_modes = true, + // .requires_symlinks = true, + // }); + // + // cases.addBuildFile("test/link/macho/dead_strip/build.zig", .{ + // .build_modes = false, + // .requires_symlinks = true, + // }); + // + // cases.addBuildFile("test/link/macho/dead_strip_dylibs/build.zig", .{ + // .build_modes = true, + // .requires_macos_sdk = true, + // .requires_symlinks = true, + // }); + // + // cases.addBuildFile("test/link/macho/dylib/build.zig", .{ + // .build_modes = true, + // .requires_symlinks = true, + // }); + // + // cases.addBuildFile("test/link/macho/empty/build.zig", .{ + // .build_modes = true, + // .requires_symlinks = true, + // }); + // + // cases.addBuildFile("test/link/macho/entry/build.zig", .{ + // .build_modes = true, + // .requires_symlinks = true, + // }); + // + // cases.addBuildFile("test/link/macho/headerpad/build.zig", .{ + // .build_modes = true, + // .requires_macos_sdk = true, + // .requires_symlinks = true, + // }); + // + // cases.addBuildFile("test/link/macho/linksection/build.zig", .{ + // .build_modes = true, + // .requires_symlinks = true, + // }); + // + // cases.addBuildFile("test/link/macho/needed_framework/build.zig", .{ + // .build_modes = true, + // .requires_macos_sdk = true, + // .requires_symlinks = true, + // }); + // + // cases.addBuildFile("test/link/macho/needed_library/build.zig", .{ + // .build_modes = true, + // .requires_symlinks = true, + // }); + // + // cases.addBuildFile("test/link/macho/objc/build.zig", .{ + // .build_modes = true, + // .requires_macos_sdk = true, + // .requires_symlinks = true, + // }); + // + // cases.addBuildFile("test/link/macho/objcpp/build.zig", .{ + // .build_modes = true, + // .requires_macos_sdk = true, + // .requires_symlinks = true, + // }); + // + // cases.addBuildFile("test/link/macho/pagezero/build.zig", .{ + // .build_modes = false, + // .requires_symlinks = true, + // }); + // + // cases.addBuildFile("test/link/macho/search_strategy/build.zig", .{ + // .build_modes = true, + // .requires_symlinks = true, + // }); + // + // cases.addBuildFile("test/link/macho/stack_size/build.zig", .{ + // .build_modes = true, + // .requires_symlinks = true, + // }); + // + // cases.addBuildFile("test/link/macho/strict_validation/build.zig", .{ + // .build_modes = true, + // .requires_symlinks = true, + // }); + // + // cases.addBuildFile("test/link/macho/tls/build.zig", .{ + // .build_modes = true, + // .requires_symlinks = true, + // }); + // + // cases.addBuildFile("test/link/macho/unwind_info/build.zig", .{ + // .build_modes = true, + // .requires_symlinks = true, + // }); + // + // cases.addBuildFile("test/link/macho/uuid/build.zig", .{ + // .build_modes = false, + // .requires_symlinks = true, + // }); + // + // cases.addBuildFile("test/link/macho/weak_library/build.zig", .{ + // .build_modes = true, + // .requires_symlinks = true, + // }); + // + // cases.addBuildFile("test/link/macho/weak_framework/build.zig", .{ + // .build_modes = true, + // .requires_macos_sdk = true, + // .requires_symlinks = true, + // }); +}; const std = @import("std"); const builtin = @import("builtin"); diff --git a/test/link/wasm/archive/build.zig b/test/link/wasm/archive/build.zig index 342c4c08d1..f586187105 100644 --- a/test/link/wasm/archive/build.zig +++ b/test/link/wasm/archive/build.zig @@ -1,15 +1,24 @@ const std = @import("std"); -pub fn build(b: *std.Build) void { - const test_step = b.step("test", "Test"); - test_step.dependOn(b.getInstallStep()); +pub const requires_stage2 = true; +pub fn build(b: *std.Build) void { + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + + add(b, test_step, .Debug); + add(b, test_step, .ReleaseFast); + add(b, test_step, .ReleaseSmall); + add(b, test_step, .ReleaseSafe); +} + +fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { // The code in question will pull-in compiler-rt, // and therefore link with its archive file. const lib = b.addSharedLibrary(.{ .name = "main", .root_source_file = .{ .path = "main.zig" }, - .optimize = b.standardOptimizeOption(.{}), + .optimize = optimize, .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, }); lib.use_llvm = false; diff --git a/test/link/wasm/basic-features/build.zig b/test/link/wasm/basic-features/build.zig index 9f57066518..f3f5320e54 100644 --- a/test/link/wasm/basic-features/build.zig +++ b/test/link/wasm/basic-features/build.zig @@ -1,11 +1,13 @@ const std = @import("std"); +pub const requires_stage2 = true; + pub fn build(b: *std.Build) void { // Library with explicitly set cpu features const lib = b.addSharedLibrary(.{ .name = "lib", .root_source_file = .{ .path = "main.zig" }, - .optimize = b.standardOptimizeOption(.{}), + .optimize = .Debug, .target = .{ .cpu_arch = .wasm32, .cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp }, @@ -24,4 +26,5 @@ pub fn build(b: *std.Build) void { const test_step = b.step("test", "Run linker test"); test_step.dependOn(&check.step); + b.default_step = test_step; } diff --git a/test/link/wasm/bss/build.zig b/test/link/wasm/bss/build.zig index 1017e70a71..8e6b19c7be 100644 --- a/test/link/wasm/bss/build.zig +++ b/test/link/wasm/bss/build.zig @@ -1,14 +1,16 @@ const std = @import("std"); +pub const requires_stage2 = true; + pub fn build(b: *std.Build) void { const test_step = b.step("test", "Test"); - test_step.dependOn(b.getInstallStep()); + b.default_step = test_step; const lib = b.addSharedLibrary(.{ .name = "lib", .root_source_file = .{ .path = "lib.zig" }, .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, - .optimize = b.standardOptimizeOption(.{}), + .optimize = .Debug, }); lib.use_llvm = false; lib.use_lld = false; @@ -36,5 +38,6 @@ pub fn build(b: *std.Build) void { check_lib.checkNext("name .rodata"); check_lib.checkNext("index 1"); // bss section always last check_lib.checkNext("name .bss"); + test_step.dependOn(&check_lib.step); } diff --git a/test/link/wasm/export-data/build.zig b/test/link/wasm/export-data/build.zig index c989153e47..0bd10921a2 100644 --- a/test/link/wasm/export-data/build.zig +++ b/test/link/wasm/export-data/build.zig @@ -2,7 +2,12 @@ const std = @import("std"); pub fn build(b: *std.Build) void { const test_step = b.step("test", "Test"); - test_step.dependOn(b.getInstallStep()); + b.default_step = test_step; + + if (@import("builtin").os.tag == .windows) { + // TODO: Fix open handle in wasm-linker refraining rename from working on Windows. + return; + } const lib = b.addSharedLibrary(.{ .name = "lib", diff --git a/test/link/wasm/export/build.zig b/test/link/wasm/export/build.zig index 69c34a320e..03c4baabe3 100644 --- a/test/link/wasm/export/build.zig +++ b/test/link/wasm/export/build.zig @@ -1,8 +1,18 @@ const std = @import("std"); -pub fn build(b: *std.Build) void { - const optimize = b.standardOptimizeOption(.{}); +pub const requires_stage2 = true; +pub fn build(b: *std.Build) void { + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + + add(b, test_step, .Debug); + add(b, test_step, .ReleaseFast); + add(b, test_step, .ReleaseSmall); + add(b, test_step, .ReleaseSafe); +} + +fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { const no_export = b.addSharedLibrary(.{ .name = "no-export", .root_source_file = .{ .path = "main.zig" }, @@ -50,7 +60,6 @@ pub fn build(b: *std.Build) void { check_force_export.checkNext("name foo"); check_force_export.checkNext("kind function"); - const test_step = b.step("test", "Run linker test"); test_step.dependOn(&check_no_export.step); test_step.dependOn(&check_dynamic_export.step); test_step.dependOn(&check_force_export.step); diff --git a/test/link/wasm/extern-mangle/build.zig b/test/link/wasm/extern-mangle/build.zig index 19913e6eca..b0655cbc1f 100644 --- a/test/link/wasm/extern-mangle/build.zig +++ b/test/link/wasm/extern-mangle/build.zig @@ -1,18 +1,24 @@ const std = @import("std"); pub fn build(b: *std.Build) void { - const test_step = b.step("test", "Test"); - test_step.dependOn(b.getInstallStep()); + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + add(b, test_step, .Debug); + add(b, test_step, .ReleaseFast); + add(b, test_step, .ReleaseSmall); + add(b, test_step, .ReleaseSafe); +} + +fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { const lib = b.addSharedLibrary(.{ .name = "lib", .root_source_file = .{ .path = "lib.zig" }, .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, - .optimize = b.standardOptimizeOption(.{}), + .optimize = optimize, }); lib.import_symbols = true; // import `a` and `b` lib.rdynamic = true; // export `foo` - lib.install(); const check_lib = lib.checkObject(.wasm); check_lib.checkStart("Section import"); diff --git a/test/link/wasm/extern/build.zig b/test/link/wasm/extern/build.zig index fede2b18ab..55562143c2 100644 --- a/test/link/wasm/extern/build.zig +++ b/test/link/wasm/extern/build.zig @@ -1,10 +1,22 @@ const std = @import("std"); +pub const requires_stage2 = true; + pub fn build(b: *std.Build) void { + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + + add(b, test_step, .Debug); + add(b, test_step, .ReleaseFast); + add(b, test_step, .ReleaseSmall); + add(b, test_step, .ReleaseSafe); +} + +fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { const exe = b.addExecutable(.{ .name = "extern", .root_source_file = .{ .path = "main.zig" }, - .optimize = b.standardOptimizeOption(.{}), + .optimize = optimize, .target = .{ .cpu_arch = .wasm32, .os_tag = .wasi }, }); exe.addCSourceFile("foo.c", &.{}); @@ -15,6 +27,5 @@ pub fn build(b: *std.Build) void { run.skip_foreign_checks = true; run.expectStdOutEqual("Result: 30"); - const test_step = b.step("test", "Run linker test"); test_step.dependOn(&run.step); } diff --git a/test/link/wasm/function-table/build.zig b/test/link/wasm/function-table/build.zig index 4c25d0d860..e1921caee3 100644 --- a/test/link/wasm/function-table/build.zig +++ b/test/link/wasm/function-table/build.zig @@ -1,13 +1,20 @@ const std = @import("std"); +pub const requires_stage2 = true; + pub fn build(b: *std.Build) void { - const optimize = b.standardOptimizeOption(.{}); + const test_step = b.step("test", "Test it"); + b.default_step = test_step; - const test_step = b.step("test", "Test"); - test_step.dependOn(b.getInstallStep()); + add(b, test_step, .Debug); + add(b, test_step, .ReleaseFast); + add(b, test_step, .ReleaseSmall); + add(b, test_step, .ReleaseSafe); +} +fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { const import_table = b.addSharedLibrary(.{ - .name = "lib", + .name = "import_table", .root_source_file = .{ .path = "lib.zig" }, .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, .optimize = optimize, @@ -17,7 +24,7 @@ pub fn build(b: *std.Build) void { import_table.import_table = true; const export_table = b.addSharedLibrary(.{ - .name = "lib", + .name = "export_table", .root_source_file = .{ .path = "lib.zig" }, .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, .optimize = optimize, @@ -27,7 +34,7 @@ pub fn build(b: *std.Build) void { export_table.export_table = true; const regular_table = b.addSharedLibrary(.{ - .name = "lib", + .name = "regular_table", .root_source_file = .{ .path = "lib.zig" }, .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, .optimize = optimize, diff --git a/test/link/wasm/infer-features/build.zig b/test/link/wasm/infer-features/build.zig index d6d706a33d..a7cfa985d5 100644 --- a/test/link/wasm/infer-features/build.zig +++ b/test/link/wasm/infer-features/build.zig @@ -1,12 +1,12 @@ const std = @import("std"); -pub fn build(b: *std.Build) void { - const optimize = b.standardOptimizeOption(.{}); +pub const requires_stage2 = true; +pub fn build(b: *std.Build) void { // Wasm Object file which we will use to infer the features from const c_obj = b.addObject(.{ .name = "c_obj", - .optimize = optimize, + .optimize = .Debug, .target = .{ .cpu_arch = .wasm32, .cpu_model = .{ .explicit = &std.Target.wasm.cpu.bleeding_edge }, @@ -20,7 +20,7 @@ pub fn build(b: *std.Build) void { const lib = b.addSharedLibrary(.{ .name = "lib", .root_source_file = .{ .path = "main.zig" }, - .optimize = optimize, + .optimize = .Debug, .target = .{ .cpu_arch = .wasm32, .cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp }, @@ -45,4 +45,5 @@ pub fn build(b: *std.Build) void { const test_step = b.step("test", "Run linker test"); test_step.dependOn(&check.step); + b.default_step = test_step; } diff --git a/test/link/wasm/producers/build.zig b/test/link/wasm/producers/build.zig index 2589b0dfcf..41952b0adb 100644 --- a/test/link/wasm/producers/build.zig +++ b/test/link/wasm/producers/build.zig @@ -1,24 +1,31 @@ const std = @import("std"); const builtin = @import("builtin"); -pub fn build(b: *std.Build) void { - const test_step = b.step("test", "Test"); - test_step.dependOn(b.getInstallStep()); +pub const requires_stage2 = true; +pub fn build(b: *std.Build) void { + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + + add(b, test_step, .Debug); + add(b, test_step, .ReleaseFast); + add(b, test_step, .ReleaseSmall); + add(b, test_step, .ReleaseSafe); +} + +fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { const lib = b.addSharedLibrary(.{ .name = "lib", .root_source_file = .{ .path = "lib.zig" }, .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, - .optimize = b.standardOptimizeOption(.{}), + .optimize = optimize, }); lib.use_llvm = false; lib.use_lld = false; lib.strip = false; lib.install(); - const zig_version = builtin.zig_version; - var version_buf: [100]u8 = undefined; - const version_fmt = std.fmt.bufPrint(&version_buf, "version {}", .{zig_version}) catch unreachable; + const version_fmt = "version " ++ builtin.zig_version_string; const check_lib = lib.checkObject(.wasm); check_lib.checkStart("name producers"); diff --git a/test/link/wasm/segments/build.zig b/test/link/wasm/segments/build.zig index 76160e905f..f8e16eee24 100644 --- a/test/link/wasm/segments/build.zig +++ b/test/link/wasm/segments/build.zig @@ -1,14 +1,23 @@ const std = @import("std"); -pub fn build(b: *std.Build) void { - const test_step = b.step("test", "Test"); - test_step.dependOn(b.getInstallStep()); +pub const requires_stage2 = true; +pub fn build(b: *std.Build) void { + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + + add(b, test_step, .Debug); + add(b, test_step, .ReleaseFast); + add(b, test_step, .ReleaseSmall); + add(b, test_step, .ReleaseSafe); +} + +fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { const lib = b.addSharedLibrary(.{ .name = "lib", .root_source_file = .{ .path = "lib.zig" }, .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, - .optimize = b.standardOptimizeOption(.{}), + .optimize = optimize, }); lib.use_llvm = false; lib.use_lld = false; diff --git a/test/link/wasm/stack_pointer/build.zig b/test/link/wasm/stack_pointer/build.zig index 95c7643880..41bdd828f2 100644 --- a/test/link/wasm/stack_pointer/build.zig +++ b/test/link/wasm/stack_pointer/build.zig @@ -1,14 +1,23 @@ const std = @import("std"); -pub fn build(b: *std.Build) void { - const test_step = b.step("test", "Test"); - test_step.dependOn(b.getInstallStep()); +pub const requires_stage2 = true; +pub fn build(b: *std.Build) void { + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + + add(b, test_step, .Debug); + add(b, test_step, .ReleaseFast); + add(b, test_step, .ReleaseSmall); + add(b, test_step, .ReleaseSafe); +} + +fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { const lib = b.addSharedLibrary(.{ .name = "lib", .root_source_file = .{ .path = "lib.zig" }, .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, - .optimize = b.standardOptimizeOption(.{}), + .optimize = optimize, }); lib.use_llvm = false; lib.use_lld = false; diff --git a/test/link/wasm/type/build.zig b/test/link/wasm/type/build.zig index 816b57ccab..df8cbad021 100644 --- a/test/link/wasm/type/build.zig +++ b/test/link/wasm/type/build.zig @@ -1,14 +1,23 @@ const std = @import("std"); -pub fn build(b: *std.Build) void { - const test_step = b.step("test", "Test"); - test_step.dependOn(b.getInstallStep()); +pub const requires_stage2 = true; +pub fn build(b: *std.Build) void { + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + + add(b, test_step, .Debug); + add(b, test_step, .ReleaseFast); + add(b, test_step, .ReleaseSmall); + add(b, test_step, .ReleaseSafe); +} + +fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { const lib = b.addSharedLibrary(.{ .name = "lib", .root_source_file = .{ .path = "lib.zig" }, .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, - .optimize = b.standardOptimizeOption(.{}), + .optimize = optimize, }); lib.use_llvm = false; lib.use_lld = false; diff --git a/test/tests.zig b/test/tests.zig index d5ee83b447..a23870b79e 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -599,18 +599,21 @@ pub fn addLinkTests( _ = test_filter; _ = optimize_modes; _ = enable_macos_sdk; - _ = omit_stage2; _ = enable_symlinks_windows; const step = b.step("test-link", "Run the linker tests"); inline for (link.cases) |link_test| { - const dep = b.anonymousDependency(link_test.build_root, link_test.import, .{}); - const dep_step = dep.builder.default_step; - assert(mem.startsWith(u8, dep.builder.dep_prefix, "test.")); - const dep_prefix_adjusted = dep.builder.dep_prefix["test.".len..]; - dep_step.name = b.fmt("{s}{s}", .{ dep_prefix_adjusted, dep_step.name }); - step.dependOn(dep_step); + const requires_stage2 = @hasDecl(link_test.import, "requires_stage2") and + link_test.import.requires_stage2; + if (!requires_stage2 or !omit_stage2) { + const dep = b.anonymousDependency(link_test.build_root, link_test.import, .{}); + const dep_step = dep.builder.default_step; + assert(mem.startsWith(u8, dep.builder.dep_prefix, "test.")); + const dep_prefix_adjusted = dep.builder.dep_prefix["test.".len..]; + dep_step.name = b.fmt("{s}{s}", .{ dep_prefix_adjusted, dep_step.name }); + step.dependOn(dep_step); + } } return step; From 9a9b0083009f62dda9500f978544380a17b1f325 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 7 Mar 2023 19:17:32 -0700 Subject: [PATCH 218/294] std.Build.CompileStep: add FileSource support to some paths Library paths, RPaths, and framework paths now support being fulfilled by FileSource arguments. --- lib/std/Build/CompileStep.zig | 79 +++++++++++++++++++++++------------ 1 file changed, 52 insertions(+), 27 deletions(-) diff --git a/lib/std/Build/CompileStep.zig b/lib/std/Build/CompileStep.zig index 9b9fc02e74..42cc63e8b4 100644 --- a/lib/std/Build/CompileStep.zig +++ b/lib/std/Build/CompileStep.zig @@ -46,9 +46,9 @@ strip: ?bool, unwind_tables: ?bool, // keep in sync with src/link.zig:CompressDebugSections compress_debug_sections: enum { none, zlib } = .none, -lib_paths: ArrayList([]const u8), -rpaths: ArrayList([]const u8), -framework_dirs: ArrayList([]const u8), +lib_paths: ArrayList(FileSource), +rpaths: ArrayList(FileSource), +framework_dirs: ArrayList(FileSource), frameworks: StringHashMap(FrameworkLinkInfo), verbose_link: bool, verbose_cc: bool, @@ -211,6 +211,7 @@ output_path_source: GeneratedFile, output_lib_path_source: GeneratedFile, output_h_path_source: GeneratedFile, output_pdb_path_source: GeneratedFile, +output_dirname_source: GeneratedFile, pub const CSourceFiles = struct { files: []const []const u8, @@ -359,9 +360,9 @@ pub fn create(owner: *std.Build, options: Options) *CompileStep { .include_dirs = ArrayList(IncludeDir).init(owner.allocator), .link_objects = ArrayList(LinkObject).init(owner.allocator), .c_macros = ArrayList([]const u8).init(owner.allocator), - .lib_paths = ArrayList([]const u8).init(owner.allocator), - .rpaths = ArrayList([]const u8).init(owner.allocator), - .framework_dirs = ArrayList([]const u8).init(owner.allocator), + .lib_paths = ArrayList(FileSource).init(owner.allocator), + .rpaths = ArrayList(FileSource).init(owner.allocator), + .framework_dirs = ArrayList(FileSource).init(owner.allocator), .installed_headers = ArrayList(*Step).init(owner.allocator), .object_src = undefined, .c_std = std.Build.CStd.C99, @@ -384,6 +385,7 @@ pub fn create(owner: *std.Build, options: Options) *CompileStep { .output_lib_path_source = GeneratedFile{ .step = &self.step }, .output_h_path_source = GeneratedFile{ .step = &self.step }, .output_pdb_path_source = GeneratedFile{ .step = &self.step }, + .output_dirname_source = GeneratedFile{ .step = &self.step }, .target_info = NativeTargetInfo.detect(self.target) catch @panic("unhandled error"), }; @@ -914,13 +916,17 @@ pub fn setLibCFile(self: *CompileStep, libc_file: ?FileSource) void { /// Returns the generated executable, library or object file. /// To run an executable built with zig build, use `run`, or create an install step and invoke it. pub fn getOutputSource(self: *CompileStep) FileSource { - return FileSource{ .generated = &self.output_path_source }; + return .{ .generated = &self.output_path_source }; +} + +pub fn getOutputDirectorySource(self: *CompileStep) FileSource { + return .{ .generated = &self.output_dirname_source }; } /// Returns the generated import library. This function can only be called for libraries. pub fn getOutputLibSource(self: *CompileStep) FileSource { assert(self.kind == .lib); - return FileSource{ .generated = &self.output_lib_path_source }; + return .{ .generated = &self.output_lib_path_source }; } /// Returns the generated header file. @@ -928,14 +934,14 @@ pub fn getOutputLibSource(self: *CompileStep) FileSource { pub fn getOutputHSource(self: *CompileStep) FileSource { assert(self.kind != .exe and self.kind != .test_exe and self.kind != .@"test"); assert(self.emit_h); - return FileSource{ .generated = &self.output_h_path_source }; + return .{ .generated = &self.output_h_path_source }; } /// Returns the generated PDB file. This function can only be called for Windows and UEFI. pub fn getOutputPdbSource(self: *CompileStep) FileSource { // TODO: Is this right? Isn't PDB for *any* PE/COFF file? assert(self.target.isWindows() or self.target.isUefi()); - return FileSource{ .generated = &self.output_pdb_path_source }; + return .{ .generated = &self.output_pdb_path_source }; } pub fn addAssemblyFile(self: *CompileStep, path: []const u8) void { @@ -989,17 +995,32 @@ pub fn addConfigHeader(self: *CompileStep, config_header: *ConfigHeaderStep) voi pub fn addLibraryPath(self: *CompileStep, path: []const u8) void { const b = self.step.owner; - self.lib_paths.append(b.dupe(path)) catch @panic("OOM"); + self.lib_paths.append(.{ .path = b.dupe(path) }) catch @panic("OOM"); +} + +pub fn addLibraryPathDirectorySource(self: *CompileStep, directory_source: FileSource) void { + self.lib_paths.append(directory_source) catch @panic("OOM"); + directory_source.addStepDependencies(&self.step); } pub fn addRPath(self: *CompileStep, path: []const u8) void { const b = self.step.owner; - self.rpaths.append(b.dupe(path)) catch @panic("OOM"); + self.rpaths.append(.{ .path = b.dupe(path) }) catch @panic("OOM"); +} + +pub fn addRPathDirectorySource(self: *CompileStep, directory_source: FileSource) void { + self.rpaths.append(directory_source) catch @panic("OOM"); + directory_source.addStepDependencies(&self.step); } pub fn addFrameworkPath(self: *CompileStep, dir_path: []const u8) void { const b = self.step.owner; - self.framework_dirs.append(b.dupe(dir_path)) catch @panic("OOM"); + self.framework_dirs.append(.{ .path = b.dupe(dir_path) }) catch @panic("OOM"); +} + +pub fn addFrameworkPathDirectorySource(self: *CompileStep, directory_source: FileSource) void { + self.framework_dirs.append(directory_source) catch @panic("OOM"); + directory_source.addStepDependencies(&self.step); } /// Adds a module to be used with `@import` and exposing it in the current @@ -1065,7 +1086,7 @@ pub fn addVcpkgPaths(self: *CompileStep, linkage: CompileStep.Linkage) !void { try self.include_dirs.append(IncludeDir{ .raw_path = include_path }); const lib_path = b.pathJoin(&.{ root, "installed", triplet, "lib" }); - try self.lib_paths.append(lib_path); + try self.lib_paths.append(.{ .path = lib_path }); self.vcpkg_bin_path = b.pathJoin(&.{ root, "installed", triplet, "bin" }); }, @@ -1768,30 +1789,32 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { } } - for (self.lib_paths.items) |lib_path| { - try zig_args.append("-L"); - try zig_args.append(lib_path); - } - - for (self.rpaths.items) |rpath| { - try zig_args.append("-rpath"); - try zig_args.append(rpath); - } - for (self.c_macros.items) |c_macro| { try zig_args.append("-D"); try zig_args.append(c_macro); } - for (self.framework_dirs.items) |dir| { + try zig_args.ensureUnusedCapacity(2 * self.lib_paths.items.len); + for (self.lib_paths.items) |lib_path| { + zig_args.appendAssumeCapacity("-L"); + zig_args.appendAssumeCapacity(lib_path.getPath2(b, step)); + } + + try zig_args.ensureUnusedCapacity(2 * self.rpaths.items.len); + for (self.rpaths.items) |rpath| { + zig_args.appendAssumeCapacity("-rpath"); + zig_args.appendAssumeCapacity(rpath.getPath2(b, step)); + } + + for (self.framework_dirs.items) |directory_source| { if (b.sysroot != null) { try zig_args.append("-iframeworkwithsysroot"); } else { try zig_args.append("-iframework"); } - try zig_args.append(dir); + try zig_args.append(directory_source.getPath2(b, step)); try zig_args.append("-F"); - try zig_args.append(dir); + try zig_args.append(directory_source.getPath2(b, step)); } { @@ -1954,6 +1977,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { // Update generated files if (self.output_dir != null) { + self.output_dirname_source.path = self.output_dir.?; + self.output_path_source.path = b.pathJoin( &.{ self.output_dir.?, self.out_filename }, ); From 1142e0534351a57b81bd4a127f29745c62cc59c9 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 7 Mar 2023 20:23:11 -0700 Subject: [PATCH 219/294] re-enable macho linker tests --- build.zig | 2 +- test/link.zig | 213 +++++++++----------- test/link/macho/bugs/13056/build.zig | 18 +- test/link/macho/bugs/13457/build.zig | 18 +- test/link/macho/dead_strip/build.zig | 13 +- test/link/macho/dead_strip_dylibs/build.zig | 30 ++- test/link/macho/dylib/build.zig | 29 ++- test/link/macho/empty/build.zig | 18 +- test/link/macho/entry/build.zig | 13 +- test/link/macho/headerpad/build.zig | 14 +- test/link/macho/linksection/build.zig | 18 +- test/link/macho/needed_framework/build.zig | 14 +- test/link/macho/needed_library/build.zig | 23 ++- test/link/macho/objc/build.zig | 13 +- test/link/macho/objcpp/build.zig | 13 +- test/link/macho/pagezero/build.zig | 12 +- test/link/macho/search_strategy/build.zig | 43 ++-- test/link/macho/stack_size/build.zig | 18 +- test/link/macho/strict_validation/build.zig | 18 +- test/link/macho/tls/build.zig | 14 +- test/link/macho/unwind_info/build.zig | 25 ++- test/link/macho/uuid/build.zig | 7 +- test/link/macho/weak_framework/build.zig | 14 +- test/link/macho/weak_library/build.zig | 22 +- test/tests.zig | 18 +- 25 files changed, 396 insertions(+), 244 deletions(-) diff --git a/build.zig b/build.zig index 83aabb6c41..b181eaefae 100644 --- a/build.zig +++ b/build.zig @@ -454,7 +454,7 @@ pub fn build(b: *std.Build) !void { // enable_symlinks_windows, //)); test_step.dependOn(tests.addCAbiTests(b, skip_non_native, skip_release)); - test_step.dependOn(tests.addLinkTests(b, test_filter, optimization_modes, enable_macos_sdk, skip_stage2_tests, enable_symlinks_windows)); + test_step.dependOn(tests.addLinkTests(b, enable_macos_sdk, skip_stage2_tests, enable_symlinks_windows)); test_step.dependOn(tests.addStackTraceTests(b, test_filter, optimization_modes)); test_step.dependOn(tests.addCliTests(b, test_filter, optimization_modes)); test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, optimization_modes)); diff --git a/test/link.zig b/test/link.zig index ae970beaba..fb18e27c22 100644 --- a/test/link.zig +++ b/test/link.zig @@ -76,122 +76,103 @@ pub const cases = [_]Case{ }, // Mach-O Cases - // cases.addBuildFile("test/link/macho/bugs/13056/build.zig", .{ - // .build_modes = true, - // .requires_macos_sdk = true, - // .requires_symlinks = true, - // }); - // - // cases.addBuildFile("test/link/macho/bugs/13457/build.zig", .{ - // .build_modes = true, - // .requires_symlinks = true, - // }); - // - // cases.addBuildFile("test/link/macho/dead_strip/build.zig", .{ - // .build_modes = false, - // .requires_symlinks = true, - // }); - // - // cases.addBuildFile("test/link/macho/dead_strip_dylibs/build.zig", .{ - // .build_modes = true, - // .requires_macos_sdk = true, - // .requires_symlinks = true, - // }); - // - // cases.addBuildFile("test/link/macho/dylib/build.zig", .{ - // .build_modes = true, - // .requires_symlinks = true, - // }); - // - // cases.addBuildFile("test/link/macho/empty/build.zig", .{ - // .build_modes = true, - // .requires_symlinks = true, - // }); - // - // cases.addBuildFile("test/link/macho/entry/build.zig", .{ - // .build_modes = true, - // .requires_symlinks = true, - // }); - // - // cases.addBuildFile("test/link/macho/headerpad/build.zig", .{ - // .build_modes = true, - // .requires_macos_sdk = true, - // .requires_symlinks = true, - // }); - // - // cases.addBuildFile("test/link/macho/linksection/build.zig", .{ - // .build_modes = true, - // .requires_symlinks = true, - // }); - // - // cases.addBuildFile("test/link/macho/needed_framework/build.zig", .{ - // .build_modes = true, - // .requires_macos_sdk = true, - // .requires_symlinks = true, - // }); - // - // cases.addBuildFile("test/link/macho/needed_library/build.zig", .{ - // .build_modes = true, - // .requires_symlinks = true, - // }); - // - // cases.addBuildFile("test/link/macho/objc/build.zig", .{ - // .build_modes = true, - // .requires_macos_sdk = true, - // .requires_symlinks = true, - // }); - // - // cases.addBuildFile("test/link/macho/objcpp/build.zig", .{ - // .build_modes = true, - // .requires_macos_sdk = true, - // .requires_symlinks = true, - // }); - // - // cases.addBuildFile("test/link/macho/pagezero/build.zig", .{ - // .build_modes = false, - // .requires_symlinks = true, - // }); - // - // cases.addBuildFile("test/link/macho/search_strategy/build.zig", .{ - // .build_modes = true, - // .requires_symlinks = true, - // }); - // - // cases.addBuildFile("test/link/macho/stack_size/build.zig", .{ - // .build_modes = true, - // .requires_symlinks = true, - // }); - // - // cases.addBuildFile("test/link/macho/strict_validation/build.zig", .{ - // .build_modes = true, - // .requires_symlinks = true, - // }); - // - // cases.addBuildFile("test/link/macho/tls/build.zig", .{ - // .build_modes = true, - // .requires_symlinks = true, - // }); - // - // cases.addBuildFile("test/link/macho/unwind_info/build.zig", .{ - // .build_modes = true, - // .requires_symlinks = true, - // }); - // - // cases.addBuildFile("test/link/macho/uuid/build.zig", .{ - // .build_modes = false, - // .requires_symlinks = true, - // }); - // - // cases.addBuildFile("test/link/macho/weak_library/build.zig", .{ - // .build_modes = true, - // .requires_symlinks = true, - // }); - // - // cases.addBuildFile("test/link/macho/weak_framework/build.zig", .{ - // .build_modes = true, - // .requires_macos_sdk = true, - // .requires_symlinks = true, - // }); + .{ + .build_root = "test/link/macho/bugs/13056", + .import = @import("link/macho/bugs/13056/build.zig"), + }, + .{ + .build_root = "test/link/macho/bugs/13457", + .import = @import("link/macho/bugs/13457/build.zig"), + }, + .{ + .build_root = "test/link/macho/bugs/13457", + .import = @import("link/macho/bugs/13457/build.zig"), + }, + .{ + .build_root = "test/link/macho/dead_strip", + .import = @import("link/macho/dead_strip/build.zig"), + }, + .{ + .build_root = "test/link/macho/dead_strip_dylibs", + .import = @import("link/macho/dead_strip_dylibs/build.zig"), + }, + .{ + .build_root = "test/link/macho/dylib", + .import = @import("link/macho/dylib/build.zig"), + }, + .{ + .build_root = "test/link/macho/empty", + .import = @import("link/macho/empty/build.zig"), + }, + .{ + .build_root = "test/link/macho/entry", + .import = @import("link/macho/entry/build.zig"), + }, + .{ + .build_root = "test/link/macho/headerpad", + .import = @import("link/macho/headerpad/build.zig"), + }, + .{ + .build_root = "test/link/macho/linksection", + .import = @import("link/macho/linksection/build.zig"), + }, + .{ + .build_root = "test/link/macho/needed_framework", + .import = @import("link/macho/needed_framework/build.zig"), + }, + .{ + .build_root = "test/link/macho/needed_library", + .import = @import("link/macho/needed_library/build.zig"), + }, + .{ + .build_root = "test/link/macho/objc", + .import = @import("link/macho/objc/build.zig"), + }, + .{ + .build_root = "test/link/macho/objcpp", + .import = @import("link/macho/objcpp/build.zig"), + }, + .{ + .build_root = "test/link/macho/pagezero", + .import = @import("link/macho/pagezero/build.zig"), + }, + .{ + .build_root = "test/link/macho/search_strategy", + .import = @import("link/macho/search_strategy/build.zig"), + }, + .{ + .build_root = "test/link/macho/stack_size", + .import = @import("link/macho/stack_size/build.zig"), + }, + .{ + .build_root = "test/link/macho/strict_validation", + .import = @import("link/macho/strict_validation/build.zig"), + }, + .{ + .build_root = "test/link/macho/tls", + .import = @import("link/macho/tls/build.zig"), + }, + .{ + .build_root = "test/link/macho/unwind_info", + .import = @import("link/macho/unwind_info/build.zig"), + }, + // TODO: re-enable this test. It currently has some incompatibilities with + // the new build system API. In particular, it depends on installing the build + // artifacts, which should be unnecessary, and it has a custom build step that + // prints directly to stderr instead of failing the step with an error message. + //.{ + // .build_root = "test/link/macho/uuid", + // .import = @import("link/macho/uuid/build.zig"), + //}, + + .{ + .build_root = "test/link/macho/weak_library", + .import = @import("link/macho/weak_library/build.zig"), + }, + .{ + .build_root = "test/link/macho/weak_framework", + .import = @import("link/macho/weak_framework/build.zig"), + }, }; const std = @import("std"); diff --git a/test/link/macho/bugs/13056/build.zig b/test/link/macho/bugs/13056/build.zig index 662fd25c92..db7c129cbf 100644 --- a/test/link/macho/bugs/13056/build.zig +++ b/test/link/macho/bugs/13056/build.zig @@ -1,20 +1,28 @@ const std = @import("std"); -pub fn build(b: *std.Build) void { - const optimize = b.standardOptimizeOption(.{}); +pub const requires_macos_sdk = true; +pub const requires_symlinks = true; +pub fn build(b: *std.Build) void { + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + + add(b, test_step, .Debug); + add(b, test_step, .ReleaseFast); + add(b, test_step, .ReleaseSmall); + add(b, test_step, .ReleaseSafe); +} + +fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { const target: std.zig.CrossTarget = .{ .os_tag = .macos }; const target_info = std.zig.system.NativeTargetInfo.detect(target) catch unreachable; const sdk = std.zig.system.darwin.getDarwinSDK(b.allocator, target_info.target) orelse @panic("macOS SDK is required to run the test"); - const test_step = b.step("test", "Test the program"); - const exe = b.addExecutable(.{ .name = "test", .optimize = optimize, }); - b.default_step.dependOn(&exe.step); exe.addIncludePath(std.fs.path.join(b.allocator, &.{ sdk.path, "/usr/include" }) catch unreachable); exe.addIncludePath(std.fs.path.join(b.allocator, &.{ sdk.path, "/usr/include/c++/v1" }) catch unreachable); exe.addCSourceFile("test.cpp", &.{ diff --git a/test/link/macho/bugs/13457/build.zig b/test/link/macho/bugs/13457/build.zig index 7ac1435015..89096bba38 100644 --- a/test/link/macho/bugs/13457/build.zig +++ b/test/link/macho/bugs/13457/build.zig @@ -1,10 +1,19 @@ const std = @import("std"); -pub fn build(b: *std.Build) void { - const optimize = b.standardOptimizeOption(.{}); - const target: std.zig.CrossTarget = .{ .os_tag = .macos }; +pub const requires_symlinks = true; - const test_step = b.step("test", "Test the program"); +pub fn build(b: *std.Build) void { + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + + add(b, test_step, .Debug); + add(b, test_step, .ReleaseFast); + add(b, test_step, .ReleaseSmall); + add(b, test_step, .ReleaseSafe); +} + +fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { + const target: std.zig.CrossTarget = .{ .os_tag = .macos }; const exe = b.addExecutable(.{ .name = "test", @@ -16,5 +25,6 @@ pub fn build(b: *std.Build) void { const run = b.addRunArtifact(exe); run.skip_foreign_checks = true; run.expectStdOutEqual(""); + test_step.dependOn(&run.step); } diff --git a/test/link/macho/dead_strip/build.zig b/test/link/macho/dead_strip/build.zig index d82c81edca..4131d7baf9 100644 --- a/test/link/macho/dead_strip/build.zig +++ b/test/link/macho/dead_strip/build.zig @@ -1,15 +1,17 @@ const std = @import("std"); +pub const requires_symlinks = true; + pub fn build(b: *std.Build) void { - const optimize = b.standardOptimizeOption(.{}); + const optimize: std.builtin.OptimizeMode = .Debug; const target: std.zig.CrossTarget = .{ .os_tag = .macos }; const test_step = b.step("test", "Test the program"); - test_step.dependOn(b.getInstallStep()); + b.default_step = test_step; { // Without -dead_strip, we expect `iAmUnused` symbol present - const exe = createScenario(b, optimize, target); + const exe = createScenario(b, optimize, target, "no-gc"); const check = exe.checkObject(.macho); check.checkInSymtab(); @@ -22,7 +24,7 @@ pub fn build(b: *std.Build) void { { // With -dead_strip, no `iAmUnused` symbol should be present - const exe = createScenario(b, optimize, target); + const exe = createScenario(b, optimize, target, "yes-gc"); exe.link_gc_sections = true; const check = exe.checkObject(.macho); @@ -39,9 +41,10 @@ fn createScenario( b: *std.Build, optimize: std.builtin.OptimizeMode, target: std.zig.CrossTarget, + name: []const u8, ) *std.Build.CompileStep { const exe = b.addExecutable(.{ - .name = "test", + .name = name, .optimize = optimize, .target = target, }); diff --git a/test/link/macho/dead_strip_dylibs/build.zig b/test/link/macho/dead_strip_dylibs/build.zig index af2f5cf0dc..4a4dda7b7f 100644 --- a/test/link/macho/dead_strip_dylibs/build.zig +++ b/test/link/macho/dead_strip_dylibs/build.zig @@ -1,14 +1,22 @@ const std = @import("std"); +pub const requires_macos_sdk = true; +pub const requires_symlinks = true; + pub fn build(b: *std.Build) void { - const optimize = b.standardOptimizeOption(.{}); + const test_step = b.step("test", "Test it"); + b.default_step = test_step; - const test_step = b.step("test", "Test the program"); - test_step.dependOn(b.getInstallStep()); + add(b, test_step, .Debug); + add(b, test_step, .ReleaseFast); + add(b, test_step, .ReleaseSmall); + add(b, test_step, .ReleaseSafe); +} +fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { { // Without -dead_strip_dylibs we expect `-la` to include liba.dylib in the final executable - const exe = createScenario(b, optimize); + const exe = createScenario(b, optimize, "no-dead-strip"); const check = exe.checkObject(.macho); check.checkStart("cmd LOAD_DYLIB"); @@ -25,18 +33,22 @@ pub fn build(b: *std.Build) void { { // With -dead_strip_dylibs, we should include liba.dylib as it's unreachable - const exe = createScenario(b, optimize); + const exe = createScenario(b, optimize, "yes-dead-strip"); exe.dead_strip_dylibs = true; - const run_cmd = exe.run(); - run_cmd.expected_term = .{ .Exited = @bitCast(u8, @as(i8, -2)) }; // should fail + const run_cmd = b.addRunArtifact(exe); + run_cmd.expectExitCode(@bitCast(u8, @as(i8, -2))); // should fail test_step.dependOn(&run_cmd.step); } } -fn createScenario(b: *std.Build, optimize: std.builtin.OptimizeMode) *std.Build.CompileStep { +fn createScenario( + b: *std.Build, + optimize: std.builtin.OptimizeMode, + name: []const u8, +) *std.Build.CompileStep { const exe = b.addExecutable(.{ - .name = "test", + .name = name, .optimize = optimize, }); exe.addCSourceFile("main.c", &[0][]const u8{}); diff --git a/test/link/macho/dylib/build.zig b/test/link/macho/dylib/build.zig index 7a1e2d862c..bf4a49f50b 100644 --- a/test/link/macho/dylib/build.zig +++ b/test/link/macho/dylib/build.zig @@ -1,11 +1,19 @@ const std = @import("std"); -pub fn build(b: *std.Build) void { - const optimize = b.standardOptimizeOption(.{}); - const target: std.zig.CrossTarget = .{ .os_tag = .macos }; +pub const requires_symlinks = true; - const test_step = b.step("test", "Test"); - test_step.dependOn(b.getInstallStep()); +pub fn build(b: *std.Build) void { + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + + add(b, test_step, .Debug); + add(b, test_step, .ReleaseFast); + add(b, test_step, .ReleaseSmall); + add(b, test_step, .ReleaseSafe); +} + +fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { + const target: std.zig.CrossTarget = .{ .os_tag = .macos }; const dylib = b.addSharedLibrary(.{ .name = "a", @@ -15,7 +23,6 @@ pub fn build(b: *std.Build) void { }); dylib.addCSourceFile("a.c", &.{}); dylib.linkLibC(); - dylib.install(); const check_dylib = dylib.checkObject(.macho); check_dylib.checkStart("cmd ID_DYLIB"); @@ -33,9 +40,9 @@ pub fn build(b: *std.Build) void { }); exe.addCSourceFile("main.c", &.{}); exe.linkSystemLibrary("a"); + exe.addLibraryPathDirectorySource(dylib.getOutputDirectorySource()); + exe.addRPathDirectorySource(dylib.getOutputDirectorySource()); exe.linkLibC(); - exe.addLibraryPath(b.pathFromRoot("zig-out/lib/")); - exe.addRPath(b.pathFromRoot("zig-out/lib")); const check_exe = exe.checkObject(.macho); check_exe.checkStart("cmd LOAD_DYLIB"); @@ -45,10 +52,12 @@ pub fn build(b: *std.Build) void { check_exe.checkNext("compatibility version 10000"); check_exe.checkStart("cmd RPATH"); - check_exe.checkNext(std.fmt.allocPrint(b.allocator, "path {s}", .{b.pathFromRoot("zig-out/lib")}) catch unreachable); + // TODO check this (perhaps with `checkNextFileSource(dylib.getOutputDirectorySource())`) + //check_exe.checkNext(std.fmt.allocPrint(b.allocator, "path {s}", .{ + // b.pathFromRoot("zig-out/lib"), + //}) catch unreachable); const run = check_exe.runAndCompare(); - run.cwd = b.pathFromRoot("."); run.expectStdOutEqual("Hello world"); test_step.dependOn(&run.step); } diff --git a/test/link/macho/empty/build.zig b/test/link/macho/empty/build.zig index 12eb67d9c8..9933746d53 100644 --- a/test/link/macho/empty/build.zig +++ b/test/link/macho/empty/build.zig @@ -1,11 +1,19 @@ const std = @import("std"); -pub fn build(b: *std.Build) void { - const optimize = b.standardOptimizeOption(.{}); - const target: std.zig.CrossTarget = .{ .os_tag = .macos }; +pub const requires_symlinks = true; - const test_step = b.step("test", "Test the program"); - test_step.dependOn(b.getInstallStep()); +pub fn build(b: *std.Build) void { + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + + add(b, test_step, .Debug); + add(b, test_step, .ReleaseFast); + add(b, test_step, .ReleaseSmall); + add(b, test_step, .ReleaseSafe); +} + +fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { + const target: std.zig.CrossTarget = .{ .os_tag = .macos }; const exe = b.addExecutable(.{ .name = "test", diff --git a/test/link/macho/entry/build.zig b/test/link/macho/entry/build.zig index 4504da9c6c..b2c41ef6dc 100644 --- a/test/link/macho/entry/build.zig +++ b/test/link/macho/entry/build.zig @@ -1,11 +1,18 @@ const std = @import("std"); +pub const requires_symlinks = true; + pub fn build(b: *std.Build) void { - const optimize = b.standardOptimizeOption(.{}); + const test_step = b.step("test", "Test it"); + b.default_step = test_step; - const test_step = b.step("test", "Test"); - test_step.dependOn(b.getInstallStep()); + add(b, test_step, .Debug); + add(b, test_step, .ReleaseFast); + add(b, test_step, .ReleaseSmall); + add(b, test_step, .ReleaseSafe); +} +fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { const exe = b.addExecutable(.{ .name = "main", .optimize = optimize, diff --git a/test/link/macho/headerpad/build.zig b/test/link/macho/headerpad/build.zig index 3ef17573f8..07d30ee6f2 100644 --- a/test/link/macho/headerpad/build.zig +++ b/test/link/macho/headerpad/build.zig @@ -1,12 +1,20 @@ const std = @import("std"); const builtin = @import("builtin"); +pub const requires_symlinks = true; +pub const requires_macos_sdk = true; + pub fn build(b: *std.Build) void { - const optimize = b.standardOptimizeOption(.{}); + const test_step = b.step("test", "Test it"); + b.default_step = test_step; - const test_step = b.step("test", "Test"); - test_step.dependOn(b.getInstallStep()); + add(b, test_step, .Debug); + add(b, test_step, .ReleaseFast); + add(b, test_step, .ReleaseSmall); + add(b, test_step, .ReleaseSafe); +} +fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { { // Test -headerpad_max_install_names const exe = simpleExe(b, optimize); diff --git a/test/link/macho/linksection/build.zig b/test/link/macho/linksection/build.zig index 227d4eeb63..37e0b7cbef 100644 --- a/test/link/macho/linksection/build.zig +++ b/test/link/macho/linksection/build.zig @@ -1,11 +1,19 @@ const std = @import("std"); -pub fn build(b: *std.Build) void { - const optimize = b.standardOptimizeOption(.{}); - const target = std.zig.CrossTarget{ .os_tag = .macos }; +pub const requires_symlinks = true; - const test_step = b.step("test", "Test"); - test_step.dependOn(b.getInstallStep()); +pub fn build(b: *std.Build) void { + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + + add(b, test_step, .Debug); + add(b, test_step, .ReleaseFast); + add(b, test_step, .ReleaseSmall); + add(b, test_step, .ReleaseSafe); +} + +fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { + const target = std.zig.CrossTarget{ .os_tag = .macos }; const obj = b.addObject(.{ .name = "test", diff --git a/test/link/macho/needed_framework/build.zig b/test/link/macho/needed_framework/build.zig index 8b6e3dd87f..cd53a5d212 100644 --- a/test/link/macho/needed_framework/build.zig +++ b/test/link/macho/needed_framework/build.zig @@ -1,11 +1,19 @@ const std = @import("std"); +pub const requires_symlinks = true; +pub const requires_macos_sdk = true; + pub fn build(b: *std.Build) void { - const optimize = b.standardOptimizeOption(.{}); + const test_step = b.step("test", "Test it"); + b.default_step = test_step; - const test_step = b.step("test", "Test the program"); - test_step.dependOn(b.getInstallStep()); + add(b, test_step, .Debug); + add(b, test_step, .ReleaseFast); + add(b, test_step, .ReleaseSmall); + add(b, test_step, .ReleaseSafe); +} +fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { // -dead_strip_dylibs // -needed_framework Cocoa const exe = b.addExecutable(.{ diff --git a/test/link/macho/needed_library/build.zig b/test/link/macho/needed_library/build.zig index e8a58d5381..6e24a12c65 100644 --- a/test/link/macho/needed_library/build.zig +++ b/test/link/macho/needed_library/build.zig @@ -1,11 +1,19 @@ const std = @import("std"); -pub fn build(b: *std.Build) void { - const optimize = b.standardOptimizeOption(.{}); - const target: std.zig.CrossTarget = .{ .os_tag = .macos }; +pub const requires_symlinks = true; - const test_step = b.step("test", "Test the program"); - test_step.dependOn(b.getInstallStep()); +pub fn build(b: *std.Build) void { + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + + add(b, test_step, .Debug); + add(b, test_step, .ReleaseFast); + add(b, test_step, .ReleaseSmall); + add(b, test_step, .ReleaseSafe); +} + +fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { + const target: std.zig.CrossTarget = .{ .os_tag = .macos }; const dylib = b.addSharedLibrary(.{ .name = "a", @@ -15,7 +23,6 @@ pub fn build(b: *std.Build) void { }); dylib.addCSourceFile("a.c", &.{}); dylib.linkLibC(); - dylib.install(); // -dead_strip_dylibs // -needed-la @@ -27,8 +34,8 @@ pub fn build(b: *std.Build) void { exe.addCSourceFile("main.c", &[0][]const u8{}); exe.linkLibC(); exe.linkSystemLibraryNeeded("a"); - exe.addLibraryPath(b.pathFromRoot("zig-out/lib")); - exe.addRPath(b.pathFromRoot("zig-out/lib")); + exe.addLibraryPathDirectorySource(dylib.getOutputDirectorySource()); + exe.addRPathDirectorySource(dylib.getOutputDirectorySource()); exe.dead_strip_dylibs = true; const check = exe.checkObject(.macho); diff --git a/test/link/macho/objc/build.zig b/test/link/macho/objc/build.zig index 9398e28a80..4cd12f786f 100644 --- a/test/link/macho/objc/build.zig +++ b/test/link/macho/objc/build.zig @@ -1,10 +1,19 @@ const std = @import("std"); +pub const requires_symlinks = true; +pub const requires_macos_sdk = true; + pub fn build(b: *std.Build) void { - const optimize = b.standardOptimizeOption(.{}); + const test_step = b.step("test", "Test it"); + b.default_step = test_step; - const test_step = b.step("test", "Test the program"); + add(b, test_step, .Debug); + add(b, test_step, .ReleaseFast); + add(b, test_step, .ReleaseSmall); + add(b, test_step, .ReleaseSafe); +} +fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { const exe = b.addExecutable(.{ .name = "test", .optimize = optimize, diff --git a/test/link/macho/objcpp/build.zig b/test/link/macho/objcpp/build.zig index 2a3459be50..06876247a9 100644 --- a/test/link/macho/objcpp/build.zig +++ b/test/link/macho/objcpp/build.zig @@ -1,10 +1,19 @@ const std = @import("std"); +pub const requires_symlinks = true; +pub const requires_macos_sdk = true; + pub fn build(b: *std.Build) void { - const optimize = b.standardOptimizeOption(.{}); + const test_step = b.step("test", "Test it"); + b.default_step = test_step; - const test_step = b.step("test", "Test the program"); + add(b, test_step, .Debug); + add(b, test_step, .ReleaseFast); + add(b, test_step, .ReleaseSmall); + add(b, test_step, .ReleaseSafe); +} +fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { const exe = b.addExecutable(.{ .name = "test", .optimize = optimize, diff --git a/test/link/macho/pagezero/build.zig b/test/link/macho/pagezero/build.zig index 0a8471b919..e7b002a389 100644 --- a/test/link/macho/pagezero/build.zig +++ b/test/link/macho/pagezero/build.zig @@ -1,11 +1,13 @@ const std = @import("std"); -pub fn build(b: *std.Build) void { - const optimize = b.standardOptimizeOption(.{}); - const target: std.zig.CrossTarget = .{ .os_tag = .macos }; +pub const requires_symlinks = true; - const test_step = b.step("test", "Test"); - test_step.dependOn(b.getInstallStep()); +pub fn build(b: *std.Build) void { + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + + const optimize: std.builtin.OptimizeMode = .Debug; + const target: std.zig.CrossTarget = .{ .os_tag = .macos }; { const exe = b.addExecutable(.{ diff --git a/test/link/macho/search_strategy/build.zig b/test/link/macho/search_strategy/build.zig index a8a4167865..ae1037c2d7 100644 --- a/test/link/macho/search_strategy/build.zig +++ b/test/link/macho/search_strategy/build.zig @@ -1,35 +1,41 @@ const std = @import("std"); -pub fn build(b: *std.Build) void { - const optimize = b.standardOptimizeOption(.{}); - const target: std.zig.CrossTarget = .{ .os_tag = .macos }; +pub const requires_symlinks = true; - const test_step = b.step("test", "Test"); - test_step.dependOn(b.getInstallStep()); +pub fn build(b: *std.Build) void { + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + + add(b, test_step, .Debug); + add(b, test_step, .ReleaseFast); + add(b, test_step, .ReleaseSmall); + add(b, test_step, .ReleaseSafe); +} + +fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { + const target: std.zig.CrossTarget = .{ .os_tag = .macos }; { // -search_dylibs_first - const exe = createScenario(b, optimize, target); + const exe = createScenario(b, optimize, target, "search_dylibs_first"); exe.search_strategy = .dylibs_first; const check = exe.checkObject(.macho); check.checkStart("cmd LOAD_DYLIB"); - check.checkNext("name @rpath/liba.dylib"); + check.checkNext("name @rpath/libsearch_dylibs_first.dylib"); const run = check.runAndCompare(); - run.cwd = b.pathFromRoot("."); run.expectStdOutEqual("Hello world"); test_step.dependOn(&run.step); } { // -search_paths_first - const exe = createScenario(b, optimize, target); + const exe = createScenario(b, optimize, target, "search_paths_first"); exe.search_strategy = .paths_first; const run = b.addRunArtifact(exe); run.skip_foreign_checks = true; - run.cwd = b.pathFromRoot("."); run.expectStdOutEqual("Hello world"); test_step.dependOn(&run.step); } @@ -39,9 +45,10 @@ fn createScenario( b: *std.Build, optimize: std.builtin.OptimizeMode, target: std.zig.CrossTarget, + name: []const u8, ) *std.Build.CompileStep { const static = b.addStaticLibrary(.{ - .name = "a", + .name = name, .optimize = optimize, .target = target, }); @@ -50,10 +57,9 @@ fn createScenario( static.override_dest_dir = std.Build.InstallDir{ .custom = "static", }; - static.install(); const dylib = b.addSharedLibrary(.{ - .name = "a", + .name = name, .version = .{ .major = 1, .minor = 0 }, .optimize = optimize, .target = target, @@ -63,18 +69,17 @@ fn createScenario( dylib.override_dest_dir = std.Build.InstallDir{ .custom = "dynamic", }; - dylib.install(); const exe = b.addExecutable(.{ - .name = "main", + .name = name, .optimize = optimize, .target = target, }); exe.addCSourceFile("main.c", &.{}); - exe.linkSystemLibraryName("a"); + exe.linkSystemLibraryName(name); exe.linkLibC(); - exe.addLibraryPath(b.pathFromRoot("zig-out/static")); - exe.addLibraryPath(b.pathFromRoot("zig-out/dynamic")); - exe.addRPath(b.pathFromRoot("zig-out/dynamic")); + exe.addLibraryPathDirectorySource(static.getOutputDirectorySource()); + exe.addLibraryPathDirectorySource(dylib.getOutputDirectorySource()); + exe.addRPathDirectorySource(dylib.getOutputDirectorySource()); return exe; } diff --git a/test/link/macho/stack_size/build.zig b/test/link/macho/stack_size/build.zig index 874f53fbff..51efed4c34 100644 --- a/test/link/macho/stack_size/build.zig +++ b/test/link/macho/stack_size/build.zig @@ -1,11 +1,19 @@ const std = @import("std"); -pub fn build(b: *std.Build) void { - const optimize = b.standardOptimizeOption(.{}); - const target: std.zig.CrossTarget = .{ .os_tag = .macos }; +pub const requires_symlinks = true; - const test_step = b.step("test", "Test"); - test_step.dependOn(b.getInstallStep()); +pub fn build(b: *std.Build) void { + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + + add(b, test_step, .Debug); + add(b, test_step, .ReleaseFast); + add(b, test_step, .ReleaseSmall); + add(b, test_step, .ReleaseSafe); +} + +fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { + const target: std.zig.CrossTarget = .{ .os_tag = .macos }; const exe = b.addExecutable(.{ .name = "main", diff --git a/test/link/macho/strict_validation/build.zig b/test/link/macho/strict_validation/build.zig index 408076657b..a95793234b 100644 --- a/test/link/macho/strict_validation/build.zig +++ b/test/link/macho/strict_validation/build.zig @@ -1,12 +1,20 @@ const std = @import("std"); const builtin = @import("builtin"); -pub fn build(b: *std.Build) void { - const optimize = b.standardOptimizeOption(.{}); - const target: std.zig.CrossTarget = .{ .os_tag = .macos }; +pub const requires_symlinks = true; - const test_step = b.step("test", "Test"); - test_step.dependOn(b.getInstallStep()); +pub fn build(b: *std.Build) void { + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + + add(b, test_step, .Debug); + add(b, test_step, .ReleaseFast); + add(b, test_step, .ReleaseSmall); + add(b, test_step, .ReleaseSafe); +} + +fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { + const target: std.zig.CrossTarget = .{ .os_tag = .macos }; const exe = b.addExecutable(.{ .name = "main", diff --git a/test/link/macho/tls/build.zig b/test/link/macho/tls/build.zig index c77588cb5d..e91f40bd59 100644 --- a/test/link/macho/tls/build.zig +++ b/test/link/macho/tls/build.zig @@ -1,7 +1,18 @@ const std = @import("std"); +pub const requires_symlinks = true; + pub fn build(b: *std.Build) void { - const optimize = b.standardOptimizeOption(.{}); + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + + add(b, test_step, .Debug); + add(b, test_step, .ReleaseFast); + add(b, test_step, .ReleaseSmall); + add(b, test_step, .ReleaseSafe); +} + +fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { const target: std.zig.CrossTarget = .{ .os_tag = .macos }; const lib = b.addSharedLibrary(.{ @@ -21,6 +32,5 @@ pub fn build(b: *std.Build) void { test_exe.linkLibrary(lib); test_exe.linkLibC(); - const test_step = b.step("test", "Test it"); test_step.dependOn(&test_exe.step); } diff --git a/test/link/macho/unwind_info/build.zig b/test/link/macho/unwind_info/build.zig index 408f762f5d..b9bcea8358 100644 --- a/test/link/macho/unwind_info/build.zig +++ b/test/link/macho/unwind_info/build.zig @@ -1,14 +1,23 @@ const std = @import("std"); const builtin = @import("builtin"); +pub const requires_symlinks = true; + pub fn build(b: *std.Build) void { - const optimize = b.standardOptimizeOption(.{}); + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + + add(b, test_step, .Debug); + add(b, test_step, .ReleaseFast); + add(b, test_step, .ReleaseSmall); + add(b, test_step, .ReleaseSafe); +} + +fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { const target: std.zig.CrossTarget = .{ .os_tag = .macos }; - const test_step = b.step("test", "Test the program"); - - testUnwindInfo(b, test_step, optimize, target, false); - testUnwindInfo(b, test_step, optimize, target, true); + testUnwindInfo(b, test_step, optimize, target, false, "no-dead-strip"); + testUnwindInfo(b, test_step, optimize, target, true, "yes-dead-strip"); } fn testUnwindInfo( @@ -17,8 +26,9 @@ fn testUnwindInfo( optimize: std.builtin.OptimizeMode, target: std.zig.CrossTarget, dead_strip: bool, + name: []const u8, ) void { - const exe = createScenario(b, optimize, target); + const exe = createScenario(b, optimize, target, name); exe.link_gc_sections = dead_strip; const check = exe.checkObject(.macho); @@ -54,9 +64,10 @@ fn createScenario( b: *std.Build, optimize: std.builtin.OptimizeMode, target: std.zig.CrossTarget, + name: []const u8, ) *std.Build.CompileStep { const exe = b.addExecutable(.{ - .name = "test", + .name = name, .optimize = optimize, .target = target, }); diff --git a/test/link/macho/uuid/build.zig b/test/link/macho/uuid/build.zig index 6ff45e1175..df58aeacb7 100644 --- a/test/link/macho/uuid/build.zig +++ b/test/link/macho/uuid/build.zig @@ -3,11 +3,14 @@ const CompileStep = std.Build.CompileStep; const FileSource = std.Build.FileSource; const Step = std.Build.Step; +pub const requires_symlinks = true; + pub fn build(b: *std.Build) void { const test_step = b.step("test", "Test"); - test_step.dependOn(b.getInstallStep()); + b.default_step = test_step; - // We force cross-compilation to ensure we always pick a generic CPU with constant set of CPU features. + // We force cross-compilation to ensure we always pick a generic CPU with + // constant set of CPU features. const aarch64_macos = std.zig.CrossTarget{ .cpu_arch = .aarch64, .os_tag = .macos, diff --git a/test/link/macho/weak_framework/build.zig b/test/link/macho/weak_framework/build.zig index ca28458d77..a4e1d6e5d9 100644 --- a/test/link/macho/weak_framework/build.zig +++ b/test/link/macho/weak_framework/build.zig @@ -1,11 +1,19 @@ const std = @import("std"); +pub const requires_symlinks = true; +pub const requires_macos_sdk = true; + pub fn build(b: *std.Build) void { - const optimize = b.standardOptimizeOption(.{}); + const test_step = b.step("test", "Test it"); + b.default_step = test_step; - const test_step = b.step("test", "Test the program"); - test_step.dependOn(b.getInstallStep()); + add(b, test_step, .Debug); + add(b, test_step, .ReleaseFast); + add(b, test_step, .ReleaseSmall); + add(b, test_step, .ReleaseSafe); +} +fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { const exe = b.addExecutable(.{ .name = "test", .optimize = optimize, diff --git a/test/link/macho/weak_library/build.zig b/test/link/macho/weak_library/build.zig index de5aa45e30..ac265689e3 100644 --- a/test/link/macho/weak_library/build.zig +++ b/test/link/macho/weak_library/build.zig @@ -1,11 +1,19 @@ const std = @import("std"); -pub fn build(b: *std.Build) void { - const optimize = b.standardOptimizeOption(.{}); - const target: std.zig.CrossTarget = .{ .os_tag = .macos }; +pub const requires_symlinks = true; - const test_step = b.step("test", "Test the program"); - test_step.dependOn(b.getInstallStep()); +pub fn build(b: *std.Build) void { + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + + add(b, test_step, .Debug); + add(b, test_step, .ReleaseFast); + add(b, test_step, .ReleaseSmall); + add(b, test_step, .ReleaseSafe); +} + +fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { + const target: std.zig.CrossTarget = .{ .os_tag = .macos }; const dylib = b.addSharedLibrary(.{ .name = "a", @@ -25,8 +33,8 @@ pub fn build(b: *std.Build) void { exe.addCSourceFile("main.c", &[0][]const u8{}); exe.linkLibC(); exe.linkSystemLibraryWeak("a"); - exe.addLibraryPath(b.pathFromRoot("zig-out/lib")); - exe.addRPath(b.pathFromRoot("zig-out/lib")); + exe.addLibraryPathDirectorySource(dylib.getOutputDirectorySource()); + exe.addRPathDirectorySource(dylib.getOutputDirectorySource()); const check = exe.checkObject(.macho); check.checkStart("cmd LOAD_WEAK_DYLIB"); diff --git a/test/tests.zig b/test/tests.zig index a23870b79e..a1945246a3 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -590,23 +590,25 @@ pub fn addStandaloneTests( pub fn addLinkTests( b: *std.Build, - test_filter: ?[]const u8, - optimize_modes: []const OptimizeMode, enable_macos_sdk: bool, omit_stage2: bool, enable_symlinks_windows: bool, ) *Step { - _ = test_filter; - _ = optimize_modes; - _ = enable_macos_sdk; - _ = enable_symlinks_windows; - const step = b.step("test-link", "Run the linker tests"); + const omit_symlinks = builtin.os.tag == .windows and !enable_symlinks_windows; inline for (link.cases) |link_test| { const requires_stage2 = @hasDecl(link_test.import, "requires_stage2") and link_test.import.requires_stage2; - if (!requires_stage2 or !omit_stage2) { + const requires_symlinks = @hasDecl(link_test.import, "requires_symlinks") and + link_test.import.requires_symlinks; + const requires_macos_sdk = @hasDecl(link_test.import, "requires_macos_sdk") and + link_test.import.requires_macos_sdk; + const bad = + (requires_stage2 and omit_stage2) or + (requires_symlinks and omit_symlinks) or + (requires_macos_sdk and !enable_macos_sdk); + if (!bad) { const dep = b.anonymousDependency(link_test.build_root, link_test.import, .{}); const dep_step = dep.builder.default_step; assert(mem.startsWith(u8, dep.builder.dep_prefix, "test.")); From 15c4fae1c93d970d02a9eede96371fb7c053837f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 7 Mar 2023 22:40:53 -0700 Subject: [PATCH 220/294] re-enable the simple standalone tests --- build.zig | 19 ++-- test/link.zig | 3 - test/src/Standalone.zig | 141 ----------------------- test/standalone.zig | 243 ++++++++++++++++++++++------------------ test/tests.zig | 40 ++++--- 5 files changed, 167 insertions(+), 279 deletions(-) delete mode 100644 test/src/Standalone.zig diff --git a/build.zig b/build.zig index b181eaefae..c64015fe88 100644 --- a/build.zig +++ b/build.zig @@ -443,16 +443,15 @@ pub fn build(b: *std.Build) !void { })); test_step.dependOn(tests.addCompareOutputTests(b, test_filter, optimization_modes)); - //test_step.dependOn(tests.addStandaloneTests( - // b, - // test_filter, - // optimization_modes, - // skip_non_native, - // enable_macos_sdk, - // target, - // skip_stage2_tests, - // enable_symlinks_windows, - //)); + test_step.dependOn(tests.addStandaloneTests( + b, + test_filter, + optimization_modes, + skip_non_native, + enable_macos_sdk, + skip_stage2_tests, + enable_symlinks_windows, + )); test_step.dependOn(tests.addCAbiTests(b, skip_non_native, skip_release)); test_step.dependOn(tests.addLinkTests(b, enable_macos_sdk, skip_stage2_tests, enable_symlinks_windows)); test_step.dependOn(tests.addStackTraceTests(b, test_filter, optimization_modes)); diff --git a/test/link.zig b/test/link.zig index fb18e27c22..610cbd4a3d 100644 --- a/test/link.zig +++ b/test/link.zig @@ -174,6 +174,3 @@ pub const cases = [_]Case{ .import = @import("link/macho/weak_framework/build.zig"), }, }; - -const std = @import("std"); -const builtin = @import("builtin"); diff --git a/test/src/Standalone.zig b/test/src/Standalone.zig deleted file mode 100644 index c07bb511c5..0000000000 --- a/test/src/Standalone.zig +++ /dev/null @@ -1,141 +0,0 @@ -b: *std.Build, -step: *Step, -test_index: usize, -test_filter: ?[]const u8, -optimize_modes: []const OptimizeMode, -skip_non_native: bool, -enable_macos_sdk: bool, -target: std.zig.CrossTarget, -omit_stage2: bool, -enable_darling: bool = false, -enable_qemu: bool = false, -enable_rosetta: bool = false, -enable_wasmtime: bool = false, -enable_wine: bool = false, -enable_symlinks_windows: bool, - -pub fn addC(self: *Standalone, root_src: []const u8) void { - self.addAllArgs(root_src, true); -} - -pub fn add(self: *Standalone, root_src: []const u8) void { - self.addAllArgs(root_src, false); -} - -pub fn addBuildFile(self: *Standalone, build_file: []const u8, features: struct { - build_modes: bool = false, - cross_targets: bool = false, - requires_macos_sdk: bool = false, - requires_stage2: bool = false, - use_emulation: bool = false, - requires_symlinks: bool = false, - extra_argv: []const []const u8 = &.{}, -}) void { - const b = self.b; - - if (features.requires_macos_sdk and !self.enable_macos_sdk) return; - if (features.requires_stage2 and self.omit_stage2) return; - if (features.requires_symlinks and !self.enable_symlinks_windows and builtin.os.tag == .windows) return; - - const annotated_case_name = b.fmt("build {s}", .{build_file}); - if (self.test_filter) |filter| { - if (mem.indexOf(u8, annotated_case_name, filter) == null) return; - } - - var zig_args = ArrayList([]const u8).init(b.allocator); - const rel_zig_exe = fs.path.relative(b.allocator, b.build_root.path orelse ".", b.zig_exe) catch unreachable; - zig_args.append(rel_zig_exe) catch unreachable; - zig_args.append("build") catch unreachable; - - // TODO: fix the various non-concurrency-safe issues in zig's standalone tests, - // and then remove this! - zig_args.append("-j1") catch @panic("OOM"); - - zig_args.append("--build-file") catch unreachable; - zig_args.append(b.pathFromRoot(build_file)) catch unreachable; - - zig_args.appendSlice(features.extra_argv) catch unreachable; - - zig_args.append("test") catch unreachable; - - if (b.verbose) { - zig_args.append("--verbose") catch unreachable; - } - - if (features.cross_targets and !self.target.isNative()) { - const target_triple = self.target.zigTriple(b.allocator) catch unreachable; - const target_arg = fmt.allocPrint(b.allocator, "-Dtarget={s}", .{target_triple}) catch unreachable; - zig_args.append(target_arg) catch unreachable; - } - - if (features.use_emulation) { - if (self.enable_darling) { - zig_args.append("-fdarling") catch unreachable; - } - if (self.enable_qemu) { - zig_args.append("-fqemu") catch unreachable; - } - if (self.enable_rosetta) { - zig_args.append("-frosetta") catch unreachable; - } - if (self.enable_wasmtime) { - zig_args.append("-fwasmtime") catch unreachable; - } - if (self.enable_wine) { - zig_args.append("-fwine") catch unreachable; - } - } - - const optimize_modes = if (features.build_modes) self.optimize_modes else &[1]OptimizeMode{.Debug}; - for (optimize_modes) |optimize_mode| { - const arg = switch (optimize_mode) { - .Debug => "", - .ReleaseFast => "-Doptimize=ReleaseFast", - .ReleaseSafe => "-Doptimize=ReleaseSafe", - .ReleaseSmall => "-Doptimize=ReleaseSmall", - }; - const zig_args_base_len = zig_args.items.len; - if (arg.len > 0) - zig_args.append(arg) catch unreachable; - defer zig_args.resize(zig_args_base_len) catch unreachable; - - const run_cmd = b.addSystemCommand(zig_args.items); - self.step.dependOn(&run_cmd.step); - } -} - -pub fn addAllArgs(self: *Standalone, root_src: []const u8, link_libc: bool) void { - const b = self.b; - - for (self.optimize_modes) |optimize| { - const annotated_case_name = fmt.allocPrint(self.b.allocator, "build {s} ({s})", .{ - root_src, - @tagName(optimize), - }) catch unreachable; - if (self.test_filter) |filter| { - if (mem.indexOf(u8, annotated_case_name, filter) == null) continue; - } - - const exe = b.addExecutable(.{ - .name = "test", - .root_source_file = .{ .path = root_src }, - .optimize = optimize, - .target = .{}, - }); - if (link_libc) { - exe.linkSystemLibrary("c"); - } - - self.step.dependOn(&exe.step); - } -} - -const Standalone = @This(); -const std = @import("std"); -const builtin = @import("builtin"); -const Step = std.Build.Step; -const OptimizeMode = std.builtin.OptimizeMode; -const fmt = std.fmt; -const mem = std.mem; -const ArrayList = std.ArrayList; -const fs = std.fs; diff --git a/test/standalone.zig b/test/standalone.zig index 7aa4d81f97..fa67d6a14d 100644 --- a/test/standalone.zig +++ b/test/standalone.zig @@ -1,117 +1,144 @@ -const std = @import("std"); -const builtin = @import("builtin"); -const tests = @import("tests.zig"); +pub const SimpleCase = struct { + src_path: []const u8, + link_libc: bool = false, + all_modes: bool = false, + target: std.zig.CrossTarget = .{}, +}; -pub fn addCases(cases: *tests.StandaloneContext) void { - cases.add("test/standalone/hello_world/hello.zig"); - cases.addC("test/standalone/hello_world/hello_libc.zig"); +pub const BuildCase = struct { + build_root: []const u8, + import: type, +}; - cases.addBuildFile("test/standalone/options/build.zig", .{ - .extra_argv = &.{ - "-Dbool_true", - "-Dbool_false=false", - "-Dint=1234", - "-De=two", - "-Dstring=hello", - }, - }); +pub const simple_cases = [_]SimpleCase{ + .{ + .src_path = "test/standalone/hello_world/hello.zig", + .all_modes = true, + }, + .{ + .src_path = "test/standalone/hello_world/hello_libc.zig", + .link_libc = true, + .all_modes = true, + }, + .{ + .src_path = "test/standalone/cat/main.zig", + }, + // https://github.com/ziglang/zig/issues/6025 + //.{ + // .src_path = "test/standalone/issue_9693/main.zig", + //}, - cases.add("test/standalone/cat/main.zig"); - if (builtin.zig_backend == .stage1) { // https://github.com/ziglang/zig/issues/6025 - cases.add("test/standalone/issue_9693/main.zig"); - } - cases.add("test/standalone/issue_12471/main.zig"); - cases.add("test/standalone/guess_number/main.zig"); - cases.add("test/standalone/main_return_error/error_u8.zig"); - cases.add("test/standalone/main_return_error/error_u8_non_zero.zig"); - cases.add("test/standalone/noreturn_call/inline.zig"); - cases.add("test/standalone/noreturn_call/as_arg.zig"); - cases.addBuildFile("test/standalone/test_runner_path/build.zig", .{ .requires_stage2 = true }); - cases.addBuildFile("test/standalone/issue_13970/build.zig", .{}); - cases.addBuildFile("test/standalone/main_pkg_path/build.zig", .{}); - cases.addBuildFile("test/standalone/shared_library/build.zig", .{}); - cases.addBuildFile("test/standalone/mix_o_files/build.zig", .{}); - cases.addBuildFile("test/standalone/mix_c_files/build.zig", .{ - .build_modes = true, - .cross_targets = true, - }); - cases.addBuildFile("test/standalone/global_linkage/build.zig", .{}); - cases.addBuildFile("test/standalone/static_c_lib/build.zig", .{}); - cases.addBuildFile("test/standalone/issue_339/build.zig", .{}); - cases.addBuildFile("test/standalone/issue_8550/build.zig", .{}); - cases.addBuildFile("test/standalone/issue_794/build.zig", .{}); - cases.addBuildFile("test/standalone/issue_5825/build.zig", .{}); - cases.addBuildFile("test/standalone/pkg_import/build.zig", .{}); - cases.addBuildFile("test/standalone/use_alias/build.zig", .{}); - cases.addBuildFile("test/standalone/brace_expansion/build.zig", .{}); - if (builtin.os.tag != .windows or builtin.cpu.arch != .aarch64) { - // https://github.com/ziglang/zig/issues/13685 - cases.addBuildFile("test/standalone/empty_env/build.zig", .{}); - } - cases.addBuildFile("test/standalone/issue_7030/build.zig", .{}); - cases.addBuildFile("test/standalone/install_raw_hex/build.zig", .{}); - if (builtin.zig_backend == .stage1) { // https://github.com/ziglang/zig/issues/12194 - cases.addBuildFile("test/standalone/issue_9812/build.zig", .{}); - } - if (builtin.os.tag != .windows) { - // https://github.com/ziglang/zig/issues/12419 - cases.addBuildFile("test/standalone/issue_11595/build.zig", .{}); - } + .{ .src_path = "test/standalone/issue_12471/main.zig" }, + .{ .src_path = "test/standalone/guess_number/main.zig" }, + .{ .src_path = "test/standalone/main_return_error/error_u8.zig" }, + .{ .src_path = "test/standalone/main_return_error/error_u8_non_zero.zig" }, + .{ .src_path = "test/standalone/noreturn_call/inline.zig" }, + .{ .src_path = "test/standalone/noreturn_call/as_arg.zig" }, - if (builtin.os.tag != .wasi and - // https://github.com/ziglang/zig/issues/13550 - (builtin.os.tag != .macos or builtin.cpu.arch != .aarch64) and - // https://github.com/ziglang/zig/issues/13686 - (builtin.os.tag != .windows or builtin.cpu.arch != .aarch64)) - { - cases.addBuildFile("test/standalone/load_dynamic_library/build.zig", .{}); - } - - if (builtin.os.tag == .windows) { - cases.addBuildFile("test/standalone/windows_spawn/build.zig", .{}); - } - - cases.addBuildFile("test/standalone/c_compiler/build.zig", .{ - .build_modes = true, - .cross_targets = true, - }); - - if (builtin.os.tag == .windows) { - cases.addC("test/standalone/issue_9402/main.zig"); - } - // Try to build and run a PIE executable. - if (builtin.os.tag == .linux) { - cases.addBuildFile("test/standalone/pie/build.zig", .{}); - } - cases.addBuildFile("test/standalone/issue_12706/build.zig", .{}); - if (std.os.have_sigpipe_support) { - cases.addBuildFile("test/standalone/sigpipe/build.zig", .{}); - } + .{ + .src_path = "test/standalone/issue_9402/main.zig", + .target = .{ .os_tag = .windows }, + .link_libc = true, + }, // Ensure the development tools are buildable. Alphabetically sorted. // No need to build `tools/spirv/grammar.zig`. - cases.add("tools/extract-grammar.zig"); - cases.add("tools/gen_outline_atomics.zig"); - cases.add("tools/gen_spirv_spec.zig"); - cases.add("tools/gen_stubs.zig"); - cases.add("tools/generate_linux_syscalls.zig"); - cases.add("tools/process_headers.zig"); - cases.add("tools/update-license-headers.zig"); - cases.add("tools/update-linux-headers.zig"); - cases.add("tools/update_clang_options.zig"); - cases.add("tools/update_cpu_features.zig"); - cases.add("tools/update_glibc.zig"); - cases.add("tools/update_spirv_features.zig"); + .{ .src_path = "tools/extract-grammar.zig" }, + .{ .src_path = "tools/gen_outline_atomics.zig" }, + .{ .src_path = "tools/gen_spirv_spec.zig" }, + .{ .src_path = "tools/gen_stubs.zig" }, + .{ .src_path = "tools/generate_linux_syscalls.zig" }, + .{ .src_path = "tools/process_headers.zig" }, + .{ .src_path = "tools/update-license-headers.zig" }, + .{ .src_path = "tools/update-linux-headers.zig" }, + .{ .src_path = "tools/update_clang_options.zig" }, + .{ .src_path = "tools/update_cpu_features.zig" }, + .{ .src_path = "tools/update_glibc.zig" }, + .{ .src_path = "tools/update_spirv_features.zig" }, +}; - cases.addBuildFile("test/standalone/issue_13030/build.zig", .{ .build_modes = true }); - cases.addBuildFile("test/standalone/emit_asm_and_bin/build.zig", .{}); - cases.addBuildFile("test/standalone/issue_12588/build.zig", .{}); - cases.addBuildFile("test/standalone/embed_generated_file/build.zig", .{}); - cases.addBuildFile("test/standalone/extern/build.zig", .{}); +pub const build_cases = [_]BuildCase{}; - cases.addBuildFile("test/standalone/dep_diamond/build.zig", .{}); - cases.addBuildFile("test/standalone/dep_triangle/build.zig", .{}); - cases.addBuildFile("test/standalone/dep_recursive/build.zig", .{}); - cases.addBuildFile("test/standalone/dep_mutually_recursive/build.zig", .{}); - cases.addBuildFile("test/standalone/dep_shared_builtin/build.zig", .{}); -} +//pub fn addCases(cases: *tests.StandaloneContext) void { +// cases.addBuildFile("test/standalone/options/build.zig", .{ +// .extra_argv = &.{ +// "-Dbool_true", +// "-Dbool_false=false", +// "-Dint=1234", +// "-De=two", +// "-Dstring=hello", +// }, +// }); +// +// cases.addBuildFile("test/standalone/test_runner_path/build.zig", .{ .requires_stage2 = true }); +// cases.addBuildFile("test/standalone/issue_13970/build.zig", .{}); +// cases.addBuildFile("test/standalone/main_pkg_path/build.zig", .{}); +// cases.addBuildFile("test/standalone/shared_library/build.zig", .{}); +// cases.addBuildFile("test/standalone/mix_o_files/build.zig", .{}); +// cases.addBuildFile("test/standalone/mix_c_files/build.zig", .{ +// .build_modes = true, +// .cross_targets = true, +// }); +// cases.addBuildFile("test/standalone/global_linkage/build.zig", .{}); +// cases.addBuildFile("test/standalone/static_c_lib/build.zig", .{}); +// cases.addBuildFile("test/standalone/issue_339/build.zig", .{}); +// cases.addBuildFile("test/standalone/issue_8550/build.zig", .{}); +// cases.addBuildFile("test/standalone/issue_794/build.zig", .{}); +// cases.addBuildFile("test/standalone/issue_5825/build.zig", .{}); +// cases.addBuildFile("test/standalone/pkg_import/build.zig", .{}); +// cases.addBuildFile("test/standalone/use_alias/build.zig", .{}); +// cases.addBuildFile("test/standalone/brace_expansion/build.zig", .{}); +// if (builtin.os.tag != .windows or builtin.cpu.arch != .aarch64) { +// // https://github.com/ziglang/zig/issues/13685 +// cases.addBuildFile("test/standalone/empty_env/build.zig", .{}); +// } +// cases.addBuildFile("test/standalone/issue_7030/build.zig", .{}); +// cases.addBuildFile("test/standalone/install_raw_hex/build.zig", .{}); +// if (builtin.zig_backend == .stage1) { // https://github.com/ziglang/zig/issues/12194 +// cases.addBuildFile("test/standalone/issue_9812/build.zig", .{}); +// } +// if (builtin.os.tag != .windows) { +// // https://github.com/ziglang/zig/issues/12419 +// cases.addBuildFile("test/standalone/issue_11595/build.zig", .{}); +// } +// +// if (builtin.os.tag != .wasi and +// // https://github.com/ziglang/zig/issues/13550 +// (builtin.os.tag != .macos or builtin.cpu.arch != .aarch64) and +// // https://github.com/ziglang/zig/issues/13686 +// (builtin.os.tag != .windows or builtin.cpu.arch != .aarch64)) +// { +// cases.addBuildFile("test/standalone/load_dynamic_library/build.zig", .{}); +// } +// +// if (builtin.os.tag == .windows) { +// cases.addBuildFile("test/standalone/windows_spawn/build.zig", .{}); +// } +// +// cases.addBuildFile("test/standalone/c_compiler/build.zig", .{ +// .build_modes = true, +// .cross_targets = true, +// }); +// +// // Try to build and run a PIE executable. +// if (builtin.os.tag == .linux) { +// cases.addBuildFile("test/standalone/pie/build.zig", .{}); +// } +// cases.addBuildFile("test/standalone/issue_12706/build.zig", .{}); +// if (std.os.have_sigpipe_support) { +// cases.addBuildFile("test/standalone/sigpipe/build.zig", .{}); +// } +// +// cases.addBuildFile("test/standalone/issue_13030/build.zig", .{ .build_modes = true }); +// cases.addBuildFile("test/standalone/emit_asm_and_bin/build.zig", .{}); +// cases.addBuildFile("test/standalone/issue_12588/build.zig", .{}); +// cases.addBuildFile("test/standalone/embed_generated_file/build.zig", .{}); +// +// cases.addBuildFile("test/standalone/dep_diamond/build.zig", .{}); +// cases.addBuildFile("test/standalone/dep_triangle/build.zig", .{}); +// cases.addBuildFile("test/standalone/dep_recursive/build.zig", .{}); +// cases.addBuildFile("test/standalone/dep_mutually_recursive/build.zig", .{}); +// cases.addBuildFile("test/standalone/dep_shared_builtin/build.zig", .{}); +//} + +const std = @import("std"); diff --git a/test/tests.zig b/test/tests.zig index a1945246a3..038111136a 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -20,7 +20,6 @@ pub const TranslateCContext = @import("src/translate_c.zig").TranslateCContext; pub const RunTranslatedCContext = @import("src/run_translated_c.zig").RunTranslatedCContext; pub const CompareOutputContext = @import("src/CompareOutput.zig"); pub const StackTracesContext = @import("src/StackTrace.zig"); -pub const StandaloneContext = @import("src/Standalone.zig"); const TestTarget = struct { target: CrossTarget = @as(CrossTarget, .{}), @@ -565,27 +564,34 @@ pub fn addStandaloneTests( optimize_modes: []const OptimizeMode, skip_non_native: bool, enable_macos_sdk: bool, - target: std.zig.CrossTarget, omit_stage2: bool, enable_symlinks_windows: bool, ) *Step { - const cases = b.allocator.create(StandaloneContext) catch @panic("OOM"); - cases.* = .{ - .b = b, - .step = b.step("test-standalone", "Run the standalone tests"), - .test_index = 0, - .test_filter = test_filter, - .optimize_modes = optimize_modes, - .skip_non_native = skip_non_native, - .enable_macos_sdk = enable_macos_sdk, - .target = target, - .omit_stage2 = omit_stage2, - .enable_symlinks_windows = enable_symlinks_windows, - }; + const step = b.step("test-standalone", "Run the standalone tests"); - standalone.addCases(cases); + _ = test_filter; + _ = skip_non_native; + _ = enable_macos_sdk; + _ = omit_stage2; + _ = enable_symlinks_windows; - return cases.step; + for (standalone.simple_cases) |case| { + for (optimize_modes) |optimize| { + if (!case.all_modes and optimize != .Debug) continue; + + const exe = b.addExecutable(.{ + .name = std.fs.path.stem(case.src_path), + .root_source_file = .{ .path = case.src_path }, + .optimize = optimize, + .target = case.target, + }); + if (case.link_libc) exe.linkLibC(); + + step.dependOn(&exe.step); + } + } + + return step; } pub fn addLinkTests( From 030742f1f73d0c9a237d7512d894e88b05ad53af Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 8 Mar 2023 00:16:16 -0700 Subject: [PATCH 221/294] re-enable standalone tests based on build.zig --- build.zig | 4 +- test/standalone.zig | 234 +++++++++++------- .../main.zig => brace_expansion.zig} | 4 +- test/standalone/brace_expansion/build.zig | 11 - test/standalone/c_compiler/build.zig | 41 ++- test/standalone/dep_diamond/build.zig | 6 +- .../dep_mutually_recursive/build.zig | 6 +- test/standalone/dep_recursive/build.zig | 6 +- test/standalone/dep_shared_builtin/build.zig | 6 +- test/standalone/dep_triangle/build.zig | 6 +- .../standalone/embed_generated_file/build.zig | 8 +- test/standalone/emit_asm_and_bin/build.zig | 4 +- test/standalone/empty_env/build.zig | 16 +- test/standalone/global_linkage/build.zig | 11 +- test/standalone/install_raw_hex/build.zig | 6 +- test/standalone/issue_11595/build.zig | 33 ++- test/standalone/issue_12588/build.zig | 8 +- test/standalone/issue_12706/build.zig | 30 +-- test/standalone/issue_13030/build.zig | 15 +- test/standalone/issue_13970/build.zig | 4 +- test/standalone/issue_339/build.zig | 11 +- test/standalone/issue_5825/build.zig | 6 +- .../{issue_7030/main.zig => issue_7030.zig} | 0 test/standalone/issue_7030/build.zig | 17 -- test/standalone/issue_794/build.zig | 6 +- test/standalone/issue_8550/build.zig | 7 +- test/standalone/issue_9812/build.zig | 7 +- .../standalone/load_dynamic_library/build.zig | 20 +- test/standalone/load_dynamic_library/main.zig | 6 +- test/standalone/main_pkg_path/build.zig | 4 +- test/standalone/mix_c_files/build.zig | 34 ++- test/standalone/mix_o_files/build.zig | 10 +- test/standalone/pie/build.zig | 15 +- test/standalone/pkg_import/build.zig | 6 +- test/standalone/shared_library/build.zig | 10 +- test/standalone/sigpipe/build.zig | 11 +- test/standalone/static_c_lib/build.zig | 6 +- test/standalone/test_runner_path/build.zig | 6 +- .../test_runner_path/test_runner.zig | 40 +-- test/standalone/use_alias/build.zig | 8 +- test/standalone/windows_spawn/build.zig | 17 +- test/tests.zig | 93 +++++-- 42 files changed, 459 insertions(+), 340 deletions(-) rename test/standalone/{brace_expansion/main.zig => brace_expansion.zig} (98%) delete mode 100644 test/standalone/brace_expansion/build.zig rename test/standalone/{issue_7030/main.zig => issue_7030.zig} (100%) delete mode 100644 test/standalone/issue_7030/build.zig diff --git a/build.zig b/build.zig index c64015fe88..ba12ae5ae0 100644 --- a/build.zig +++ b/build.zig @@ -445,9 +445,7 @@ pub fn build(b: *std.Build) !void { test_step.dependOn(tests.addCompareOutputTests(b, test_filter, optimization_modes)); test_step.dependOn(tests.addStandaloneTests( b, - test_filter, optimization_modes, - skip_non_native, enable_macos_sdk, skip_stage2_tests, enable_symlinks_windows, @@ -455,7 +453,7 @@ pub fn build(b: *std.Build) !void { test_step.dependOn(tests.addCAbiTests(b, skip_non_native, skip_release)); test_step.dependOn(tests.addLinkTests(b, enable_macos_sdk, skip_stage2_tests, enable_symlinks_windows)); test_step.dependOn(tests.addStackTraceTests(b, test_filter, optimization_modes)); - test_step.dependOn(tests.addCliTests(b, test_filter, optimization_modes)); + test_step.dependOn(tests.addCliTests(b)); test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, optimization_modes)); test_step.dependOn(tests.addTranslateCTests(b, test_filter)); if (!skip_run_translated_c) { diff --git a/test/standalone.zig b/test/standalone.zig index fa67d6a14d..6e0adcaa00 100644 --- a/test/standalone.zig +++ b/test/standalone.zig @@ -3,6 +3,8 @@ pub const SimpleCase = struct { link_libc: bool = false, all_modes: bool = false, target: std.zig.CrossTarget = .{}, + is_test: bool = false, + is_exe: bool = true, }; pub const BuildCase = struct { @@ -27,6 +29,17 @@ pub const simple_cases = [_]SimpleCase{ //.{ // .src_path = "test/standalone/issue_9693/main.zig", //}, + .{ + .src_path = "test/standalone/brace_expansion.zig", + .is_test = true, + }, + .{ + .src_path = "test/standalone/issue_7030.zig", + .target = .{ + .cpu_arch = .wasm32, + .os_tag = .freestanding, + }, + }, .{ .src_path = "test/standalone/issue_12471/main.zig" }, .{ .src_path = "test/standalone/guess_number/main.zig" }, @@ -57,88 +70,143 @@ pub const simple_cases = [_]SimpleCase{ .{ .src_path = "tools/update_spirv_features.zig" }, }; -pub const build_cases = [_]BuildCase{}; - -//pub fn addCases(cases: *tests.StandaloneContext) void { -// cases.addBuildFile("test/standalone/options/build.zig", .{ -// .extra_argv = &.{ -// "-Dbool_true", -// "-Dbool_false=false", -// "-Dint=1234", -// "-De=two", -// "-Dstring=hello", -// }, -// }); -// -// cases.addBuildFile("test/standalone/test_runner_path/build.zig", .{ .requires_stage2 = true }); -// cases.addBuildFile("test/standalone/issue_13970/build.zig", .{}); -// cases.addBuildFile("test/standalone/main_pkg_path/build.zig", .{}); -// cases.addBuildFile("test/standalone/shared_library/build.zig", .{}); -// cases.addBuildFile("test/standalone/mix_o_files/build.zig", .{}); -// cases.addBuildFile("test/standalone/mix_c_files/build.zig", .{ -// .build_modes = true, -// .cross_targets = true, -// }); -// cases.addBuildFile("test/standalone/global_linkage/build.zig", .{}); -// cases.addBuildFile("test/standalone/static_c_lib/build.zig", .{}); -// cases.addBuildFile("test/standalone/issue_339/build.zig", .{}); -// cases.addBuildFile("test/standalone/issue_8550/build.zig", .{}); -// cases.addBuildFile("test/standalone/issue_794/build.zig", .{}); -// cases.addBuildFile("test/standalone/issue_5825/build.zig", .{}); -// cases.addBuildFile("test/standalone/pkg_import/build.zig", .{}); -// cases.addBuildFile("test/standalone/use_alias/build.zig", .{}); -// cases.addBuildFile("test/standalone/brace_expansion/build.zig", .{}); -// if (builtin.os.tag != .windows or builtin.cpu.arch != .aarch64) { -// // https://github.com/ziglang/zig/issues/13685 -// cases.addBuildFile("test/standalone/empty_env/build.zig", .{}); -// } -// cases.addBuildFile("test/standalone/issue_7030/build.zig", .{}); -// cases.addBuildFile("test/standalone/install_raw_hex/build.zig", .{}); -// if (builtin.zig_backend == .stage1) { // https://github.com/ziglang/zig/issues/12194 -// cases.addBuildFile("test/standalone/issue_9812/build.zig", .{}); -// } -// if (builtin.os.tag != .windows) { -// // https://github.com/ziglang/zig/issues/12419 -// cases.addBuildFile("test/standalone/issue_11595/build.zig", .{}); -// } -// -// if (builtin.os.tag != .wasi and -// // https://github.com/ziglang/zig/issues/13550 -// (builtin.os.tag != .macos or builtin.cpu.arch != .aarch64) and -// // https://github.com/ziglang/zig/issues/13686 -// (builtin.os.tag != .windows or builtin.cpu.arch != .aarch64)) -// { -// cases.addBuildFile("test/standalone/load_dynamic_library/build.zig", .{}); -// } -// -// if (builtin.os.tag == .windows) { -// cases.addBuildFile("test/standalone/windows_spawn/build.zig", .{}); -// } -// -// cases.addBuildFile("test/standalone/c_compiler/build.zig", .{ -// .build_modes = true, -// .cross_targets = true, -// }); -// -// // Try to build and run a PIE executable. -// if (builtin.os.tag == .linux) { -// cases.addBuildFile("test/standalone/pie/build.zig", .{}); -// } -// cases.addBuildFile("test/standalone/issue_12706/build.zig", .{}); -// if (std.os.have_sigpipe_support) { -// cases.addBuildFile("test/standalone/sigpipe/build.zig", .{}); -// } -// -// cases.addBuildFile("test/standalone/issue_13030/build.zig", .{ .build_modes = true }); -// cases.addBuildFile("test/standalone/emit_asm_and_bin/build.zig", .{}); -// cases.addBuildFile("test/standalone/issue_12588/build.zig", .{}); -// cases.addBuildFile("test/standalone/embed_generated_file/build.zig", .{}); -// -// cases.addBuildFile("test/standalone/dep_diamond/build.zig", .{}); -// cases.addBuildFile("test/standalone/dep_triangle/build.zig", .{}); -// cases.addBuildFile("test/standalone/dep_recursive/build.zig", .{}); -// cases.addBuildFile("test/standalone/dep_mutually_recursive/build.zig", .{}); -// cases.addBuildFile("test/standalone/dep_shared_builtin/build.zig", .{}); -//} +pub const build_cases = [_]BuildCase{ + .{ + .build_root = "test/standalone/test_runner_path", + .import = @import("standalone/test_runner_path/build.zig"), + }, + .{ + .build_root = "test/standalone/issue_13970", + .import = @import("standalone/issue_13970/build.zig"), + }, + .{ + .build_root = "test/standalone/main_pkg_path", + .import = @import("standalone/main_pkg_path/build.zig"), + }, + .{ + .build_root = "test/standalone/shared_library", + .import = @import("standalone/shared_library/build.zig"), + }, + .{ + .build_root = "test/standalone/mix_o_files", + .import = @import("standalone/mix_o_files/build.zig"), + }, + .{ + .build_root = "test/standalone/mix_c_files", + .import = @import("standalone/mix_c_files/build.zig"), + }, + .{ + .build_root = "test/standalone/global_linkage", + .import = @import("standalone/global_linkage/build.zig"), + }, + .{ + .build_root = "test/standalone/static_c_lib", + .import = @import("standalone/static_c_lib/build.zig"), + }, + .{ + .build_root = "test/standalone/issue_339", + .import = @import("standalone/issue_339/build.zig"), + }, + .{ + .build_root = "test/standalone/issue_8550", + .import = @import("standalone/issue_8550/build.zig"), + }, + .{ + .build_root = "test/standalone/issue_794", + .import = @import("standalone/issue_794/build.zig"), + }, + .{ + .build_root = "test/standalone/issue_5825", + .import = @import("standalone/issue_5825/build.zig"), + }, + .{ + .build_root = "test/standalone/pkg_import", + .import = @import("standalone/pkg_import/build.zig"), + }, + .{ + .build_root = "test/standalone/use_alias", + .import = @import("standalone/use_alias/build.zig"), + }, + .{ + .build_root = "test/standalone/install_raw_hex", + .import = @import("standalone/install_raw_hex/build.zig"), + }, + // TODO take away EmitOption.emit_to option and make it give a FileSource + //.{ + // .build_root = "test/standalone/emit_asm_and_bin", + // .import = @import("standalone/emit_asm_and_bin/build.zig"), + //}, + // TODO take away EmitOption.emit_to option and make it give a FileSource + //.{ + // .build_root = "test/standalone/issue_12588", + // .import = @import("standalone/issue_12588/build.zig"), + //}, + .{ + .build_root = "test/standalone/embed_generated_file", + .import = @import("standalone/embed_generated_file/build.zig"), + }, + .{ + .build_root = "test/standalone/dep_diamond", + .import = @import("standalone/dep_diamond/build.zig"), + }, + .{ + .build_root = "test/standalone/dep_triangle", + .import = @import("standalone/dep_triangle/build.zig"), + }, + .{ + .build_root = "test/standalone/dep_recursive", + .import = @import("standalone/dep_recursive/build.zig"), + }, + .{ + .build_root = "test/standalone/dep_mutually_recursive", + .import = @import("standalone/dep_mutually_recursive/build.zig"), + }, + .{ + .build_root = "test/standalone/dep_shared_builtin", + .import = @import("standalone/dep_shared_builtin/build.zig"), + }, + .{ + .build_root = "test/standalone/empty_env", + .import = @import("standalone/empty_env/build.zig"), + }, + .{ + .build_root = "test/standalone/issue_9812", + .import = @import("standalone/issue_9812/build.zig"), + }, + .{ + .build_root = "test/standalone/issue_11595", + .import = @import("standalone/issue_11595/build.zig"), + }, + .{ + .build_root = "test/standalone/load_dynamic_library", + .import = @import("standalone/load_dynamic_library/build.zig"), + }, + .{ + .build_root = "test/standalone/windows_spawn", + .import = @import("standalone/windows_spawn/build.zig"), + }, + .{ + .build_root = "test/standalone/c_compiler", + .import = @import("standalone/c_compiler/build.zig"), + }, + .{ + .build_root = "test/standalone/pie", + .import = @import("standalone/pie/build.zig"), + }, + .{ + .build_root = "test/standalone/issue_12706", + .import = @import("standalone/issue_12706/build.zig"), + }, + // TODO This test is disabled for doing naughty things in the build script. + // The logic needs to get moved to a child process instead of build.zig. + //.{ + // .build_root = "test/standalone/sigpipe", + // .import = @import("standalone/sigpipe/build.zig"), + //}, + .{ + .build_root = "test/standalone/issue_13030", + .import = @import("standalone/issue_13030/build.zig"), + }, +}; const std = @import("std"); diff --git a/test/standalone/brace_expansion/main.zig b/test/standalone/brace_expansion.zig similarity index 98% rename from test/standalone/brace_expansion/main.zig rename to test/standalone/brace_expansion.zig index dcdcad3865..7a769f6af7 100644 --- a/test/standalone/brace_expansion/main.zig +++ b/test/standalone/brace_expansion.zig @@ -234,8 +234,8 @@ pub fn main() !void { var result_buf = ArrayList(u8).init(global_allocator); defer result_buf.deinit(); - try expandString(stdin.items, &result_buf); - try stdout_file.write(result_buf.items); + try expandString(stdin, &result_buf); + try stdout_file.writeAll(result_buf.items); } test "invalid inputs" { diff --git a/test/standalone/brace_expansion/build.zig b/test/standalone/brace_expansion/build.zig deleted file mode 100644 index 7c32a09bef..0000000000 --- a/test/standalone/brace_expansion/build.zig +++ /dev/null @@ -1,11 +0,0 @@ -const std = @import("std"); - -pub fn build(b: *std.Build) void { - const main = b.addTest(.{ - .root_source_file = .{ .path = "main.zig" }, - .optimize = b.standardOptimizeOption(.{}), - }); - - const test_step = b.step("test", "Test it"); - test_step.dependOn(&main.step); -} diff --git a/test/standalone/c_compiler/build.zig b/test/standalone/c_compiler/build.zig index dce999d4a2..6c5f2b4db6 100644 --- a/test/standalone/c_compiler/build.zig +++ b/test/standalone/c_compiler/build.zig @@ -1,27 +1,24 @@ const std = @import("std"); const builtin = @import("builtin"); -const CrossTarget = std.zig.CrossTarget; - -// TODO integrate this with the std.Build executor API -fn isRunnableTarget(t: CrossTarget) bool { - if (t.isNative()) return true; - - return (t.getOsTag() == builtin.os.tag and - t.getCpuArch() == builtin.cpu.arch); -} pub fn build(b: *std.Build) void { - const optimize = b.standardOptimizeOption(.{}); - const target = b.standardTargetOptions(.{}); + const test_step = b.step("test", "Test it"); + b.default_step = test_step; - const test_step = b.step("test", "Test the program"); + add(b, test_step, .Debug); + add(b, test_step, .ReleaseFast); + add(b, test_step, .ReleaseSmall); + add(b, test_step, .ReleaseSafe); +} + +fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { + const target: std.zig.CrossTarget = .{}; const exe_c = b.addExecutable(.{ .name = "test_c", .optimize = optimize, .target = target, }); - b.default_step.dependOn(&exe_c.step); exe_c.addCSourceFile("test.c", &[0][]const u8{}); exe_c.linkLibC(); @@ -47,13 +44,13 @@ pub fn build(b: *std.Build) void { else => {}, } - if (isRunnableTarget(target)) { - const run_c_cmd = exe_c.run(); - test_step.dependOn(&run_c_cmd.step); - const run_cpp_cmd = exe_cpp.run(); - test_step.dependOn(&run_cpp_cmd.step); - } else { - test_step.dependOn(&exe_c.step); - test_step.dependOn(&exe_cpp.step); - } + const run_c_cmd = b.addRunArtifact(exe_c); + run_c_cmd.expectExitCode(0); + run_c_cmd.skip_foreign_checks = true; + test_step.dependOn(&run_c_cmd.step); + + const run_cpp_cmd = b.addRunArtifact(exe_cpp); + run_cpp_cmd.expectExitCode(0); + run_cpp_cmd.skip_foreign_checks = true; + test_step.dependOn(&run_cpp_cmd.step); } diff --git a/test/standalone/dep_diamond/build.zig b/test/standalone/dep_diamond/build.zig index b60f898f0b..12eda4ec5d 100644 --- a/test/standalone/dep_diamond/build.zig +++ b/test/standalone/dep_diamond/build.zig @@ -1,7 +1,10 @@ const std = @import("std"); pub fn build(b: *std.Build) void { - const optimize = b.standardOptimizeOption(.{}); + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + + const optimize: std.builtin.OptimizeMode = .Debug; const shared = b.createModule(.{ .source_file = .{ .path = "shared.zig" }, @@ -23,6 +26,5 @@ pub fn build(b: *std.Build) void { const run = exe.run(); - const test_step = b.step("test", "Test it"); test_step.dependOn(&run.step); } diff --git a/test/standalone/dep_mutually_recursive/build.zig b/test/standalone/dep_mutually_recursive/build.zig index 0123646a9a..1a6bff8501 100644 --- a/test/standalone/dep_mutually_recursive/build.zig +++ b/test/standalone/dep_mutually_recursive/build.zig @@ -1,7 +1,10 @@ const std = @import("std"); pub fn build(b: *std.Build) void { - const optimize = b.standardOptimizeOption(.{}); + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + + const optimize: std.builtin.OptimizeMode = .Debug; const foo = b.createModule(.{ .source_file = .{ .path = "foo.zig" }, @@ -21,6 +24,5 @@ pub fn build(b: *std.Build) void { const run = exe.run(); - const test_step = b.step("test", "Test it"); test_step.dependOn(&run.step); } diff --git a/test/standalone/dep_recursive/build.zig b/test/standalone/dep_recursive/build.zig index 32d546e283..35b9f3cc47 100644 --- a/test/standalone/dep_recursive/build.zig +++ b/test/standalone/dep_recursive/build.zig @@ -1,7 +1,10 @@ const std = @import("std"); pub fn build(b: *std.Build) void { - const optimize = b.standardOptimizeOption(.{}); + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + + const optimize: std.builtin.OptimizeMode = .Debug; const foo = b.createModule(.{ .source_file = .{ .path = "foo.zig" }, @@ -17,6 +20,5 @@ pub fn build(b: *std.Build) void { const run = exe.run(); - const test_step = b.step("test", "Test it"); test_step.dependOn(&run.step); } diff --git a/test/standalone/dep_shared_builtin/build.zig b/test/standalone/dep_shared_builtin/build.zig index 6c029b654b..776794f95e 100644 --- a/test/standalone/dep_shared_builtin/build.zig +++ b/test/standalone/dep_shared_builtin/build.zig @@ -1,7 +1,10 @@ const std = @import("std"); pub fn build(b: *std.Build) void { - const optimize = b.standardOptimizeOption(.{}); + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + + const optimize: std.builtin.OptimizeMode = .Debug; const exe = b.addExecutable(.{ .name = "test", @@ -14,6 +17,5 @@ pub fn build(b: *std.Build) void { const run = exe.run(); - const test_step = b.step("test", "Test it"); test_step.dependOn(&run.step); } diff --git a/test/standalone/dep_triangle/build.zig b/test/standalone/dep_triangle/build.zig index f3b73aaf35..14163df84c 100644 --- a/test/standalone/dep_triangle/build.zig +++ b/test/standalone/dep_triangle/build.zig @@ -1,7 +1,10 @@ const std = @import("std"); pub fn build(b: *std.Build) void { - const optimize = b.standardOptimizeOption(.{}); + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + + const optimize: std.builtin.OptimizeMode = .Debug; const shared = b.createModule(.{ .source_file = .{ .path = "shared.zig" }, @@ -20,6 +23,5 @@ pub fn build(b: *std.Build) void { const run = exe.run(); - const test_step = b.step("test", "Test it"); test_step.dependOn(&run.step); } diff --git a/test/standalone/embed_generated_file/build.zig b/test/standalone/embed_generated_file/build.zig index 3b17ff0b8f..af1ae0a00f 100644 --- a/test/standalone/embed_generated_file/build.zig +++ b/test/standalone/embed_generated_file/build.zig @@ -1,8 +1,8 @@ const std = @import("std"); pub fn build(b: *std.Build) void { - const target = b.standardTargetOptions(.{}); - const optimize = b.standardOptimizeOption(.{}); + const test_step = b.step("test", "Test it"); + b.default_step = test_step; const bootloader = b.addExecutable(.{ .name = "bootloader", @@ -16,13 +16,11 @@ pub fn build(b: *std.Build) void { const exe = b.addTest(.{ .root_source_file = .{ .path = "main.zig" }, - .target = target, - .optimize = optimize, + .optimize = .Debug, }); exe.addAnonymousModule("bootloader.elf", .{ .source_file = bootloader.getOutputSource(), }); - const test_step = b.step("test", "Test the program"); test_step.dependOn(&exe.step); } diff --git a/test/standalone/emit_asm_and_bin/build.zig b/test/standalone/emit_asm_and_bin/build.zig index 5345f0f538..9bdfadc33d 100644 --- a/test/standalone/emit_asm_and_bin/build.zig +++ b/test/standalone/emit_asm_and_bin/build.zig @@ -1,6 +1,9 @@ const std = @import("std"); pub fn build(b: *std.Build) void { + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + const main = b.addTest(.{ .root_source_file = .{ .path = "main.zig" }, .optimize = b.standardOptimizeOption(.{}), @@ -8,6 +11,5 @@ pub fn build(b: *std.Build) void { main.emit_asm = .{ .emit_to = b.pathFromRoot("main.s") }; main.emit_bin = .{ .emit_to = b.pathFromRoot("main") }; - const test_step = b.step("test", "Run test"); test_step.dependOn(&main.step); } diff --git a/test/standalone/empty_env/build.zig b/test/standalone/empty_env/build.zig index c4b4846141..27ec75be22 100644 --- a/test/standalone/empty_env/build.zig +++ b/test/standalone/empty_env/build.zig @@ -1,15 +1,25 @@ const std = @import("std"); +const builtin = @import("builtin"); pub fn build(b: *std.Build) void { + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + + const optimize: std.builtin.OptimizeMode = .Debug; + + if (builtin.os.tag == .windows and builtin.cpu.arch == .aarch64) { + // https://github.com/ziglang/zig/issues/13685 + return; + } + const main = b.addExecutable(.{ .name = "main", .root_source_file = .{ .path = "main.zig" }, - .optimize = b.standardOptimizeOption(.{}), + .optimize = optimize, }); - const run = main.run(); + const run = b.addRunArtifact(main); run.clearEnvironment(); - const test_step = b.step("test", "Test it"); test_step.dependOn(&run.step); } diff --git a/test/standalone/global_linkage/build.zig b/test/standalone/global_linkage/build.zig index 9f79c80fcf..2cf1b248a5 100644 --- a/test/standalone/global_linkage/build.zig +++ b/test/standalone/global_linkage/build.zig @@ -1,20 +1,24 @@ const std = @import("std"); pub fn build(b: *std.Build) void { - const optimize = b.standardOptimizeOption(.{}); + const test_step = b.step("test", "Test the program"); + b.default_step = test_step; + + const optimize: std.builtin.OptimizeMode = .Debug; + const target: std.zig.CrossTarget = .{}; const obj1 = b.addStaticLibrary(.{ .name = "obj1", .root_source_file = .{ .path = "obj1.zig" }, .optimize = optimize, - .target = .{}, + .target = target, }); const obj2 = b.addStaticLibrary(.{ .name = "obj2", .root_source_file = .{ .path = "obj2.zig" }, .optimize = optimize, - .target = .{}, + .target = target, }); const main = b.addTest(.{ @@ -24,6 +28,5 @@ pub fn build(b: *std.Build) void { main.linkLibrary(obj1); main.linkLibrary(obj2); - const test_step = b.step("test", "Test it"); test_step.dependOn(&main.step); } diff --git a/test/standalone/install_raw_hex/build.zig b/test/standalone/install_raw_hex/build.zig index 6ed515e381..b34bb01378 100644 --- a/test/standalone/install_raw_hex/build.zig +++ b/test/standalone/install_raw_hex/build.zig @@ -3,8 +3,8 @@ const std = @import("std"); const CheckFileStep = std.Build.CheckFileStep; pub fn build(b: *std.Build) void { - const test_step = b.step("test", "Test the program"); - b.default_step.dependOn(test_step); + const test_step = b.step("test", "Test it"); + b.default_step = test_step; const target = .{ .cpu_arch = .thumb, @@ -13,7 +13,7 @@ pub fn build(b: *std.Build) void { .abi = .gnueabihf, }; - const optimize = b.standardOptimizeOption(.{}); + const optimize: std.builtin.OptimizeMode = .Debug; const elf = b.addExecutable(.{ .name = "zig-nrf52-blink.elf", diff --git a/test/standalone/issue_11595/build.zig b/test/standalone/issue_11595/build.zig index c335fb73da..7d9530c690 100644 --- a/test/standalone/issue_11595/build.zig +++ b/test/standalone/issue_11595/build.zig @@ -1,18 +1,17 @@ const std = @import("std"); const builtin = @import("builtin"); -const CrossTarget = std.zig.CrossTarget; - -// TODO integrate this with the std.Build executor API -fn isRunnableTarget(t: CrossTarget) bool { - if (t.isNative()) return true; - - return (t.getOsTag() == builtin.os.tag and - t.getCpuArch() == builtin.cpu.arch); -} pub fn build(b: *std.Build) void { - const optimize = b.standardOptimizeOption(.{}); - const target = b.standardTargetOptions(.{}); + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + + const optimize: std.builtin.OptimizeMode = .Debug; + const target: std.zig.CrossTarget = .{}; + + if (builtin.os.tag == .windows) { + // https://github.com/ziglang/zig/issues/12419 + return; + } const exe = b.addExecutable(.{ .name = "zigtest", @@ -44,11 +43,9 @@ pub fn build(b: *std.Build) void { b.default_step.dependOn(&exe.step); - const test_step = b.step("test", "Test the program"); - if (isRunnableTarget(target)) { - const run_cmd = exe.run(); - test_step.dependOn(&run_cmd.step); - } else { - test_step.dependOn(&exe.step); - } + const run_cmd = b.addRunArtifact(exe); + run_cmd.skip_foreign_checks = true; + run_cmd.expectExitCode(0); + + test_step.dependOn(&run_cmd.step); } diff --git a/test/standalone/issue_12588/build.zig b/test/standalone/issue_12588/build.zig index 9f14c53e38..fa22252fcc 100644 --- a/test/standalone/issue_12588/build.zig +++ b/test/standalone/issue_12588/build.zig @@ -1,8 +1,11 @@ const std = @import("std"); pub fn build(b: *std.Build) void { - const optimize = b.standardOptimizeOption(.{}); - const target = b.standardTargetOptions(.{}); + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + + const optimize: std.builtin.OptimizeMode = .Debug; + const target: std.zig.CrossTarget = .{}; const obj = b.addObject(.{ .name = "main", @@ -15,6 +18,5 @@ pub fn build(b: *std.Build) void { obj.emit_bin = .no_emit; b.default_step.dependOn(&obj.step); - const test_step = b.step("test", "Test the program"); test_step.dependOn(&obj.step); } diff --git a/test/standalone/issue_12706/build.zig b/test/standalone/issue_12706/build.zig index 9d616477a2..04eb826b44 100644 --- a/test/standalone/issue_12706/build.zig +++ b/test/standalone/issue_12706/build.zig @@ -2,17 +2,12 @@ const std = @import("std"); const builtin = @import("builtin"); const CrossTarget = std.zig.CrossTarget; -// TODO integrate this with the std.Build executor API -fn isRunnableTarget(t: CrossTarget) bool { - if (t.isNative()) return true; - - return (t.getOsTag() == builtin.os.tag and - t.getCpuArch() == builtin.cpu.arch); -} - pub fn build(b: *std.Build) void { - const optimize = b.standardOptimizeOption(.{}); - const target = b.standardTargetOptions(.{}); + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + + const optimize: std.builtin.OptimizeMode = .Debug; + const target: std.zig.CrossTarget = .{}; const exe = b.addExecutable(.{ .name = "main", @@ -20,22 +15,15 @@ pub fn build(b: *std.Build) void { .optimize = optimize, .target = target, }); - exe.install(); const c_sources = [_][]const u8{ "test.c", }; - exe.addCSourceFiles(&c_sources, &.{}); exe.linkLibC(); - b.default_step.dependOn(&exe.step); - - const test_step = b.step("test", "Test the program"); - if (isRunnableTarget(target)) { - const run_cmd = exe.run(); - test_step.dependOn(&run_cmd.step); - } else { - test_step.dependOn(&exe.step); - } + const run_cmd = b.addRunArtifact(exe); + run_cmd.expectExitCode(0); + run_cmd.skip_foreign_checks = true; + test_step.dependOn(&run_cmd.step); } diff --git a/test/standalone/issue_13030/build.zig b/test/standalone/issue_13030/build.zig index 258d9b7db8..e31863fee2 100644 --- a/test/standalone/issue_13030/build.zig +++ b/test/standalone/issue_13030/build.zig @@ -3,17 +3,22 @@ const builtin = @import("builtin"); const CrossTarget = std.zig.CrossTarget; pub fn build(b: *std.Build) void { - const optimize = b.standardOptimizeOption(.{}); - const target = b.standardTargetOptions(.{}); + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + add(b, test_step, .Debug); + add(b, test_step, .ReleaseFast); + add(b, test_step, .ReleaseSmall); + add(b, test_step, .ReleaseSafe); +} + +fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { const obj = b.addObject(.{ .name = "main", .root_source_file = .{ .path = "main.zig" }, .optimize = optimize, - .target = target, + .target = .{}, }); - b.default_step.dependOn(&obj.step); - const test_step = b.step("test", "Test the program"); test_step.dependOn(&obj.step); } diff --git a/test/standalone/issue_13970/build.zig b/test/standalone/issue_13970/build.zig index f5e07d8903..bbaaac5886 100644 --- a/test/standalone/issue_13970/build.zig +++ b/test/standalone/issue_13970/build.zig @@ -1,6 +1,9 @@ const std = @import("std"); pub fn build(b: *std.Build) void { + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + const test1 = b.addTest(.{ .root_source_file = .{ .path = "test_root/empty.zig" }, }); @@ -14,7 +17,6 @@ pub fn build(b: *std.Build) void { test2.setTestRunner("src/main.zig"); test3.setTestRunner("src/main.zig"); - const test_step = b.step("test", "Test package path resolution of custom test runner"); test_step.dependOn(&test1.step); test_step.dependOn(&test2.step); test_step.dependOn(&test3.step); diff --git a/test/standalone/issue_339/build.zig b/test/standalone/issue_339/build.zig index 62ac128aab..f4215dbb8b 100644 --- a/test/standalone/issue_339/build.zig +++ b/test/standalone/issue_339/build.zig @@ -1,13 +1,18 @@ const std = @import("std"); pub fn build(b: *std.Build) void { + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + + const optimize: std.builtin.OptimizeMode = .Debug; + const target: std.zig.CrossTarget = .{}; + const obj = b.addObject(.{ .name = "test", .root_source_file = .{ .path = "test.zig" }, - .target = b.standardTargetOptions(.{}), - .optimize = b.standardOptimizeOption(.{}), + .target = target, + .optimize = optimize, }); - const test_step = b.step("test", "Test the program"); test_step.dependOn(&obj.step); } diff --git a/test/standalone/issue_5825/build.zig b/test/standalone/issue_5825/build.zig index 89272280d4..e8e8d48772 100644 --- a/test/standalone/issue_5825/build.zig +++ b/test/standalone/issue_5825/build.zig @@ -1,12 +1,15 @@ const std = @import("std"); pub fn build(b: *std.Build) void { + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + const target = .{ .cpu_arch = .x86_64, .os_tag = .windows, .abi = .msvc, }; - const optimize = b.standardOptimizeOption(.{}); + const optimize: std.builtin.OptimizeMode = .Debug; const obj = b.addObject(.{ .name = "issue_5825", .root_source_file = .{ .path = "main.zig" }, @@ -24,6 +27,5 @@ pub fn build(b: *std.Build) void { exe.linkSystemLibrary("ntdll"); exe.addObject(obj); - const test_step = b.step("test", "Test the program"); test_step.dependOn(&exe.step); } diff --git a/test/standalone/issue_7030/main.zig b/test/standalone/issue_7030.zig similarity index 100% rename from test/standalone/issue_7030/main.zig rename to test/standalone/issue_7030.zig diff --git a/test/standalone/issue_7030/build.zig b/test/standalone/issue_7030/build.zig deleted file mode 100644 index dc535318cc..0000000000 --- a/test/standalone/issue_7030/build.zig +++ /dev/null @@ -1,17 +0,0 @@ -const std = @import("std"); - -pub fn build(b: *std.Build) void { - const exe = b.addExecutable(.{ - .name = "issue_7030", - .root_source_file = .{ .path = "main.zig" }, - .target = .{ - .cpu_arch = .wasm32, - .os_tag = .freestanding, - }, - }); - exe.install(); - b.default_step.dependOn(&exe.step); - - const test_step = b.step("test", "Test the program"); - test_step.dependOn(&exe.step); -} diff --git a/test/standalone/issue_794/build.zig b/test/standalone/issue_794/build.zig index 3089a28fd0..8527f4af2c 100644 --- a/test/standalone/issue_794/build.zig +++ b/test/standalone/issue_794/build.zig @@ -1,13 +1,13 @@ const std = @import("std"); pub fn build(b: *std.Build) void { + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + const test_artifact = b.addTest(.{ .root_source_file = .{ .path = "main.zig" }, }); test_artifact.addIncludePath("a_directory"); - b.default_step.dependOn(&test_artifact.step); - - const test_step = b.step("test", "Test the program"); test_step.dependOn(&test_artifact.step); } diff --git a/test/standalone/issue_8550/build.zig b/test/standalone/issue_8550/build.zig index c3303d55db..8f7631e68f 100644 --- a/test/standalone/issue_8550/build.zig +++ b/test/standalone/issue_8550/build.zig @@ -1,6 +1,10 @@ const std = @import("std"); pub fn build(b: *std.Build) !void { + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + + const optimize: std.builtin.OptimizeMode = .Debug; const target = std.zig.CrossTarget{ .os_tag = .freestanding, .cpu_arch = .arm, @@ -8,7 +12,7 @@ pub fn build(b: *std.Build) !void { .explicit = &std.Target.arm.cpu.arm1176jz_s, }, }; - const optimize = b.standardOptimizeOption(.{}); + const kernel = b.addExecutable(.{ .name = "kernel", .root_source_file = .{ .path = "./main.zig" }, @@ -19,6 +23,5 @@ pub fn build(b: *std.Build) !void { kernel.setLinkerScriptPath(.{ .path = "./linker.ld" }); kernel.install(); - const test_step = b.step("test", "Test it"); test_step.dependOn(&kernel.step); } diff --git a/test/standalone/issue_9812/build.zig b/test/standalone/issue_9812/build.zig index 4ca55ce999..71104b903c 100644 --- a/test/standalone/issue_9812/build.zig +++ b/test/standalone/issue_9812/build.zig @@ -1,7 +1,11 @@ const std = @import("std"); pub fn build(b: *std.Build) !void { - const optimize = b.standardOptimizeOption(.{}); + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + + const optimize: std.builtin.OptimizeMode = .Debug; + const zip_add = b.addTest(.{ .root_source_file = .{ .path = "main.zig" }, .optimize = optimize, @@ -13,6 +17,5 @@ pub fn build(b: *std.Build) !void { zip_add.addIncludePath("vendor/kuba-zip"); zip_add.linkLibC(); - const test_step = b.step("test", "Test it"); test_step.dependOn(&zip_add.step); } diff --git a/test/standalone/load_dynamic_library/build.zig b/test/standalone/load_dynamic_library/build.zig index 44fc37893c..06a5424a8d 100644 --- a/test/standalone/load_dynamic_library/build.zig +++ b/test/standalone/load_dynamic_library/build.zig @@ -1,8 +1,19 @@ const std = @import("std"); +const builtin = @import("builtin"); pub fn build(b: *std.Build) void { - const target = b.standardTargetOptions(.{}); - const optimize = b.standardOptimizeOption(.{}); + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + + const optimize: std.builtin.OptimizeMode = .Debug; + const target: std.zig.CrossTarget = .{}; + + const ok = (builtin.os.tag != .wasi and + // https://github.com/ziglang/zig/issues/13550 + (builtin.os.tag != .macos or builtin.cpu.arch != .aarch64) and + // https://github.com/ziglang/zig/issues/13686 + (builtin.os.tag != .windows or builtin.cpu.arch != .aarch64)); + if (!ok) return; const lib = b.addSharedLibrary(.{ .name = "add", @@ -19,9 +30,10 @@ pub fn build(b: *std.Build) void { .target = target, }); - const run = main.run(); + const run = b.addRunArtifact(main); run.addArtifactArg(lib); + run.skip_foreign_checks = true; + run.expectExitCode(0); - const test_step = b.step("test", "Test the program"); test_step.dependOn(&run.step); } diff --git a/test/standalone/load_dynamic_library/main.zig b/test/standalone/load_dynamic_library/main.zig index baf47c23ad..b47ea8a81f 100644 --- a/test/standalone/load_dynamic_library/main.zig +++ b/test/standalone/load_dynamic_library/main.zig @@ -11,11 +11,7 @@ pub fn main() !void { var lib = try std.DynLib.open(dynlib_name); defer lib.close(); - const Add = switch (@import("builtin").zig_backend) { - .stage1 => fn (i32, i32) callconv(.C) i32, - else => *const fn (i32, i32) callconv(.C) i32, - }; - + const Add = *const fn (i32, i32) callconv(.C) i32; const addFn = lib.lookup(Add, "add") orelse return error.SymbolNotFound; const result = addFn(12, 34); diff --git a/test/standalone/main_pkg_path/build.zig b/test/standalone/main_pkg_path/build.zig index f9919d5ab5..cd49573692 100644 --- a/test/standalone/main_pkg_path/build.zig +++ b/test/standalone/main_pkg_path/build.zig @@ -1,11 +1,13 @@ const std = @import("std"); pub fn build(b: *std.Build) void { + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + const test_exe = b.addTest(.{ .root_source_file = .{ .path = "a/test.zig" }, }); test_exe.setMainPkgPath("."); - const test_step = b.step("test", "Test the program"); test_step.dependOn(&test_exe.step); } diff --git a/test/standalone/mix_c_files/build.zig b/test/standalone/mix_c_files/build.zig index f2dfb2093f..0ea585e4e0 100644 --- a/test/standalone/mix_c_files/build.zig +++ b/test/standalone/mix_c_files/build.zig @@ -1,34 +1,28 @@ const std = @import("std"); -const builtin = @import("builtin"); -const CrossTarget = std.zig.CrossTarget; - -// TODO integrate this with the std.Build executor API -fn isRunnableTarget(t: CrossTarget) bool { - if (t.isNative()) return true; - - return (t.getOsTag() == builtin.os.tag and - t.getCpuArch() == builtin.cpu.arch); -} pub fn build(b: *std.Build) void { - const optimize = b.standardOptimizeOption(.{}); - const target = b.standardTargetOptions(.{}); + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + add(b, test_step, .Debug); + add(b, test_step, .ReleaseFast); + add(b, test_step, .ReleaseSmall); + add(b, test_step, .ReleaseSafe); +} + +fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { const exe = b.addExecutable(.{ .name = "test", .root_source_file = .{ .path = "main.zig" }, .optimize = optimize, - .target = target, }); exe.addCSourceFile("test.c", &[_][]const u8{"-std=c11"}); exe.linkLibC(); b.default_step.dependOn(&exe.step); - const test_step = b.step("test", "Test the program"); - if (isRunnableTarget(target)) { - const run_cmd = exe.run(); - test_step.dependOn(&run_cmd.step); - } else { - test_step.dependOn(&exe.step); - } + const run_cmd = b.addRunArtifact(exe); + run_cmd.skip_foreign_checks = true; + run_cmd.expectExitCode(0); + + test_step.dependOn(&run_cmd.step); } diff --git a/test/standalone/mix_o_files/build.zig b/test/standalone/mix_o_files/build.zig index 2708343aa5..17ce55a8aa 100644 --- a/test/standalone/mix_o_files/build.zig +++ b/test/standalone/mix_o_files/build.zig @@ -1,18 +1,23 @@ const std = @import("std"); pub fn build(b: *std.Build) void { - const optimize = b.standardOptimizeOption(.{}); + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + + const optimize: std.builtin.OptimizeMode = .Debug; + const target: std.zig.CrossTarget = .{}; const obj = b.addObject(.{ .name = "base64", .root_source_file = .{ .path = "base64.zig" }, .optimize = optimize, - .target = .{}, + .target = target, }); const exe = b.addExecutable(.{ .name = "test", .optimize = optimize, + .target = target, }); exe.addCSourceFile("test.c", &[_][]const u8{"-std=c99"}); exe.addObject(obj); @@ -22,6 +27,5 @@ pub fn build(b: *std.Build) void { const run_cmd = exe.run(); - const test_step = b.step("test", "Test the program"); test_step.dependOn(&run_cmd.step); } diff --git a/test/standalone/pie/build.zig b/test/standalone/pie/build.zig index d51ea27328..615111b6c2 100644 --- a/test/standalone/pie/build.zig +++ b/test/standalone/pie/build.zig @@ -1,14 +1,21 @@ const std = @import("std"); pub fn build(b: *std.Build) void { + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + + const optimize: std.builtin.OptimizeMode = .Debug; + const target: std.zig.CrossTarget = .{ + .os_tag = .linux, + .cpu_arch = .x86_64, + }; + const main = b.addTest(.{ .root_source_file = .{ .path = "main.zig" }, - .optimize = b.standardOptimizeOption(.{}), + .optimize = optimize, + .target = target, }); main.pie = true; - const test_step = b.step("test", "Test the program"); test_step.dependOn(&main.step); - - b.default_step.dependOn(test_step); } diff --git a/test/standalone/pkg_import/build.zig b/test/standalone/pkg_import/build.zig index 5ea6c90af7..42799ab896 100644 --- a/test/standalone/pkg_import/build.zig +++ b/test/standalone/pkg_import/build.zig @@ -1,7 +1,10 @@ const std = @import("std"); pub fn build(b: *std.Build) void { - const optimize = b.standardOptimizeOption(.{}); + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + + const optimize: std.builtin.OptimizeMode = .Debug; const exe = b.addExecutable(.{ .name = "test", @@ -12,6 +15,5 @@ pub fn build(b: *std.Build) void { const run = exe.run(); - const test_step = b.step("test", "Test it"); test_step.dependOn(&run.step); } diff --git a/test/standalone/shared_library/build.zig b/test/standalone/shared_library/build.zig index 91f7c8a06a..63370af0cc 100644 --- a/test/standalone/shared_library/build.zig +++ b/test/standalone/shared_library/build.zig @@ -1,8 +1,11 @@ const std = @import("std"); pub fn build(b: *std.Build) void { - const optimize = b.standardOptimizeOption(.{}); - const target = b.standardTargetOptions(.{}); + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + + const optimize: std.builtin.OptimizeMode = .Debug; + const target: std.zig.CrossTarget = .{}; const lib = b.addSharedLibrary(.{ .name = "mathtest", .root_source_file = .{ .path = "mathtest.zig" }, @@ -20,10 +23,7 @@ pub fn build(b: *std.Build) void { exe.linkLibrary(lib); exe.linkSystemLibrary("c"); - b.default_step.dependOn(&exe.step); - const run_cmd = exe.run(); - const test_step = b.step("test", "Test the program"); test_step.dependOn(&run_cmd.step); } diff --git a/test/standalone/sigpipe/build.zig b/test/standalone/sigpipe/build.zig index 400f1a970d..6f50a86d68 100644 --- a/test/standalone/sigpipe/build.zig +++ b/test/standalone/sigpipe/build.zig @@ -2,7 +2,16 @@ const std = @import("std"); const os = std.os; pub fn build(b: *std.build.Builder) !void { - const test_step = b.step("test", "Run the tests"); + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + + // TODO signal handling code has no business being in a build script. + // this logic needs to move to a file called parent.zig which is + // added as an executable. + + //if (!std.os.have_sigpipe_support) { + // return; + //} // This test runs "breakpipe" as a child process and that process // depends on inheriting a SIGPIPE disposition of "default". diff --git a/test/standalone/static_c_lib/build.zig b/test/standalone/static_c_lib/build.zig index 9937888843..5996c978d8 100644 --- a/test/standalone/static_c_lib/build.zig +++ b/test/standalone/static_c_lib/build.zig @@ -1,7 +1,10 @@ const std = @import("std"); pub fn build(b: *std.Build) void { - const optimize = b.standardOptimizeOption(.{}); + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + + const optimize: std.builtin.OptimizeMode = .Debug; const foo = b.addStaticLibrary(.{ .name = "foo", @@ -18,6 +21,5 @@ pub fn build(b: *std.Build) void { test_exe.linkLibrary(foo); test_exe.addIncludePath("."); - const test_step = b.step("test", "Test it"); test_step.dependOn(&test_exe.step); } diff --git a/test/standalone/test_runner_path/build.zig b/test/standalone/test_runner_path/build.zig index f073c55d4a..40aad42b21 100644 --- a/test/standalone/test_runner_path/build.zig +++ b/test/standalone/test_runner_path/build.zig @@ -1,6 +1,11 @@ const std = @import("std"); +pub const requires_stage2 = true; + pub fn build(b: *std.Build) void { + const test_step = b.step("test", "Test the program"); + b.default_step = test_step; + const test_exe = b.addTest(.{ .root_source_file = .{ .path = "test.zig" }, .kind = .test_exe, @@ -9,6 +14,5 @@ pub fn build(b: *std.Build) void { const test_run = test_exe.run(); - const test_step = b.step("test", "Test the program"); test_step.dependOn(&test_run.step); } diff --git a/test/standalone/test_runner_path/test_runner.zig b/test/standalone/test_runner_path/test_runner.zig index f49ff55cae..2139ea8f68 100644 --- a/test/standalone/test_runner_path/test_runner.zig +++ b/test/standalone/test_runner_path/test_runner.zig @@ -1,51 +1,19 @@ const std = @import("std"); -const io = std.io; const builtin = @import("builtin"); -pub const io_mode: io.Mode = builtin.test_io_mode; - pub fn main() void { - const test_fn_list = builtin.test_functions; var ok_count: usize = 0; var skip_count: usize = 0; var fail_count: usize = 0; - var async_frame_buffer: []align(std.Target.stack_align) u8 = undefined; - // TODO this is on the next line (using `undefined` above) because otherwise zig incorrectly - // ignores the alignment of the slice. - async_frame_buffer = &[_]u8{}; - - for (test_fn_list) |test_fn| { - const result = if (test_fn.async_frame_size) |size| switch (io_mode) { - .evented => blk: { - if (async_frame_buffer.len < size) { - std.heap.page_allocator.free(async_frame_buffer); - async_frame_buffer = std.heap.page_allocator.alignedAlloc(u8, std.Target.stack_align, size) catch @panic("out of memory"); - } - const casted_fn = @ptrCast(fn () callconv(.Async) anyerror!void, test_fn.func); - break :blk await @asyncCall(async_frame_buffer, {}, casted_fn, .{}); - }, - .blocking => { - skip_count += 1; - continue; - }, - } else test_fn.func(); - if (result) |_| { + for (builtin.test_functions) |test_fn| { + if (test_fn.func()) |_| { ok_count += 1; } else |err| switch (err) { - error.SkipZigTest => { - skip_count += 1; - }, - else => { - fail_count += 1; - }, + error.SkipZigTest => skip_count += 1, + else => fail_count += 1, } } - if (ok_count == test_fn_list.len) { - std.debug.print("All {d} tests passed.\n", .{ok_count}); - } else { - std.debug.print("{d} passed; {d} skipped; {d} failed.\n", .{ ok_count, skip_count, fail_count }); - } if (ok_count != 1 or skip_count != 1 or fail_count != 1) { std.process.exit(1); } diff --git a/test/standalone/use_alias/build.zig b/test/standalone/use_alias/build.zig index 89e07efb22..947db0828d 100644 --- a/test/standalone/use_alias/build.zig +++ b/test/standalone/use_alias/build.zig @@ -1,12 +1,16 @@ const std = @import("std"); pub fn build(b: *std.Build) void { + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + + const optimize: std.builtin.OptimizeMode = .Debug; + const main = b.addTest(.{ .root_source_file = .{ .path = "main.zig" }, - .optimize = b.standardOptimizeOption(.{}), + .optimize = optimize, }); main.addIncludePath("."); - const test_step = b.step("test", "Test it"); test_step.dependOn(&main.step); } diff --git a/test/standalone/windows_spawn/build.zig b/test/standalone/windows_spawn/build.zig index 3ebde5a50c..8cc6e18599 100644 --- a/test/standalone/windows_spawn/build.zig +++ b/test/standalone/windows_spawn/build.zig @@ -1,23 +1,34 @@ const std = @import("std"); +const builtin = @import("builtin"); pub fn build(b: *std.Build) void { - const optimize = b.standardOptimizeOption(.{}); + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + + const optimize: std.builtin.OptimizeMode = .Debug; + const target: std.zig.CrossTarget = .{ + .os_tag = .windows, + .cpu_arch = .x86_64, + }; const hello = b.addExecutable(.{ .name = "hello", .root_source_file = .{ .path = "hello.zig" }, .optimize = optimize, + .target = target, }); const main = b.addExecutable(.{ .name = "main", .root_source_file = .{ .path = "main.zig" }, .optimize = optimize, + .target = target, }); - const run = main.run(); + const run = b.addRunArtifact(main); run.addArtifactArg(hello); + run.expectExitCode(0); + run.skip_foreign_checks = true; - const test_step = b.step("test", "Test it"); test_step.dependOn(&run.step); } diff --git a/test/tests.zig b/test/tests.zig index 038111136a..2cd06b18b9 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -560,34 +560,62 @@ pub fn addStackTraceTests( pub fn addStandaloneTests( b: *std.Build, - test_filter: ?[]const u8, optimize_modes: []const OptimizeMode, - skip_non_native: bool, enable_macos_sdk: bool, omit_stage2: bool, enable_symlinks_windows: bool, ) *Step { const step = b.step("test-standalone", "Run the standalone tests"); - - _ = test_filter; - _ = skip_non_native; - _ = enable_macos_sdk; - _ = omit_stage2; - _ = enable_symlinks_windows; + const omit_symlinks = builtin.os.tag == .windows and !enable_symlinks_windows; for (standalone.simple_cases) |case| { for (optimize_modes) |optimize| { if (!case.all_modes and optimize != .Debug) continue; - const exe = b.addExecutable(.{ - .name = std.fs.path.stem(case.src_path), - .root_source_file = .{ .path = case.src_path }, - .optimize = optimize, - .target = case.target, - }); - if (case.link_libc) exe.linkLibC(); + if (case.is_exe) { + const exe = b.addExecutable(.{ + .name = std.fs.path.stem(case.src_path), + .root_source_file = .{ .path = case.src_path }, + .optimize = optimize, + .target = case.target, + }); + if (case.link_libc) exe.linkLibC(); - step.dependOn(&exe.step); + step.dependOn(&exe.step); + } + + if (case.is_test) { + const exe = b.addTest(.{ + .name = std.fs.path.stem(case.src_path), + .root_source_file = .{ .path = case.src_path }, + .optimize = optimize, + .target = case.target, + }); + if (case.link_libc) exe.linkLibC(); + + step.dependOn(&exe.step); + } + } + } + + inline for (standalone.build_cases) |case| { + const requires_stage2 = @hasDecl(case.import, "requires_stage2") and + case.import.requires_stage2; + const requires_symlinks = @hasDecl(case.import, "requires_symlinks") and + case.import.requires_symlinks; + const requires_macos_sdk = @hasDecl(case.import, "requires_macos_sdk") and + case.import.requires_macos_sdk; + const bad = + (requires_stage2 and omit_stage2) or + (requires_symlinks and omit_symlinks) or + (requires_macos_sdk and !enable_macos_sdk); + if (!bad) { + const dep = b.anonymousDependency(case.build_root, case.import, .{}); + const dep_step = dep.builder.default_step; + assert(mem.startsWith(u8, dep.builder.dep_prefix, "test.")); + const dep_prefix_adjusted = dep.builder.dep_prefix["test.".len..]; + dep_step.name = b.fmt("{s}{s}", .{ dep_prefix_adjusted, dep_step.name }); + step.dependOn(dep_step); } } @@ -603,19 +631,19 @@ pub fn addLinkTests( const step = b.step("test-link", "Run the linker tests"); const omit_symlinks = builtin.os.tag == .windows and !enable_symlinks_windows; - inline for (link.cases) |link_test| { - const requires_stage2 = @hasDecl(link_test.import, "requires_stage2") and - link_test.import.requires_stage2; - const requires_symlinks = @hasDecl(link_test.import, "requires_symlinks") and - link_test.import.requires_symlinks; - const requires_macos_sdk = @hasDecl(link_test.import, "requires_macos_sdk") and - link_test.import.requires_macos_sdk; + inline for (link.cases) |case| { + const requires_stage2 = @hasDecl(case.import, "requires_stage2") and + case.import.requires_stage2; + const requires_symlinks = @hasDecl(case.import, "requires_symlinks") and + case.import.requires_symlinks; + const requires_macos_sdk = @hasDecl(case.import, "requires_macos_sdk") and + case.import.requires_macos_sdk; const bad = (requires_stage2 and omit_stage2) or (requires_symlinks and omit_symlinks) or (requires_macos_sdk and !enable_macos_sdk); if (!bad) { - const dep = b.anonymousDependency(link_test.build_root, link_test.import, .{}); + const dep = b.anonymousDependency(case.build_root, case.import, .{}); const dep_step = dep.builder.default_step; assert(mem.startsWith(u8, dep.builder.dep_prefix, "test.")); const dep_prefix_adjusted = dep.builder.dep_prefix["test.".len..]; @@ -627,9 +655,7 @@ pub fn addLinkTests( return step; } -pub fn addCliTests(b: *std.Build, test_filter: ?[]const u8, optimize_modes: []const OptimizeMode) *Step { - _ = test_filter; - _ = optimize_modes; +pub fn addCliTests(b: *std.Build) *Step { const step = b.step("test-cli", "Test the command line interface"); { @@ -815,6 +841,19 @@ pub fn addCliTests(b: *std.Build, test_filter: ?[]const u8, optimize_modes: []co step.dependOn(&cleanup.step); } + { + // TODO this should move to become a CLI test rather than standalone + // cases.addBuildFile("test/standalone/options/build.zig", .{ + // .extra_argv = &.{ + // "-Dbool_true", + // "-Dbool_false=false", + // "-Dint=1234", + // "-De=two", + // "-Dstring=hello", + // }, + // }); + } + return step; } From 8d4067e7a37c13c0761b2685f46375b32f01037a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 8 Mar 2023 00:19:55 -0700 Subject: [PATCH 222/294] CI: take advantage of zig build concurrency I ain't afraid of no ghost. This reverts commit 14a176b9b16e07a66a2f9cd485aaf80fed0f5a12. --- ci/aarch64-linux-debug.sh | 1 - ci/aarch64-linux-release.sh | 1 - ci/aarch64-macos.sh | 1 - ci/x86_64-linux-debug.sh | 1 - ci/x86_64-linux-release.sh | 1 - ci/x86_64-macos-debug.sh | 1 - ci/x86_64-macos-release.sh | 1 - 7 files changed, 7 deletions(-) diff --git a/ci/aarch64-linux-debug.sh b/ci/aarch64-linux-debug.sh index 90ed8bbc35..94f40c557b 100644 --- a/ci/aarch64-linux-debug.sh +++ b/ci/aarch64-linux-debug.sh @@ -60,7 +60,6 @@ stage3-debug/bin/zig build -Dtarget=arm-linux-musleabihf # TODO: add -fqemu back to this line stage3-debug/bin/zig build test docs \ - -j1 \ -fwasmtime \ -Dstatic-llvm \ -Dtarget=native-native-musl \ diff --git a/ci/aarch64-linux-release.sh b/ci/aarch64-linux-release.sh index e99e3e1c08..65d6063f25 100644 --- a/ci/aarch64-linux-release.sh +++ b/ci/aarch64-linux-release.sh @@ -60,7 +60,6 @@ stage3-release/bin/zig build -Dtarget=arm-linux-musleabihf # TODO: add -fqemu back to this line stage3-release/bin/zig build test docs \ - -j1 \ -fwasmtime \ -Dstatic-llvm \ -Dtarget=native-native-musl \ diff --git a/ci/aarch64-macos.sh b/ci/aarch64-macos.sh index 634a60b600..b4533e149f 100755 --- a/ci/aarch64-macos.sh +++ b/ci/aarch64-macos.sh @@ -44,7 +44,6 @@ PATH="$HOME/local/bin:$PATH" cmake .. \ $HOME/local/bin/ninja install stage3-release/bin/zig build test docs \ - -j1 \ --zig-lib-dir "$(pwd)/../lib" \ -Denable-macos-sdk \ -Dstatic-llvm \ diff --git a/ci/x86_64-linux-debug.sh b/ci/x86_64-linux-debug.sh index 1267a2d753..7f2382f04a 100755 --- a/ci/x86_64-linux-debug.sh +++ b/ci/x86_64-linux-debug.sh @@ -58,7 +58,6 @@ stage3-debug/bin/zig fmt --check .. \ stage3-debug/bin/zig build -Dtarget=arm-linux-musleabihf stage3-debug/bin/zig build test docs \ - -j1 \ -fqemu \ -fwasmtime \ -Dstatic-llvm \ diff --git a/ci/x86_64-linux-release.sh b/ci/x86_64-linux-release.sh index 3305f5e951..cdb24e4a6f 100755 --- a/ci/x86_64-linux-release.sh +++ b/ci/x86_64-linux-release.sh @@ -59,7 +59,6 @@ stage3-release/bin/zig fmt --check .. \ stage3-release/bin/zig build -Dtarget=arm-linux-musleabihf stage3-release/bin/zig build test docs \ - -j1 \ -fqemu \ -fwasmtime \ -Dstatic-llvm \ diff --git a/ci/x86_64-macos-debug.sh b/ci/x86_64-macos-debug.sh index edd35970b8..c24ff5d295 100755 --- a/ci/x86_64-macos-debug.sh +++ b/ci/x86_64-macos-debug.sh @@ -48,7 +48,6 @@ cmake .. \ make $JOBS install stage3/bin/zig build test docs \ - -j1 \ --zig-lib-dir "$(pwd)/../lib" \ -Denable-macos-sdk \ -Dstatic-llvm \ diff --git a/ci/x86_64-macos-release.sh b/ci/x86_64-macos-release.sh index 65dea3afcb..a4dedb4446 100755 --- a/ci/x86_64-macos-release.sh +++ b/ci/x86_64-macos-release.sh @@ -48,7 +48,6 @@ cmake .. \ make $JOBS install stage3/bin/zig build test docs \ - -j1 \ --zig-lib-dir "$(pwd)/../lib" \ -Denable-macos-sdk \ -Dstatic-llvm \ From 974a6fe757fd53873e7dace177ad3ae3c425b504 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 8 Mar 2023 14:34:17 -0700 Subject: [PATCH 223/294] std.Build.RunStep: support -fqemu solving bad dynamic linker --- lib/std/Build/RunStep.zig | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/std/Build/RunStep.zig b/lib/std/Build/RunStep.zig index 9ab9972c2e..9a1c887d7d 100644 --- a/lib/std/Build/RunStep.zig +++ b/lib/std/Build/RunStep.zig @@ -555,7 +555,9 @@ fn runCommand( try Step.handleVerbose(step.owner, self.cwd, argv); const result = spawnChildAndCollect(self, argv, has_side_effects) catch |err| term: { - if (err == error.InvalidExe) interpret: { + // InvalidExe: cpu arch mismatch + // FileNotFound: can happen with a wrong dynamic linker path + if (err == error.InvalidExe or err == error.FileNotFound) interpret: { // TODO: learn the target from the binary directly rather than from // relying on it being a CompileStep. This will make this logic // work even for the edge case that the binary was produced by a From 23295f64ca8b93f32e75cef42cc1293ec334e890 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 9 Mar 2023 14:17:25 -0700 Subject: [PATCH 224/294] fix ZIR decoding of error notes --- src/AstGen.zig | 4 ++-- src/Compilation.zig | 2 +- src/Zir.zig | 6 ++++++ 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/AstGen.zig b/src/AstGen.zig index 8ac67a7107..6d7b8156c8 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -10382,7 +10382,7 @@ fn appendErrorTok( comptime format: []const u8, args: anytype, ) !void { - try astgen.appendErrorTokNotes(token, format, args, &[0]u32{}); + try astgen.appendErrorTokNotesOff(token, 0, format, args, &[0]u32{}); } fn failTokNotes( @@ -10392,7 +10392,7 @@ fn failTokNotes( args: anytype, notes: []const u32, ) InnerError { - try appendErrorTokNotes(astgen, token, format, args, notes); + try appendErrorTokNotesOff(astgen, token, 0, format, args, notes); return error.AnalysisFail; } diff --git a/src/Compilation.zig b/src/Compilation.zig index b542c86511..ec21d2c483 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -2909,7 +2909,7 @@ pub fn addZirErrorMessages(eb: *ErrorBundle.Wip, file: *Module.File) !void { .column = @intCast(u32, err_loc.column), .source_line = try eb.addString(err_loc.source_line), }), - .notes_len = item.data.notes, + .notes_len = item.data.notesLen(file.zir), }); } diff --git a/src/Zir.zig b/src/Zir.zig index 65e2f21cc9..001c4e8101 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -3594,6 +3594,12 @@ pub const Inst = struct { /// 0 or a payload index of a `Block`, each is a payload /// index of another `Item`. notes: u32, + + pub fn notesLen(item: Item, zir: Zir) u32 { + if (item.notes == 0) return 0; + const block = zir.extraData(Block, item.notes); + return block.data.body_len; + } }; }; From 7106a91b097c5ac0feb13f328bec5e6788f8c8ef Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 9 Mar 2023 14:18:25 -0700 Subject: [PATCH 225/294] CLI: fix ast-check printing ZIR errors twice --- src/main.zig | 67 ++++++++++++---------------------------------------- 1 file changed, 15 insertions(+), 52 deletions(-) diff --git a/src/main.zig b/src/main.zig index 699122d26f..669f1afd0c 100644 --- a/src/main.zig +++ b/src/main.zig @@ -4082,12 +4082,7 @@ fn updateModule(gpa: Allocator, comp: *Compilation, hook: AfterUpdateHook) !void defer errors.deinit(comp.gpa); if (errors.errorMessageCount() > 0) { - const ttyconf: std.debug.TTY.Config = switch (comp.color) { - .auto => std.debug.detectTTYConfig(std.io.getStdErr()), - .on => .escape_codes, - .off => .no_color, - }; - errors.renderToStdErr(ttyconf); + errors.renderToStdErr(get_tty_conf(comp.color)); const log_text = comp.getCompileLogOutput(); if (log_text.len != 0) { std.debug.print("\nCompile Log Output:\n{s}", .{log_text}); @@ -4714,14 +4709,9 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi &all_modules, ); if (wip_errors.root_list.items.len > 0) { - const ttyconf: std.debug.TTY.Config = switch (color) { - .auto => std.debug.detectTTYConfig(std.io.getStdErr()), - .on => .escape_codes, - .off => .no_color, - }; var errors = try wip_errors.toOwnedBundle(); defer errors.deinit(gpa); - errors.renderToStdErr(ttyconf); + errors.renderToStdErr(get_tty_conf(color)); process.exit(1); } try fetch_result; @@ -4767,7 +4757,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi defer comp.destroy(); updateModule(gpa, comp, .none) catch |err| switch (err) { - error.SemanticAnalyzeFail => process.exit(1), + error.SemanticAnalyzeFail => process.exit(2), else => |e| return e, }; try comp.makeBinFileExecutable(); @@ -4982,14 +4972,9 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void try wip_errors.init(gpa); defer wip_errors.deinit(); try Compilation.addZirErrorMessages(&wip_errors, &file); - const ttyconf: std.debug.TTY.Config = switch (color) { - .auto => std.debug.detectTTYConfig(std.io.getStdErr()), - .on => .escape_codes, - .off => .no_color, - }; var error_bundle = try wip_errors.toOwnedBundle(); defer error_bundle.deinit(gpa); - error_bundle.renderToStdErr(ttyconf); + error_bundle.renderToStdErr(get_tty_conf(color)); process.exit(2); } } else if (tree.errors.len != 0) { @@ -5193,14 +5178,9 @@ fn fmtPathFile( try wip_errors.init(gpa); defer wip_errors.deinit(); try Compilation.addZirErrorMessages(&wip_errors, &file); - const ttyconf: std.debug.TTY.Config = switch (fmt.color) { - .auto => std.debug.detectTTYConfig(std.io.getStdErr()), - .on => .escape_codes, - .off => .no_color, - }; var error_bundle = try wip_errors.toOwnedBundle(); defer error_bundle.deinit(gpa); - error_bundle.renderToStdErr(ttyconf); + error_bundle.renderToStdErr(get_tty_conf(fmt.color)); fmt.any_error = true; } } @@ -5235,14 +5215,9 @@ fn printAstErrorsToStderr(gpa: Allocator, tree: Ast, path: []const u8, color: Co try putAstErrorsIntoBundle(gpa, tree, path, &wip_errors); - const ttyconf: std.debug.TTY.Config = switch (color) { - .auto => std.debug.detectTTYConfig(std.io.getStdErr()), - .on => .escape_codes, - .off => .no_color, - }; var error_bundle = try wip_errors.toOwnedBundle(); defer error_bundle.deinit(gpa); - error_bundle.renderToStdErr(ttyconf); + error_bundle.renderToStdErr(get_tty_conf(color)); } pub fn putAstErrorsIntoBundle( @@ -5848,11 +5823,6 @@ pub fn cmdAstCheck( file.tree_loaded = true; defer file.tree.deinit(gpa); - try printAstErrorsToStderr(gpa, file.tree, file.sub_file_path, color); - if (file.tree.errors.len != 0) { - process.exit(1); - } - file.zir = try AstGen.generate(gpa, file.tree); file.zir_loaded = true; defer file.zir.deinit(gpa); @@ -5862,14 +5832,9 @@ pub fn cmdAstCheck( try wip_errors.init(gpa); defer wip_errors.deinit(); try Compilation.addZirErrorMessages(&wip_errors, &file); - const ttyconf: std.debug.TTY.Config = switch (color) { - .auto => std.debug.detectTTYConfig(std.io.getStdErr()), - .on => .escape_codes, - .off => .no_color, - }; var error_bundle = try wip_errors.toOwnedBundle(); defer error_bundle.deinit(gpa); - error_bundle.renderToStdErr(ttyconf); + error_bundle.renderToStdErr(get_tty_conf(color)); process.exit(1); } @@ -5974,11 +5939,6 @@ pub fn cmdChangelist( file.tree_loaded = true; defer file.tree.deinit(gpa); - try printAstErrorsToStderr(gpa, file.tree, old_source_file, .auto); - if (file.tree.errors.len != 0) { - process.exit(1); - } - file.zir = try AstGen.generate(gpa, file.tree); file.zir_loaded = true; defer file.zir.deinit(gpa); @@ -6013,11 +5973,6 @@ pub fn cmdChangelist( var new_tree = try Ast.parse(gpa, new_source, .zig); defer new_tree.deinit(gpa); - try printAstErrorsToStderr(gpa, new_tree, new_source_file, .auto); - if (new_tree.errors.len != 0) { - process.exit(1); - } - var old_zir = file.zir; defer old_zir.deinit(gpa); file.zir_loaded = false; @@ -6293,3 +6248,11 @@ const ClangSearchSanitizer = struct { iframework: bool = false, }; }; + +fn get_tty_conf(color: Color) std.debug.TTY.Config { + return switch (color) { + .auto => std.debug.detectTTYConfig(std.io.getStdErr()), + .on => .escape_codes, + .off => .no_color, + }; +} From 7db74009db4a4dc820e3e32c805ab7e29394205b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 9 Mar 2023 18:06:07 -0700 Subject: [PATCH 226/294] std.Progress.Node: add a setName method This can be used to update an existing node's label rather than indicate that more things have been accomplished. --- lib/std/Progress.zig | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/lib/std/Progress.zig b/lib/std/Progress.zig index 64084e761f..dba7166398 100644 --- a/lib/std/Progress.zig +++ b/lib/std/Progress.zig @@ -126,6 +126,21 @@ pub const Node = struct { } } + /// Thread-safe. + pub fn setName(self: *Node, name: []const u8) void { + const progress = self.context; + progress.update_mutex.lock(); + defer progress.update_mutex.unlock(); + self.name = name; + if (self.parent) |parent| { + @atomicStore(?*Node, &parent.recently_updated_child, self, .Release); + if (parent.parent) |grand_parent| { + @atomicStore(?*Node, &grand_parent.recently_updated_child, parent, .Release); + } + if (progress.timer) |*timer| progress.maybeRefreshWithHeldLock(timer); + } + } + /// Thread-safe. 0 means unknown. pub fn setEstimatedTotalItems(self: *Node, count: usize) void { @atomicStore(usize, &self.unprotected_estimated_total_items, count, .Monotonic); @@ -174,16 +189,20 @@ pub fn maybeRefresh(self: *Progress) void { if (self.timer) |*timer| { if (!self.update_mutex.tryLock()) return; defer self.update_mutex.unlock(); - const now = timer.read(); - if (now < self.initial_delay_ns) return; - // TODO I have observed this to happen sometimes. I think we need to follow Rust's - // lead and guarantee monotonically increasing times in the std lib itself. - if (now < self.prev_refresh_timestamp) return; - if (now - self.prev_refresh_timestamp < self.refresh_rate_ns) return; - return self.refreshWithHeldLock(); + maybeRefreshWithHeldLock(self, timer); } } +fn maybeRefreshWithHeldLock(self: *Progress, timer: *std.time.Timer) void { + const now = timer.read(); + if (now < self.initial_delay_ns) return; + // TODO I have observed this to happen sometimes. I think we need to follow Rust's + // lead and guarantee monotonically increasing times in the std lib itself. + if (now < self.prev_refresh_timestamp) return; + if (now - self.prev_refresh_timestamp < self.refresh_rate_ns) return; + return self.refreshWithHeldLock(); +} + /// Updates the terminal and resets `self.next_refresh_timestamp`. Thread-safe. pub fn refresh(self: *Progress) void { if (!self.update_mutex.tryLock()) return; From 25c3878c00b92ffc884d89d32817ca9c244f7972 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 9 Mar 2023 18:06:56 -0700 Subject: [PATCH 227/294] std.fs.File.readvAll: fix behavior for 0-length vectors The OS layer expects pointer addresses to be inside the application's address space even if the length is zero. Meanwhile, in Zig, slices may have undefined pointer addresses when the length is zero. So this function now modifies the iov_base fields when the length is zero. --- lib/std/fs/file.zig | 21 ++++++++++++++++++--- lib/std/os.zig | 3 +++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/lib/std/fs/file.zig b/lib/std/fs/file.zig index bf93a61239..f81841a662 100644 --- a/lib/std/fs/file.zig +++ b/lib/std/fs/file.zig @@ -1048,12 +1048,27 @@ pub const File = struct { /// Returns the number of bytes read. If the number read is smaller than the total bytes /// from all the buffers, it means the file reached the end. Reaching the end of a file /// is not an error condition. - /// The `iovecs` parameter is mutable because this function needs to mutate the fields in - /// order to handle partial reads from the underlying OS layer. - /// See https://github.com/ziglang/zig/issues/7699 + /// + /// The `iovecs` parameter is mutable because: + /// * This function needs to mutate the fields in order to handle partial + /// reads from the underlying OS layer. + /// * The OS layer expects pointer addresses to be inside the application's address space + /// even if the length is zero. Meanwhile, in Zig, slices may have undefined pointer + /// addresses when the length is zero. So this function modifies the iov_base fields + /// when the length is zero. + /// + /// Related open issue: https://github.com/ziglang/zig/issues/7699 pub fn readvAll(self: File, iovecs: []os.iovec) ReadError!usize { if (iovecs.len == 0) return 0; + // We use the address of this local variable for all zero-length + // vectors so that the OS does not complain that we are giving it + // addresses outside the application's address space. + var garbage: [1]u8 = undefined; + for (iovecs) |*v| { + if (v.iov_len == 0) v.iov_base = &garbage; + } + var i: usize = 0; var off: usize = 0; while (true) { diff --git a/lib/std/os.zig b/lib/std/os.zig index 9d5625c13e..b11aac493f 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -766,6 +766,9 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize { /// This operation is non-atomic on the following systems: /// * Windows /// On these systems, the read races with concurrent writes to the same file descriptor. +/// +/// This function assumes that all zero-length vectors have a pointer within the address +/// space of the application. pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize { if (builtin.os.tag == .windows) { // TODO improve this to use ReadFileScatter From 3b00e341fd8d1f12a3a14ee79a675a36549e716b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 9 Mar 2023 18:08:54 -0700 Subject: [PATCH 228/294] AstGen: skip walking the AST when there are parse errors The AST -> ZIR lowering process assumes an AST that does not have any parse errors. --- src/AstGen.zig | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/src/AstGen.zig b/src/AstGen.zig index 6d7b8156c8..182a28084f 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -133,8 +133,6 @@ pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir { try astgen.extra.ensureTotalCapacity(gpa, tree.nodes.len + reserved_count); astgen.extra.items.len += reserved_count; - try lowerAstErrors(&astgen); - var top_scope: Scope.Top = .{}; var gz_instructions: std.ArrayListUnmanaged(Zir.Inst.Index) = .{}; @@ -150,18 +148,24 @@ pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir { }; defer gz_instructions.deinit(gpa); - if (AstGen.structDeclInner( - &gen_scope, - &gen_scope.base, - 0, - tree.containerDeclRoot(), - .Auto, - 0, - )) |struct_decl_ref| { - assert(refToIndex(struct_decl_ref).? == 0); - } else |err| switch (err) { - error.OutOfMemory => return error.OutOfMemory, - error.AnalysisFail => {}, // Handled via compile_errors below. + // The AST -> ZIR lowering process assumes an AST that does not have any + // parse errors. + if (tree.errors.len == 0) { + if (AstGen.structDeclInner( + &gen_scope, + &gen_scope.base, + 0, + tree.containerDeclRoot(), + .Auto, + 0, + )) |struct_decl_ref| { + assert(refToIndex(struct_decl_ref).? == 0); + } else |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + error.AnalysisFail => {}, // Handled via compile_errors below. + } + } else { + try lowerAstErrors(&astgen); } const err_index = @enumToInt(Zir.ExtraIndex.compile_errors); @@ -12642,7 +12646,7 @@ fn emitDbgStmt(gz: *GenZir, line: u32, column: u32) !void { fn lowerAstErrors(astgen: *AstGen) !void { const tree = astgen.tree; - if (tree.errors.len == 0) return; + assert(tree.errors.len > 0); const gpa = astgen.gpa; const parse_err = tree.errors[0]; From 3186658e602b13a98c388872bbdc0b5cc1eb9267 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 9 Mar 2023 18:10:21 -0700 Subject: [PATCH 229/294] std.Build.CheckFileStep: add a way to expect exact This is done in a bit of a haphazard way. Eventually the API needs to break in favor of a "checks" system similar to how RunStep works. --- lib/std/Build/CheckFileStep.zig | 41 +++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/lib/std/Build/CheckFileStep.zig b/lib/std/Build/CheckFileStep.zig index a65810681b..1c2b6b7786 100644 --- a/lib/std/Build/CheckFileStep.zig +++ b/lib/std/Build/CheckFileStep.zig @@ -1,19 +1,19 @@ -const std = @import("../std.zig"); -const Step = std.Build.Step; -const fs = std.fs; -const mem = std.mem; - -const CheckFileStep = @This(); - -pub const base_id = .check_file; +//! Fail the build step if a file does not match certain checks. +//! TODO: make this more flexible, supporting more kinds of checks. +//! TODO: generalize the code in std.testing.expectEqualStrings and make this +//! CheckFileStep produce those helpful diagnostics when there is not a match. step: Step, expected_matches: []const []const u8, +expected_exact: ?[]const u8, source: std.Build.FileSource, max_bytes: usize = 20 * 1024 * 1024, +pub const base_id = .check_file; + pub const Options = struct { - expected_matches: []const []const u8, + expected_matches: []const []const u8 = &.{}, + expected_exact: ?[]const u8 = null, }; pub fn create( @@ -31,6 +31,7 @@ pub fn create( }), .source = source.dupe(owner), .expected_matches = owner.dupeStrings(options.expected_matches), + .expected_exact = options.expected_exact, }; self.source.addStepDependencies(&self.step); return self; @@ -60,8 +61,28 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { \\{s} \\========= but file does not contain it: ======= \\{s} - \\ + \\=============================================== , .{ expected_match, contents }); } } + + if (self.expected_exact) |expected_exact| { + if (!mem.eql(u8, expected_exact, contents)) { + return step.fail( + \\ + \\========= expected: ===================== + \\{s} + \\========= but found: ==================== + \\{s} + \\========= from the following file: ====== + \\{s} + , .{ expected_exact, contents, src_path }); + } + } } + +const CheckFileStep = @This(); +const std = @import("../std.zig"); +const Step = std.Build.Step; +const fs = std.fs; +const mem = std.mem; From 7cc4a6965c28e427cbfba57a985f837734d6257e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 9 Mar 2023 18:13:55 -0700 Subject: [PATCH 230/294] build runner enhancements in preparation for test-cases * std.zig.ErrorBundle: support rendering options for whether to include the reference trace, whether to include the source line, and TTY configuration. * build runner: don't print progress in dumb terminals * std.Build.CompileStep: - add a way to expect compilation errors via the new `expect_errors` field. This is an advanced setting that can change the intent of the CompileStep. If this slice has nonzero length, it means that the CompileStep exists to check for compile errors and return *success* if they match, and failure otherwise. - remove the object format parameter from `checkObject`. The object format is known based on the CompileStep's target. - Avoid passing -L and -I flags for nonexistent directories within search_prefixes. This prevents a warning, that should probably be upgraded to an error in Zig's CLI parsing code, when the linker sees an -L directory that does not exist. * std.Build.Step: - When spawning the zig compiler process, takes advantage of the new `std.Progress.Node.setName` API to avoid ticking up a meaningless number at every progress update. --- lib/build_runner.zig | 30 ++++++--- lib/std/Build/CompileStep.zig | 113 ++++++++++++++++++++++++++++++---- lib/std/Build/Step.zig | 20 ++++-- lib/std/zig/ErrorBundle.zig | 29 +++++---- src/Sema.zig | 2 +- src/main.zig | 28 ++++++--- 6 files changed, 173 insertions(+), 49 deletions(-) diff --git a/lib/build_runner.zig b/lib/build_runner.zig index bb04b3e132..8e0ebb4558 100644 --- a/lib/build_runner.zig +++ b/lib/build_runner.zig @@ -84,8 +84,6 @@ pub fn main() !void { ); defer builder.destroy(); - const Color = enum { auto, off, on }; - var targets = ArrayList([]const u8).init(arena); var debug_log_scopes = ArrayList([]const u8).init(arena); var thread_pool_options: std.Thread.Pool.Options = .{ .allocator = arena }; @@ -273,13 +271,9 @@ pub fn main() !void { } const stderr = std.io.getStdErr(); - const ttyconf: std.debug.TTY.Config = switch (color) { - .auto => std.debug.detectTTYConfig(stderr), - .on => .escape_codes, - .off => .no_color, - }; + const ttyconf = get_tty_conf(color, stderr); - var progress: std.Progress = .{}; + var progress: std.Progress = .{ .dont_print_on_dumb = true }; const main_progress_node = progress.start("", 0); builder.debug_log_scopes = debug_log_scopes.items; @@ -498,7 +492,7 @@ fn runStepNames( if (total_compile_errors > 0) { for (compile_error_steps.items) |s| { if (s.result_error_bundle.errorMessageCount() > 0) { - s.result_error_bundle.renderToStdErr(ttyconf); + s.result_error_bundle.renderToStdErr(renderOptions(ttyconf)); } } @@ -961,3 +955,21 @@ fn cleanExit() void { // of calling exit. process.exit(0); } + +const Color = enum { auto, off, on }; + +fn get_tty_conf(color: Color, stderr: std.fs.File) std.debug.TTY.Config { + return switch (color) { + .auto => std.debug.detectTTYConfig(stderr), + .on => .escape_codes, + .off => .no_color, + }; +} + +fn renderOptions(ttyconf: std.debug.TTY.Config) std.zig.ErrorBundle.RenderOptions { + return .{ + .ttyconf = ttyconf, + .include_source_line = ttyconf != .no_color, + .include_reference_trace = ttyconf != .no_color, + }; +} diff --git a/lib/std/Build/CompileStep.zig b/lib/std/Build/CompileStep.zig index 42cc63e8b4..5c44ab82d3 100644 --- a/lib/std/Build/CompileStep.zig +++ b/lib/std/Build/CompileStep.zig @@ -207,6 +207,12 @@ want_lto: ?bool = null, use_llvm: ?bool = null, use_lld: ?bool = null, +/// This is an advanced setting that can change the intent of this CompileStep. +/// If this slice has nonzero length, it means that this CompileStep exists to +/// check for compile errors and return *success* if they match, and failure +/// otherwise. +expect_errors: []const []const u8 = &.{}, + output_path_source: GeneratedFile, output_lib_path_source: GeneratedFile, output_h_path_source: GeneratedFile, @@ -552,8 +558,8 @@ pub fn run(cs: *CompileStep) *RunStep { return cs.step.owner.addRunArtifact(cs); } -pub fn checkObject(self: *CompileStep, obj_format: std.Target.ObjectFormat) *CheckObjectStep { - return CheckObjectStep.create(self.step.owner, self.getOutputSource(), obj_format); +pub fn checkObject(self: *CompileStep) *CheckObjectStep { + return CheckObjectStep.create(self.step.owner, self.getOutputSource(), self.target_info.target.ofmt); } pub fn setLinkerScriptPath(self: *CompileStep, source: FileSource) void { @@ -1838,14 +1844,38 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { } for (b.search_prefixes.items) |search_prefix| { - try zig_args.append("-L"); - try zig_args.append(b.pathJoin(&.{ - search_prefix, "lib", - })); - try zig_args.append("-I"); - try zig_args.append(b.pathJoin(&.{ - search_prefix, "include", - })); + var prefix_dir = fs.cwd().openDir(search_prefix, .{}) catch |err| { + return step.fail("unable to open prefix directory '{s}': {s}", .{ + search_prefix, @errorName(err), + }); + }; + defer prefix_dir.close(); + + // Avoid passing -L and -I flags for nonexistent directories. + // This prevents a warning, that should probably be upgraded to an error in Zig's + // CLI parsing code, when the linker sees an -L directory that does not exist. + + if (prefix_dir.accessZ("lib", .{})) |_| { + try zig_args.appendSlice(&.{ + "-L", try fs.path.join(b.allocator, &.{ search_prefix, "lib" }), + }); + } else |err| switch (err) { + error.FileNotFound => {}, + else => |e| return step.fail("unable to access '{s}/lib' directory: {s}", .{ + search_prefix, @errorName(e), + }), + } + + if (prefix_dir.accessZ("include", .{})) |_| { + try zig_args.appendSlice(&.{ + "-I", try fs.path.join(b.allocator, &.{ search_prefix, "include" }), + }); + } else |err| switch (err) { + error.FileNotFound => {}, + else => |e| return step.fail("unable to access '{s}/include' directory: {s}", .{ + search_prefix, @errorName(e), + }), + } } try addFlag(&zig_args, "valgrind", self.valgrind_support); @@ -1943,7 +1973,14 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { try zig_args.append(resolved_args_file); } - const output_bin_path = try step.evalZigProcess(zig_args.items, prog_node); + const output_bin_path = step.evalZigProcess(zig_args.items, prog_node) catch |err| switch (err) { + error.NeedCompileErrorCheck => { + assert(self.expect_errors.len != 0); + try checkCompileErrors(self); + return; + }, + else => |e| return e, + }; const build_output_dir = fs.path.dirname(output_bin_path).?; if (self.output_dir) |output_dir| { @@ -2178,3 +2215,57 @@ const TransitiveDeps = struct { } } }; + +fn checkCompileErrors(self: *CompileStep) !void { + // Clear this field so that it does not get printed by the build runner. + const actual_eb = self.step.result_error_bundle; + self.step.result_error_bundle = std.zig.ErrorBundle.empty; + + const arena = self.step.owner.allocator; + + var actual_stderr_list = std.ArrayList(u8).init(arena); + try actual_eb.renderToWriter(.{ + .ttyconf = .no_color, + .include_reference_trace = false, + .include_source_line = false, + }, actual_stderr_list.writer()); + const actual_stderr = try actual_stderr_list.toOwnedSlice(); + + // Render the expected lines into a string that we can compare verbatim. + var expected_generated = std.ArrayList(u8).init(arena); + + var actual_line_it = mem.split(u8, actual_stderr, "\n"); + for (self.expect_errors) |expect_line| { + const actual_line = actual_line_it.next() orelse { + try expected_generated.appendSlice(expect_line); + try expected_generated.append('\n'); + continue; + }; + if (mem.endsWith(u8, actual_line, expect_line)) { + try expected_generated.appendSlice(actual_line); + try expected_generated.append('\n'); + continue; + } + if (mem.startsWith(u8, expect_line, ":?:?: ")) { + if (mem.endsWith(u8, actual_line, expect_line[":?:?: ".len..])) { + try expected_generated.appendSlice(actual_line); + try expected_generated.append('\n'); + continue; + } + } + try expected_generated.appendSlice(expect_line); + try expected_generated.append('\n'); + } + + if (mem.eql(u8, expected_generated.items, actual_stderr)) return; + + // TODO merge this with the testing.expectEqualStrings logic, and also CheckFile + return self.step.fail( + \\ + \\========= expected: ===================== + \\{s} + \\========= but found: ==================== + \\{s} + \\========================================= + , .{ expected_generated.items, actual_stderr }); +} diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig index f1edab5881..45aa635972 100644 --- a/lib/std/Build/Step.zig +++ b/lib/std/Build/Step.zig @@ -295,8 +295,8 @@ pub fn evalZigProcess( var node_name: std.ArrayListUnmanaged(u8) = .{}; defer node_name.deinit(gpa); - var sub_prog_node: ?std.Progress.Node = null; - defer if (sub_prog_node) |*n| n.end(); + var sub_prog_node = prog_node.start("", 0); + defer sub_prog_node.end(); const stdout = poller.fifo(.stdout); @@ -336,11 +336,9 @@ pub fn evalZigProcess( }; }, .progress => { - if (sub_prog_node) |*n| n.end(); node_name.clearRetainingCapacity(); try node_name.appendSlice(gpa, body); - sub_prog_node = prog_node.start(node_name.items, 0); - sub_prog_node.?.activate(); + sub_prog_node.setName(node_name.items); }, .emit_bin_path => { const EbpHdr = std.zig.Server.Message.EmitBinPath; @@ -371,6 +369,18 @@ pub fn evalZigProcess( s.result_duration_ns = timer.read(); s.result_peak_rss = child.resource_usage_statistics.getMaxRss() orelse 0; + // Special handling for CompileStep that is expecting compile errors. + if (s.cast(Build.CompileStep)) |compile| switch (term) { + .Exited => { + // Note that the exit code may be 0 in this case due to the + // compiler server protocol. + if (compile.expect_errors.len != 0 and s.result_error_bundle.errorMessageCount() > 0) { + return error.NeedCompileErrorCheck; + } + }, + else => {}, + }; + try handleChildProcessTerm(s, term, null, argv); if (s.result_error_bundle.errorMessageCount() > 0) { diff --git a/lib/std/zig/ErrorBundle.zig b/lib/std/zig/ErrorBundle.zig index 94f80797a8..845e9d8ff5 100644 --- a/lib/std/zig/ErrorBundle.zig +++ b/lib/std/zig/ErrorBundle.zig @@ -141,32 +141,35 @@ pub fn nullTerminatedString(eb: ErrorBundle, index: usize) [:0]const u8 { return string_bytes[index..end :0]; } -pub fn renderToStdErr(eb: ErrorBundle, ttyconf: std.debug.TTY.Config) void { +pub const RenderOptions = struct { + ttyconf: std.debug.TTY.Config, + include_reference_trace: bool = true, + include_source_line: bool = true, +}; + +pub fn renderToStdErr(eb: ErrorBundle, options: RenderOptions) void { std.debug.getStderrMutex().lock(); defer std.debug.getStderrMutex().unlock(); const stderr = std.io.getStdErr(); - return renderToWriter(eb, ttyconf, stderr.writer()) catch return; + return renderToWriter(eb, options, stderr.writer()) catch return; } -pub fn renderToWriter( - eb: ErrorBundle, - ttyconf: std.debug.TTY.Config, - writer: anytype, -) anyerror!void { +pub fn renderToWriter(eb: ErrorBundle, options: RenderOptions, writer: anytype) anyerror!void { for (eb.getMessages()) |err_msg| { - try renderErrorMessageToWriter(eb, err_msg, ttyconf, writer, "error", .Red, 0); + try renderErrorMessageToWriter(eb, options, err_msg, writer, "error", .Red, 0); } } fn renderErrorMessageToWriter( eb: ErrorBundle, + options: RenderOptions, err_msg_index: MessageIndex, - ttyconf: std.debug.TTY.Config, stderr: anytype, kind: []const u8, color: std.debug.TTY.Color, indent: usize, ) anyerror!void { + const ttyconf = options.ttyconf; var counting_writer = std.io.countingWriter(stderr); const counting_stderr = counting_writer.writer(); const err_msg = eb.getErrorMessage(err_msg_index); @@ -196,7 +199,7 @@ fn renderErrorMessageToWriter( try stderr.print(" ({d} times)\n", .{err_msg.count}); } try ttyconf.setColor(stderr, .Reset); - if (src.data.source_line != 0) { + if (src.data.source_line != 0 and options.include_source_line) { const line = eb.nullTerminatedString(src.data.source_line); for (line) |b| switch (b) { '\t' => try stderr.writeByte(' '), @@ -216,9 +219,9 @@ fn renderErrorMessageToWriter( try ttyconf.setColor(stderr, .Reset); } for (eb.getNotes(err_msg_index)) |note| { - try renderErrorMessageToWriter(eb, note, ttyconf, stderr, "note", .Cyan, indent); + try renderErrorMessageToWriter(eb, options, note, stderr, "note", .Cyan, indent); } - if (src.data.reference_trace_len > 0) { + if (src.data.reference_trace_len > 0 and options.include_reference_trace) { try ttyconf.setColor(stderr, .Reset); try ttyconf.setColor(stderr, .Dim); try stderr.print("referenced by:\n", .{}); @@ -266,7 +269,7 @@ fn renderErrorMessageToWriter( } try ttyconf.setColor(stderr, .Reset); for (eb.getNotes(err_msg_index)) |note| { - try renderErrorMessageToWriter(eb, note, ttyconf, stderr, "note", .Cyan, indent + 4); + try renderErrorMessageToWriter(eb, options, note, stderr, "note", .Cyan, indent + 4); } } } diff --git a/src/Sema.zig b/src/Sema.zig index 03ebbbdbac..c9f4f27fe2 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -2220,7 +2220,7 @@ fn failWithOwnedErrorMsg(sema: *Sema, err_msg: *Module.ErrorMsg) CompileError { Compilation.addModuleErrorMsg(&wip_errors, err_msg.*) catch unreachable; std.debug.print("compile error during Sema:\n", .{}); var error_bundle = wip_errors.toOwnedBundle() catch unreachable; - error_bundle.renderToStdErr(.no_color); + error_bundle.renderToStdErr(.{ .ttyconf = .no_color }); crash_report.compilerPanic("unexpected compile error occurred", null, null); } diff --git a/src/main.zig b/src/main.zig index 669f1afd0c..03d746af0c 100644 --- a/src/main.zig +++ b/src/main.zig @@ -4082,7 +4082,7 @@ fn updateModule(gpa: Allocator, comp: *Compilation, hook: AfterUpdateHook) !void defer errors.deinit(comp.gpa); if (errors.errorMessageCount() > 0) { - errors.renderToStdErr(get_tty_conf(comp.color)); + errors.renderToStdErr(renderOptions(comp.color)); const log_text = comp.getCompileLogOutput(); if (log_text.len != 0) { std.debug.print("\nCompile Log Output:\n{s}", .{log_text}); @@ -4711,7 +4711,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi if (wip_errors.root_list.items.len > 0) { var errors = try wip_errors.toOwnedBundle(); defer errors.deinit(gpa); - errors.renderToStdErr(get_tty_conf(color)); + errors.renderToStdErr(renderOptions(color)); process.exit(1); } try fetch_result; @@ -4974,7 +4974,7 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void try Compilation.addZirErrorMessages(&wip_errors, &file); var error_bundle = try wip_errors.toOwnedBundle(); defer error_bundle.deinit(gpa); - error_bundle.renderToStdErr(get_tty_conf(color)); + error_bundle.renderToStdErr(renderOptions(color)); process.exit(2); } } else if (tree.errors.len != 0) { @@ -5180,7 +5180,7 @@ fn fmtPathFile( try Compilation.addZirErrorMessages(&wip_errors, &file); var error_bundle = try wip_errors.toOwnedBundle(); defer error_bundle.deinit(gpa); - error_bundle.renderToStdErr(get_tty_conf(fmt.color)); + error_bundle.renderToStdErr(renderOptions(fmt.color)); fmt.any_error = true; } } @@ -5217,7 +5217,7 @@ fn printAstErrorsToStderr(gpa: Allocator, tree: Ast, path: []const u8, color: Co var error_bundle = try wip_errors.toOwnedBundle(); defer error_bundle.deinit(gpa); - error_bundle.renderToStdErr(get_tty_conf(color)); + error_bundle.renderToStdErr(renderOptions(color)); } pub fn putAstErrorsIntoBundle( @@ -5834,7 +5834,7 @@ pub fn cmdAstCheck( try Compilation.addZirErrorMessages(&wip_errors, &file); var error_bundle = try wip_errors.toOwnedBundle(); defer error_bundle.deinit(gpa); - error_bundle.renderToStdErr(get_tty_conf(color)); + error_bundle.renderToStdErr(renderOptions(color)); process.exit(1); } @@ -5892,6 +5892,7 @@ pub fn cmdChangelist( arena: Allocator, args: []const []const u8, ) !void { + const color: Color = .auto; const Zir = @import("Zir.zig"); const old_source_file = args[0]; @@ -5948,10 +5949,9 @@ pub fn cmdChangelist( try wip_errors.init(gpa); defer wip_errors.deinit(); try Compilation.addZirErrorMessages(&wip_errors, &file); - const ttyconf = std.debug.detectTTYConfig(std.io.getStdErr()); var error_bundle = try wip_errors.toOwnedBundle(); defer error_bundle.deinit(gpa); - error_bundle.renderToStdErr(ttyconf); + error_bundle.renderToStdErr(renderOptions(color)); process.exit(1); } @@ -5984,10 +5984,9 @@ pub fn cmdChangelist( try wip_errors.init(gpa); defer wip_errors.deinit(); try Compilation.addZirErrorMessages(&wip_errors, &file); - const ttyconf = std.debug.detectTTYConfig(std.io.getStdErr()); var error_bundle = try wip_errors.toOwnedBundle(); defer error_bundle.deinit(gpa); - error_bundle.renderToStdErr(ttyconf); + error_bundle.renderToStdErr(renderOptions(color)); process.exit(1); } @@ -6256,3 +6255,12 @@ fn get_tty_conf(color: Color) std.debug.TTY.Config { .off => .no_color, }; } + +fn renderOptions(color: Color) std.zig.ErrorBundle.RenderOptions { + const ttyconf = get_tty_conf(color); + return .{ + .ttyconf = ttyconf, + .include_source_line = ttyconf != .no_color, + .include_reference_trace = ttyconf != .no_color, + }; +} From 29cfd47d6509fc6ee1a165b3bc03180f6cf351a5 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 9 Mar 2023 18:22:51 -0700 Subject: [PATCH 231/294] re-enable test-cases and get them all passing Instead of using `zig test` to build a special version of the compiler that runs all the test-cases, the zig build system is now used as much as possible - all with the basic steps found in the standard library. For incremental compilation tests (the ones that look like foo.0.zig, foo.1.zig, foo.2.zig, etc.), a special version of the compiler is compiled into a utility executable called "check-case" which checks exactly one sequence of incremental updates in an independent subprocess. Previously, all incremental and non-incremental test cases were done in the same test runner process. The compile error checking code is now simpler, but also a bit rudimentary, and so it additionally makes sure that the actual compile errors do not include *extra* messages, and it makes sure that the actual compile errors output in the same order as expected. It is also based on the "ends-with" property of each line rather than the previous logic, which frankly I didn't want to touch with a ten-meter pole. The compile error test cases have been updated to pass in light of these differences. Previously, 'error' mode with 0 compile errors was used to shoehorn in a different kind of test-case - one that only checks if a piece of code compiles without errors. Now there is a 'compile' mode of test-cases, and 'error' must be only used when there are greater than 0 errors. link test cases are updated to omit the target object format argument when calling checkObject since that is no longer needed. The test/stage2 directory is removed; the 2 files within are moved to be directly in the test/ directory. --- build.zig | 50 +- src/test.zig | 1968 ----------------- test/cases.zig | 10 +- .../access_inactive_union_field_comptime.zig | 1 + test/cases/compile_errors/bad_import.zig | 2 +- .../condition_comptime_reason_explained.zig | 2 + ...edding_opaque_type_in_struct_and_union.zig | 1 + ...xtern_function_with_comptime_parameter.zig | 2 +- .../function_parameter_is_opaque.zig | 1 + .../helpful_return_type_error_message.zig | 2 +- .../implicit_semicolon-block_expr.zig | 2 + .../implicit_semicolon-block_statement.zig | 2 + ...implicit_semicolon-comptime_expression.zig | 2 + .../implicit_semicolon-comptime_statement.zig | 2 + .../implicit_semicolon-defer.zig | 2 + .../implicit_semicolon-for_expression.zig | 3 + .../implicit_semicolon-for_statement.zig | 3 + ...t_semicolon-if-else-if-else_expression.zig | 2 + ...it_semicolon-if-else-if-else_statement.zig | 2 + ...plicit_semicolon-if-else-if_expression.zig | 2 + ...mplicit_semicolon-if-else-if_statement.zig | 2 + .../implicit_semicolon-if-else_expression.zig | 2 + .../implicit_semicolon-if-else_statement.zig | 2 + .../implicit_semicolon-if_expression.zig | 2 + .../implicit_semicolon-if_statement.zig | 2 + .../implicit_semicolon-test_expression.zig | 3 + .../implicit_semicolon-test_statement.zig | 3 + ...it_semicolon-while-continue_expression.zig | 2 + ...cit_semicolon-while-continue_statement.zig | 2 + .../implicit_semicolon-while_expression.zig | 2 + .../implicit_semicolon-while_statement.zig | 2 + .../invalid_member_of_builtin_enum.zig | 2 +- .../invalid_store_to_comptime_field.zig | 2 +- .../compile_errors/invalid_struct_field.zig | 1 + .../missing_main_fn_in_executable.zig | 6 +- test/cases/compile_errors/private_main_fn.zig | 6 +- ...runtime_index_into_comptime_type_slice.zig | 5 +- .../struct_type_mismatch_in_arg.zig | 2 +- ...nion_init_with_none_or_multiple_fields.zig | 3 +- ...ess_chaining_pointer_to_optional_array.zig | 2 +- ..._pointer_access_chaining_array_pointer.zig | 2 +- ...spaces_pointer_access_chaining_complex.zig | 2 +- ...pointer_access_chaining_struct_pointer.zig | 2 +- ..._multiple_pointers_with_address_spaces.zig | 2 +- .../llvm/pointer_keeps_address_space.zig | 2 +- ...ace_when_taking_address_of_dereference.zig | 2 +- ...ress_space_coerces_to_implicit_pointer.zig | 2 +- test/{stage2 => }/cbe.zig | 77 +- test/compile_errors.zig | 224 +- test/link/macho/dead_strip/build.zig | 4 +- test/link/macho/dead_strip_dylibs/build.zig | 2 +- test/link/macho/dylib/build.zig | 4 +- test/link/macho/entry/build.zig | 2 +- test/link/macho/headerpad/build.zig | 8 +- test/link/macho/linksection/build.zig | 2 +- test/link/macho/needed_framework/build.zig | 2 +- test/link/macho/needed_library/build.zig | 2 +- test/link/macho/pagezero/build.zig | 4 +- test/link/macho/search_strategy/build.zig | 2 +- test/link/macho/stack_size/build.zig | 2 +- test/link/macho/strict_validation/build.zig | 2 +- test/link/macho/unwind_info/build.zig | 2 +- test/link/macho/weak_framework/build.zig | 2 +- test/link/macho/weak_library/build.zig | 2 +- test/link/wasm/archive/build.zig | 2 +- test/link/wasm/basic-features/build.zig | 2 +- test/link/wasm/bss/build.zig | 2 +- test/link/wasm/export-data/build.zig | 2 +- test/link/wasm/export/build.zig | 6 +- test/link/wasm/extern-mangle/build.zig | 2 +- test/link/wasm/function-table/build.zig | 6 +- test/link/wasm/infer-features/build.zig | 2 +- test/link/wasm/producers/build.zig | 2 +- test/link/wasm/segments/build.zig | 2 +- test/link/wasm/stack_pointer/build.zig | 2 +- test/link/wasm/type/build.zig | 2 +- test/{stage2 => }/nvptx.zig | 31 +- test/src/Cases.zig | 1587 +++++++++++++ test/tests.zig | 27 + 79 files changed, 1805 insertions(+), 2343 deletions(-) delete mode 100644 src/test.zig rename test/{stage2 => }/cbe.zig (92%) rename test/{stage2 => }/nvptx.zig (76%) create mode 100644 test/src/Cases.zig diff --git a/build.zig b/build.zig index ba12ae5ae0..9ba2b1acef 100644 --- a/build.zig +++ b/build.zig @@ -53,13 +53,14 @@ pub fn build(b: *std.Build) !void { const docs_step = b.step("docs", "Build documentation"); docs_step.dependOn(&docgen_cmd.step); - const test_cases = b.addTest(.{ - .root_source_file = .{ .path = "src/test.zig" }, + const check_case_exe = b.addExecutable(.{ + .name = "check-case", + .root_source_file = .{ .path = "test/src/Cases.zig" }, .optimize = optimize, }); - test_cases.main_pkg_path = "."; - test_cases.stack_size = stack_size; - test_cases.single_threaded = single_threaded; + check_case_exe.main_pkg_path = "."; + check_case_exe.stack_size = stack_size; + check_case_exe.single_threaded = single_threaded; const skip_debug = b.option(bool, "skip-debug", "Main test suite skips debug builds") orelse false; const skip_release = b.option(bool, "skip-release", "Main test suite skips release builds") orelse false; @@ -178,7 +179,7 @@ pub fn build(b: *std.Build) !void { if (target.isWindows() and target.getAbi() == .gnu) { // LTO is currently broken on mingw, this can be removed when it's fixed. exe.want_lto = false; - test_cases.want_lto = false; + check_case_exe.want_lto = false; } const exe_options = b.addOptions(); @@ -196,7 +197,7 @@ pub fn build(b: *std.Build) !void { if (link_libc) { exe.linkLibC(); - test_cases.linkLibC(); + check_case_exe.linkLibC(); } const is_debug = optimize == .Debug; @@ -282,14 +283,14 @@ pub fn build(b: *std.Build) !void { } try addCmakeCfgOptionsToExe(b, cfg, exe, use_zig_libcxx); - try addCmakeCfgOptionsToExe(b, cfg, test_cases, use_zig_libcxx); + try addCmakeCfgOptionsToExe(b, cfg, check_case_exe, use_zig_libcxx); } else { // Here we are -Denable-llvm but no cmake integration. try addStaticLlvmOptionsToExe(exe); - try addStaticLlvmOptionsToExe(test_cases); + try addStaticLlvmOptionsToExe(check_case_exe); } if (target.isWindows()) { - inline for (.{ exe, test_cases }) |artifact| { + inline for (.{ exe, check_case_exe }) |artifact| { artifact.linkSystemLibrary("version"); artifact.linkSystemLibrary("uuid"); artifact.linkSystemLibrary("ole32"); @@ -334,8 +335,9 @@ pub fn build(b: *std.Build) !void { const test_filter = b.option([]const u8, "test-filter", "Skip tests that do not match filter"); const test_cases_options = b.addOptions(); - test_cases.addOptions("build_options", test_cases_options); + check_case_exe.addOptions("build_options", test_cases_options); + test_cases_options.addOption(bool, "enable_tracy", false); test_cases_options.addOption(bool, "enable_logging", enable_logging); test_cases_options.addOption(bool, "enable_link_snapshots", enable_link_snapshots); test_cases_options.addOption(bool, "skip_non_native", skip_non_native); @@ -358,12 +360,6 @@ pub fn build(b: *std.Build) !void { test_cases_options.addOption(std.SemanticVersion, "semver", semver); test_cases_options.addOption(?[]const u8, "test_filter", test_filter); - const test_cases_step = b.step("test-cases", "Run the main compiler test cases"); - test_cases_step.dependOn(&test_cases.step); - if (!skip_stage2_tests) { - test_step.dependOn(test_cases_step); - } - var chosen_opt_modes_buf: [4]builtin.Mode = undefined; var chosen_mode_index: usize = 0; if (!skip_debug) { @@ -386,21 +382,20 @@ pub fn build(b: *std.Build) !void { const fmt_include_paths = &.{ "doc", "lib", "src", "test", "tools", "build.zig" }; const fmt_exclude_paths = &.{"test/cases"}; - const check_fmt = b.addFmt(.{ - .paths = fmt_include_paths, - .exclude_paths = fmt_exclude_paths, - .check = true, - }); const do_fmt = b.addFmt(.{ .paths = fmt_include_paths, .exclude_paths = fmt_exclude_paths, }); - const test_fmt_step = b.step("test-fmt", "Check whether source files have conforming formatting"); - test_fmt_step.dependOn(&check_fmt.step); + b.step("test-fmt", "Check source files having conforming formatting").dependOn(&b.addFmt(.{ + .paths = fmt_include_paths, + .exclude_paths = fmt_exclude_paths, + .check = true, + }).step); - const do_fmt_step = b.step("fmt", "Modify source files in place to have conforming formatting"); - do_fmt_step.dependOn(&do_fmt.step); + const test_cases_step = b.step("test-cases", "Run the main compiler test cases"); + try tests.addCases(b, test_cases_step, test_filter, check_case_exe); + if (!skip_stage2_tests) test_step.dependOn(test_cases_step); test_step.dependOn(tests.addModuleTests(b, .{ .test_filter = test_filter, @@ -475,6 +470,9 @@ pub fn build(b: *std.Build) !void { })); try addWasiUpdateStep(b, version); + + b.step("fmt", "Modify source files in place to have conforming formatting") + .dependOn(&do_fmt.step); } fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void { diff --git a/src/test.zig b/src/test.zig deleted file mode 100644 index 5b73e516c6..0000000000 --- a/src/test.zig +++ /dev/null @@ -1,1968 +0,0 @@ -const std = @import("std"); -const builtin = @import("builtin"); -const Allocator = std.mem.Allocator; -const CrossTarget = std.zig.CrossTarget; -const print = std.debug.print; -const assert = std.debug.assert; -const ThreadPool = std.Thread.Pool; -const WaitGroup = std.Thread.WaitGroup; - -const link = @import("link.zig"); -const Compilation = @import("Compilation.zig"); -const Package = @import("Package.zig"); -const introspect = @import("introspect.zig"); -const build_options = @import("build_options"); -const zig_h = link.File.C.zig_h; - -const enable_qemu: bool = build_options.enable_qemu; -const enable_wine: bool = build_options.enable_wine; -const enable_wasmtime: bool = build_options.enable_wasmtime; -const enable_darling: bool = build_options.enable_darling; -const enable_rosetta: bool = build_options.enable_rosetta; -const glibc_runtimes_dir: ?[]const u8 = build_options.glibc_runtimes_dir; -const skip_stage1 = true; - -const hr = "=" ** 80; - -test { - const use_gpa = build_options.force_gpa or !builtin.link_libc; - const gpa = gpa: { - if (use_gpa) { - break :gpa std.testing.allocator; - } - // We would prefer to use raw libc allocator here, but cannot - // use it if it won't support the alignment we need. - if (@alignOf(std.c.max_align_t) < @alignOf(i128)) { - break :gpa std.heap.c_allocator; - } - break :gpa std.heap.raw_c_allocator; - }; - - var arena_allocator = std.heap.ArenaAllocator.init(gpa); - defer arena_allocator.deinit(); - const arena = arena_allocator.allocator(); - - var ctx = TestContext.init(gpa, arena); - defer ctx.deinit(); - - { - const dir_path = try std.fs.path.join(arena, &.{ - std.fs.path.dirname(@src().file).?, "..", "test", "cases", - }); - - var dir = try std.fs.cwd().openIterableDir(dir_path, .{}); - defer dir.close(); - - ctx.addTestCasesFromDir(dir); - } - - try @import("../test/cases.zig").addCases(&ctx); - - try ctx.run(); -} - -const ErrorMsg = union(enum) { - src: struct { - src_path: []const u8, - msg: []const u8, - // maxint means match anything - // this is a workaround for stage1 compiler bug I ran into when making it ?u32 - line: u32, - // maxint means match anything - // this is a workaround for stage1 compiler bug I ran into when making it ?u32 - column: u32, - kind: Kind, - count: u32, - }, - plain: struct { - msg: []const u8, - kind: Kind, - count: u32, - }, - - const Kind = enum { - @"error", - note, - }; - - fn init(other: Compilation.AllErrors.Message, kind: Kind) ErrorMsg { - switch (other) { - .src => |src| return .{ - .src = .{ - .src_path = src.src_path, - .msg = src.msg, - .line = @intCast(u32, src.line), - .column = @intCast(u32, src.column), - .kind = kind, - .count = src.count, - }, - }, - .plain => |plain| return .{ - .plain = .{ - .msg = plain.msg, - .kind = kind, - .count = plain.count, - }, - }, - } - } - - pub fn format( - self: ErrorMsg, - comptime fmt: []const u8, - options: std.fmt.FormatOptions, - writer: anytype, - ) !void { - _ = fmt; - _ = options; - switch (self) { - .src => |src| { - if (!std.mem.eql(u8, src.src_path, "?") or - src.line != std.math.maxInt(u32) or - src.column != std.math.maxInt(u32)) - { - try writer.print("{s}:", .{src.src_path}); - if (src.line != std.math.maxInt(u32)) { - try writer.print("{d}:", .{src.line + 1}); - } else { - try writer.writeAll("?:"); - } - if (src.column != std.math.maxInt(u32)) { - try writer.print("{d}: ", .{src.column + 1}); - } else { - try writer.writeAll("?: "); - } - } - try writer.print("{s}: {s}", .{ @tagName(src.kind), src.msg }); - if (src.count != 1) { - try writer.print(" ({d} times)", .{src.count}); - } - }, - .plain => |plain| { - try writer.print("{s}: {s}", .{ @tagName(plain.kind), plain.msg }); - if (plain.count != 1) { - try writer.print(" ({d} times)", .{plain.count}); - } - }, - } - } -}; - -/// Default config values for known test manifest key-value pairings. -/// Currently handled defaults are: -/// * backend -/// * target -/// * output_mode -/// * is_test -const TestManifestConfigDefaults = struct { - /// Asserts if the key doesn't exist - yep, it's an oversight alright. - fn get(@"type": TestManifest.Type, key: []const u8) []const u8 { - if (std.mem.eql(u8, key, "backend")) { - return "stage2"; - } else if (std.mem.eql(u8, key, "target")) { - comptime { - var defaults: []const u8 = ""; - // TODO should we only return "mainstream" targets by default here? - // TODO we should also specify ABIs explicitly as the backends are - // getting more and more complete - // Linux - inline for (&[_][]const u8{ "x86_64", "arm", "aarch64" }) |arch| { - defaults = defaults ++ arch ++ "-linux" ++ ","; - } - // macOS - inline for (&[_][]const u8{ "x86_64", "aarch64" }) |arch| { - defaults = defaults ++ arch ++ "-macos" ++ ","; - } - // Windows - defaults = defaults ++ "x86_64-windows" ++ ","; - // Wasm - defaults = defaults ++ "wasm32-wasi"; - return defaults; - } - } else if (std.mem.eql(u8, key, "output_mode")) { - return switch (@"type") { - .@"error" => "Obj", - .run => "Exe", - .cli => @panic("TODO test harness for CLI tests"), - }; - } else if (std.mem.eql(u8, key, "is_test")) { - return "0"; - } else unreachable; - } -}; - -/// Manifest syntax example: -/// (see https://github.com/ziglang/zig/issues/11288) -/// -/// error -/// backend=stage1,stage2 -/// output_mode=exe -/// -/// :3:19: error: foo -/// -/// run -/// target=x86_64-linux,aarch64-macos -/// -/// I am expected stdout! Hello! -/// -/// cli -/// -/// build test -const TestManifest = struct { - type: Type, - config_map: std.StringHashMap([]const u8), - trailing_bytes: []const u8 = "", - - const Type = enum { - @"error", - run, - cli, - }; - - const TrailingIterator = struct { - inner: std.mem.TokenIterator(u8), - - fn next(self: *TrailingIterator) ?[]const u8 { - const next_inner = self.inner.next() orelse return null; - return std.mem.trim(u8, next_inner[2..], " \t"); - } - }; - - fn ConfigValueIterator(comptime T: type) type { - return struct { - inner: std.mem.SplitIterator(u8), - - fn next(self: *@This()) !?T { - const next_raw = self.inner.next() orelse return null; - const parseFn = getDefaultParser(T); - return try parseFn(next_raw); - } - }; - } - - fn parse(arena: Allocator, bytes: []const u8) !TestManifest { - // The manifest is the last contiguous block of comments in the file - // We scan for the beginning by searching backward for the first non-empty line that does not start with "//" - var start: ?usize = null; - var end: usize = bytes.len; - if (bytes.len > 0) { - var cursor: usize = bytes.len - 1; - while (true) { - // Move to beginning of line - while (cursor > 0 and bytes[cursor - 1] != '\n') cursor -= 1; - - if (std.mem.startsWith(u8, bytes[cursor..], "//")) { - start = cursor; // Contiguous comment line, include in manifest - } else { - if (start != null) break; // Encountered non-comment line, end of manifest - - // We ignore all-whitespace lines following the comment block, but anything else - // means that there is no manifest present. - if (std.mem.trim(u8, bytes[cursor..end], " \r\n\t").len == 0) { - end = cursor; - } else break; // If it's not whitespace, there is no manifest - } - - // Move to previous line - if (cursor != 0) cursor -= 1 else break; - } - } - - const actual_start = start orelse return error.MissingTestManifest; - const manifest_bytes = bytes[actual_start..end]; - - var it = std.mem.tokenize(u8, manifest_bytes, "\r\n"); - - // First line is the test type - const tt: Type = blk: { - const line = it.next() orelse return error.MissingTestCaseType; - const raw = std.mem.trim(u8, line[2..], " \t"); - if (std.mem.eql(u8, raw, "error")) { - break :blk .@"error"; - } else if (std.mem.eql(u8, raw, "run")) { - break :blk .run; - } else if (std.mem.eql(u8, raw, "cli")) { - break :blk .cli; - } else { - std.log.warn("unknown test case type requested: {s}", .{raw}); - return error.UnknownTestCaseType; - } - }; - - var manifest: TestManifest = .{ - .type = tt, - .config_map = std.StringHashMap([]const u8).init(arena), - }; - - // Any subsequent line until a blank comment line is key=value(s) pair - while (it.next()) |line| { - const trimmed = std.mem.trim(u8, line[2..], " \t"); - if (trimmed.len == 0) break; - - // Parse key=value(s) - var kv_it = std.mem.split(u8, trimmed, "="); - const key = kv_it.first(); - try manifest.config_map.putNoClobber(key, kv_it.next() orelse return error.MissingValuesForConfig); - } - - // Finally, trailing is expected output - manifest.trailing_bytes = manifest_bytes[it.index..]; - - return manifest; - } - - fn getConfigForKey( - self: TestManifest, - key: []const u8, - comptime T: type, - ) ConfigValueIterator(T) { - const bytes = self.config_map.get(key) orelse TestManifestConfigDefaults.get(self.type, key); - return ConfigValueIterator(T){ - .inner = std.mem.split(u8, bytes, ","), - }; - } - - fn getConfigForKeyAlloc( - self: TestManifest, - allocator: Allocator, - key: []const u8, - comptime T: type, - ) ![]const T { - var out = std.ArrayList(T).init(allocator); - defer out.deinit(); - var it = self.getConfigForKey(key, T); - while (try it.next()) |item| { - try out.append(item); - } - return try out.toOwnedSlice(); - } - - fn getConfigForKeyAssertSingle(self: TestManifest, key: []const u8, comptime T: type) !T { - var it = self.getConfigForKey(key, T); - const res = (try it.next()) orelse unreachable; - assert((try it.next()) == null); - return res; - } - - fn trailing(self: TestManifest) TrailingIterator { - return .{ - .inner = std.mem.tokenize(u8, self.trailing_bytes, "\r\n"), - }; - } - - fn trailingAlloc(self: TestManifest, allocator: Allocator) error{OutOfMemory}![]const []const u8 { - var out = std.ArrayList([]const u8).init(allocator); - defer out.deinit(); - var it = self.trailing(); - while (it.next()) |line| { - try out.append(line); - } - return try out.toOwnedSlice(); - } - - fn ParseFn(comptime T: type) type { - return fn ([]const u8) anyerror!T; - } - - fn getDefaultParser(comptime T: type) ParseFn(T) { - if (T == CrossTarget) return struct { - fn parse(str: []const u8) anyerror!T { - var opts = CrossTarget.ParseOptions{ - .arch_os_abi = str, - }; - return try CrossTarget.parse(opts); - } - }.parse; - - switch (@typeInfo(T)) { - .Int => return struct { - fn parse(str: []const u8) anyerror!T { - return try std.fmt.parseInt(T, str, 0); - } - }.parse, - .Bool => return struct { - fn parse(str: []const u8) anyerror!T { - const as_int = try std.fmt.parseInt(u1, str, 0); - return as_int > 0; - } - }.parse, - .Enum => return struct { - fn parse(str: []const u8) anyerror!T { - return std.meta.stringToEnum(T, str) orelse { - std.log.err("unknown enum variant for {s}: {s}", .{ @typeName(T), str }); - return error.UnknownEnumVariant; - }; - } - }.parse, - .Struct => @compileError("no default parser for " ++ @typeName(T)), - else => @compileError("no default parser for " ++ @typeName(T)), - } - } -}; - -const TestStrategy = enum { - /// Execute tests as independent compilations, unless they are explicitly - /// incremental ("foo.0.zig", "foo.1.zig", etc.) - independent, - /// Execute all tests as incremental updates to a single compilation. Explicitly - /// incremental tests ("foo.0.zig", "foo.1.zig", etc.) still execute in order - incremental, -}; - -/// Iterates a set of filenames extracting batches that are either incremental -/// ("foo.0.zig", "foo.1.zig", etc.) or independent ("foo.zig", "bar.zig", etc.). -/// Assumes filenames are sorted. -const TestIterator = struct { - start: usize = 0, - end: usize = 0, - filenames: []const []const u8, - /// reset on each call to `next` - index: usize = 0, - - const Error = error{InvalidIncrementalTestIndex}; - - fn next(it: *TestIterator) Error!?[]const []const u8 { - try it.nextInner(); - if (it.start == it.end) return null; - return it.filenames[it.start..it.end]; - } - - fn nextInner(it: *TestIterator) Error!void { - it.start = it.end; - if (it.end == it.filenames.len) return; - if (it.end + 1 == it.filenames.len) { - it.end += 1; - return; - } - - const remaining = it.filenames[it.end..]; - it.index = 0; - while (it.index < remaining.len - 1) : (it.index += 1) { - // First, check if this file is part of an incremental update sequence - // Split filename into ".." - const prev_parts = getTestFileNameParts(remaining[it.index]); - const new_parts = getTestFileNameParts(remaining[it.index + 1]); - - // If base_name and file_ext match, these files are in the same test sequence - // and the new one should be the incremented version of the previous test - if (std.mem.eql(u8, prev_parts.base_name, new_parts.base_name) and - std.mem.eql(u8, prev_parts.file_ext, new_parts.file_ext)) - { - // This is "foo.X.zig" followed by "foo.Y.zig". Make sure that X = Y + 1 - if (prev_parts.test_index == null) - return error.InvalidIncrementalTestIndex; - if (new_parts.test_index == null) - return error.InvalidIncrementalTestIndex; - if (new_parts.test_index.? != prev_parts.test_index.? + 1) - return error.InvalidIncrementalTestIndex; - } else { - // This is not the same test sequence, so the new file must be the first file - // in a new sequence ("*.0.zig") or an independent test file ("*.zig") - if (new_parts.test_index != null and new_parts.test_index.? != 0) - return error.InvalidIncrementalTestIndex; - - it.end += it.index + 1; - break; - } - } else { - it.end += remaining.len; - } - } - - /// In the event of an `error.InvalidIncrementalTestIndex`, this function can - /// be used to find the current filename that was being processed. - /// Asserts the iterator hasn't reached the end. - fn currentFilename(it: TestIterator) []const u8 { - assert(it.end != it.filenames.len); - const remaining = it.filenames[it.end..]; - return remaining[it.index + 1]; - } -}; - -/// For a filename in the format ".X." or ".", returns -/// "", "" and X parsed as a decimal number. If X is not present, or -/// cannot be parsed as a decimal number, it is treated as part of -fn getTestFileNameParts(name: []const u8) struct { - base_name: []const u8, - file_ext: []const u8, - test_index: ?usize, -} { - const file_ext = std.fs.path.extension(name); - const trimmed = name[0 .. name.len - file_ext.len]; // Trim off "." - const maybe_index = std.fs.path.extension(trimmed); // Extract ".X" - - // Attempt to parse index - const index: ?usize = if (maybe_index.len > 0) - std.fmt.parseInt(usize, maybe_index[1..], 10) catch null - else - null; - - // Adjust "" extent based on parsing success - const base_name_end = trimmed.len - if (index != null) maybe_index.len else 0; - return .{ - .base_name = name[0..base_name_end], - .file_ext = if (file_ext.len > 0) file_ext[1..] else file_ext, - .test_index = index, - }; -} - -/// Sort test filenames in-place, so that incremental test cases ("foo.0.zig", -/// "foo.1.zig", etc.) are contiguous and appear in numerical order. -fn sortTestFilenames(filenames: [][]const u8) void { - const Context = struct { - pub fn lessThan(_: @This(), a: []const u8, b: []const u8) bool { - const a_parts = getTestFileNameParts(a); - const b_parts = getTestFileNameParts(b); - - // Sort ".X." based on "" and "" first - return switch (std.mem.order(u8, a_parts.base_name, b_parts.base_name)) { - .lt => true, - .gt => false, - .eq => switch (std.mem.order(u8, a_parts.file_ext, b_parts.file_ext)) { - .lt => true, - .gt => false, - .eq => { - // a and b differ only in their ".X" part - - // Sort "." before any ".X." - if (a_parts.test_index) |a_index| { - if (b_parts.test_index) |b_index| { - // Make sure that incremental tests appear in linear order - return a_index < b_index; - } else { - return false; - } - } else { - return b_parts.test_index != null; - } - }, - }, - }; - } - }; - std.sort.sort([]const u8, filenames, Context{}, Context.lessThan); -} - -pub const TestContext = struct { - gpa: Allocator, - arena: Allocator, - cases: std.ArrayList(Case), - - pub const Update = struct { - /// The input to the current update. We simulate an incremental update - /// with the file's contents changed to this value each update. - /// - /// This value can change entirely between updates, which would be akin - /// to deleting the source file and creating a new one from scratch; or - /// you can keep it mostly consistent, with small changes, testing the - /// effects of the incremental compilation. - src: [:0]const u8, - name: []const u8, - case: union(enum) { - /// Check the main binary output file against an expected set of bytes. - /// This is most useful with, for example, `-ofmt=c`. - CompareObjectFile: []const u8, - /// An error update attempts to compile bad code, and ensures that it - /// fails to compile, and for the expected reasons. - /// A slice containing the expected errors *in sequential order*. - Error: []const ErrorMsg, - /// An execution update compiles and runs the input, testing the - /// stdout against the expected results - /// This is a slice containing the expected message. - Execution: []const u8, - /// A header update compiles the input with the equivalent of - /// `-femit-h` and tests the produced header against the - /// expected result - Header: []const u8, - }, - }; - - pub const File = struct { - /// Contents of the importable file. Doesn't yet support incremental updates. - src: [:0]const u8, - path: []const u8, - }; - - pub const DepModule = struct { - name: []const u8, - path: []const u8, - }; - - pub const Backend = enum { - stage1, - stage2, - llvm, - }; - - /// A `Case` consists of a list of `Update`. The same `Compilation` is used for each - /// update, so each update's source is treated as a single file being - /// updated by the test harness and incrementally compiled. - pub const Case = struct { - /// The name of the test case. This is shown if a test fails, and - /// otherwise ignored. - name: []const u8, - /// The platform the test targets. For non-native platforms, an emulator - /// such as QEMU is required for tests to complete. - target: CrossTarget, - /// In order to be able to run e.g. Execution updates, this must be set - /// to Executable. - output_mode: std.builtin.OutputMode, - optimize_mode: std.builtin.Mode = .Debug, - updates: std.ArrayList(Update), - emit_h: bool = false, - is_test: bool = false, - expect_exact: bool = false, - backend: Backend = .stage2, - link_libc: bool = false, - - files: std.ArrayList(File), - deps: std.ArrayList(DepModule), - - result: anyerror!void = {}, - - pub fn addSourceFile(case: *Case, name: []const u8, src: [:0]const u8) void { - case.files.append(.{ .path = name, .src = src }) catch @panic("out of memory"); - } - - pub fn addDepModule(case: *Case, name: []const u8, path: []const u8) void { - case.deps.append(.{ - .name = name, - .path = path, - }) catch @panic("out of memory"); - } - - /// Adds a subcase in which the module is updated with `src`, and a C - /// header is generated. - pub fn addHeader(self: *Case, src: [:0]const u8, result: [:0]const u8) void { - self.emit_h = true; - self.updates.append(.{ - .src = src, - .name = "update", - .case = .{ .Header = result }, - }) catch @panic("out of memory"); - } - - /// Adds a subcase in which the module is updated with `src`, compiled, - /// run, and the output is tested against `result`. - pub fn addCompareOutput(self: *Case, src: [:0]const u8, result: []const u8) void { - self.updates.append(.{ - .src = src, - .name = "update", - .case = .{ .Execution = result }, - }) catch @panic("out of memory"); - } - - /// Adds a subcase in which the module is updated with `src`, compiled, - /// and the object file data is compared against `result`. - pub fn addCompareObjectFile(self: *Case, src: [:0]const u8, result: []const u8) void { - self.updates.append(.{ - .src = src, - .name = "update", - .case = .{ .CompareObjectFile = result }, - }) catch @panic("out of memory"); - } - - pub fn addError(self: *Case, src: [:0]const u8, errors: []const []const u8) void { - return self.addErrorNamed("update", src, errors); - } - - /// Adds a subcase in which the module is updated with `src`, which - /// should contain invalid input, and ensures that compilation fails - /// for the expected reasons, given in sequential order in `errors` in - /// the form `:line:column: error: message`. - pub fn addErrorNamed( - self: *Case, - name: []const u8, - src: [:0]const u8, - errors: []const []const u8, - ) void { - var array = self.updates.allocator.alloc(ErrorMsg, errors.len) catch @panic("out of memory"); - for (errors, 0..) |err_msg_line, i| { - if (std.mem.startsWith(u8, err_msg_line, "error: ")) { - array[i] = .{ - .plain = .{ - .msg = err_msg_line["error: ".len..], - .kind = .@"error", - .count = 1, - }, - }; - continue; - } else if (std.mem.startsWith(u8, err_msg_line, "note: ")) { - array[i] = .{ - .plain = .{ - .msg = err_msg_line["note: ".len..], - .kind = .note, - .count = 1, - }, - }; - continue; - } - // example: "file.zig:1:2: error: bad thing happened" - var it = std.mem.split(u8, err_msg_line, ":"); - const src_path = it.first(); - const line_text = it.next() orelse @panic("missing line"); - const col_text = it.next() orelse @panic("missing column"); - const kind_text = it.next() orelse @panic("missing 'error'/'note'"); - var msg = it.rest()[1..]; // skip over the space at end of "error: " - - const line: ?u32 = if (std.mem.eql(u8, line_text, "?")) - null - else - std.fmt.parseInt(u32, line_text, 10) catch @panic("bad line number"); - const column: ?u32 = if (std.mem.eql(u8, line_text, "?")) - null - else - std.fmt.parseInt(u32, col_text, 10) catch @panic("bad column number"); - const kind: ErrorMsg.Kind = if (std.mem.eql(u8, kind_text, " error")) - .@"error" - else if (std.mem.eql(u8, kind_text, " note")) - .note - else - @panic("expected 'error'/'note'"); - - const line_0based: u32 = if (line) |n| blk: { - if (n == 0) { - print("{s}: line must be specified starting at one\n", .{self.name}); - return; - } - break :blk n - 1; - } else std.math.maxInt(u32); - - const column_0based: u32 = if (column) |n| blk: { - if (n == 0) { - print("{s}: line must be specified starting at one\n", .{self.name}); - return; - } - break :blk n - 1; - } else std.math.maxInt(u32); - - const suffix = " times)"; - const count = if (std.mem.endsWith(u8, msg, suffix)) count: { - const lparen = std.mem.lastIndexOfScalar(u8, msg, '(').?; - const count = std.fmt.parseInt(u32, msg[lparen + 1 .. msg.len - suffix.len], 10) catch @panic("bad error note count number"); - msg = msg[0 .. lparen - 1]; - break :count count; - } else 1; - - array[i] = .{ - .src = .{ - .src_path = src_path, - .msg = msg, - .line = line_0based, - .column = column_0based, - .kind = kind, - .count = count, - }, - }; - } - self.updates.append(.{ - .src = src, - .name = name, - .case = .{ .Error = array }, - }) catch @panic("out of memory"); - } - - /// Adds a subcase in which the module is updated with `src`, and - /// asserts that it compiles without issue - pub fn compiles(self: *Case, src: [:0]const u8) void { - self.addError(src, &[_][]const u8{}); - } - }; - - pub fn addExe( - ctx: *TestContext, - name: []const u8, - target: CrossTarget, - ) *Case { - ctx.cases.append(Case{ - .name = name, - .target = target, - .updates = std.ArrayList(Update).init(ctx.cases.allocator), - .output_mode = .Exe, - .files = std.ArrayList(File).init(ctx.arena), - .deps = std.ArrayList(DepModule).init(ctx.arena), - }) catch @panic("out of memory"); - return &ctx.cases.items[ctx.cases.items.len - 1]; - } - - /// Adds a test case for Zig input, producing an executable - pub fn exe(ctx: *TestContext, name: []const u8, target: CrossTarget) *Case { - return ctx.addExe(name, target); - } - - pub fn exeFromCompiledC(ctx: *TestContext, name: []const u8, target: CrossTarget) *Case { - const prefixed_name = std.fmt.allocPrint(ctx.arena, "CBE: {s}", .{name}) catch - @panic("out of memory"); - var target_adjusted = target; - target_adjusted.ofmt = std.Target.ObjectFormat.c; - ctx.cases.append(Case{ - .name = prefixed_name, - .target = target_adjusted, - .updates = std.ArrayList(Update).init(ctx.cases.allocator), - .output_mode = .Exe, - .files = std.ArrayList(File).init(ctx.arena), - .deps = std.ArrayList(DepModule).init(ctx.arena), - .link_libc = true, - }) catch @panic("out of memory"); - return &ctx.cases.items[ctx.cases.items.len - 1]; - } - - /// Adds a test case that uses the LLVM backend to emit an executable. - /// Currently this implies linking libc, because only then we can generate a testable executable. - pub fn exeUsingLlvmBackend(ctx: *TestContext, name: []const u8, target: CrossTarget) *Case { - ctx.cases.append(Case{ - .name = name, - .target = target, - .updates = std.ArrayList(Update).init(ctx.cases.allocator), - .output_mode = .Exe, - .files = std.ArrayList(File).init(ctx.arena), - .deps = std.ArrayList(DepModule).init(ctx.arena), - .backend = .llvm, - .link_libc = true, - }) catch @panic("out of memory"); - return &ctx.cases.items[ctx.cases.items.len - 1]; - } - - pub fn addObj( - ctx: *TestContext, - name: []const u8, - target: CrossTarget, - ) *Case { - ctx.cases.append(Case{ - .name = name, - .target = target, - .updates = std.ArrayList(Update).init(ctx.cases.allocator), - .output_mode = .Obj, - .files = std.ArrayList(File).init(ctx.arena), - .deps = std.ArrayList(DepModule).init(ctx.arena), - }) catch @panic("out of memory"); - return &ctx.cases.items[ctx.cases.items.len - 1]; - } - - pub fn addTest( - ctx: *TestContext, - name: []const u8, - target: CrossTarget, - ) *Case { - ctx.cases.append(Case{ - .name = name, - .target = target, - .updates = std.ArrayList(Update).init(ctx.cases.allocator), - .output_mode = .Exe, - .is_test = true, - .files = std.ArrayList(File).init(ctx.arena), - .deps = std.ArrayList(DepModule).init(ctx.arena), - }) catch @panic("out of memory"); - return &ctx.cases.items[ctx.cases.items.len - 1]; - } - - /// Adds a test case for Zig input, producing an object file. - pub fn obj(ctx: *TestContext, name: []const u8, target: CrossTarget) *Case { - return ctx.addObj(name, target); - } - - /// Adds a test case for ZIR input, producing an object file. - pub fn objZIR(ctx: *TestContext, name: []const u8, target: CrossTarget) *Case { - return ctx.addObj(name, target, .ZIR); - } - - /// Adds a test case for Zig or ZIR input, producing C code. - pub fn addC(ctx: *TestContext, name: []const u8, target: CrossTarget) *Case { - var target_adjusted = target; - target_adjusted.ofmt = std.Target.ObjectFormat.c; - ctx.cases.append(Case{ - .name = name, - .target = target_adjusted, - .updates = std.ArrayList(Update).init(ctx.cases.allocator), - .output_mode = .Obj, - .files = std.ArrayList(File).init(ctx.arena), - .deps = std.ArrayList(DepModule).init(ctx.arena), - }) catch @panic("out of memory"); - return &ctx.cases.items[ctx.cases.items.len - 1]; - } - - pub fn c(ctx: *TestContext, name: []const u8, target: CrossTarget, src: [:0]const u8, comptime out: [:0]const u8) void { - ctx.addC(name, target).addCompareObjectFile(src, zig_h ++ out); - } - - pub fn h(ctx: *TestContext, name: []const u8, target: CrossTarget, src: [:0]const u8, comptime out: [:0]const u8) void { - ctx.addC(name, target).addHeader(src, zig_h ++ out); - } - - pub fn objErrStage1( - ctx: *TestContext, - name: []const u8, - src: [:0]const u8, - expected_errors: []const []const u8, - ) void { - const case = ctx.addObj(name, .{}); - case.backend = .stage1; - case.addError(src, expected_errors); - } - - pub fn testErrStage1( - ctx: *TestContext, - name: []const u8, - src: [:0]const u8, - expected_errors: []const []const u8, - ) void { - const case = ctx.addTest(name, .{}); - case.backend = .stage1; - case.addError(src, expected_errors); - } - - pub fn exeErrStage1( - ctx: *TestContext, - name: []const u8, - src: [:0]const u8, - expected_errors: []const []const u8, - ) void { - const case = ctx.addExe(name, .{}); - case.backend = .stage1; - case.addError(src, expected_errors); - } - - pub fn addCompareOutput( - ctx: *TestContext, - name: []const u8, - src: [:0]const u8, - expected_stdout: []const u8, - ) void { - ctx.addExe(name, .{}).addCompareOutput(src, expected_stdout); - } - - /// Adds a test case that compiles the Zig source given in `src`, executes - /// it, runs it, and tests the output against `expected_stdout` - pub fn compareOutput( - ctx: *TestContext, - name: []const u8, - src: [:0]const u8, - expected_stdout: []const u8, - ) void { - return ctx.addCompareOutput(name, src, expected_stdout); - } - - /// Adds a test case that compiles the ZIR source given in `src`, executes - /// it, runs it, and tests the output against `expected_stdout` - pub fn compareOutputZIR( - ctx: *TestContext, - name: []const u8, - src: [:0]const u8, - expected_stdout: []const u8, - ) void { - ctx.addCompareOutput(name, .ZIR, src, expected_stdout); - } - - pub fn addTransform( - ctx: *TestContext, - name: []const u8, - target: CrossTarget, - src: [:0]const u8, - result: [:0]const u8, - ) void { - ctx.addObj(name, target).addTransform(src, result); - } - - /// Adds a test case that compiles the Zig given in `src` to ZIR and tests - /// the ZIR against `result` - pub fn transform( - ctx: *TestContext, - name: []const u8, - target: CrossTarget, - src: [:0]const u8, - result: [:0]const u8, - ) void { - ctx.addTransform(name, target, src, result); - } - - pub fn addError( - ctx: *TestContext, - name: []const u8, - target: CrossTarget, - src: [:0]const u8, - expected_errors: []const []const u8, - ) void { - ctx.addObj(name, target).addError(src, expected_errors); - } - - /// Adds a test case that ensures that the Zig given in `src` fails to - /// compile for the expected reasons, given in sequential order in - /// `expected_errors` in the form `:line:column: error: message`. - pub fn compileError( - ctx: *TestContext, - name: []const u8, - target: CrossTarget, - src: [:0]const u8, - expected_errors: []const []const u8, - ) void { - ctx.addError(name, target, src, expected_errors); - } - - /// Adds a test case that ensures that the ZIR given in `src` fails to - /// compile for the expected reasons, given in sequential order in - /// `expected_errors` in the form `:line:column: error: message`. - pub fn compileErrorZIR( - ctx: *TestContext, - name: []const u8, - target: CrossTarget, - src: [:0]const u8, - expected_errors: []const []const u8, - ) void { - ctx.addError(name, target, .ZIR, src, expected_errors); - } - - pub fn addCompiles( - ctx: *TestContext, - name: []const u8, - target: CrossTarget, - src: [:0]const u8, - ) void { - ctx.addObj(name, target).compiles(src); - } - - /// Adds a test case that asserts that the Zig given in `src` compiles - /// without any errors. - pub fn compiles( - ctx: *TestContext, - name: []const u8, - target: CrossTarget, - src: [:0]const u8, - ) void { - ctx.addCompiles(name, target, src); - } - - /// Adds a test case that asserts that the ZIR given in `src` compiles - /// without any errors. - pub fn compilesZIR( - ctx: *TestContext, - name: []const u8, - target: CrossTarget, - src: [:0]const u8, - ) void { - ctx.addCompiles(name, target, .ZIR, src); - } - - /// Adds a test case that first ensures that the Zig given in `src` fails - /// to compile for the reasons given in sequential order in - /// `expected_errors` in the form `:line:column: error: message`, then - /// asserts that fixing the source (updating with `fixed_src`) isn't broken - /// by incremental compilation. - pub fn incrementalFailure( - ctx: *TestContext, - name: []const u8, - target: CrossTarget, - src: [:0]const u8, - expected_errors: []const []const u8, - fixed_src: [:0]const u8, - ) void { - var case = ctx.addObj(name, target); - case.addError(src, expected_errors); - case.compiles(fixed_src); - } - - /// Adds a test case that first ensures that the ZIR given in `src` fails - /// to compile for the reasons given in sequential order in - /// `expected_errors` in the form `:line:column: error: message`, then - /// asserts that fixing the source (updating with `fixed_src`) isn't broken - /// by incremental compilation. - pub fn incrementalFailureZIR( - ctx: *TestContext, - name: []const u8, - target: CrossTarget, - src: [:0]const u8, - expected_errors: []const []const u8, - fixed_src: [:0]const u8, - ) void { - var case = ctx.addObj(name, target, .ZIR); - case.addError(src, expected_errors); - case.compiles(fixed_src); - } - - /// Adds a test for each file in the provided directory. - /// Testing strategy (TestStrategy) is inferred automatically from filenames. - /// Recurses nested directories. - /// - /// Each file should include a test manifest as a contiguous block of comments at - /// the end of the file. The first line should be the test type, followed by a set of - /// key-value config values, followed by a blank line, then the expected output. - pub fn addTestCasesFromDir(ctx: *TestContext, dir: std.fs.IterableDir) void { - var current_file: []const u8 = "none"; - ctx.addTestCasesFromDirInner(dir, ¤t_file) catch |err| { - std.debug.panic("test harness failed to process file '{s}': {s}\n", .{ - current_file, @errorName(err), - }); - }; - } - - fn addTestCasesFromDirInner( - ctx: *TestContext, - iterable_dir: std.fs.IterableDir, - /// This is kept up to date with the currently being processed file so - /// that if any errors occur the caller knows it happened during this file. - current_file: *[]const u8, - ) !void { - var it = try iterable_dir.walk(ctx.arena); - var filenames = std.ArrayList([]const u8).init(ctx.arena); - - while (try it.next()) |entry| { - if (entry.kind != .File) continue; - - // Ignore stuff such as .swp files - switch (Compilation.classifyFileExt(entry.basename)) { - .unknown => continue, - else => {}, - } - try filenames.append(try ctx.arena.dupe(u8, entry.path)); - } - - // Sort filenames, so that incremental tests are contiguous and in-order - sortTestFilenames(filenames.items); - - var test_it = TestIterator{ .filenames = filenames.items }; - while (test_it.next()) |maybe_batch| { - const batch = maybe_batch orelse break; - const strategy: TestStrategy = if (batch.len > 1) .incremental else .independent; - var cases = std.ArrayList(usize).init(ctx.arena); - - for (batch) |filename| { - current_file.* = filename; - - const max_file_size = 10 * 1024 * 1024; - const src = try iterable_dir.dir.readFileAllocOptions(ctx.arena, filename, max_file_size, null, 1, 0); - - // Parse the manifest - var manifest = try TestManifest.parse(ctx.arena, src); - - if (cases.items.len == 0) { - const backends = try manifest.getConfigForKeyAlloc(ctx.arena, "backend", Backend); - const targets = try manifest.getConfigForKeyAlloc(ctx.arena, "target", CrossTarget); - const is_test = try manifest.getConfigForKeyAssertSingle("is_test", bool); - const output_mode = try manifest.getConfigForKeyAssertSingle("output_mode", std.builtin.OutputMode); - - const name_prefix = blk: { - const ext_index = std.mem.lastIndexOfScalar(u8, current_file.*, '.') orelse - return error.InvalidFilename; - const index = std.mem.lastIndexOfScalar(u8, current_file.*[0..ext_index], '.') orelse ext_index; - break :blk current_file.*[0..index]; - }; - - // Cross-product to get all possible test combinations - for (backends) |backend| { - for (targets) |target| { - const name = try std.fmt.allocPrint(ctx.arena, "{s} ({s}, {s})", .{ - name_prefix, - @tagName(backend), - try target.zigTriple(ctx.arena), - }); - const next = ctx.cases.items.len; - try ctx.cases.append(.{ - .name = name, - .target = target, - .backend = backend, - .updates = std.ArrayList(TestContext.Update).init(ctx.cases.allocator), - .is_test = is_test, - .output_mode = output_mode, - .link_libc = backend == .llvm, - .files = std.ArrayList(TestContext.File).init(ctx.cases.allocator), - .deps = std.ArrayList(DepModule).init(ctx.cases.allocator), - }); - try cases.append(next); - } - } - } - - for (cases.items) |case_index| { - const case = &ctx.cases.items[case_index]; - switch (manifest.type) { - .@"error" => { - const errors = try manifest.trailingAlloc(ctx.arena); - switch (strategy) { - .independent => { - case.addError(src, errors); - }, - .incremental => { - case.addErrorNamed("update", src, errors); - }, - } - }, - .run => { - var output = std.ArrayList(u8).init(ctx.arena); - var trailing_it = manifest.trailing(); - while (trailing_it.next()) |line| { - try output.appendSlice(line); - try output.append('\n'); - } - if (output.items.len > 0) { - try output.resize(output.items.len - 1); - } - case.addCompareOutput(src, try output.toOwnedSlice()); - }, - .cli => @panic("TODO cli tests"), - } - } - } - } else |err| { - // make sure the current file is set to the file that produced an error - current_file.* = test_it.currentFilename(); - return err; - } - } - - fn init(gpa: Allocator, arena: Allocator) TestContext { - return .{ - .gpa = gpa, - .cases = std.ArrayList(Case).init(gpa), - .arena = arena, - }; - } - - fn deinit(self: *TestContext) void { - for (self.cases.items) |case| { - for (case.updates.items) |u| { - if (u.case == .Error) { - case.updates.allocator.free(u.case.Error); - } - } - case.updates.deinit(); - } - self.cases.deinit(); - self.* = undefined; - } - - fn run(self: *TestContext) !void { - const host = try std.zig.system.NativeTargetInfo.detect(.{}); - const zig_exe_path = try std.process.getEnvVarOwned(self.arena, "ZIG_EXE"); - - var progress = std.Progress{}; - const root_node = progress.start("compiler", self.cases.items.len); - defer root_node.end(); - - var zig_lib_directory = try introspect.findZigLibDir(self.gpa); - defer zig_lib_directory.handle.close(); - defer self.gpa.free(zig_lib_directory.path.?); - - var aux_thread_pool: ThreadPool = undefined; - try aux_thread_pool.init(.{ .allocator = self.gpa }); - defer aux_thread_pool.deinit(); - - // Use the same global cache dir for all the tests, such that we for example don't have to - // rebuild musl libc for every case (when LLVM backend is enabled). - var global_tmp = std.testing.tmpDir(.{}); - defer global_tmp.cleanup(); - - var cache_dir = try global_tmp.dir.makeOpenPath("zig-cache", .{}); - defer cache_dir.close(); - const tmp_dir_path = try std.fs.path.join(self.gpa, &[_][]const u8{ ".", "zig-cache", "tmp", &global_tmp.sub_path }); - defer self.gpa.free(tmp_dir_path); - - const global_cache_directory: Compilation.Directory = .{ - .handle = cache_dir, - .path = try std.fs.path.join(self.gpa, &[_][]const u8{ tmp_dir_path, "zig-cache" }), - }; - defer self.gpa.free(global_cache_directory.path.?); - - { - for (self.cases.items) |*case| { - if (build_options.skip_non_native) { - if (case.target.getCpuArch() != builtin.cpu.arch) - continue; - if (case.target.getObjectFormat() != builtin.object_format) - continue; - } - - // Skip tests that require LLVM backend when it is not available - if (!build_options.have_llvm and case.backend == .llvm) - continue; - - if (skip_stage1 and case.backend == .stage1) - continue; - - if (build_options.test_filter) |test_filter| { - if (std.mem.indexOf(u8, case.name, test_filter) == null) continue; - } - - var prg_node = root_node.start(case.name, case.updates.items.len); - prg_node.activate(); - defer prg_node.end(); - - case.result = runOneCase( - self.gpa, - &prg_node, - case.*, - zig_lib_directory, - zig_exe_path, - &aux_thread_pool, - global_cache_directory, - host, - ); - } - } - - var fail_count: usize = 0; - for (self.cases.items) |*case| { - case.result catch |err| { - fail_count += 1; - print("{s} failed: {s}\n", .{ case.name, @errorName(err) }); - }; - } - - if (fail_count != 0) { - print("{d} tests failed\n", .{fail_count}); - return error.TestFailed; - } - } - - fn runOneCase( - allocator: Allocator, - root_node: *std.Progress.Node, - case: Case, - zig_lib_directory: Compilation.Directory, - zig_exe_path: []const u8, - thread_pool: *ThreadPool, - global_cache_directory: Compilation.Directory, - host: std.zig.system.NativeTargetInfo, - ) !void { - const target_info = try std.zig.system.NativeTargetInfo.detect(case.target); - const target = target_info.target; - - var arena_allocator = std.heap.ArenaAllocator.init(allocator); - defer arena_allocator.deinit(); - const arena = arena_allocator.allocator(); - - var tmp = std.testing.tmpDir(.{}); - defer tmp.cleanup(); - - var cache_dir = try tmp.dir.makeOpenPath("zig-cache", .{}); - defer cache_dir.close(); - - const tmp_dir_path = try std.fs.path.join( - arena, - &[_][]const u8{ ".", "zig-cache", "tmp", &tmp.sub_path }, - ); - const tmp_dir_path_plus_slash = try std.fmt.allocPrint( - arena, - "{s}" ++ std.fs.path.sep_str, - .{tmp_dir_path}, - ); - const local_cache_path = try std.fs.path.join( - arena, - &[_][]const u8{ tmp_dir_path, "zig-cache" }, - ); - - for (case.files.items) |file| { - try tmp.dir.writeFile(file.path, file.src); - } - - if (case.backend == .stage1) { - // stage1 backend has limitations: - // * leaks memory - // * calls exit() when a compile error happens - // * cannot handle updates - // because of this we must spawn a child process rather than - // using Compilation directly. - - if (!std.process.can_spawn) { - print("Unable to spawn child processes on {s}, skipping test.\n", .{@tagName(builtin.os.tag)}); - return; // Pass test. - } - - assert(case.updates.items.len == 1); - const update = case.updates.items[0]; - try tmp.dir.writeFile(tmp_src_path, update.src); - - var zig_args = std.ArrayList([]const u8).init(arena); - try zig_args.append(zig_exe_path); - - if (case.is_test) { - try zig_args.append("test"); - } else if (update.case == .Execution) { - try zig_args.append("run"); - } else switch (case.output_mode) { - .Obj => try zig_args.append("build-obj"), - .Exe => try zig_args.append("build-exe"), - .Lib => try zig_args.append("build-lib"), - } - - try zig_args.append(try std.fs.path.join(arena, &.{ tmp_dir_path, tmp_src_path })); - - try zig_args.append("--name"); - try zig_args.append("test"); - - try zig_args.append("--cache-dir"); - try zig_args.append(local_cache_path); - - try zig_args.append("--global-cache-dir"); - try zig_args.append(global_cache_directory.path orelse "."); - - if (!case.target.isNative()) { - try zig_args.append("-target"); - try zig_args.append(try target.zigTriple(arena)); - } - - try zig_args.append("-O"); - try zig_args.append(@tagName(case.optimize_mode)); - - // Prevent sub-process progress bar from interfering with the - // one in this parent process. - try zig_args.append("--color"); - try zig_args.append("off"); - - const result = try std.ChildProcess.exec(.{ - .allocator = arena, - .argv = zig_args.items, - }); - switch (update.case) { - .Error => |case_error_list| { - switch (result.term) { - .Exited => |code| { - if (code == 0) { - dumpArgs(zig_args.items); - return error.CompilationIncorrectlySucceeded; - } - }, - else => { - std.debug.print("{s}", .{result.stderr}); - dumpArgs(zig_args.items); - return error.CompilationCrashed; - }, - } - var ok = true; - if (case.expect_exact) { - var err_iter = std.mem.split(u8, result.stderr, "\n"); - var i: usize = 0; - ok = while (err_iter.next()) |line| : (i += 1) { - if (i >= case_error_list.len) break false; - const expected = try std.mem.replaceOwned( - u8, - arena, - try std.fmt.allocPrint(arena, "{s}", .{case_error_list[i]}), - "${DIR}", - tmp_dir_path_plus_slash, - ); - - if (std.mem.indexOf(u8, line, expected) == null) break false; - continue; - } else true; - - ok = ok and i == case_error_list.len; - - if (!ok) { - print("\n======== Expected these compile errors: ========\n", .{}); - for (case_error_list) |msg| { - const expected = try std.fmt.allocPrint(arena, "{s}", .{msg}); - print("{s}\n", .{expected}); - } - } - } else { - for (case_error_list) |msg| { - const expected = try std.mem.replaceOwned( - u8, - arena, - try std.fmt.allocPrint(arena, "{s}", .{msg}), - "${DIR}", - tmp_dir_path_plus_slash, - ); - if (std.mem.indexOf(u8, result.stderr, expected) == null) { - print( - \\ - \\=========== Expected compile error: ============ - \\{s} - \\ - , .{expected}); - ok = false; - break; - } - } - } - - if (!ok) { - print( - \\================= Full output: ================= - \\{s} - \\================================================ - \\ - , .{result.stderr}); - return error.TestFailed; - } - }, - .CompareObjectFile => @panic("TODO implement in the test harness"), - .Execution => |expected_stdout| { - switch (result.term) { - .Exited => |code| { - if (code != 0) { - std.debug.print("{s}", .{result.stderr}); - dumpArgs(zig_args.items); - return error.CompilationFailed; - } - }, - else => { - std.debug.print("{s}", .{result.stderr}); - dumpArgs(zig_args.items); - return error.CompilationCrashed; - }, - } - try std.testing.expectEqualStrings("", result.stderr); - try std.testing.expectEqualStrings(expected_stdout, result.stdout); - }, - .Header => @panic("TODO implement in the test harness"), - } - return; - } - - const zig_cache_directory: Compilation.Directory = .{ - .handle = cache_dir, - .path = local_cache_path, - }; - - var main_pkg: Package = .{ - .root_src_directory = .{ .path = tmp_dir_path, .handle = tmp.dir }, - .root_src_path = tmp_src_path, - }; - defer { - var it = main_pkg.table.iterator(); - while (it.next()) |kv| { - allocator.free(kv.key_ptr.*); - kv.value_ptr.*.destroy(allocator); - } - main_pkg.table.deinit(allocator); - } - - for (case.deps.items) |dep| { - var pkg = try Package.create( - allocator, - tmp_dir_path, - dep.path, - ); - errdefer pkg.destroy(allocator); - try main_pkg.add(allocator, dep.name, pkg); - } - - const bin_name = try std.zig.binNameAlloc(arena, .{ - .root_name = "test_case", - .target = target, - .output_mode = case.output_mode, - }); - - const emit_directory: Compilation.Directory = .{ - .path = tmp_dir_path, - .handle = tmp.dir, - }; - const emit_bin: Compilation.EmitLoc = .{ - .directory = emit_directory, - .basename = bin_name, - }; - const emit_h: ?Compilation.EmitLoc = if (case.emit_h) .{ - .directory = emit_directory, - .basename = "test_case.h", - } else null; - const use_llvm: bool = switch (case.backend) { - .llvm => true, - else => false, - }; - const comp = try Compilation.create(allocator, .{ - .local_cache_directory = zig_cache_directory, - .global_cache_directory = global_cache_directory, - .zig_lib_directory = zig_lib_directory, - .thread_pool = thread_pool, - .root_name = "test_case", - .target = target, - // TODO: support tests for object file building, and library builds - // and linking. This will require a rework to support multi-file - // tests. - .output_mode = case.output_mode, - .is_test = case.is_test, - .optimize_mode = case.optimize_mode, - .emit_bin = emit_bin, - .emit_h = emit_h, - .main_pkg = &main_pkg, - .keep_source_files_loaded = true, - .is_native_os = case.target.isNativeOs(), - .is_native_abi = case.target.isNativeAbi(), - .dynamic_linker = target_info.dynamic_linker.get(), - .link_libc = case.link_libc, - .use_llvm = use_llvm, - .self_exe_path = zig_exe_path, - // TODO instead of turning off color, pass in a std.Progress.Node - .color = .off, - .reference_trace = 0, - // TODO: force self-hosted linkers with stage2 backend to avoid LLD creeping in - // until the auto-select mechanism deems them worthy - .use_lld = switch (case.backend) { - .stage2 => false, - else => null, - }, - }); - defer comp.destroy(); - - update: for (case.updates.items, 0..) |update, update_index| { - var update_node = root_node.start(update.name, 3); - update_node.activate(); - defer update_node.end(); - - var sync_node = update_node.start("write", 0); - sync_node.activate(); - try tmp.dir.writeFile(tmp_src_path, update.src); - sync_node.end(); - - var module_node = update_node.start("parse/analysis/codegen", 0); - module_node.activate(); - try comp.makeBinFileWritable(); - try comp.update(&module_node); - module_node.end(); - - if (update.case != .Error) { - var all_errors = try comp.getAllErrorsAlloc(); - defer all_errors.deinit(allocator); - if (all_errors.errorMessageCount() > 0) { - all_errors.renderToStdErr(std.debug.detectTTYConfig(std.io.getStdErr())); - // TODO print generated C code - return error.UnexpectedCompileErrors; - } - } - - switch (update.case) { - .Header => |expected_output| { - var file = try tmp.dir.openFile("test_case.h", .{ .mode = .read_only }); - defer file.close(); - const out = try file.reader().readAllAlloc(arena, 5 * 1024 * 1024); - - try std.testing.expectEqualStrings(expected_output, out); - }, - .CompareObjectFile => |expected_output| { - var file = try tmp.dir.openFile(bin_name, .{ .mode = .read_only }); - defer file.close(); - const out = try file.reader().readAllAlloc(arena, 5 * 1024 * 1024); - - try std.testing.expectEqualStrings(expected_output, out); - }, - .Error => |case_error_list| { - var test_node = update_node.start("assert", 0); - test_node.activate(); - defer test_node.end(); - - const handled_errors = try arena.alloc(bool, case_error_list.len); - std.mem.set(bool, handled_errors, false); - - var actual_errors = try comp.getAllErrorsAlloc(); - defer actual_errors.deinit(allocator); - - var any_failed = false; - var notes_to_check = std.ArrayList(*const Compilation.AllErrors.Message).init(allocator); - defer notes_to_check.deinit(); - - for (actual_errors.list) |actual_error| { - for (case_error_list, 0..) |case_msg, i| { - if (handled_errors[i]) continue; - - const ex_tag: std.meta.Tag(@TypeOf(case_msg)) = case_msg; - switch (actual_error) { - .src => |actual_msg| { - for (actual_msg.notes) |*note| { - try notes_to_check.append(note); - } - - if (ex_tag != .src) continue; - - const src_path_ok = case_msg.src.src_path.len == 0 or - std.mem.eql(u8, case_msg.src.src_path, actual_msg.src_path); - - const expected_msg = try std.mem.replaceOwned( - u8, - arena, - case_msg.src.msg, - "${DIR}", - tmp_dir_path_plus_slash, - ); - - var buf: [1024]u8 = undefined; - const rendered_msg = blk: { - var msg: Compilation.AllErrors.Message = actual_error; - msg.src.src_path = case_msg.src.src_path; - msg.src.notes = &.{}; - msg.src.source_line = null; - var fib = std.io.fixedBufferStream(&buf); - try msg.renderToWriter(.no_color, fib.writer(), "error", .Red, 0); - var it = std.mem.split(u8, fib.getWritten(), "error: "); - _ = it.first(); - const rendered = it.rest(); - break :blk rendered[0 .. rendered.len - 1]; // trim final newline - }; - - if (src_path_ok and - (case_msg.src.line == std.math.maxInt(u32) or - actual_msg.line == case_msg.src.line) and - (case_msg.src.column == std.math.maxInt(u32) or - actual_msg.column == case_msg.src.column) and - std.mem.eql(u8, expected_msg, rendered_msg) and - case_msg.src.kind == .@"error" and - actual_msg.count == case_msg.src.count) - { - handled_errors[i] = true; - break; - } - }, - .plain => |plain| { - if (ex_tag != .plain) continue; - - if (std.mem.eql(u8, case_msg.plain.msg, plain.msg) and - case_msg.plain.kind == .@"error" and - case_msg.plain.count == plain.count) - { - handled_errors[i] = true; - break; - } - }, - } - } else { - print( - "\nUnexpected error:\n{s}\n{}\n{s}", - .{ hr, ErrorMsg.init(actual_error, .@"error"), hr }, - ); - any_failed = true; - } - } - while (notes_to_check.popOrNull()) |note| { - for (case_error_list, 0..) |case_msg, i| { - const ex_tag: std.meta.Tag(@TypeOf(case_msg)) = case_msg; - switch (note.*) { - .src => |actual_msg| { - for (actual_msg.notes) |*sub_note| { - try notes_to_check.append(sub_note); - } - if (ex_tag != .src) continue; - - const expected_msg = try std.mem.replaceOwned( - u8, - arena, - case_msg.src.msg, - "${DIR}", - tmp_dir_path_plus_slash, - ); - - if ((case_msg.src.line == std.math.maxInt(u32) or - actual_msg.line == case_msg.src.line) and - (case_msg.src.column == std.math.maxInt(u32) or - actual_msg.column == case_msg.src.column) and - std.mem.eql(u8, expected_msg, actual_msg.msg) and - case_msg.src.kind == .note and - actual_msg.count == case_msg.src.count) - { - handled_errors[i] = true; - break; - } - }, - .plain => |plain| { - if (ex_tag != .plain) continue; - - if (std.mem.eql(u8, case_msg.plain.msg, plain.msg) and - case_msg.plain.kind == .note and - case_msg.plain.count == plain.count) - { - handled_errors[i] = true; - break; - } - }, - } - } else { - print( - "\nUnexpected note:\n{s}\n{}\n{s}", - .{ hr, ErrorMsg.init(note.*, .note), hr }, - ); - any_failed = true; - } - } - - for (handled_errors, 0..) |handled, i| { - if (!handled) { - print( - "\nExpected error not found:\n{s}\n{}\n{s}", - .{ hr, case_error_list[i], hr }, - ); - any_failed = true; - } - } - - if (any_failed) { - print("\nupdate_index={d}\n", .{update_index}); - return error.WrongCompileErrors; - } - }, - .Execution => |expected_stdout| { - if (!std.process.can_spawn) { - print("Unable to spawn child processes on {s}, skipping test.\n", .{@tagName(builtin.os.tag)}); - continue :update; // Pass test. - } - - update_node.setEstimatedTotalItems(4); - - var argv = std.ArrayList([]const u8).init(allocator); - defer argv.deinit(); - - var exec_result = x: { - var exec_node = update_node.start("execute", 0); - exec_node.activate(); - defer exec_node.end(); - - // We go out of our way here to use the unique temporary directory name in - // the exe_path so that it makes its way into the cache hash, avoiding - // cache collisions from multiple threads doing `zig run` at the same time - // on the same test_case.c input filename. - const ss = std.fs.path.sep_str; - const exe_path = try std.fmt.allocPrint( - arena, - ".." ++ ss ++ "{s}" ++ ss ++ "{s}", - .{ &tmp.sub_path, bin_name }, - ); - if (case.target.ofmt != null and case.target.ofmt.? == .c) { - if (host.getExternalExecutor(target_info, .{ .link_libc = true }) != .native) { - // We wouldn't be able to run the compiled C code. - continue :update; // Pass test. - } - try argv.appendSlice(&[_][]const u8{ - zig_exe_path, - "run", - "-cflags", - "-std=c99", - "-pedantic", - "-Werror", - "-Wno-incompatible-library-redeclaration", // https://github.com/ziglang/zig/issues/875 - "--", - "-lc", - exe_path, - }); - if (zig_lib_directory.path) |p| { - try argv.appendSlice(&.{ "-I", p }); - } - } else switch (host.getExternalExecutor(target_info, .{ .link_libc = case.link_libc })) { - .native => { - if (case.backend == .stage2 and case.target.getCpuArch() == .arm) { - // https://github.com/ziglang/zig/issues/13623 - continue :update; // Pass test. - } - try argv.append(exe_path); - }, - .bad_dl, .bad_os_or_cpu => continue :update, // Pass test. - - .rosetta => if (enable_rosetta) { - try argv.append(exe_path); - } else { - continue :update; // Rosetta not available, pass test. - }, - - .qemu => |qemu_bin_name| if (enable_qemu) { - const need_cross_glibc = target.isGnuLibC() and case.link_libc; - const glibc_dir_arg: ?[]const u8 = if (need_cross_glibc) - glibc_runtimes_dir orelse continue :update // glibc dir not available; pass test - else - null; - try argv.append(qemu_bin_name); - if (glibc_dir_arg) |dir| { - const linux_triple = try target.linuxTriple(arena); - const full_dir = try std.fs.path.join(arena, &[_][]const u8{ - dir, - linux_triple, - }); - - try argv.append("-L"); - try argv.append(full_dir); - } - try argv.append(exe_path); - } else { - continue :update; // QEMU not available; pass test. - }, - - .wine => |wine_bin_name| if (enable_wine) { - try argv.append(wine_bin_name); - try argv.append(exe_path); - } else { - continue :update; // Wine not available; pass test. - }, - - .wasmtime => |wasmtime_bin_name| if (enable_wasmtime) { - try argv.append(wasmtime_bin_name); - try argv.append("--dir=."); - try argv.append(exe_path); - } else { - continue :update; // wasmtime not available; pass test. - }, - - .darling => |darling_bin_name| if (enable_darling) { - try argv.append(darling_bin_name); - // Since we use relative to cwd here, we invoke darling with - // "shell" subcommand. - try argv.append("shell"); - try argv.append(exe_path); - } else { - continue :update; // Darling not available; pass test. - }, - } - - try comp.makeBinFileExecutable(); - - while (true) { - break :x std.ChildProcess.exec(.{ - .allocator = allocator, - .argv = argv.items, - .cwd_dir = tmp.dir, - .cwd = tmp_dir_path, - }) catch |err| switch (err) { - error.FileBusy => { - // There is a fundamental design flaw in Unix systems with how - // ETXTBSY interacts with fork+exec. - // https://github.com/golang/go/issues/22315 - // https://bugs.openjdk.org/browse/JDK-8068370 - // Unfortunately, this could be a real error, but we can't - // tell the difference here. - continue; - }, - else => { - print("\n{s}.{d} The following command failed with {s}:\n", .{ - case.name, update_index, @errorName(err), - }); - dumpArgs(argv.items); - return error.ChildProcessExecution; - }, - }; - } - }; - var test_node = update_node.start("test", 0); - test_node.activate(); - defer test_node.end(); - defer allocator.free(exec_result.stdout); - defer allocator.free(exec_result.stderr); - switch (exec_result.term) { - .Exited => |code| { - if (code != 0) { - print("\n{s}\n{s}: execution exited with code {d}:\n", .{ - exec_result.stderr, case.name, code, - }); - dumpArgs(argv.items); - return error.ChildProcessExecution; - } - }, - else => { - print("\n{s}\n{s}: execution crashed:\n", .{ - exec_result.stderr, case.name, - }); - dumpArgs(argv.items); - return error.ChildProcessExecution; - }, - } - try std.testing.expectEqualStrings(expected_stdout, exec_result.stdout); - // We allow stderr to have garbage in it because wasmtime prints a - // warning about --invoke even though we don't pass it. - //std.testing.expectEqualStrings("", exec_result.stderr); - }, - } - } - } -}; - -fn dumpArgs(argv: []const []const u8) void { - for (argv) |arg| { - print("{s} ", .{arg}); - } - print("\n", .{}); -} - -const tmp_src_path = "tmp.zig"; diff --git a/test/cases.zig b/test/cases.zig index 412b4cb5e2..ffe046c70e 100644 --- a/test/cases.zig +++ b/test/cases.zig @@ -1,8 +1,8 @@ const std = @import("std"); -const TestContext = @import("../src/test.zig").TestContext; +const Cases = @import("src/Cases.zig"); -pub fn addCases(ctx: *TestContext) !void { - try @import("compile_errors.zig").addCases(ctx); - try @import("stage2/cbe.zig").addCases(ctx); - try @import("stage2/nvptx.zig").addCases(ctx); +pub fn addCases(cases: *Cases) !void { + try @import("compile_errors.zig").addCases(cases); + try @import("cbe.zig").addCases(cases); + try @import("nvptx.zig").addCases(cases); } diff --git a/test/cases/compile_errors/access_inactive_union_field_comptime.zig b/test/cases/compile_errors/access_inactive_union_field_comptime.zig index d990a85f9e..2098b19d14 100644 --- a/test/cases/compile_errors/access_inactive_union_field_comptime.zig +++ b/test/cases/compile_errors/access_inactive_union_field_comptime.zig @@ -21,3 +21,4 @@ pub export fn entry1() void { // :9:15: error: access of union field 'a' while field 'b' is active // :2:21: note: union declared here // :14:16: error: access of union field 'a' while field 'b' is active +// :2:21: note: union declared here diff --git a/test/cases/compile_errors/bad_import.zig b/test/cases/compile_errors/bad_import.zig index 49e78a4be4..e624d7104c 100644 --- a/test/cases/compile_errors/bad_import.zig +++ b/test/cases/compile_errors/bad_import.zig @@ -4,4 +4,4 @@ const bogus = @import("bogus-does-not-exist.zig",); // backend=stage2 // target=native // -// :1:23: error: unable to load '${DIR}bogus-does-not-exist.zig': FileNotFound +// bogus-does-not-exist.zig': FileNotFound diff --git a/test/cases/compile_errors/condition_comptime_reason_explained.zig b/test/cases/compile_errors/condition_comptime_reason_explained.zig index 332ae8afc8..d0193986a8 100644 --- a/test/cases/compile_errors/condition_comptime_reason_explained.zig +++ b/test/cases/compile_errors/condition_comptime_reason_explained.zig @@ -45,4 +45,6 @@ pub export fn entry2() void { // :22:13: error: unable to resolve comptime value // :22:13: note: condition in comptime switch must be comptime-known // :21:17: note: expression is evaluated at comptime because the function returns a comptime-only type 'tmp.S' +// :2:12: note: struct requires comptime because of this field +// :2:12: note: use '*const fn() void' for a function pointer type // :32:19: note: called from here diff --git a/test/cases/compile_errors/directly_embedding_opaque_type_in_struct_and_union.zig b/test/cases/compile_errors/directly_embedding_opaque_type_in_struct_and_union.zig index 2a64326093..ace90bccfc 100644 --- a/test/cases/compile_errors/directly_embedding_opaque_type_in_struct_and_union.zig +++ b/test/cases/compile_errors/directly_embedding_opaque_type_in_struct_and_union.zig @@ -32,6 +32,7 @@ export fn d() void { // :3:8: error: opaque types have unknown size and therefore cannot be directly embedded in structs // :1:11: note: opaque declared here // :7:10: error: opaque types have unknown size and therefore cannot be directly embedded in unions +// :1:11: note: opaque declared here // :19:18: error: opaque types have unknown size and therefore cannot be directly embedded in structs // :18:22: note: opaque declared here // :24:23: error: opaque types have unknown size and therefore cannot be directly embedded in structs diff --git a/test/cases/compile_errors/extern_function_with_comptime_parameter.zig b/test/cases/compile_errors/extern_function_with_comptime_parameter.zig index de69fa409f..58f15f7fab 100644 --- a/test/cases/compile_errors/extern_function_with_comptime_parameter.zig +++ b/test/cases/compile_errors/extern_function_with_comptime_parameter.zig @@ -12,6 +12,6 @@ comptime { _ = entry2; } // backend=stage2 // target=native // -// :1:15: error: comptime parameters not allowed in function with calling convention 'C' // :5:30: error: comptime parameters not allowed in function with calling convention 'C' // :6:30: error: generic parameters not allowed in function with calling convention 'C' +// :1:15: error: comptime parameters not allowed in function with calling convention 'C' diff --git a/test/cases/compile_errors/function_parameter_is_opaque.zig b/test/cases/compile_errors/function_parameter_is_opaque.zig index 1f92274577..57c89bd7f4 100644 --- a/test/cases/compile_errors/function_parameter_is_opaque.zig +++ b/test/cases/compile_errors/function_parameter_is_opaque.zig @@ -27,4 +27,5 @@ export fn entry4() void { // :1:17: note: opaque declared here // :8:28: error: parameter of type '@TypeOf(null)' not allowed // :12:8: error: parameter of opaque type 'tmp.FooType' not allowed +// :1:17: note: opaque declared here // :17:8: error: parameter of type '@TypeOf(null)' not allowed diff --git a/test/cases/compile_errors/helpful_return_type_error_message.zig b/test/cases/compile_errors/helpful_return_type_error_message.zig index 871e948537..83342c7ec3 100644 --- a/test/cases/compile_errors/helpful_return_type_error_message.zig +++ b/test/cases/compile_errors/helpful_return_type_error_message.zig @@ -24,9 +24,9 @@ export fn quux() u32 { // :8:5: error: expected type 'void', found '@typeInfo(@typeInfo(@TypeOf(tmp.bar)).Fn.return_type.?).ErrorUnion.error_set' // :7:17: note: function cannot return an error // :11:15: error: expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(tmp.bar)).Fn.return_type.?).ErrorUnion.error_set!u32' -// :10:17: note: function cannot return an error // :11:15: note: cannot convert error union to payload type // :11:15: note: consider using 'try', 'catch', or 'if' +// :10:17: note: function cannot return an error // :15:14: error: expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(tmp.bar)).Fn.return_type.?).ErrorUnion.error_set!u32' // :15:14: note: cannot convert error union to payload type // :15:14: note: consider using 'try', 'catch', or 'if' diff --git a/test/cases/compile_errors/implicit_semicolon-block_expr.zig b/test/cases/compile_errors/implicit_semicolon-block_expr.zig index 7dd82b897b..bab8ec29c0 100644 --- a/test/cases/compile_errors/implicit_semicolon-block_expr.zig +++ b/test/cases/compile_errors/implicit_semicolon-block_expr.zig @@ -3,6 +3,8 @@ export fn entry() void { var good = {}; _ = {} var bad = {}; + _ = good; + _ = bad; } // error diff --git a/test/cases/compile_errors/implicit_semicolon-block_statement.zig b/test/cases/compile_errors/implicit_semicolon-block_statement.zig index 189ba84d98..912ccbc790 100644 --- a/test/cases/compile_errors/implicit_semicolon-block_statement.zig +++ b/test/cases/compile_errors/implicit_semicolon-block_statement.zig @@ -3,6 +3,8 @@ export fn entry() void { var good = {}; ({}) var bad = {}; + _ = good; + _ = bad; } // error diff --git a/test/cases/compile_errors/implicit_semicolon-comptime_expression.zig b/test/cases/compile_errors/implicit_semicolon-comptime_expression.zig index decbc352e8..e8dc8bb534 100644 --- a/test/cases/compile_errors/implicit_semicolon-comptime_expression.zig +++ b/test/cases/compile_errors/implicit_semicolon-comptime_expression.zig @@ -3,6 +3,8 @@ export fn entry() void { var good = {}; _ = comptime {} var bad = {}; + _ = good; + _ = bad; } // error diff --git a/test/cases/compile_errors/implicit_semicolon-comptime_statement.zig b/test/cases/compile_errors/implicit_semicolon-comptime_statement.zig index d17db15924..afc1798669 100644 --- a/test/cases/compile_errors/implicit_semicolon-comptime_statement.zig +++ b/test/cases/compile_errors/implicit_semicolon-comptime_statement.zig @@ -3,6 +3,8 @@ export fn entry() void { var good = {}; comptime ({}) var bad = {}; + _ = good; + _ = bad; } // error diff --git a/test/cases/compile_errors/implicit_semicolon-defer.zig b/test/cases/compile_errors/implicit_semicolon-defer.zig index 57fd3a2626..e91dbae7f8 100644 --- a/test/cases/compile_errors/implicit_semicolon-defer.zig +++ b/test/cases/compile_errors/implicit_semicolon-defer.zig @@ -3,6 +3,8 @@ export fn entry() void { var good = {}; defer ({}) var bad = {}; + _ = good; + _ = bad; } // error diff --git a/test/cases/compile_errors/implicit_semicolon-for_expression.zig b/test/cases/compile_errors/implicit_semicolon-for_expression.zig index c751384e11..1fbe4dd3ad 100644 --- a/test/cases/compile_errors/implicit_semicolon-for_expression.zig +++ b/test/cases/compile_errors/implicit_semicolon-for_expression.zig @@ -3,7 +3,10 @@ export fn entry() void { var good = {}; _ = for(foo()) |_| {} var bad = {}; + _ = good; + _ = bad; } +fn foo() void {} // error // backend=stage2 diff --git a/test/cases/compile_errors/implicit_semicolon-for_statement.zig b/test/cases/compile_errors/implicit_semicolon-for_statement.zig index 14709cef4c..2830293b70 100644 --- a/test/cases/compile_errors/implicit_semicolon-for_statement.zig +++ b/test/cases/compile_errors/implicit_semicolon-for_statement.zig @@ -3,7 +3,10 @@ export fn entry() void { var good = {}; for(foo()) |_| ({}) var bad = {}; + _ = good; + _ = bad; } +fn foo() void {} // error // backend=stage2 diff --git a/test/cases/compile_errors/implicit_semicolon-if-else-if-else_expression.zig b/test/cases/compile_errors/implicit_semicolon-if-else-if-else_expression.zig index 72a4fa7d3e..9e99421cd1 100644 --- a/test/cases/compile_errors/implicit_semicolon-if-else-if-else_expression.zig +++ b/test/cases/compile_errors/implicit_semicolon-if-else-if-else_expression.zig @@ -3,6 +3,8 @@ export fn entry() void { var good = {}; _ = if(true) {} else if(true) {} else {} var bad = {}; + _ = good; + _ = bad; } // error diff --git a/test/cases/compile_errors/implicit_semicolon-if-else-if-else_statement.zig b/test/cases/compile_errors/implicit_semicolon-if-else-if-else_statement.zig index 95135006ba..e2e7b7e3b3 100644 --- a/test/cases/compile_errors/implicit_semicolon-if-else-if-else_statement.zig +++ b/test/cases/compile_errors/implicit_semicolon-if-else-if-else_statement.zig @@ -3,6 +3,8 @@ export fn entry() void { var good = {}; if(true) ({}) else if(true) ({}) else ({}) var bad = {}; + _ = good; + _ = bad; } // error diff --git a/test/cases/compile_errors/implicit_semicolon-if-else-if_expression.zig b/test/cases/compile_errors/implicit_semicolon-if-else-if_expression.zig index a29636bd1d..33ca6ab600 100644 --- a/test/cases/compile_errors/implicit_semicolon-if-else-if_expression.zig +++ b/test/cases/compile_errors/implicit_semicolon-if-else-if_expression.zig @@ -3,6 +3,8 @@ export fn entry() void { var good = {}; _ = if(true) {} else if(true) {} var bad = {}; + _ = good; + _ = bad; } // error diff --git a/test/cases/compile_errors/implicit_semicolon-if-else-if_statement.zig b/test/cases/compile_errors/implicit_semicolon-if-else-if_statement.zig index c62430a0a2..e3d004fee1 100644 --- a/test/cases/compile_errors/implicit_semicolon-if-else-if_statement.zig +++ b/test/cases/compile_errors/implicit_semicolon-if-else-if_statement.zig @@ -3,6 +3,8 @@ export fn entry() void { var good = {}; if(true) ({}) else if(true) ({}) var bad = {}; + _ = good; + _ = bad; } // error diff --git a/test/cases/compile_errors/implicit_semicolon-if-else_expression.zig b/test/cases/compile_errors/implicit_semicolon-if-else_expression.zig index d5bee6e52b..a23809528b 100644 --- a/test/cases/compile_errors/implicit_semicolon-if-else_expression.zig +++ b/test/cases/compile_errors/implicit_semicolon-if-else_expression.zig @@ -3,6 +3,8 @@ export fn entry() void { var good = {}; _ = if(true) {} else {} var bad = {}; + _ = good; + _ = bad; } // error diff --git a/test/cases/compile_errors/implicit_semicolon-if-else_statement.zig b/test/cases/compile_errors/implicit_semicolon-if-else_statement.zig index 94df128626..ed01aa7df2 100644 --- a/test/cases/compile_errors/implicit_semicolon-if-else_statement.zig +++ b/test/cases/compile_errors/implicit_semicolon-if-else_statement.zig @@ -3,6 +3,8 @@ export fn entry() void { var good = {}; if(true) ({}) else ({}) var bad = {}; + _ = good; + _ = bad; } // error diff --git a/test/cases/compile_errors/implicit_semicolon-if_expression.zig b/test/cases/compile_errors/implicit_semicolon-if_expression.zig index 339a5378cf..e28f8616e2 100644 --- a/test/cases/compile_errors/implicit_semicolon-if_expression.zig +++ b/test/cases/compile_errors/implicit_semicolon-if_expression.zig @@ -3,6 +3,8 @@ export fn entry() void { var good = {}; _ = if(true) {} var bad = {}; + _ = good; + _ = bad; } // error diff --git a/test/cases/compile_errors/implicit_semicolon-if_statement.zig b/test/cases/compile_errors/implicit_semicolon-if_statement.zig index b8ccb5e401..3067c07767 100644 --- a/test/cases/compile_errors/implicit_semicolon-if_statement.zig +++ b/test/cases/compile_errors/implicit_semicolon-if_statement.zig @@ -3,6 +3,8 @@ export fn entry() void { var good = {}; if(true) ({}) var bad = {}; + _ = good; + _ = bad; } // error diff --git a/test/cases/compile_errors/implicit_semicolon-test_expression.zig b/test/cases/compile_errors/implicit_semicolon-test_expression.zig index 2a37c0aa0e..0bb345b387 100644 --- a/test/cases/compile_errors/implicit_semicolon-test_expression.zig +++ b/test/cases/compile_errors/implicit_semicolon-test_expression.zig @@ -3,7 +3,10 @@ export fn entry() void { var good = {}; _ = if (foo()) |_| {} var bad = {}; + _ = good; + _ = bad; } +fn foo() void {} // error // backend=stage2 diff --git a/test/cases/compile_errors/implicit_semicolon-test_statement.zig b/test/cases/compile_errors/implicit_semicolon-test_statement.zig index afe00eba75..3a4eb8b5ba 100644 --- a/test/cases/compile_errors/implicit_semicolon-test_statement.zig +++ b/test/cases/compile_errors/implicit_semicolon-test_statement.zig @@ -3,7 +3,10 @@ export fn entry() void { var good = {}; if (foo()) |_| ({}) var bad = {}; + _ = good; + _ = bad; } +fn foo() void {} // error // backend=stage2 diff --git a/test/cases/compile_errors/implicit_semicolon-while-continue_expression.zig b/test/cases/compile_errors/implicit_semicolon-while-continue_expression.zig index 5587627597..f05c70bc14 100644 --- a/test/cases/compile_errors/implicit_semicolon-while-continue_expression.zig +++ b/test/cases/compile_errors/implicit_semicolon-while-continue_expression.zig @@ -3,6 +3,8 @@ export fn entry() void { var good = {}; _ = while(true):({}) {} var bad = {}; + _ = good; + _ = bad; } // error diff --git a/test/cases/compile_errors/implicit_semicolon-while-continue_statement.zig b/test/cases/compile_errors/implicit_semicolon-while-continue_statement.zig index 9bebe3861e..2d27824f6b 100644 --- a/test/cases/compile_errors/implicit_semicolon-while-continue_statement.zig +++ b/test/cases/compile_errors/implicit_semicolon-while-continue_statement.zig @@ -3,6 +3,8 @@ export fn entry() void { var good = {}; while(true):({}) ({}) var bad = {}; + _ = good; + _ = bad; } // error diff --git a/test/cases/compile_errors/implicit_semicolon-while_expression.zig b/test/cases/compile_errors/implicit_semicolon-while_expression.zig index df388a7c39..4b39ed7c16 100644 --- a/test/cases/compile_errors/implicit_semicolon-while_expression.zig +++ b/test/cases/compile_errors/implicit_semicolon-while_expression.zig @@ -3,6 +3,8 @@ export fn entry() void { var good = {}; _ = while(true) {} var bad = {}; + _ = good; + _ = bad; } // error diff --git a/test/cases/compile_errors/implicit_semicolon-while_statement.zig b/test/cases/compile_errors/implicit_semicolon-while_statement.zig index d9ed3d1e2a..538a56faf1 100644 --- a/test/cases/compile_errors/implicit_semicolon-while_statement.zig +++ b/test/cases/compile_errors/implicit_semicolon-while_statement.zig @@ -3,6 +3,8 @@ export fn entry() void { var good = {}; while(true) 1 var bad = {}; + _ = good; + _ = bad; } // error diff --git a/test/cases/compile_errors/invalid_member_of_builtin_enum.zig b/test/cases/compile_errors/invalid_member_of_builtin_enum.zig index b0a176d792..f3ea66ae1c 100644 --- a/test/cases/compile_errors/invalid_member_of_builtin_enum.zig +++ b/test/cases/compile_errors/invalid_member_of_builtin_enum.zig @@ -9,4 +9,4 @@ export fn entry() void { // target=native // // :3:38: error: enum 'builtin.OptimizeMode' has no member named 'x86' -// :?:18: note: enum declared here +// : note: enum declared here diff --git a/test/cases/compile_errors/invalid_store_to_comptime_field.zig b/test/cases/compile_errors/invalid_store_to_comptime_field.zig index 0f444ba78c..fd6fff5e17 100644 --- a/test/cases/compile_errors/invalid_store_to_comptime_field.zig +++ b/test/cases/compile_errors/invalid_store_to_comptime_field.zig @@ -73,11 +73,11 @@ pub export fn entry8() void { // // :6:19: error: value stored in comptime field does not match the default value of the field // :14:19: error: value stored in comptime field does not match the default value of the field -// :53:16: error: value stored in comptime field does not match the default value of the field // :19:38: error: value stored in comptime field does not match the default value of the field // :31:19: error: value stored in comptime field does not match the default value of the field // :25:29: note: default value set here // :41:16: error: value stored in comptime field does not match the default value of the field // :45:12: error: value stored in comptime field does not match the default value of the field +// :53:16: error: value stored in comptime field does not match the default value of the field // :66:43: error: value stored in comptime field does not match the default value of the field // :59:35: error: value stored in comptime field does not match the default value of the field diff --git a/test/cases/compile_errors/invalid_struct_field.zig b/test/cases/compile_errors/invalid_struct_field.zig index 4450375cb8..ff8c96a0b6 100644 --- a/test/cases/compile_errors/invalid_struct_field.zig +++ b/test/cases/compile_errors/invalid_struct_field.zig @@ -25,5 +25,6 @@ export fn e() void { // :4:7: error: no field named 'foo' in struct 'tmp.A' // :1:11: note: struct declared here // :10:17: error: no field named 'bar' in struct 'tmp.A' +// :1:11: note: struct declared here // :18:45: error: no field named 'f' in struct 'tmp.e.B' // :14:15: note: struct declared here diff --git a/test/cases/compile_errors/missing_main_fn_in_executable.zig b/test/cases/compile_errors/missing_main_fn_in_executable.zig index 2d608ad2b8..3c1ae631ac 100644 --- a/test/cases/compile_errors/missing_main_fn_in_executable.zig +++ b/test/cases/compile_errors/missing_main_fn_in_executable.zig @@ -5,5 +5,7 @@ // target=x86_64-linux // output_mode=Exe // -// :?:?: error: root struct of file 'tmp' has no member named 'main' -// :?:?: note: called from here +// : error: root struct of file 'tmp' has no member named 'main' +// : note: called from here +// : note: called from here +// : note: called from here diff --git a/test/cases/compile_errors/private_main_fn.zig b/test/cases/compile_errors/private_main_fn.zig index 26ad3d22db..6e53fbdce2 100644 --- a/test/cases/compile_errors/private_main_fn.zig +++ b/test/cases/compile_errors/private_main_fn.zig @@ -5,6 +5,8 @@ fn main() void {} // target=x86_64-linux // output_mode=Exe // -// :?:?: error: 'main' is not marked 'pub' +// : error: 'main' is not marked 'pub' // :1:1: note: declared here -// :?:?: note: called from here +// : note: called from here +// : note: called from here +// : note: called from here diff --git a/test/cases/compile_errors/runtime_index_into_comptime_type_slice.zig b/test/cases/compile_errors/runtime_index_into_comptime_type_slice.zig index 4c235ed94b..9cd0fe1798 100644 --- a/test/cases/compile_errors/runtime_index_into_comptime_type_slice.zig +++ b/test/cases/compile_errors/runtime_index_into_comptime_type_slice.zig @@ -15,5 +15,6 @@ export fn entry() void { // target=native // // :9:51: error: values of type '[]const builtin.Type.StructField' must be comptime-known, but index value is runtime-known -// :?:21: note: struct requires comptime because of this field -// :?:21: note: types are not available at runtime +// : note: struct requires comptime because of this field +// : note: types are not available at runtime +// : struct requires comptime because of this field diff --git a/test/cases/compile_errors/struct_type_mismatch_in_arg.zig b/test/cases/compile_errors/struct_type_mismatch_in_arg.zig index a52bdfab6c..d051966c52 100644 --- a/test/cases/compile_errors/struct_type_mismatch_in_arg.zig +++ b/test/cases/compile_errors/struct_type_mismatch_in_arg.zig @@ -13,6 +13,6 @@ comptime { // target=native // // :7:16: error: expected type 'tmp.Foo', found 'tmp.Bar' -// :1:13: note: struct declared here // :2:13: note: struct declared here +// :1:13: note: struct declared here // :4:18: note: parameter type declared here diff --git a/test/cases/compile_errors/union_init_with_none_or_multiple_fields.zig b/test/cases/compile_errors/union_init_with_none_or_multiple_fields.zig index 5f486bf2b7..a700f0d0f2 100644 --- a/test/cases/compile_errors/union_init_with_none_or_multiple_fields.zig +++ b/test/cases/compile_errors/union_init_with_none_or_multiple_fields.zig @@ -28,10 +28,11 @@ export fn u2m() void { // target=native // // :9:1: error: union initializer must initialize one field +// :1:12: note: union declared here // :14:20: error: cannot initialize multiple union fields at once, unions can only have one active field // :14:31: note: additional initializer here +// :1:12: note: union declared here // :18:21: error: union initializer must initialize one field // :22:20: error: cannot initialize multiple union fields at once, unions can only have one active field // :22:31: note: additional initializer here -// :1:12: note: union declared here // :5:12: note: union declared here diff --git a/test/cases/llvm/address_space_pointer_access_chaining_pointer_to_optional_array.zig b/test/cases/llvm/address_space_pointer_access_chaining_pointer_to_optional_array.zig index cf43513159..00d4a7ecc9 100644 --- a/test/cases/llvm/address_space_pointer_access_chaining_pointer_to_optional_array.zig +++ b/test/cases/llvm/address_space_pointer_access_chaining_pointer_to_optional_array.zig @@ -5,7 +5,7 @@ pub fn main() void { _ = entry; } -// error +// compile // output_mode=Exe // backend=llvm // target=x86_64-linux,x86_64-macos diff --git a/test/cases/llvm/address_spaces_pointer_access_chaining_array_pointer.zig b/test/cases/llvm/address_spaces_pointer_access_chaining_array_pointer.zig index 5907c1dad5..f23498e955 100644 --- a/test/cases/llvm/address_spaces_pointer_access_chaining_array_pointer.zig +++ b/test/cases/llvm/address_spaces_pointer_access_chaining_array_pointer.zig @@ -5,7 +5,7 @@ pub fn main() void { _ = entry; } -// error +// compile // output_mode=Exe // backend=stage2,llvm // target=x86_64-linux,x86_64-macos diff --git a/test/cases/llvm/address_spaces_pointer_access_chaining_complex.zig b/test/cases/llvm/address_spaces_pointer_access_chaining_complex.zig index ece0614f73..4f54f38e6b 100644 --- a/test/cases/llvm/address_spaces_pointer_access_chaining_complex.zig +++ b/test/cases/llvm/address_spaces_pointer_access_chaining_complex.zig @@ -6,7 +6,7 @@ pub fn main() void { _ = entry; } -// error +// compile // output_mode=Exe // backend=llvm // target=x86_64-linux,x86_64-macos diff --git a/test/cases/llvm/address_spaces_pointer_access_chaining_struct_pointer.zig b/test/cases/llvm/address_spaces_pointer_access_chaining_struct_pointer.zig index 9175bcbc0e..84695cb35b 100644 --- a/test/cases/llvm/address_spaces_pointer_access_chaining_struct_pointer.zig +++ b/test/cases/llvm/address_spaces_pointer_access_chaining_struct_pointer.zig @@ -6,7 +6,7 @@ pub fn main() void { _ = entry; } -// error +// compile // output_mode=Exe // backend=stage2,llvm // target=x86_64-linux,x86_64-macos diff --git a/test/cases/llvm/dereferencing_though_multiple_pointers_with_address_spaces.zig b/test/cases/llvm/dereferencing_though_multiple_pointers_with_address_spaces.zig index 8f36700757..badab821d3 100644 --- a/test/cases/llvm/dereferencing_though_multiple_pointers_with_address_spaces.zig +++ b/test/cases/llvm/dereferencing_though_multiple_pointers_with_address_spaces.zig @@ -5,7 +5,7 @@ pub fn main() void { _ = entry; } -// error +// compile // output_mode=Exe // backend=stage2,llvm // target=x86_64-linux,x86_64-macos diff --git a/test/cases/llvm/pointer_keeps_address_space.zig b/test/cases/llvm/pointer_keeps_address_space.zig index bfd40566f8..f894c96d7b 100644 --- a/test/cases/llvm/pointer_keeps_address_space.zig +++ b/test/cases/llvm/pointer_keeps_address_space.zig @@ -5,7 +5,7 @@ pub fn main() void { _ = entry; } -// error +// compile // output_mode=Exe // backend=stage2,llvm // target=x86_64-linux,x86_64-macos diff --git a/test/cases/llvm/pointer_keeps_address_space_when_taking_address_of_dereference.zig b/test/cases/llvm/pointer_keeps_address_space_when_taking_address_of_dereference.zig index 8114e86c5d..b5803a3076 100644 --- a/test/cases/llvm/pointer_keeps_address_space_when_taking_address_of_dereference.zig +++ b/test/cases/llvm/pointer_keeps_address_space_when_taking_address_of_dereference.zig @@ -5,7 +5,7 @@ pub fn main() void { _ = entry; } -// error +// compile // output_mode=Exe // backend=stage2,llvm // target=x86_64-linux,x86_64-macos diff --git a/test/cases/llvm/pointer_to_explicit_generic_address_space_coerces_to_implicit_pointer.zig b/test/cases/llvm/pointer_to_explicit_generic_address_space_coerces_to_implicit_pointer.zig index 78bc3e4bd6..b3c0116983 100644 --- a/test/cases/llvm/pointer_to_explicit_generic_address_space_coerces_to_implicit_pointer.zig +++ b/test/cases/llvm/pointer_to_explicit_generic_address_space_coerces_to_implicit_pointer.zig @@ -5,7 +5,7 @@ pub fn main() void { _ = entry; } -// error +// compile // output_mode=Exe // backend=stage2,llvm // target=x86_64-linux,x86_64-macos diff --git a/test/stage2/cbe.zig b/test/cbe.zig similarity index 92% rename from test/stage2/cbe.zig rename to test/cbe.zig index e9750853a6..25ac3cb137 100644 --- a/test/stage2/cbe.zig +++ b/test/cbe.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const TestContext = @import("../../src/test.zig").TestContext; +const Cases = @import("src/Cases.zig"); // These tests should work with all platforms, but we're using linux_x64 for // now for consistency. Will be expanded eventually. @@ -8,7 +8,7 @@ const linux_x64 = std.zig.CrossTarget{ .os_tag = .linux, }; -pub fn addCases(ctx: *TestContext) !void { +pub fn addCases(ctx: *Cases) !void { { var case = ctx.exeFromCompiledC("hello world with updates", .{}); @@ -71,7 +71,7 @@ pub fn addCases(ctx: *TestContext) !void { } { - var case = ctx.exeFromCompiledC("@intToError", .{}); + var case = ctx.exeFromCompiledC("intToError", .{}); case.addCompareOutput( \\pub export fn main() c_int { @@ -837,7 +837,7 @@ pub fn addCases(ctx: *TestContext) !void { } { - var case = ctx.exeFromCompiledC("shift right + left", .{}); + var case = ctx.exeFromCompiledC("shift right and left", .{}); case.addCompareOutput( \\pub export fn main() c_int { \\ var i: u32 = 16; @@ -883,7 +883,7 @@ pub fn addCases(ctx: *TestContext) !void { { // TODO: add u64 tests, ran into issues with the literal generated for std.math.maxInt(u64) - var case = ctx.exeFromCompiledC("add/sub wrapping operations", .{}); + var case = ctx.exeFromCompiledC("add and sub wrapping operations", .{}); case.addCompareOutput( \\pub export fn main() c_int { \\ // Addition @@ -932,7 +932,7 @@ pub fn addCases(ctx: *TestContext) !void { } { - var case = ctx.exeFromCompiledC("@rem", linux_x64); + var case = ctx.exeFromCompiledC("rem", linux_x64); case.addCompareOutput( \\fn assert(ok: bool) void { \\ if (!ok) unreachable; @@ -947,69 +947,4 @@ pub fn addCases(ctx: *TestContext) !void { \\} , ""); } - - ctx.h("simple header", linux_x64, - \\export fn start() void{} - , - \\zig_extern void start(void); - \\ - ); - ctx.h("header with single param function", linux_x64, - \\export fn start(a: u8) void{ - \\ _ = a; - \\} - , - \\zig_extern void start(uint8_t const a0); - \\ - ); - ctx.h("header with multiple param function", linux_x64, - \\export fn start(a: u8, b: u8, c: u8) void{ - \\ _ = a; _ = b; _ = c; - \\} - , - \\zig_extern void start(uint8_t const a0, uint8_t const a1, uint8_t const a2); - \\ - ); - ctx.h("header with u32 param function", linux_x64, - \\export fn start(a: u32) void{ _ = a; } - , - \\zig_extern void start(uint32_t const a0); - \\ - ); - ctx.h("header with usize param function", linux_x64, - \\export fn start(a: usize) void{ _ = a; } - , - \\zig_extern void start(uintptr_t const a0); - \\ - ); - ctx.h("header with bool param function", linux_x64, - \\export fn start(a: bool) void{_ = a;} - , - \\zig_extern void start(bool const a0); - \\ - ); - ctx.h("header with noreturn function", linux_x64, - \\export fn start() noreturn { - \\ unreachable; - \\} - , - \\zig_extern zig_noreturn void start(void); - \\ - ); - ctx.h("header with multiple functions", linux_x64, - \\export fn a() void{} - \\export fn b() void{} - \\export fn c() void{} - , - \\zig_extern void a(void); - \\zig_extern void b(void); - \\zig_extern void c(void); - \\ - ); - ctx.h("header with multiple includes", linux_x64, - \\export fn start(a: u32, b: usize) void{ _ = a; _ = b; } - , - \\zig_extern void start(uint32_t const a0, uintptr_t const a1); - \\ - ); } diff --git a/test/compile_errors.zig b/test/compile_errors.zig index e0b78b3000..2d796b9463 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,146 +1,10 @@ const std = @import("std"); const builtin = @import("builtin"); -const TestContext = @import("../src/test.zig").TestContext; - -pub fn addCases(ctx: *TestContext) !void { - { - const case = ctx.obj("wrong same named struct", .{}); - case.backend = .stage1; - - case.addSourceFile("a.zig", - \\pub const Foo = struct { - \\ x: i32, - \\}; - ); - - case.addSourceFile("b.zig", - \\pub const Foo = struct { - \\ z: f64, - \\}; - ); - - case.addError( - \\const a = @import("a.zig"); - \\const b = @import("b.zig"); - \\ - \\export fn entry() void { - \\ var a1: a.Foo = undefined; - \\ bar(&a1); - \\} - \\ - \\fn bar(x: *b.Foo) void {_ = x;} - , &[_][]const u8{ - "tmp.zig:6:10: error: expected type '*b.Foo', found '*a.Foo'", - "tmp.zig:6:10: note: pointer type child 'a.Foo' cannot cast into pointer type child 'b.Foo'", - "a.zig:1:17: note: a.Foo declared here", - "b.zig:1:17: note: b.Foo declared here", - }); - } - - { - const case = ctx.obj("multiple files with private function error", .{}); - case.backend = .stage1; - - case.addSourceFile("foo.zig", - \\fn privateFunction() void { } - ); - - case.addError( - \\const foo = @import("foo.zig",); - \\ - \\export fn callPrivFunction() void { - \\ foo.privateFunction(); - \\} - , &[_][]const u8{ - "tmp.zig:4:8: error: 'privateFunction' is private", - "foo.zig:1:1: note: declared here", - }); - } - - { - const case = ctx.obj("multiple files with private member instance function (canonical invocation) error", .{}); - case.backend = .stage1; - - case.addSourceFile("foo.zig", - \\pub const Foo = struct { - \\ fn privateFunction(self: *Foo) void { _ = self; } - \\}; - ); - - case.addError( - \\const Foo = @import("foo.zig",).Foo; - \\ - \\export fn callPrivFunction() void { - \\ var foo = Foo{}; - \\ Foo.privateFunction(foo); - \\} - , &[_][]const u8{ - "tmp.zig:5:8: error: 'privateFunction' is private", - "foo.zig:2:5: note: declared here", - }); - } - - { - const case = ctx.obj("multiple files with private member instance function error", .{}); - case.backend = .stage1; - - case.addSourceFile("foo.zig", - \\pub const Foo = struct { - \\ fn privateFunction(self: *Foo) void { _ = self; } - \\}; - ); - - case.addError( - \\const Foo = @import("foo.zig",).Foo; - \\ - \\export fn callPrivFunction() void { - \\ var foo = Foo{}; - \\ foo.privateFunction(); - \\} - , &[_][]const u8{ - "tmp.zig:5:8: error: 'privateFunction' is private", - "foo.zig:2:5: note: declared here", - }); - } - - { - const case = ctx.obj("export collision", .{}); - case.backend = .stage1; - - case.addSourceFile("foo.zig", - \\export fn bar() void {} - \\pub const baz = 1234; - ); - - case.addError( - \\const foo = @import("foo.zig",); - \\ - \\export fn bar() usize { - \\ return foo.baz; - \\} - , &[_][]const u8{ - "foo.zig:1:1: error: exported symbol collision: 'bar'", - "tmp.zig:3:1: note: other symbol here", - }); - } - - ctx.objErrStage1("non-printable invalid character", "\xff\xfe" ++ - "fn foo() bool {\r\n" ++ - " return true;\r\n" ++ - "}\r\n", &[_][]const u8{ - "tmp.zig:1:1: error: expected test, comptime, var decl, or container field, found 'invalid bytes'", - "tmp.zig:1:1: note: invalid byte: '\\xff'", - }); - - ctx.objErrStage1("non-printable invalid character with escape alternative", "fn foo() bool {\n" ++ - "\treturn true;\n" ++ - "}\n", &[_][]const u8{ - "tmp.zig:2:1: error: invalid character: '\\t'", - }); +const Cases = @import("src/Cases.zig"); +pub fn addCases(ctx: *Cases) !void { { const case = ctx.obj("multiline error messages", .{}); - case.backend = .stage2; case.addError( \\comptime { @@ -176,7 +40,6 @@ pub fn addCases(ctx: *TestContext) !void { { const case = ctx.obj("isolated carriage return in multiline string literal", .{}); - case.backend = .stage2; case.addError("const foo = \\\\\test\r\r rogue carriage return\n;", &[_][]const u8{ ":1:19: error: expected ';' after declaration", @@ -195,16 +58,6 @@ pub fn addCases(ctx: *TestContext) !void { { const case = ctx.obj("argument causes error", .{}); - case.backend = .stage2; - - case.addSourceFile("b.zig", - \\pub const ElfDynLib = struct { - \\ pub fn lookup(self: *ElfDynLib, comptime T: type) ?T { - \\ _ = self; - \\ return undefined; - \\ } - \\}; - ); case.addError( \\pub export fn entry() void { @@ -216,15 +69,18 @@ pub fn addCases(ctx: *TestContext) !void { ":3:12: note: argument to function being called at comptime must be comptime-known", ":2:55: note: expression is evaluated at comptime because the generic function was instantiated with a comptime-only return type", }); + case.addSourceFile("b.zig", + \\pub const ElfDynLib = struct { + \\ pub fn lookup(self: *ElfDynLib, comptime T: type) ?T { + \\ _ = self; + \\ return undefined; + \\ } + \\}; + ); } { const case = ctx.obj("astgen failure in file struct", .{}); - case.backend = .stage2; - - case.addSourceFile("b.zig", - \\+ - ); case.addError( \\pub export fn entry() void { @@ -233,21 +89,13 @@ pub fn addCases(ctx: *TestContext) !void { , &[_][]const u8{ ":1:1: error: expected type expression, found '+'", }); + case.addSourceFile("b.zig", + \\+ + ); } { const case = ctx.obj("invalid store to comptime field", .{}); - case.backend = .stage2; - - case.addSourceFile("a.zig", - \\pub const S = struct { - \\ comptime foo: u32 = 1, - \\ bar: u32, - \\ pub fn foo(x: @This()) void { - \\ _ = x; - \\ } - \\}; - ); case.addError( \\const a = @import("a.zig"); @@ -259,44 +107,19 @@ pub fn addCases(ctx: *TestContext) !void { ":4:23: error: value stored in comptime field does not match the default value of the field", ":2:25: note: default value set here", }); + case.addSourceFile("a.zig", + \\pub const S = struct { + \\ comptime foo: u32 = 1, + \\ bar: u32, + \\ pub fn foo(x: @This()) void { + \\ _ = x; + \\ } + \\}; + ); } - // TODO test this in stage2, but we won't even try in stage1 - //ctx.objErrStage1("inline fn calls itself indirectly", - // \\export fn foo() void { - // \\ bar(); - // \\} - // \\fn bar() callconv(.Inline) void { - // \\ baz(); - // \\ quux(); - // \\} - // \\fn baz() callconv(.Inline) void { - // \\ bar(); - // \\ quux(); - // \\} - // \\extern fn quux() void; - //, &[_][]const u8{ - // "tmp.zig:4:1: error: unable to inline function", - //}); - - //ctx.objErrStage1("save reference to inline function", - // \\export fn foo() void { - // \\ quux(@ptrToInt(bar)); - // \\} - // \\fn bar() callconv(.Inline) void { } - // \\extern fn quux(usize) void; - //, &[_][]const u8{ - // "tmp.zig:4:1: error: unable to inline function", - //}); - { const case = ctx.obj("file in multiple modules", .{}); - case.backend = .stage2; - - case.addSourceFile("foo.zig", - \\const dummy = 0; - ); - case.addDepModule("foo", "foo.zig"); case.addError( @@ -309,5 +132,8 @@ pub fn addCases(ctx: *TestContext) !void { ":1:1: note: root of module root.foo", ":3:17: note: imported from module root", }); + case.addSourceFile("foo.zig", + \\const dummy = 0; + ); } } diff --git a/test/link/macho/dead_strip/build.zig b/test/link/macho/dead_strip/build.zig index 4131d7baf9..4c739b3d8c 100644 --- a/test/link/macho/dead_strip/build.zig +++ b/test/link/macho/dead_strip/build.zig @@ -13,7 +13,7 @@ pub fn build(b: *std.Build) void { // Without -dead_strip, we expect `iAmUnused` symbol present const exe = createScenario(b, optimize, target, "no-gc"); - const check = exe.checkObject(.macho); + const check = exe.checkObject(); check.checkInSymtab(); check.checkNext("{*} (__TEXT,__text) external _iAmUnused"); @@ -27,7 +27,7 @@ pub fn build(b: *std.Build) void { const exe = createScenario(b, optimize, target, "yes-gc"); exe.link_gc_sections = true; - const check = exe.checkObject(.macho); + const check = exe.checkObject(); check.checkInSymtab(); check.checkNotPresent("{*} (__TEXT,__text) external _iAmUnused"); diff --git a/test/link/macho/dead_strip_dylibs/build.zig b/test/link/macho/dead_strip_dylibs/build.zig index 4a4dda7b7f..b9b97949c1 100644 --- a/test/link/macho/dead_strip_dylibs/build.zig +++ b/test/link/macho/dead_strip_dylibs/build.zig @@ -18,7 +18,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize // Without -dead_strip_dylibs we expect `-la` to include liba.dylib in the final executable const exe = createScenario(b, optimize, "no-dead-strip"); - const check = exe.checkObject(.macho); + const check = exe.checkObject(); check.checkStart("cmd LOAD_DYLIB"); check.checkNext("name {*}Cocoa"); diff --git a/test/link/macho/dylib/build.zig b/test/link/macho/dylib/build.zig index bf4a49f50b..2d775aa23f 100644 --- a/test/link/macho/dylib/build.zig +++ b/test/link/macho/dylib/build.zig @@ -24,7 +24,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize dylib.addCSourceFile("a.c", &.{}); dylib.linkLibC(); - const check_dylib = dylib.checkObject(.macho); + const check_dylib = dylib.checkObject(); check_dylib.checkStart("cmd ID_DYLIB"); check_dylib.checkNext("name @rpath/liba.dylib"); check_dylib.checkNext("timestamp 2"); @@ -44,7 +44,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize exe.addRPathDirectorySource(dylib.getOutputDirectorySource()); exe.linkLibC(); - const check_exe = exe.checkObject(.macho); + const check_exe = exe.checkObject(); check_exe.checkStart("cmd LOAD_DYLIB"); check_exe.checkNext("name @rpath/liba.dylib"); check_exe.checkNext("timestamp 2"); diff --git a/test/link/macho/entry/build.zig b/test/link/macho/entry/build.zig index b2c41ef6dc..e983bc9391 100644 --- a/test/link/macho/entry/build.zig +++ b/test/link/macho/entry/build.zig @@ -22,7 +22,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize exe.linkLibC(); exe.entry_symbol_name = "_non_main"; - const check_exe = exe.checkObject(.macho); + const check_exe = exe.checkObject(); check_exe.checkStart("segname __TEXT"); check_exe.checkNext("vmaddr {vmaddr}"); diff --git a/test/link/macho/headerpad/build.zig b/test/link/macho/headerpad/build.zig index 07d30ee6f2..97161e8e60 100644 --- a/test/link/macho/headerpad/build.zig +++ b/test/link/macho/headerpad/build.zig @@ -20,7 +20,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize const exe = simpleExe(b, optimize); exe.headerpad_max_install_names = true; - const check = exe.checkObject(.macho); + const check = exe.checkObject(); check.checkStart("sectname __text"); check.checkNext("offset {offset}"); @@ -45,7 +45,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize const exe = simpleExe(b, optimize); exe.headerpad_size = 0x10000; - const check = exe.checkObject(.macho); + const check = exe.checkObject(); check.checkStart("sectname __text"); check.checkNext("offset {offset}"); check.checkComputeCompare("offset", .{ .op = .gte, .value = .{ .literal = 0x10000 } }); @@ -62,7 +62,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize exe.headerpad_max_install_names = true; exe.headerpad_size = 0x10000; - const check = exe.checkObject(.macho); + const check = exe.checkObject(); check.checkStart("sectname __text"); check.checkNext("offset {offset}"); check.checkComputeCompare("offset", .{ .op = .gte, .value = .{ .literal = 0x10000 } }); @@ -79,7 +79,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize exe.headerpad_size = 0x1000; exe.headerpad_max_install_names = true; - const check = exe.checkObject(.macho); + const check = exe.checkObject(); check.checkStart("sectname __text"); check.checkNext("offset {offset}"); diff --git a/test/link/macho/linksection/build.zig b/test/link/macho/linksection/build.zig index 37e0b7cbef..b8b3a59f35 100644 --- a/test/link/macho/linksection/build.zig +++ b/test/link/macho/linksection/build.zig @@ -22,7 +22,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize .target = target, }); - const check = obj.checkObject(.macho); + const check = obj.checkObject(); check.checkInSymtab(); check.checkNext("{*} (__DATA,__TestGlobal) external _test_global"); diff --git a/test/link/macho/needed_framework/build.zig b/test/link/macho/needed_framework/build.zig index cd53a5d212..3de96efbc7 100644 --- a/test/link/macho/needed_framework/build.zig +++ b/test/link/macho/needed_framework/build.zig @@ -25,7 +25,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize exe.linkFrameworkNeeded("Cocoa"); exe.dead_strip_dylibs = true; - const check = exe.checkObject(.macho); + const check = exe.checkObject(); check.checkStart("cmd LOAD_DYLIB"); check.checkNext("name {*}Cocoa"); test_step.dependOn(&check.step); diff --git a/test/link/macho/needed_library/build.zig b/test/link/macho/needed_library/build.zig index 6e24a12c65..cb9ea38d4b 100644 --- a/test/link/macho/needed_library/build.zig +++ b/test/link/macho/needed_library/build.zig @@ -38,7 +38,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize exe.addRPathDirectorySource(dylib.getOutputDirectorySource()); exe.dead_strip_dylibs = true; - const check = exe.checkObject(.macho); + const check = exe.checkObject(); check.checkStart("cmd LOAD_DYLIB"); check.checkNext("name @rpath/liba.dylib"); diff --git a/test/link/macho/pagezero/build.zig b/test/link/macho/pagezero/build.zig index e7b002a389..b467df2b20 100644 --- a/test/link/macho/pagezero/build.zig +++ b/test/link/macho/pagezero/build.zig @@ -19,7 +19,7 @@ pub fn build(b: *std.Build) void { exe.linkLibC(); exe.pagezero_size = 0x4000; - const check = exe.checkObject(.macho); + const check = exe.checkObject(); check.checkStart("LC 0"); check.checkNext("segname __PAGEZERO"); check.checkNext("vmaddr 0"); @@ -41,7 +41,7 @@ pub fn build(b: *std.Build) void { exe.linkLibC(); exe.pagezero_size = 0; - const check = exe.checkObject(.macho); + const check = exe.checkObject(); check.checkStart("LC 0"); check.checkNext("segname __TEXT"); check.checkNext("vmaddr 0"); diff --git a/test/link/macho/search_strategy/build.zig b/test/link/macho/search_strategy/build.zig index ae1037c2d7..4777629c8b 100644 --- a/test/link/macho/search_strategy/build.zig +++ b/test/link/macho/search_strategy/build.zig @@ -20,7 +20,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize const exe = createScenario(b, optimize, target, "search_dylibs_first"); exe.search_strategy = .dylibs_first; - const check = exe.checkObject(.macho); + const check = exe.checkObject(); check.checkStart("cmd LOAD_DYLIB"); check.checkNext("name @rpath/libsearch_dylibs_first.dylib"); diff --git a/test/link/macho/stack_size/build.zig b/test/link/macho/stack_size/build.zig index 51efed4c34..c7d308d004 100644 --- a/test/link/macho/stack_size/build.zig +++ b/test/link/macho/stack_size/build.zig @@ -24,7 +24,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize exe.linkLibC(); exe.stack_size = 0x100000000; - const check_exe = exe.checkObject(.macho); + const check_exe = exe.checkObject(); check_exe.checkStart("cmd MAIN"); check_exe.checkNext("stacksize 100000000"); diff --git a/test/link/macho/strict_validation/build.zig b/test/link/macho/strict_validation/build.zig index a95793234b..34a0cd73fc 100644 --- a/test/link/macho/strict_validation/build.zig +++ b/test/link/macho/strict_validation/build.zig @@ -24,7 +24,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize }); exe.linkLibC(); - const check_exe = exe.checkObject(.macho); + const check_exe = exe.checkObject(); check_exe.checkStart("cmd SEGMENT_64"); check_exe.checkNext("segname __LINKEDIT"); diff --git a/test/link/macho/unwind_info/build.zig b/test/link/macho/unwind_info/build.zig index b9bcea8358..4ace2a4e96 100644 --- a/test/link/macho/unwind_info/build.zig +++ b/test/link/macho/unwind_info/build.zig @@ -31,7 +31,7 @@ fn testUnwindInfo( const exe = createScenario(b, optimize, target, name); exe.link_gc_sections = dead_strip; - const check = exe.checkObject(.macho); + const check = exe.checkObject(); check.checkStart("segname __TEXT"); check.checkNext("sectname __gcc_except_tab"); check.checkNext("sectname __unwind_info"); diff --git a/test/link/macho/weak_framework/build.zig b/test/link/macho/weak_framework/build.zig index a4e1d6e5d9..7cc08f5b9d 100644 --- a/test/link/macho/weak_framework/build.zig +++ b/test/link/macho/weak_framework/build.zig @@ -22,7 +22,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize exe.linkLibC(); exe.linkFrameworkWeak("Cocoa"); - const check = exe.checkObject(.macho); + const check = exe.checkObject(); check.checkStart("cmd LOAD_WEAK_DYLIB"); check.checkNext("name {*}Cocoa"); test_step.dependOn(&check.step); diff --git a/test/link/macho/weak_library/build.zig b/test/link/macho/weak_library/build.zig index ac265689e3..b12ec087f5 100644 --- a/test/link/macho/weak_library/build.zig +++ b/test/link/macho/weak_library/build.zig @@ -36,7 +36,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize exe.addLibraryPathDirectorySource(dylib.getOutputDirectorySource()); exe.addRPathDirectorySource(dylib.getOutputDirectorySource()); - const check = exe.checkObject(.macho); + const check = exe.checkObject(); check.checkStart("cmd LOAD_WEAK_DYLIB"); check.checkNext("name @rpath/liba.dylib"); diff --git a/test/link/wasm/archive/build.zig b/test/link/wasm/archive/build.zig index f586187105..6c242a6bf1 100644 --- a/test/link/wasm/archive/build.zig +++ b/test/link/wasm/archive/build.zig @@ -25,7 +25,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize lib.use_lld = false; lib.strip = false; - const check = lib.checkObject(.wasm); + const check = lib.checkObject(); check.checkStart("Section custom"); check.checkNext("name __truncsfhf2"); // Ensure it was imported and resolved diff --git a/test/link/wasm/basic-features/build.zig b/test/link/wasm/basic-features/build.zig index f3f5320e54..be709a698f 100644 --- a/test/link/wasm/basic-features/build.zig +++ b/test/link/wasm/basic-features/build.zig @@ -19,7 +19,7 @@ pub fn build(b: *std.Build) void { lib.use_lld = false; // Verify the result contains the features explicitly set on the target for the library. - const check = lib.checkObject(.wasm); + const check = lib.checkObject(); check.checkStart("name target_features"); check.checkNext("features 1"); check.checkNext("+ atomics"); diff --git a/test/link/wasm/bss/build.zig b/test/link/wasm/bss/build.zig index 8e6b19c7be..bba2e7c602 100644 --- a/test/link/wasm/bss/build.zig +++ b/test/link/wasm/bss/build.zig @@ -19,7 +19,7 @@ pub fn build(b: *std.Build) void { lib.import_memory = true; lib.install(); - const check_lib = lib.checkObject(.wasm); + const check_lib = lib.checkObject(); // since we import memory, make sure it exists with the correct naming check_lib.checkStart("Section import"); diff --git a/test/link/wasm/export-data/build.zig b/test/link/wasm/export-data/build.zig index 0bd10921a2..38b8c3e19e 100644 --- a/test/link/wasm/export-data/build.zig +++ b/test/link/wasm/export-data/build.zig @@ -19,7 +19,7 @@ pub fn build(b: *std.Build) void { lib.export_symbol_names = &.{ "foo", "bar" }; lib.global_base = 0; // put data section at address 0 to make data symbols easier to parse - const check_lib = lib.checkObject(.wasm); + const check_lib = lib.checkObject(); check_lib.checkStart("Section global"); check_lib.checkNext("entries 3"); diff --git a/test/link/wasm/export/build.zig b/test/link/wasm/export/build.zig index 03c4baabe3..794201dbf6 100644 --- a/test/link/wasm/export/build.zig +++ b/test/link/wasm/export/build.zig @@ -42,19 +42,19 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize force_export.use_llvm = false; force_export.use_lld = false; - const check_no_export = no_export.checkObject(.wasm); + const check_no_export = no_export.checkObject(); check_no_export.checkStart("Section export"); check_no_export.checkNext("entries 1"); check_no_export.checkNext("name memory"); check_no_export.checkNext("kind memory"); - const check_dynamic_export = dynamic_export.checkObject(.wasm); + const check_dynamic_export = dynamic_export.checkObject(); check_dynamic_export.checkStart("Section export"); check_dynamic_export.checkNext("entries 2"); check_dynamic_export.checkNext("name foo"); check_dynamic_export.checkNext("kind function"); - const check_force_export = force_export.checkObject(.wasm); + const check_force_export = force_export.checkObject(); check_force_export.checkStart("Section export"); check_force_export.checkNext("entries 2"); check_force_export.checkNext("name foo"); diff --git a/test/link/wasm/extern-mangle/build.zig b/test/link/wasm/extern-mangle/build.zig index b0655cbc1f..6c292acbab 100644 --- a/test/link/wasm/extern-mangle/build.zig +++ b/test/link/wasm/extern-mangle/build.zig @@ -20,7 +20,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize lib.import_symbols = true; // import `a` and `b` lib.rdynamic = true; // export `foo` - const check_lib = lib.checkObject(.wasm); + const check_lib = lib.checkObject(); check_lib.checkStart("Section import"); check_lib.checkNext("entries 2"); // a.hello & b.hello check_lib.checkNext("module a"); diff --git a/test/link/wasm/function-table/build.zig b/test/link/wasm/function-table/build.zig index e1921caee3..4ce6294727 100644 --- a/test/link/wasm/function-table/build.zig +++ b/test/link/wasm/function-table/build.zig @@ -42,9 +42,9 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize regular_table.use_llvm = false; regular_table.use_lld = false; - const check_import = import_table.checkObject(.wasm); - const check_export = export_table.checkObject(.wasm); - const check_regular = regular_table.checkObject(.wasm); + const check_import = import_table.checkObject(); + const check_export = export_table.checkObject(); + const check_regular = regular_table.checkObject(); check_import.checkStart("Section import"); check_import.checkNext("entries 1"); diff --git a/test/link/wasm/infer-features/build.zig b/test/link/wasm/infer-features/build.zig index a7cfa985d5..00fb48651b 100644 --- a/test/link/wasm/infer-features/build.zig +++ b/test/link/wasm/infer-features/build.zig @@ -32,7 +32,7 @@ pub fn build(b: *std.Build) void { lib.addObject(c_obj); // Verify the result contains the features from the C Object file. - const check = lib.checkObject(.wasm); + const check = lib.checkObject(); check.checkStart("name target_features"); check.checkNext("features 7"); check.checkNext("+ atomics"); diff --git a/test/link/wasm/producers/build.zig b/test/link/wasm/producers/build.zig index 41952b0adb..7b7cefd7e0 100644 --- a/test/link/wasm/producers/build.zig +++ b/test/link/wasm/producers/build.zig @@ -27,7 +27,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize const version_fmt = "version " ++ builtin.zig_version_string; - const check_lib = lib.checkObject(.wasm); + const check_lib = lib.checkObject(); check_lib.checkStart("name producers"); check_lib.checkNext("fields 2"); check_lib.checkNext("field_name language"); diff --git a/test/link/wasm/segments/build.zig b/test/link/wasm/segments/build.zig index f8e16eee24..281d8ae32b 100644 --- a/test/link/wasm/segments/build.zig +++ b/test/link/wasm/segments/build.zig @@ -24,7 +24,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize lib.strip = false; lib.install(); - const check_lib = lib.checkObject(.wasm); + const check_lib = lib.checkObject(); check_lib.checkStart("Section data"); check_lib.checkNext("entries 2"); // rodata & data, no bss because we're exporting memory diff --git a/test/link/wasm/stack_pointer/build.zig b/test/link/wasm/stack_pointer/build.zig index 41bdd828f2..794b7d27fb 100644 --- a/test/link/wasm/stack_pointer/build.zig +++ b/test/link/wasm/stack_pointer/build.zig @@ -25,7 +25,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize lib.stack_size = std.wasm.page_size * 2; // set an explicit stack size lib.install(); - const check_lib = lib.checkObject(.wasm); + const check_lib = lib.checkObject(); // ensure global exists and its initial value is equal to explitic stack size check_lib.checkStart("Section global"); diff --git a/test/link/wasm/type/build.zig b/test/link/wasm/type/build.zig index df8cbad021..4a8395645f 100644 --- a/test/link/wasm/type/build.zig +++ b/test/link/wasm/type/build.zig @@ -24,7 +24,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize lib.strip = false; lib.install(); - const check_lib = lib.checkObject(.wasm); + const check_lib = lib.checkObject(); check_lib.checkStart("Section type"); // only 2 entries, although we have 3 functions. // This is to test functions with the same function signature diff --git a/test/stage2/nvptx.zig b/test/nvptx.zig similarity index 76% rename from test/stage2/nvptx.zig rename to test/nvptx.zig index f08aa9fca4..57853a657d 100644 --- a/test/stage2/nvptx.zig +++ b/test/nvptx.zig @@ -1,11 +1,11 @@ const std = @import("std"); -const TestContext = @import("../../src/test.zig").TestContext; +const Cases = @import("src/Cases.zig"); -pub fn addCases(ctx: *TestContext) !void { +pub fn addCases(ctx: *Cases) !void { { - var case = addPtx(ctx, "nvptx: simple addition and subtraction"); + var case = addPtx(ctx, "simple addition and subtraction"); - case.compiles( + case.addCompile( \\fn add(a: i32, b: i32) i32 { \\ return a + b; \\} @@ -20,9 +20,9 @@ pub fn addCases(ctx: *TestContext) !void { } { - var case = addPtx(ctx, "nvptx: read special registers"); + var case = addPtx(ctx, "read special registers"); - case.compiles( + case.addCompile( \\fn threadIdX() u32 { \\ return asm ("mov.u32 \t%[r], %tid.x;" \\ : [r] "=r" (-> u32), @@ -37,9 +37,9 @@ pub fn addCases(ctx: *TestContext) !void { } { - var case = addPtx(ctx, "nvptx: address spaces"); + var case = addPtx(ctx, "address spaces"); - case.compiles( + case.addCompile( \\var x: i32 addrspace(.global) = 0; \\ \\pub export fn increment(out: *i32) callconv(.PtxKernel) void { @@ -50,8 +50,8 @@ pub fn addCases(ctx: *TestContext) !void { } { - var case = addPtx(ctx, "nvptx: reduce in shared mem"); - case.compiles( + var case = addPtx(ctx, "reduce in shared mem"); + case.addCompile( \\fn threadIdX() u32 { \\ return asm ("mov.u32 \t%[r], %tid.x;" \\ : [r] "=r" (-> u32), @@ -88,16 +88,15 @@ const nvptx_target = std.zig.CrossTarget{ }; pub fn addPtx( - ctx: *TestContext, + ctx: *Cases, name: []const u8, -) *TestContext.Case { - ctx.cases.append(TestContext.Case{ +) *Cases.Case { + ctx.cases.append(.{ .name = name, .target = nvptx_target, - .updates = std.ArrayList(TestContext.Update).init(ctx.cases.allocator), + .updates = std.ArrayList(Cases.Update).init(ctx.cases.allocator), .output_mode = .Obj, - .files = std.ArrayList(TestContext.File).init(ctx.cases.allocator), - .deps = std.ArrayList(TestContext.DepModule).init(ctx.cases.allocator), + .deps = std.ArrayList(Cases.DepModule).init(ctx.cases.allocator), .link_libc = false, .backend = .llvm, // Bug in Debug mode diff --git a/test/src/Cases.zig b/test/src/Cases.zig new file mode 100644 index 0000000000..27b0cf6b21 --- /dev/null +++ b/test/src/Cases.zig @@ -0,0 +1,1587 @@ +gpa: Allocator, +arena: Allocator, +cases: std.ArrayList(Case), +incremental_cases: std.ArrayList(IncrementalCase), + +pub const IncrementalCase = struct { + base_path: []const u8, +}; + +pub const Update = struct { + /// The input to the current update. We simulate an incremental update + /// with the file's contents changed to this value each update. + /// + /// This value can change entirely between updates, which would be akin + /// to deleting the source file and creating a new one from scratch; or + /// you can keep it mostly consistent, with small changes, testing the + /// effects of the incremental compilation. + files: std.ArrayList(File), + /// This is a description of what happens with the update, for debugging + /// purposes. + name: []const u8, + case: union(enum) { + /// Check that it compiles with no errors. + Compile: void, + /// Check the main binary output file against an expected set of bytes. + /// This is most useful with, for example, `-ofmt=c`. + CompareObjectFile: []const u8, + /// An error update attempts to compile bad code, and ensures that it + /// fails to compile, and for the expected reasons. + /// A slice containing the expected stderr template, which + /// gets some values substituted. + Error: []const []const u8, + /// An execution update compiles and runs the input, testing the + /// stdout against the expected results + /// This is a slice containing the expected message. + Execution: []const u8, + /// A header update compiles the input with the equivalent of + /// `-femit-h` and tests the produced header against the + /// expected result + Header: []const u8, + }, + + pub fn addSourceFile(update: *Update, name: []const u8, src: [:0]const u8) void { + update.files.append(.{ .path = name, .src = src }) catch @panic("out of memory"); + } +}; + +pub const File = struct { + src: [:0]const u8, + path: []const u8, +}; + +pub const DepModule = struct { + name: []const u8, + path: []const u8, +}; + +pub const Backend = enum { + stage1, + stage2, + llvm, +}; + +/// A `Case` consists of a list of `Update`. The same `Compilation` is used for each +/// update, so each update's source is treated as a single file being +/// updated by the test harness and incrementally compiled. +pub const Case = struct { + /// The name of the test case. This is shown if a test fails, and + /// otherwise ignored. + name: []const u8, + /// The platform the test targets. For non-native platforms, an emulator + /// such as QEMU is required for tests to complete. + target: CrossTarget, + /// In order to be able to run e.g. Execution updates, this must be set + /// to Executable. + output_mode: std.builtin.OutputMode, + optimize_mode: std.builtin.Mode = .Debug, + updates: std.ArrayList(Update), + emit_h: bool = false, + is_test: bool = false, + expect_exact: bool = false, + backend: Backend = .stage2, + link_libc: bool = false, + + deps: std.ArrayList(DepModule), + + pub fn addSourceFile(case: *Case, name: []const u8, src: [:0]const u8) void { + const update = &case.updates.items[case.updates.items.len - 1]; + update.files.append(.{ .path = name, .src = src }) catch @panic("OOM"); + } + + pub fn addDepModule(case: *Case, name: []const u8, path: []const u8) void { + case.deps.append(.{ + .name = name, + .path = path, + }) catch @panic("out of memory"); + } + + /// Adds a subcase in which the module is updated with `src`, compiled, + /// run, and the output is tested against `result`. + pub fn addCompareOutput(self: *Case, src: [:0]const u8, result: []const u8) void { + self.updates.append(.{ + .files = std.ArrayList(File).init(self.updates.allocator), + .name = "update", + .case = .{ .Execution = result }, + }) catch @panic("out of memory"); + addSourceFile(self, "tmp.zig", src); + } + + pub fn addError(self: *Case, src: [:0]const u8, errors: []const []const u8) void { + return self.addErrorNamed("update", src, errors); + } + + /// Adds a subcase in which the module is updated with `src`, which + /// should contain invalid input, and ensures that compilation fails + /// for the expected reasons, given in sequential order in `errors` in + /// the form `:line:column: error: message`. + pub fn addErrorNamed( + self: *Case, + name: []const u8, + src: [:0]const u8, + errors: []const []const u8, + ) void { + assert(errors.len != 0); + self.updates.append(.{ + .files = std.ArrayList(File).init(self.updates.allocator), + .name = name, + .case = .{ .Error = errors }, + }) catch @panic("out of memory"); + addSourceFile(self, "tmp.zig", src); + } + + /// Adds a subcase in which the module is updated with `src`, and + /// asserts that it compiles without issue + pub fn addCompile(self: *Case, src: [:0]const u8) void { + self.updates.append(.{ + .files = std.ArrayList(File).init(self.updates.allocator), + .name = "compile", + .case = .{ .Compile = {} }, + }) catch @panic("out of memory"); + addSourceFile(self, "tmp.zig", src); + } +}; + +pub fn addExe( + ctx: *Cases, + name: []const u8, + target: CrossTarget, +) *Case { + ctx.cases.append(Case{ + .name = name, + .target = target, + .updates = std.ArrayList(Update).init(ctx.cases.allocator), + .output_mode = .Exe, + .deps = std.ArrayList(DepModule).init(ctx.arena), + }) catch @panic("out of memory"); + return &ctx.cases.items[ctx.cases.items.len - 1]; +} + +/// Adds a test case for Zig input, producing an executable +pub fn exe(ctx: *Cases, name: []const u8, target: CrossTarget) *Case { + return ctx.addExe(name, target); +} + +pub fn exeFromCompiledC(ctx: *Cases, name: []const u8, target: CrossTarget) *Case { + var target_adjusted = target; + target_adjusted.ofmt = .c; + ctx.cases.append(Case{ + .name = name, + .target = target_adjusted, + .updates = std.ArrayList(Update).init(ctx.cases.allocator), + .output_mode = .Exe, + .deps = std.ArrayList(DepModule).init(ctx.arena), + .link_libc = true, + }) catch @panic("out of memory"); + return &ctx.cases.items[ctx.cases.items.len - 1]; +} + +/// Adds a test case that uses the LLVM backend to emit an executable. +/// Currently this implies linking libc, because only then we can generate a testable executable. +pub fn exeUsingLlvmBackend(ctx: *Cases, name: []const u8, target: CrossTarget) *Case { + ctx.cases.append(Case{ + .name = name, + .target = target, + .updates = std.ArrayList(Update).init(ctx.cases.allocator), + .output_mode = .Exe, + .deps = std.ArrayList(DepModule).init(ctx.arena), + .backend = .llvm, + .link_libc = true, + }) catch @panic("out of memory"); + return &ctx.cases.items[ctx.cases.items.len - 1]; +} + +pub fn addObj( + ctx: *Cases, + name: []const u8, + target: CrossTarget, +) *Case { + ctx.cases.append(Case{ + .name = name, + .target = target, + .updates = std.ArrayList(Update).init(ctx.cases.allocator), + .output_mode = .Obj, + .deps = std.ArrayList(DepModule).init(ctx.arena), + }) catch @panic("out of memory"); + return &ctx.cases.items[ctx.cases.items.len - 1]; +} + +pub fn addTest( + ctx: *Cases, + name: []const u8, + target: CrossTarget, +) *Case { + ctx.cases.append(Case{ + .name = name, + .target = target, + .updates = std.ArrayList(Update).init(ctx.cases.allocator), + .output_mode = .Exe, + .is_test = true, + .deps = std.ArrayList(DepModule).init(ctx.arena), + }) catch @panic("out of memory"); + return &ctx.cases.items[ctx.cases.items.len - 1]; +} + +/// Adds a test case for Zig input, producing an object file. +pub fn obj(ctx: *Cases, name: []const u8, target: CrossTarget) *Case { + return ctx.addObj(name, target); +} + +/// Adds a test case for ZIR input, producing an object file. +pub fn objZIR(ctx: *Cases, name: []const u8, target: CrossTarget) *Case { + return ctx.addObj(name, target, .ZIR); +} + +/// Adds a test case for Zig or ZIR input, producing C code. +pub fn addC(ctx: *Cases, name: []const u8, target: CrossTarget) *Case { + var target_adjusted = target; + target_adjusted.ofmt = std.Target.ObjectFormat.c; + ctx.cases.append(Case{ + .name = name, + .target = target_adjusted, + .updates = std.ArrayList(Update).init(ctx.cases.allocator), + .output_mode = .Obj, + .deps = std.ArrayList(DepModule).init(ctx.arena), + }) catch @panic("out of memory"); + return &ctx.cases.items[ctx.cases.items.len - 1]; +} + +pub fn addCompareOutput( + ctx: *Cases, + name: []const u8, + src: [:0]const u8, + expected_stdout: []const u8, +) void { + ctx.addExe(name, .{}).addCompareOutput(src, expected_stdout); +} + +/// Adds a test case that compiles the Zig source given in `src`, executes +/// it, runs it, and tests the output against `expected_stdout` +pub fn compareOutput( + ctx: *Cases, + name: []const u8, + src: [:0]const u8, + expected_stdout: []const u8, +) void { + return ctx.addCompareOutput(name, src, expected_stdout); +} + +pub fn addTransform( + ctx: *Cases, + name: []const u8, + target: CrossTarget, + src: [:0]const u8, + result: [:0]const u8, +) void { + ctx.addObj(name, target).addTransform(src, result); +} + +/// Adds a test case that compiles the Zig given in `src` to ZIR and tests +/// the ZIR against `result` +pub fn transform( + ctx: *Cases, + name: []const u8, + target: CrossTarget, + src: [:0]const u8, + result: [:0]const u8, +) void { + ctx.addTransform(name, target, src, result); +} + +pub fn addError( + ctx: *Cases, + name: []const u8, + target: CrossTarget, + src: [:0]const u8, + expected_errors: []const []const u8, +) void { + ctx.addObj(name, target).addError(src, expected_errors); +} + +/// Adds a test case that ensures that the Zig given in `src` fails to +/// compile for the expected reasons, given in sequential order in +/// `expected_errors` in the form `:line:column: error: message`. +pub fn compileError( + ctx: *Cases, + name: []const u8, + target: CrossTarget, + src: [:0]const u8, + expected_errors: []const []const u8, +) void { + ctx.addError(name, target, src, expected_errors); +} + +/// Adds a test case that asserts that the Zig given in `src` compiles +/// without any errors. +pub fn addCompile( + ctx: *Cases, + name: []const u8, + target: CrossTarget, + src: [:0]const u8, +) void { + ctx.addObj(name, target).addCompile(src); +} + +/// Adds a test for each file in the provided directory. +/// Testing strategy (TestStrategy) is inferred automatically from filenames. +/// Recurses nested directories. +/// +/// Each file should include a test manifest as a contiguous block of comments at +/// the end of the file. The first line should be the test type, followed by a set of +/// key-value config values, followed by a blank line, then the expected output. +pub fn addFromDir(ctx: *Cases, dir: std.fs.IterableDir) void { + var current_file: []const u8 = "none"; + ctx.addFromDirInner(dir, ¤t_file) catch |err| { + std.debug.panic("test harness failed to process file '{s}': {s}\n", .{ + current_file, @errorName(err), + }); + }; +} + +fn addFromDirInner( + ctx: *Cases, + iterable_dir: std.fs.IterableDir, + /// This is kept up to date with the currently being processed file so + /// that if any errors occur the caller knows it happened during this file. + current_file: *[]const u8, +) !void { + var it = try iterable_dir.walk(ctx.arena); + var filenames = std.ArrayList([]const u8).init(ctx.arena); + + while (try it.next()) |entry| { + if (entry.kind != .File) continue; + + // Ignore stuff such as .swp files + switch (Compilation.classifyFileExt(entry.basename)) { + .unknown => continue, + else => {}, + } + try filenames.append(try ctx.arena.dupe(u8, entry.path)); + } + + // Sort filenames, so that incremental tests are contiguous and in-order + sortTestFilenames(filenames.items); + + var test_it = TestIterator{ .filenames = filenames.items }; + while (test_it.next()) |maybe_batch| { + const batch = maybe_batch orelse break; + const strategy: TestStrategy = if (batch.len > 1) .incremental else .independent; + const filename = batch[0]; + current_file.* = filename; + if (strategy == .incremental) { + try ctx.incremental_cases.append(.{ .base_path = filename }); + continue; + } + + const max_file_size = 10 * 1024 * 1024; + const src = try iterable_dir.dir.readFileAllocOptions(ctx.arena, filename, max_file_size, null, 1, 0); + + // Parse the manifest + var manifest = try TestManifest.parse(ctx.arena, src); + + const backends = try manifest.getConfigForKeyAlloc(ctx.arena, "backend", Backend); + const targets = try manifest.getConfigForKeyAlloc(ctx.arena, "target", CrossTarget); + const is_test = try manifest.getConfigForKeyAssertSingle("is_test", bool); + const output_mode = try manifest.getConfigForKeyAssertSingle("output_mode", std.builtin.OutputMode); + + var cases = std.ArrayList(usize).init(ctx.arena); + + // Cross-product to get all possible test combinations + for (backends) |backend| { + for (targets) |target| { + const next = ctx.cases.items.len; + try ctx.cases.append(.{ + .name = std.fs.path.stem(filename), + .target = target, + .backend = backend, + .updates = std.ArrayList(Cases.Update).init(ctx.cases.allocator), + .is_test = is_test, + .output_mode = output_mode, + .link_libc = backend == .llvm, + .deps = std.ArrayList(DepModule).init(ctx.cases.allocator), + }); + try cases.append(next); + } + } + + for (cases.items) |case_index| { + const case = &ctx.cases.items[case_index]; + switch (manifest.type) { + .compile => { + case.addCompile(src); + }, + .@"error" => { + const errors = try manifest.trailingAlloc(ctx.arena); + case.addError(src, errors); + }, + .run => { + var output = std.ArrayList(u8).init(ctx.arena); + var trailing_it = manifest.trailing(); + while (trailing_it.next()) |line| { + try output.appendSlice(line); + try output.append('\n'); + } + if (output.items.len > 0) { + try output.resize(output.items.len - 1); + } + case.addCompareOutput(src, try output.toOwnedSlice()); + }, + .cli => @panic("TODO cli tests"), + } + } + } else |err| { + // make sure the current file is set to the file that produced an error + current_file.* = test_it.currentFilename(); + return err; + } +} + +pub fn init(gpa: Allocator, arena: Allocator) Cases { + return .{ + .gpa = gpa, + .cases = std.ArrayList(Case).init(gpa), + .incremental_cases = std.ArrayList(IncrementalCase).init(gpa), + .arena = arena, + }; +} + +pub fn lowerToBuildSteps( + self: *Cases, + b: *std.Build, + parent_step: *std.Build.Step, + opt_test_filter: ?[]const u8, + cases_dir_path: []const u8, + incremental_exe: *std.Build.CompileStep, +) void { + for (self.incremental_cases.items) |incr_case| { + if (opt_test_filter) |test_filter| { + if (std.mem.indexOf(u8, incr_case.base_path, test_filter) == null) continue; + } + const case_base_path_with_dir = std.fs.path.join(b.allocator, &.{ + cases_dir_path, incr_case.base_path, + }) catch @panic("OOM"); + const run = b.addRunArtifact(incremental_exe); + run.setName(incr_case.base_path); + run.addArgs(&.{ + case_base_path_with_dir, + b.zig_exe, + }); + run.expectStdOutEqual(""); + parent_step.dependOn(&run.step); + } + + for (self.cases.items) |case| { + if (case.updates.items.len != 1) continue; // handled with incremental_cases above + assert(case.updates.items.len == 1); + const update = case.updates.items[0]; + + if (opt_test_filter) |test_filter| { + if (std.mem.indexOf(u8, case.name, test_filter) == null) continue; + } + + const writefiles = b.addWriteFiles(); + for (update.files.items) |file| { + writefiles.add(file.path, file.src); + } + const root_source_file = writefiles.getFileSource(update.files.items[0].path).?; + + const artifact = switch (case.output_mode) { + .Obj => b.addObject(.{ + .root_source_file = root_source_file, + .name = case.name, + .target = case.target, + .optimize = case.optimize_mode, + }), + .Lib => b.addStaticLibrary(.{ + .root_source_file = root_source_file, + .name = case.name, + .target = case.target, + .optimize = case.optimize_mode, + }), + .Exe => if (case.is_test) b.addTest(.{ + .root_source_file = root_source_file, + .name = case.name, + .target = case.target, + .optimize = case.optimize_mode, + }) else b.addExecutable(.{ + .root_source_file = root_source_file, + .name = case.name, + .target = case.target, + .optimize = case.optimize_mode, + }), + }; + + if (case.link_libc) artifact.linkLibC(); + + switch (case.backend) { + .stage1 => continue, + .stage2 => { + artifact.use_llvm = false; + artifact.use_lld = false; + }, + .llvm => { + artifact.use_llvm = true; + }, + } + + for (case.deps.items) |dep| { + artifact.addAnonymousModule(dep.name, .{ + .source_file = writefiles.getFileSource(dep.path).?, + }); + } + + switch (update.case) { + .Compile => { + parent_step.dependOn(&artifact.step); + }, + .CompareObjectFile => |expected_output| { + const check = b.addCheckFile(artifact.getOutputSource(), .{ + .expected_exact = expected_output, + }); + + parent_step.dependOn(&check.step); + }, + .Error => |expected_msgs| { + assert(expected_msgs.len != 0); + artifact.expect_errors = expected_msgs; + parent_step.dependOn(&artifact.step); + }, + .Execution => |expected_stdout| { + if (case.is_test) { + parent_step.dependOn(&artifact.step); + } else { + const run = b.addRunArtifact(artifact); + run.skip_foreign_checks = true; + run.expectStdOutEqual(expected_stdout); + + parent_step.dependOn(&run.step); + } + }, + .Header => @panic("TODO"), + } + } +} + +/// Sort test filenames in-place, so that incremental test cases ("foo.0.zig", +/// "foo.1.zig", etc.) are contiguous and appear in numerical order. +fn sortTestFilenames(filenames: [][]const u8) void { + const Context = struct { + pub fn lessThan(_: @This(), a: []const u8, b: []const u8) bool { + const a_parts = getTestFileNameParts(a); + const b_parts = getTestFileNameParts(b); + + // Sort ".X." based on "" and "" first + return switch (std.mem.order(u8, a_parts.base_name, b_parts.base_name)) { + .lt => true, + .gt => false, + .eq => switch (std.mem.order(u8, a_parts.file_ext, b_parts.file_ext)) { + .lt => true, + .gt => false, + .eq => { + // a and b differ only in their ".X" part + + // Sort "." before any ".X." + if (a_parts.test_index) |a_index| { + if (b_parts.test_index) |b_index| { + // Make sure that incremental tests appear in linear order + return a_index < b_index; + } else { + return false; + } + } else { + return b_parts.test_index != null; + } + }, + }, + }; + } + }; + std.sort.sort([]const u8, filenames, Context{}, Context.lessThan); +} + +/// Iterates a set of filenames extracting batches that are either incremental +/// ("foo.0.zig", "foo.1.zig", etc.) or independent ("foo.zig", "bar.zig", etc.). +/// Assumes filenames are sorted. +const TestIterator = struct { + start: usize = 0, + end: usize = 0, + filenames: []const []const u8, + /// reset on each call to `next` + index: usize = 0, + + const Error = error{InvalidIncrementalTestIndex}; + + fn next(it: *TestIterator) Error!?[]const []const u8 { + try it.nextInner(); + if (it.start == it.end) return null; + return it.filenames[it.start..it.end]; + } + + fn nextInner(it: *TestIterator) Error!void { + it.start = it.end; + if (it.end == it.filenames.len) return; + if (it.end + 1 == it.filenames.len) { + it.end += 1; + return; + } + + const remaining = it.filenames[it.end..]; + it.index = 0; + while (it.index < remaining.len - 1) : (it.index += 1) { + // First, check if this file is part of an incremental update sequence + // Split filename into ".." + const prev_parts = getTestFileNameParts(remaining[it.index]); + const new_parts = getTestFileNameParts(remaining[it.index + 1]); + + // If base_name and file_ext match, these files are in the same test sequence + // and the new one should be the incremented version of the previous test + if (std.mem.eql(u8, prev_parts.base_name, new_parts.base_name) and + std.mem.eql(u8, prev_parts.file_ext, new_parts.file_ext)) + { + // This is "foo.X.zig" followed by "foo.Y.zig". Make sure that X = Y + 1 + if (prev_parts.test_index == null) + return error.InvalidIncrementalTestIndex; + if (new_parts.test_index == null) + return error.InvalidIncrementalTestIndex; + if (new_parts.test_index.? != prev_parts.test_index.? + 1) + return error.InvalidIncrementalTestIndex; + } else { + // This is not the same test sequence, so the new file must be the first file + // in a new sequence ("*.0.zig") or an independent test file ("*.zig") + if (new_parts.test_index != null and new_parts.test_index.? != 0) + return error.InvalidIncrementalTestIndex; + + it.end += it.index + 1; + break; + } + } else { + it.end += remaining.len; + } + } + + /// In the event of an `error.InvalidIncrementalTestIndex`, this function can + /// be used to find the current filename that was being processed. + /// Asserts the iterator hasn't reached the end. + fn currentFilename(it: TestIterator) []const u8 { + assert(it.end != it.filenames.len); + const remaining = it.filenames[it.end..]; + return remaining[it.index + 1]; + } +}; + +/// For a filename in the format ".X." or ".", returns +/// "", "" and X parsed as a decimal number. If X is not present, or +/// cannot be parsed as a decimal number, it is treated as part of +fn getTestFileNameParts(name: []const u8) struct { + base_name: []const u8, + file_ext: []const u8, + test_index: ?usize, +} { + const file_ext = std.fs.path.extension(name); + const trimmed = name[0 .. name.len - file_ext.len]; // Trim off "." + const maybe_index = std.fs.path.extension(trimmed); // Extract ".X" + + // Attempt to parse index + const index: ?usize = if (maybe_index.len > 0) + std.fmt.parseInt(usize, maybe_index[1..], 10) catch null + else + null; + + // Adjust "" extent based on parsing success + const base_name_end = trimmed.len - if (index != null) maybe_index.len else 0; + return .{ + .base_name = name[0..base_name_end], + .file_ext = if (file_ext.len > 0) file_ext[1..] else file_ext, + .test_index = index, + }; +} + +const TestStrategy = enum { + /// Execute tests as independent compilations, unless they are explicitly + /// incremental ("foo.0.zig", "foo.1.zig", etc.) + independent, + /// Execute all tests as incremental updates to a single compilation. Explicitly + /// incremental tests ("foo.0.zig", "foo.1.zig", etc.) still execute in order + incremental, +}; + +/// Default config values for known test manifest key-value pairings. +/// Currently handled defaults are: +/// * backend +/// * target +/// * output_mode +/// * is_test +const TestManifestConfigDefaults = struct { + /// Asserts if the key doesn't exist - yep, it's an oversight alright. + fn get(@"type": TestManifest.Type, key: []const u8) []const u8 { + if (std.mem.eql(u8, key, "backend")) { + return "stage2"; + } else if (std.mem.eql(u8, key, "target")) { + if (@"type" == .@"error") { + return "native"; + } + comptime { + var defaults: []const u8 = ""; + // TODO should we only return "mainstream" targets by default here? + // TODO we should also specify ABIs explicitly as the backends are + // getting more and more complete + // Linux + inline for (&[_][]const u8{ "x86_64", "arm", "aarch64" }) |arch| { + defaults = defaults ++ arch ++ "-linux" ++ ","; + } + // macOS + inline for (&[_][]const u8{ "x86_64", "aarch64" }) |arch| { + defaults = defaults ++ arch ++ "-macos" ++ ","; + } + // Windows + defaults = defaults ++ "x86_64-windows" ++ ","; + // Wasm + defaults = defaults ++ "wasm32-wasi"; + return defaults; + } + } else if (std.mem.eql(u8, key, "output_mode")) { + return switch (@"type") { + .@"error" => "Obj", + .run => "Exe", + .compile => "Obj", + .cli => @panic("TODO test harness for CLI tests"), + }; + } else if (std.mem.eql(u8, key, "is_test")) { + return "0"; + } else unreachable; + } +}; + +/// Manifest syntax example: +/// (see https://github.com/ziglang/zig/issues/11288) +/// +/// error +/// backend=stage1,stage2 +/// output_mode=exe +/// +/// :3:19: error: foo +/// +/// run +/// target=x86_64-linux,aarch64-macos +/// +/// I am expected stdout! Hello! +/// +/// cli +/// +/// build test +const TestManifest = struct { + type: Type, + config_map: std.StringHashMap([]const u8), + trailing_bytes: []const u8 = "", + + const Type = enum { + @"error", + run, + cli, + compile, + }; + + const TrailingIterator = struct { + inner: std.mem.TokenIterator(u8), + + fn next(self: *TrailingIterator) ?[]const u8 { + const next_inner = self.inner.next() orelse return null; + return std.mem.trim(u8, next_inner[2..], " \t"); + } + }; + + fn ConfigValueIterator(comptime T: type) type { + return struct { + inner: std.mem.SplitIterator(u8), + + fn next(self: *@This()) !?T { + const next_raw = self.inner.next() orelse return null; + const parseFn = getDefaultParser(T); + return try parseFn(next_raw); + } + }; + } + + fn parse(arena: Allocator, bytes: []const u8) !TestManifest { + // The manifest is the last contiguous block of comments in the file + // We scan for the beginning by searching backward for the first non-empty line that does not start with "//" + var start: ?usize = null; + var end: usize = bytes.len; + if (bytes.len > 0) { + var cursor: usize = bytes.len - 1; + while (true) { + // Move to beginning of line + while (cursor > 0 and bytes[cursor - 1] != '\n') cursor -= 1; + + if (std.mem.startsWith(u8, bytes[cursor..], "//")) { + start = cursor; // Contiguous comment line, include in manifest + } else { + if (start != null) break; // Encountered non-comment line, end of manifest + + // We ignore all-whitespace lines following the comment block, but anything else + // means that there is no manifest present. + if (std.mem.trim(u8, bytes[cursor..end], " \r\n\t").len == 0) { + end = cursor; + } else break; // If it's not whitespace, there is no manifest + } + + // Move to previous line + if (cursor != 0) cursor -= 1 else break; + } + } + + const actual_start = start orelse return error.MissingTestManifest; + const manifest_bytes = bytes[actual_start..end]; + + var it = std.mem.tokenize(u8, manifest_bytes, "\r\n"); + + // First line is the test type + const tt: Type = blk: { + const line = it.next() orelse return error.MissingTestCaseType; + const raw = std.mem.trim(u8, line[2..], " \t"); + if (std.mem.eql(u8, raw, "error")) { + break :blk .@"error"; + } else if (std.mem.eql(u8, raw, "run")) { + break :blk .run; + } else if (std.mem.eql(u8, raw, "cli")) { + break :blk .cli; + } else if (std.mem.eql(u8, raw, "compile")) { + break :blk .compile; + } else { + std.log.warn("unknown test case type requested: {s}", .{raw}); + return error.UnknownTestCaseType; + } + }; + + var manifest: TestManifest = .{ + .type = tt, + .config_map = std.StringHashMap([]const u8).init(arena), + }; + + // Any subsequent line until a blank comment line is key=value(s) pair + while (it.next()) |line| { + const trimmed = std.mem.trim(u8, line[2..], " \t"); + if (trimmed.len == 0) break; + + // Parse key=value(s) + var kv_it = std.mem.split(u8, trimmed, "="); + const key = kv_it.first(); + try manifest.config_map.putNoClobber(key, kv_it.next() orelse return error.MissingValuesForConfig); + } + + // Finally, trailing is expected output + manifest.trailing_bytes = manifest_bytes[it.index..]; + + return manifest; + } + + fn getConfigForKey( + self: TestManifest, + key: []const u8, + comptime T: type, + ) ConfigValueIterator(T) { + const bytes = self.config_map.get(key) orelse TestManifestConfigDefaults.get(self.type, key); + return ConfigValueIterator(T){ + .inner = std.mem.split(u8, bytes, ","), + }; + } + + fn getConfigForKeyAlloc( + self: TestManifest, + allocator: Allocator, + key: []const u8, + comptime T: type, + ) ![]const T { + var out = std.ArrayList(T).init(allocator); + defer out.deinit(); + var it = self.getConfigForKey(key, T); + while (try it.next()) |item| { + try out.append(item); + } + return try out.toOwnedSlice(); + } + + fn getConfigForKeyAssertSingle(self: TestManifest, key: []const u8, comptime T: type) !T { + var it = self.getConfigForKey(key, T); + const res = (try it.next()) orelse unreachable; + assert((try it.next()) == null); + return res; + } + + fn trailing(self: TestManifest) TrailingIterator { + return .{ + .inner = std.mem.tokenize(u8, self.trailing_bytes, "\r\n"), + }; + } + + fn trailingAlloc(self: TestManifest, allocator: Allocator) error{OutOfMemory}![]const []const u8 { + var out = std.ArrayList([]const u8).init(allocator); + defer out.deinit(); + var it = self.trailing(); + while (it.next()) |line| { + try out.append(line); + } + return try out.toOwnedSlice(); + } + + fn ParseFn(comptime T: type) type { + return fn ([]const u8) anyerror!T; + } + + fn getDefaultParser(comptime T: type) ParseFn(T) { + if (T == CrossTarget) return struct { + fn parse(str: []const u8) anyerror!T { + var opts = CrossTarget.ParseOptions{ + .arch_os_abi = str, + }; + return try CrossTarget.parse(opts); + } + }.parse; + + switch (@typeInfo(T)) { + .Int => return struct { + fn parse(str: []const u8) anyerror!T { + return try std.fmt.parseInt(T, str, 0); + } + }.parse, + .Bool => return struct { + fn parse(str: []const u8) anyerror!T { + const as_int = try std.fmt.parseInt(u1, str, 0); + return as_int > 0; + } + }.parse, + .Enum => return struct { + fn parse(str: []const u8) anyerror!T { + return std.meta.stringToEnum(T, str) orelse { + std.log.err("unknown enum variant for {s}: {s}", .{ @typeName(T), str }); + return error.UnknownEnumVariant; + }; + } + }.parse, + .Struct => @compileError("no default parser for " ++ @typeName(T)), + else => @compileError("no default parser for " ++ @typeName(T)), + } + } +}; + +const Cases = @This(); +const builtin = @import("builtin"); +const std = @import("std"); +const assert = std.debug.assert; +const Allocator = std.mem.Allocator; +const CrossTarget = std.zig.CrossTarget; +const Compilation = @import("../../src/Compilation.zig"); +const zig_h = @import("../../src/link.zig").File.C.zig_h; +const introspect = @import("../../src/introspect.zig"); +const ThreadPool = std.Thread.Pool; +const WaitGroup = std.Thread.WaitGroup; +const build_options = @import("build_options"); +const Package = @import("../../src/Package.zig"); + +pub const std_options = struct { + pub const log_level: std.log.Level = .err; +}; + +var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{ + .stack_trace_frames = build_options.mem_leak_frames, +}){}; + +// TODO: instead of embedding the compiler in this process, spawn the compiler +// as a sub-process and communicate the updates using the compiler protocol. +pub fn main() !void { + const use_gpa = build_options.force_gpa or !builtin.link_libc; + const gpa = gpa: { + if (use_gpa) { + break :gpa general_purpose_allocator.allocator(); + } + // We would prefer to use raw libc allocator here, but cannot + // use it if it won't support the alignment we need. + if (@alignOf(std.c.max_align_t) < @alignOf(i128)) { + break :gpa std.heap.c_allocator; + } + break :gpa std.heap.raw_c_allocator; + }; + + var single_threaded_arena = std.heap.ArenaAllocator.init(gpa); + defer single_threaded_arena.deinit(); + + var thread_safe_arena: std.heap.ThreadSafeAllocator = .{ + .child_allocator = single_threaded_arena.allocator(), + }; + const arena = thread_safe_arena.allocator(); + + const args = try std.process.argsAlloc(arena); + const case_file_path = args[1]; + const zig_exe_path = args[2]; + + var filenames = std.ArrayList([]const u8).init(arena); + + const case_dirname = std.fs.path.dirname(case_file_path).?; + var iterable_dir = try std.fs.cwd().openIterableDir(case_dirname, .{}); + defer iterable_dir.close(); + + if (std.mem.endsWith(u8, case_file_path, ".0.zig")) { + const stem = case_file_path[case_dirname.len + 1 .. case_file_path.len - "0.zig".len]; + var it = iterable_dir.iterate(); + while (try it.next()) |entry| { + if (entry.kind != .File) continue; + if (!std.mem.startsWith(u8, entry.name, stem)) continue; + try filenames.append(try std.fs.path.join(arena, &.{ case_dirname, entry.name })); + } + } else { + try filenames.append(case_file_path); + } + + if (filenames.items.len == 0) { + std.debug.print("failed to find the input source file(s) from '{s}'\n", .{ + case_file_path, + }); + std.process.exit(1); + } + + // Sort filenames, so that incremental tests are contiguous and in-order + sortTestFilenames(filenames.items); + + var ctx = Cases.init(gpa, arena); + + var test_it = TestIterator{ .filenames = filenames.items }; + while (test_it.next()) |maybe_batch| { + const batch = maybe_batch orelse break; + const strategy: TestStrategy = if (batch.len > 1) .incremental else .independent; + var cases = std.ArrayList(usize).init(arena); + + for (batch) |filename| { + const max_file_size = 10 * 1024 * 1024; + const src = try iterable_dir.dir.readFileAllocOptions(arena, filename, max_file_size, null, 1, 0); + + // Parse the manifest + var manifest = try TestManifest.parse(arena, src); + + if (cases.items.len == 0) { + const backends = try manifest.getConfigForKeyAlloc(arena, "backend", Backend); + const targets = try manifest.getConfigForKeyAlloc(arena, "target", CrossTarget); + const is_test = try manifest.getConfigForKeyAssertSingle("is_test", bool); + const output_mode = try manifest.getConfigForKeyAssertSingle("output_mode", std.builtin.OutputMode); + + // Cross-product to get all possible test combinations + for (backends) |backend| { + for (targets) |target| { + const next = ctx.cases.items.len; + try ctx.cases.append(.{ + .name = std.fs.path.stem(filename), + .target = target, + .backend = backend, + .updates = std.ArrayList(Cases.Update).init(ctx.cases.allocator), + .is_test = is_test, + .output_mode = output_mode, + .link_libc = backend == .llvm, + .deps = std.ArrayList(DepModule).init(ctx.cases.allocator), + }); + try cases.append(next); + } + } + } + + for (cases.items) |case_index| { + const case = &ctx.cases.items[case_index]; + switch (manifest.type) { + .compile => { + case.addCompile(src); + }, + .@"error" => { + const errors = try manifest.trailingAlloc(arena); + switch (strategy) { + .independent => { + case.addError(src, errors); + }, + .incremental => { + case.addErrorNamed("update", src, errors); + }, + } + }, + .run => { + var output = std.ArrayList(u8).init(arena); + var trailing_it = manifest.trailing(); + while (trailing_it.next()) |line| { + try output.appendSlice(line); + try output.append('\n'); + } + if (output.items.len > 0) { + try output.resize(output.items.len - 1); + } + case.addCompareOutput(src, try output.toOwnedSlice()); + }, + .cli => @panic("TODO cli tests"), + } + } + } + } else |err| { + return err; + } + + return runCases(&ctx, zig_exe_path); +} + +fn runCases(self: *Cases, zig_exe_path: []const u8) !void { + const host = try std.zig.system.NativeTargetInfo.detect(.{}); + + var progress = std.Progress{}; + const root_node = progress.start("compiler", self.cases.items.len); + progress.terminal = null; + defer root_node.end(); + + var zig_lib_directory = try introspect.findZigLibDir(self.gpa); + defer zig_lib_directory.handle.close(); + defer self.gpa.free(zig_lib_directory.path.?); + + var aux_thread_pool: ThreadPool = undefined; + try aux_thread_pool.init(.{ .allocator = self.gpa }); + defer aux_thread_pool.deinit(); + + // Use the same global cache dir for all the tests, such that we for example don't have to + // rebuild musl libc for every case (when LLVM backend is enabled). + var global_tmp = std.testing.tmpDir(.{}); + defer global_tmp.cleanup(); + + var cache_dir = try global_tmp.dir.makeOpenPath("zig-cache", .{}); + defer cache_dir.close(); + const tmp_dir_path = try std.fs.path.join(self.gpa, &[_][]const u8{ ".", "zig-cache", "tmp", &global_tmp.sub_path }); + defer self.gpa.free(tmp_dir_path); + + const global_cache_directory: Compilation.Directory = .{ + .handle = cache_dir, + .path = try std.fs.path.join(self.gpa, &[_][]const u8{ tmp_dir_path, "zig-cache" }), + }; + defer self.gpa.free(global_cache_directory.path.?); + + { + for (self.cases.items) |*case| { + if (build_options.skip_non_native) { + if (case.target.getCpuArch() != builtin.cpu.arch) + continue; + if (case.target.getObjectFormat() != builtin.object_format) + continue; + } + + // Skip tests that require LLVM backend when it is not available + if (!build_options.have_llvm and case.backend == .llvm) + continue; + + assert(case.backend != .stage1); + + if (build_options.test_filter) |test_filter| { + if (std.mem.indexOf(u8, case.name, test_filter) == null) continue; + } + + var prg_node = root_node.start(case.name, case.updates.items.len); + prg_node.activate(); + defer prg_node.end(); + + try runOneCase( + self.gpa, + &prg_node, + case.*, + zig_lib_directory, + zig_exe_path, + &aux_thread_pool, + global_cache_directory, + host, + ); + } + } +} + +fn runOneCase( + allocator: Allocator, + root_node: *std.Progress.Node, + case: Case, + zig_lib_directory: Compilation.Directory, + zig_exe_path: []const u8, + thread_pool: *ThreadPool, + global_cache_directory: Compilation.Directory, + host: std.zig.system.NativeTargetInfo, +) !void { + const tmp_src_path = "tmp.zig"; + const enable_rosetta = build_options.enable_rosetta; + const enable_qemu = build_options.enable_qemu; + const enable_wine = build_options.enable_wine; + const enable_wasmtime = build_options.enable_wasmtime; + const enable_darling = build_options.enable_darling; + const glibc_runtimes_dir: ?[]const u8 = build_options.glibc_runtimes_dir; + + const target_info = try std.zig.system.NativeTargetInfo.detect(case.target); + const target = target_info.target; + + var arena_allocator = std.heap.ArenaAllocator.init(allocator); + defer arena_allocator.deinit(); + const arena = arena_allocator.allocator(); + + var tmp = std.testing.tmpDir(.{}); + defer tmp.cleanup(); + + var cache_dir = try tmp.dir.makeOpenPath("zig-cache", .{}); + defer cache_dir.close(); + + const tmp_dir_path = try std.fs.path.join( + arena, + &[_][]const u8{ ".", "zig-cache", "tmp", &tmp.sub_path }, + ); + const local_cache_path = try std.fs.path.join( + arena, + &[_][]const u8{ tmp_dir_path, "zig-cache" }, + ); + + const zig_cache_directory: Compilation.Directory = .{ + .handle = cache_dir, + .path = local_cache_path, + }; + + var main_pkg: Package = .{ + .root_src_directory = .{ .path = tmp_dir_path, .handle = tmp.dir }, + .root_src_path = tmp_src_path, + }; + defer { + var it = main_pkg.table.iterator(); + while (it.next()) |kv| { + allocator.free(kv.key_ptr.*); + kv.value_ptr.*.destroy(allocator); + } + main_pkg.table.deinit(allocator); + } + + for (case.deps.items) |dep| { + var pkg = try Package.create( + allocator, + tmp_dir_path, + dep.path, + ); + errdefer pkg.destroy(allocator); + try main_pkg.add(allocator, dep.name, pkg); + } + + const bin_name = try std.zig.binNameAlloc(arena, .{ + .root_name = "test_case", + .target = target, + .output_mode = case.output_mode, + }); + + const emit_directory: Compilation.Directory = .{ + .path = tmp_dir_path, + .handle = tmp.dir, + }; + const emit_bin: Compilation.EmitLoc = .{ + .directory = emit_directory, + .basename = bin_name, + }; + const emit_h: ?Compilation.EmitLoc = if (case.emit_h) .{ + .directory = emit_directory, + .basename = "test_case.h", + } else null; + const use_llvm: bool = switch (case.backend) { + .llvm => true, + else => false, + }; + const comp = try Compilation.create(allocator, .{ + .local_cache_directory = zig_cache_directory, + .global_cache_directory = global_cache_directory, + .zig_lib_directory = zig_lib_directory, + .thread_pool = thread_pool, + .root_name = "test_case", + .target = target, + // TODO: support tests for object file building, and library builds + // and linking. This will require a rework to support multi-file + // tests. + .output_mode = case.output_mode, + .is_test = case.is_test, + .optimize_mode = case.optimize_mode, + .emit_bin = emit_bin, + .emit_h = emit_h, + .main_pkg = &main_pkg, + .keep_source_files_loaded = true, + .is_native_os = case.target.isNativeOs(), + .is_native_abi = case.target.isNativeAbi(), + .dynamic_linker = target_info.dynamic_linker.get(), + .link_libc = case.link_libc, + .use_llvm = use_llvm, + .self_exe_path = zig_exe_path, + // TODO instead of turning off color, pass in a std.Progress.Node + .color = .off, + .reference_trace = 0, + // TODO: force self-hosted linkers with stage2 backend to avoid LLD creeping in + // until the auto-select mechanism deems them worthy + .use_lld = switch (case.backend) { + .stage2 => false, + else => null, + }, + }); + defer comp.destroy(); + + update: for (case.updates.items, 0..) |update, update_index| { + var update_node = root_node.start(update.name, 3); + update_node.activate(); + defer update_node.end(); + + var sync_node = update_node.start("write", 0); + sync_node.activate(); + for (update.files.items) |file| { + try tmp.dir.writeFile(file.path, file.src); + } + sync_node.end(); + + var module_node = update_node.start("parse/analysis/codegen", 0); + module_node.activate(); + try comp.makeBinFileWritable(); + try comp.update(&module_node); + module_node.end(); + + if (update.case != .Error) { + var all_errors = try comp.getAllErrorsAlloc(); + defer all_errors.deinit(allocator); + if (all_errors.errorMessageCount() > 0) { + all_errors.renderToStdErr(.{ + .ttyconf = std.debug.detectTTYConfig(std.io.getStdErr()), + }); + // TODO print generated C code + return error.UnexpectedCompileErrors; + } + } + + switch (update.case) { + .Header => |expected_output| { + var file = try tmp.dir.openFile("test_case.h", .{ .mode = .read_only }); + defer file.close(); + const out = try file.reader().readAllAlloc(arena, 5 * 1024 * 1024); + + try std.testing.expectEqualStrings(expected_output, out); + }, + .CompareObjectFile => |expected_output| { + var file = try tmp.dir.openFile(bin_name, .{ .mode = .read_only }); + defer file.close(); + const out = try file.reader().readAllAlloc(arena, 5 * 1024 * 1024); + + try std.testing.expectEqualStrings(expected_output, out); + }, + .Compile => {}, + .Error => |expected_errors| { + var test_node = update_node.start("assert", 0); + test_node.activate(); + defer test_node.end(); + + var error_bundle = try comp.getAllErrorsAlloc(); + defer error_bundle.deinit(allocator); + + if (error_bundle.errorMessageCount() == 0) { + return error.ExpectedCompilationErrors; + } + + var actual_stderr = std.ArrayList(u8).init(arena); + try error_bundle.renderToWriter(.{ + .ttyconf = .no_color, + .include_reference_trace = false, + .include_source_line = false, + }, actual_stderr.writer()); + + // Render the expected lines into a string that we can compare verbatim. + var expected_generated = std.ArrayList(u8).init(arena); + + var actual_line_it = std.mem.split(u8, actual_stderr.items, "\n"); + for (expected_errors) |expect_line| { + const actual_line = actual_line_it.next() orelse { + try expected_generated.appendSlice(expect_line); + try expected_generated.append('\n'); + continue; + }; + if (std.mem.endsWith(u8, actual_line, expect_line)) { + try expected_generated.appendSlice(actual_line); + try expected_generated.append('\n'); + continue; + } + if (std.mem.startsWith(u8, expect_line, ":?:?: ")) { + if (std.mem.endsWith(u8, actual_line, expect_line[":?:?: ".len..])) { + try expected_generated.appendSlice(actual_line); + try expected_generated.append('\n'); + continue; + } + } + try expected_generated.appendSlice(expect_line); + try expected_generated.append('\n'); + } + + try std.testing.expectEqualStrings(expected_generated.items, actual_stderr.items); + }, + .Execution => |expected_stdout| { + if (!std.process.can_spawn) { + std.debug.print("Unable to spawn child processes on {s}, skipping test.\n", .{@tagName(builtin.os.tag)}); + continue :update; // Pass test. + } + + update_node.setEstimatedTotalItems(4); + + var argv = std.ArrayList([]const u8).init(allocator); + defer argv.deinit(); + + var exec_result = x: { + var exec_node = update_node.start("execute", 0); + exec_node.activate(); + defer exec_node.end(); + + // We go out of our way here to use the unique temporary directory name in + // the exe_path so that it makes its way into the cache hash, avoiding + // cache collisions from multiple threads doing `zig run` at the same time + // on the same test_case.c input filename. + const ss = std.fs.path.sep_str; + const exe_path = try std.fmt.allocPrint( + arena, + ".." ++ ss ++ "{s}" ++ ss ++ "{s}", + .{ &tmp.sub_path, bin_name }, + ); + if (case.target.ofmt != null and case.target.ofmt.? == .c) { + if (host.getExternalExecutor(target_info, .{ .link_libc = true }) != .native) { + // We wouldn't be able to run the compiled C code. + continue :update; // Pass test. + } + try argv.appendSlice(&[_][]const u8{ + zig_exe_path, + "run", + "-cflags", + "-std=c99", + "-pedantic", + "-Werror", + "-Wno-incompatible-library-redeclaration", // https://github.com/ziglang/zig/issues/875 + "--", + "-lc", + exe_path, + }); + if (zig_lib_directory.path) |p| { + try argv.appendSlice(&.{ "-I", p }); + } + } else switch (host.getExternalExecutor(target_info, .{ .link_libc = case.link_libc })) { + .native => { + if (case.backend == .stage2 and case.target.getCpuArch() == .arm) { + // https://github.com/ziglang/zig/issues/13623 + continue :update; // Pass test. + } + try argv.append(exe_path); + }, + .bad_dl, .bad_os_or_cpu => continue :update, // Pass test. + + .rosetta => if (enable_rosetta) { + try argv.append(exe_path); + } else { + continue :update; // Rosetta not available, pass test. + }, + + .qemu => |qemu_bin_name| if (enable_qemu) { + const need_cross_glibc = target.isGnuLibC() and case.link_libc; + const glibc_dir_arg: ?[]const u8 = if (need_cross_glibc) + glibc_runtimes_dir orelse continue :update // glibc dir not available; pass test + else + null; + try argv.append(qemu_bin_name); + if (glibc_dir_arg) |dir| { + const linux_triple = try target.linuxTriple(arena); + const full_dir = try std.fs.path.join(arena, &[_][]const u8{ + dir, + linux_triple, + }); + + try argv.append("-L"); + try argv.append(full_dir); + } + try argv.append(exe_path); + } else { + continue :update; // QEMU not available; pass test. + }, + + .wine => |wine_bin_name| if (enable_wine) { + try argv.append(wine_bin_name); + try argv.append(exe_path); + } else { + continue :update; // Wine not available; pass test. + }, + + .wasmtime => |wasmtime_bin_name| if (enable_wasmtime) { + try argv.append(wasmtime_bin_name); + try argv.append("--dir=."); + try argv.append(exe_path); + } else { + continue :update; // wasmtime not available; pass test. + }, + + .darling => |darling_bin_name| if (enable_darling) { + try argv.append(darling_bin_name); + // Since we use relative to cwd here, we invoke darling with + // "shell" subcommand. + try argv.append("shell"); + try argv.append(exe_path); + } else { + continue :update; // Darling not available; pass test. + }, + } + + try comp.makeBinFileExecutable(); + + while (true) { + break :x std.ChildProcess.exec(.{ + .allocator = allocator, + .argv = argv.items, + .cwd_dir = tmp.dir, + .cwd = tmp_dir_path, + }) catch |err| switch (err) { + error.FileBusy => { + // There is a fundamental design flaw in Unix systems with how + // ETXTBSY interacts with fork+exec. + // https://github.com/golang/go/issues/22315 + // https://bugs.openjdk.org/browse/JDK-8068370 + // Unfortunately, this could be a real error, but we can't + // tell the difference here. + continue; + }, + else => { + std.debug.print("\n{s}.{d} The following command failed with {s}:\n", .{ + case.name, update_index, @errorName(err), + }); + dumpArgs(argv.items); + return error.ChildProcessExecution; + }, + }; + } + }; + var test_node = update_node.start("test", 0); + test_node.activate(); + defer test_node.end(); + defer allocator.free(exec_result.stdout); + defer allocator.free(exec_result.stderr); + switch (exec_result.term) { + .Exited => |code| { + if (code != 0) { + std.debug.print("\n{s}\n{s}: execution exited with code {d}:\n", .{ + exec_result.stderr, case.name, code, + }); + dumpArgs(argv.items); + return error.ChildProcessExecution; + } + }, + else => { + std.debug.print("\n{s}\n{s}: execution crashed:\n", .{ + exec_result.stderr, case.name, + }); + dumpArgs(argv.items); + return error.ChildProcessExecution; + }, + } + try std.testing.expectEqualStrings(expected_stdout, exec_result.stdout); + // We allow stderr to have garbage in it because wasmtime prints a + // warning about --invoke even though we don't pass it. + //std.testing.expectEqualStrings("", exec_result.stderr); + }, + } + } +} + +fn dumpArgs(argv: []const []const u8) void { + for (argv) |arg| { + std.debug.print("{s} ", .{arg}); + } + std.debug.print("\n", .{}); +} diff --git a/test/tests.zig b/test/tests.zig index 2cd06b18b9..c07b669376 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -1055,3 +1055,30 @@ pub fn addCAbiTests(b: *std.Build, skip_non_native: bool, skip_release: bool) *S } return step; } + +pub fn addCases( + b: *std.Build, + parent_step: *Step, + opt_test_filter: ?[]const u8, + check_case_exe: *std.Build.CompileStep, +) !void { + const arena = b.allocator; + const gpa = b.allocator; + + var cases = @import("src/Cases.zig").init(gpa, arena); + + var dir = try b.build_root.handle.openIterableDir("test/cases", .{}); + defer dir.close(); + + cases.addFromDir(dir); + try @import("cases.zig").addCases(&cases); + + const cases_dir_path = try b.build_root.join(b.allocator, &.{ "test", "cases" }); + cases.lowerToBuildSteps( + b, + parent_step, + opt_test_filter, + cases_dir_path, + check_case_exe, + ); +} From 59f5df3af9c31ffef33862c15bcbce6b4227ff1f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 9 Mar 2023 21:16:53 -0700 Subject: [PATCH 232/294] std.Build: use Cache hash helper for package prefix dirs Previously this code used SipHash(1, 3) directly; now that we have the cache system available in the build system, borrow the same implementation as is being used everywhere else. --- lib/std/Build.zig | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/lib/std/Build.zig b/lib/std/Build.zig index c091079b38..58cead7e7a 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -384,20 +384,17 @@ fn applyArgs(b: *Build, args: anytype) !void { }, } } - const Hasher = std.crypto.auth.siphash.SipHash128(1, 3); + + // Create an installation directory local to this package. This will be used when + // dependant packages require a standard prefix, such as include directories for C headers. + var hash = b.cache.hash; // Random bytes to make unique. Refresh this with new random bytes when // implementation is modified in a non-backwards-compatible way. - var hash = Hasher.init("ZaEsvQ5ClaA2IdH9"); - hash.update(b.dep_prefix); + hash.add(@as(u32, 0xd8cb0055)); + hash.addBytes(b.dep_prefix); // TODO additionally update the hash with `args`. - - var digest: [16]u8 = undefined; - hash.final(&digest); - var hash_basename: [digest.len * 2]u8 = undefined; - _ = std.fmt.bufPrint(&hash_basename, "{s}", .{std.fmt.fmtSliceHexLower(&digest)}) catch - unreachable; - - const install_prefix = try b.cache_root.join(b.allocator, &.{ "i", &hash_basename }); + const digest = hash.final(); + const install_prefix = try b.cache_root.join(b.allocator, &.{ "i", &digest }); b.resolveInstallPrefix(install_prefix, .{}); } From a333bb91ffcf63fbe377ddc82fd7f1633e269430 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 9 Mar 2023 21:17:56 -0700 Subject: [PATCH 233/294] zig objcopy: support the compiler protocol This commit extracts out server code into src/Server.zig and uses it both in the main CLI as well as `zig objcopy`. std.Build.ObjCopyStep now adds `--listen=-` to the CLI for `zig objcopy` and observes the protocol for progress and other kinds of integrations. This fixes the last two test failures of this branch when I run `zig build test` locally. --- CMakeLists.txt | 1 + lib/std/Build/ObjCopyStep.zig | 2 + src/Server.zig | 113 ++++++++++++++++++++++++++++++++++ src/main.zig | 111 ++++++--------------------------- src/objcopy.zig | 47 ++++++++++++-- 5 files changed, 177 insertions(+), 97 deletions(-) create mode 100644 src/Server.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index c77c66add4..7ff8249f4d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -623,6 +623,7 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/src/print_targets.zig" "${CMAKE_SOURCE_DIR}/src/print_zir.zig" "${CMAKE_SOURCE_DIR}/src/register_manager.zig" + "${CMAKE_SOURCE_DIR}/src/Server.zig" "${CMAKE_SOURCE_DIR}/src/target.zig" "${CMAKE_SOURCE_DIR}/src/tracy.zig" "${CMAKE_SOURCE_DIR}/src/translate_c.zig" diff --git a/lib/std/Build/ObjCopyStep.zig b/lib/std/Build/ObjCopyStep.zig index 5f675ed383..608c56591f 100644 --- a/lib/std/Build/ObjCopyStep.zig +++ b/lib/std/Build/ObjCopyStep.zig @@ -113,6 +113,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { }; try argv.appendSlice(&.{ full_src_path, full_dest_path }); + + try argv.append("--listen=-"); _ = try step.evalZigProcess(argv.items, prog_node); self.output_file.path = full_dest_path; diff --git a/src/Server.zig b/src/Server.zig new file mode 100644 index 0000000000..a25dc93857 --- /dev/null +++ b/src/Server.zig @@ -0,0 +1,113 @@ +in: std.fs.File, +out: std.fs.File, +receive_fifo: std.fifo.LinearFifo(u8, .Dynamic), + +pub const Options = struct { + gpa: Allocator, + in: std.fs.File, + out: std.fs.File, +}; + +pub fn init(options: Options) !Server { + var s: Server = .{ + .in = options.in, + .out = options.out, + .receive_fifo = std.fifo.LinearFifo(u8, .Dynamic).init(options.gpa), + }; + try s.serveStringMessage(.zig_version, build_options.version); + return s; +} + +pub fn deinit(s: *Server) void { + s.receive_fifo.deinit(); + s.* = undefined; +} + +pub fn receiveMessage(s: *Server) !InMessage.Header { + const Header = InMessage.Header; + const fifo = &s.receive_fifo; + + while (true) { + const buf = fifo.readableSlice(0); + assert(fifo.readableLength() == buf.len); + if (buf.len >= @sizeOf(Header)) { + const header = @ptrCast(*align(1) const Header, buf[0..@sizeOf(Header)]); + if (header.bytes_len != 0) + return error.InvalidClientMessage; + const result = header.*; + fifo.discard(@sizeOf(Header)); + return result; + } + + const write_buffer = try fifo.writableWithSize(256); + const amt = try s.in.read(write_buffer); + fifo.update(amt); + } +} + +pub fn serveStringMessage(s: *Server, tag: OutMessage.Tag, msg: []const u8) !void { + return s.serveMessage(.{ + .tag = tag, + .bytes_len = @intCast(u32, msg.len), + }, &.{msg}); +} + +pub fn serveMessage( + s: *const Server, + header: OutMessage.Header, + bufs: []const []const u8, +) !void { + var iovecs: [10]std.os.iovec_const = undefined; + iovecs[0] = .{ + .iov_base = @ptrCast([*]const u8, &header), + .iov_len = @sizeOf(OutMessage.Header), + }; + for (bufs, iovecs[1 .. bufs.len + 1]) |buf, *iovec| { + iovec.* = .{ + .iov_base = buf.ptr, + .iov_len = buf.len, + }; + } + try s.out.writevAll(iovecs[0 .. bufs.len + 1]); +} + +pub fn serveEmitBinPath( + s: *Server, + fs_path: []const u8, + header: std.zig.Server.Message.EmitBinPath, +) !void { + try s.serveMessage(.{ + .tag = .emit_bin_path, + .bytes_len = @intCast(u32, fs_path.len + @sizeOf(std.zig.Server.Message.EmitBinPath)), + }, &.{ + std.mem.asBytes(&header), + fs_path, + }); +} + +pub fn serveErrorBundle(s: *Server, error_bundle: std.zig.ErrorBundle) !void { + const eb_hdr: std.zig.Server.Message.ErrorBundle = .{ + .extra_len = @intCast(u32, error_bundle.extra.len), + .string_bytes_len = @intCast(u32, error_bundle.string_bytes.len), + }; + const bytes_len = @sizeOf(std.zig.Server.Message.ErrorBundle) + + 4 * error_bundle.extra.len + error_bundle.string_bytes.len; + try s.serveMessage(.{ + .tag = .error_bundle, + .bytes_len = @intCast(u32, bytes_len), + }, &.{ + std.mem.asBytes(&eb_hdr), + // TODO: implement @ptrCast between slices changing the length + std.mem.sliceAsBytes(error_bundle.extra), + error_bundle.string_bytes, + }); +} + +const OutMessage = std.zig.Server.Message; +const InMessage = std.zig.Client.Message; + +const Server = @This(); +const std = @import("std"); +const build_options = @import("build_options"); +const Allocator = std.mem.Allocator; +const assert = std.debug.assert; diff --git a/src/main.zig b/src/main.zig index 03d746af0c..e0283143d0 100644 --- a/src/main.zig +++ b/src/main.zig @@ -26,6 +26,7 @@ const target_util = @import("target.zig"); const crash_report = @import("crash_report.zig"); const Module = @import("Module.zig"); const AstGen = @import("AstGen.zig"); +const Server = @import("Server.zig"); pub const std_options = struct { pub const wasiCwd = wasi_cwd; @@ -3540,11 +3541,14 @@ fn serve( ) !void { const gpa = comp.gpa; - try serveStringMessage(out, .zig_version, build_options.version); + var server = try Server.init(.{ + .gpa = gpa, + .in = in, + .out = out, + }); + defer server.deinit(); var child_pid: ?std.ChildProcess.Id = null; - var receive_fifo = std.fifo.LinearFifo(u8, .Dynamic).init(gpa); - defer receive_fifo.deinit(); var progress: std.Progress = .{ .terminal = null, @@ -3564,7 +3568,7 @@ fn serve( main_progress_node.context = &progress; while (true) { - const hdr = try receiveMessage(in, &receive_fifo); + const hdr = try server.receiveMessage(); switch (hdr.tag) { .exit => { @@ -3580,7 +3584,7 @@ fn serve( const arena = arena_instance.allocator(); var output: TranslateCOutput = undefined; try cmdTranslateC(comp, arena, &output); - try serveEmitBinPath(out, output.path, .{ + try server.serveEmitBinPath(output.path, .{ .flags = .{ .cache_hit = output.cache_hit }, }); continue; @@ -3594,7 +3598,7 @@ fn serve( var reset: std.Thread.ResetEvent = .{}; var progress_thread = try std.Thread.spawn(.{}, progressThread, .{ - &progress, out, &reset, + &progress, &server, &reset, }); defer { reset.set(); @@ -3605,7 +3609,7 @@ fn serve( } try comp.makeBinFileExecutable(); - try serveUpdateResults(out, comp); + try serveUpdateResults(&server, comp); }, .run => { if (child_pid != null) { @@ -3632,14 +3636,14 @@ fn serve( assert(main_progress_node.recently_updated_child == null); if (child_pid) |pid| { try comp.hotCodeSwap(main_progress_node, pid); - try serveUpdateResults(out, comp); + try serveUpdateResults(&server, comp); } else { if (comp.bin_file.options.output_mode == .Exe) { try comp.makeBinFileWritable(); } try comp.update(main_progress_node); try comp.makeBinFileExecutable(); - try serveUpdateResults(out, comp); + try serveUpdateResults(&server, comp); child_pid = try runOrTestHotSwap( comp, @@ -3659,7 +3663,7 @@ fn serve( } } -fn progressThread(progress: *std.Progress, out: fs.File, reset: *std.Thread.ResetEvent) void { +fn progressThread(progress: *std.Progress, server: *const Server, reset: *std.Thread.ResetEvent) void { while (true) { if (reset.timedWait(500 * std.time.ns_per_ms)) |_| { // The Compilation update has completed. @@ -3705,7 +3709,7 @@ fn progressThread(progress: *std.Progress, out: fs.File, reset: *std.Thread.Rese const progress_string = buf.slice(); - serveMessage(out, .{ + server.serveMessage(.{ .tag = .progress, .bytes_len = @intCast(u32, progress_string.len), }, &.{ @@ -3716,100 +3720,21 @@ fn progressThread(progress: *std.Progress, out: fs.File, reset: *std.Thread.Rese } } -fn serveMessage( - out: fs.File, - header: std.zig.Server.Message.Header, - bufs: []const []const u8, -) !void { - var iovecs: [10]std.os.iovec_const = undefined; - iovecs[0] = .{ - .iov_base = @ptrCast([*]const u8, &header), - .iov_len = @sizeOf(std.zig.Server.Message.Header), - }; - for (bufs, iovecs[1 .. bufs.len + 1]) |buf, *iovec| { - iovec.* = .{ - .iov_base = buf.ptr, - .iov_len = buf.len, - }; - } - try out.writevAll(iovecs[0 .. bufs.len + 1]); -} - -fn serveErrorBundle(out: fs.File, error_bundle: std.zig.ErrorBundle) !void { - const eb_hdr: std.zig.Server.Message.ErrorBundle = .{ - .extra_len = @intCast(u32, error_bundle.extra.len), - .string_bytes_len = @intCast(u32, error_bundle.string_bytes.len), - }; - const bytes_len = @sizeOf(std.zig.Server.Message.ErrorBundle) + - 4 * error_bundle.extra.len + error_bundle.string_bytes.len; - try serveMessage(out, .{ - .tag = .error_bundle, - .bytes_len = @intCast(u32, bytes_len), - }, &.{ - std.mem.asBytes(&eb_hdr), - // TODO: implement @ptrCast between slices changing the length - std.mem.sliceAsBytes(error_bundle.extra), - error_bundle.string_bytes, - }); -} - -fn serveUpdateResults(out: fs.File, comp: *Compilation) !void { +fn serveUpdateResults(s: *Server, comp: *Compilation) !void { const gpa = comp.gpa; var error_bundle = try comp.getAllErrorsAlloc(); defer error_bundle.deinit(gpa); if (error_bundle.errorMessageCount() > 0) { - try serveErrorBundle(out, error_bundle); + try s.serveErrorBundle(error_bundle); } else if (comp.bin_file.options.emit) |emit| { const full_path = try emit.directory.join(gpa, &.{emit.sub_path}); defer gpa.free(full_path); - try serveEmitBinPath(out, full_path, .{ + try s.serveEmitBinPath(full_path, .{ .flags = .{ .cache_hit = comp.last_update_was_cache_hit }, }); } } -fn serveEmitBinPath( - out: fs.File, - fs_path: []const u8, - header: std.zig.Server.Message.EmitBinPath, -) !void { - try serveMessage(out, .{ - .tag = .emit_bin_path, - .bytes_len = @intCast(u32, fs_path.len + @sizeOf(std.zig.Server.Message.EmitBinPath)), - }, &.{ - std.mem.asBytes(&header), - fs_path, - }); -} - -fn serveStringMessage(out: fs.File, tag: std.zig.Server.Message.Tag, s: []const u8) !void { - try serveMessage(out, .{ - .tag = tag, - .bytes_len = @intCast(u32, s.len), - }, &.{s}); -} - -fn receiveMessage(in: fs.File, fifo: *std.fifo.LinearFifo(u8, .Dynamic)) !std.zig.Client.Message.Header { - const Header = std.zig.Client.Message.Header; - - while (true) { - const buf = fifo.readableSlice(0); - assert(fifo.readableLength() == buf.len); - if (buf.len >= @sizeOf(Header)) { - const header = @ptrCast(*align(1) const Header, buf[0..@sizeOf(Header)]); - if (header.bytes_len != 0) - return error.InvalidClientMessage; - const result = header.*; - fifo.discard(@sizeOf(Header)); - return result; - } - - const write_buffer = try fifo.writableWithSize(256); - const amt = try in.read(write_buffer); - fifo.update(amt); - } -} - const ModuleDepIterator = struct { split: mem.SplitIterator(u8), diff --git a/src/objcopy.zig b/src/objcopy.zig index 31e3d60d0d..e821a94b59 100644 --- a/src/objcopy.zig +++ b/src/objcopy.zig @@ -4,22 +4,25 @@ const fs = std.fs; const elf = std.elf; const Allocator = std.mem.Allocator; const File = std.fs.File; +const assert = std.debug.assert; + const main = @import("main.zig"); const fatal = main.fatal; const cleanExit = main.cleanExit; +const Server = @import("Server.zig"); pub fn cmdObjCopy( gpa: Allocator, arena: Allocator, args: []const []const u8, ) !void { - _ = gpa; var i: usize = 0; var opt_out_fmt: ?std.Target.ObjectFormat = null; var opt_input: ?[]const u8 = null; var opt_output: ?[]const u8 = null; var only_section: ?[]const u8 = null; var pad_to: ?u64 = null; + var listen = false; while (i < args.len) : (i += 1) { const arg = args[i]; if (!mem.startsWith(u8, arg, "-")) { @@ -54,6 +57,8 @@ pub fn cmdObjCopy( i += 1; if (i >= args.len) fatal("expected another argument after '{s}'", .{arg}); only_section = args[i]; + } else if (mem.eql(u8, arg, "--listen=-")) { + listen = true; } else if (mem.startsWith(u8, arg, "--only-section=")) { only_section = arg["--output-target=".len..]; } else if (mem.eql(u8, arg, "--pad-to")) { @@ -102,10 +107,44 @@ pub fn cmdObjCopy( .only_section = only_section, .pad_to = pad_to, }); - return cleanExit(); }, else => fatal("unsupported output object format: {s}", .{@tagName(out_fmt)}), } + + if (listen) { + var server = try Server.init(.{ + .gpa = gpa, + .in = std.io.getStdIn(), + .out = std.io.getStdOut(), + }); + defer server.deinit(); + + var seen_update = false; + while (true) { + const hdr = try server.receiveMessage(); + switch (hdr.tag) { + .exit => { + return cleanExit(); + }, + .update => { + if (seen_update) { + std.debug.print("zig objcopy only supports 1 update for now\n", .{}); + std.process.exit(1); + } + seen_update = true; + + try server.serveEmitBinPath(output, .{ + .flags = .{ .cache_hit = false }, + }); + }, + else => { + std.debug.print("unsupported message: {s}", .{@tagName(hdr.tag)}); + std.process.exit(1); + }, + } + } + } + return cleanExit(); } const usage = @@ -417,7 +456,7 @@ const HexWriter = struct { } fn Address(address: u32) Record { - std.debug.assert(address > 0xFFFF); + assert(address > 0xFFFF); const segment = @intCast(u16, address / 0x10000); if (address > 0xFFFFF) { return Record{ @@ -460,7 +499,7 @@ const HexWriter = struct { const BUFSIZE = 1 + (1 + 2 + 1 + MAX_PAYLOAD_LEN + 1) * 2 + linesep.len; var outbuf: [BUFSIZE]u8 = undefined; const payload_bytes = self.getPayloadBytes(); - std.debug.assert(payload_bytes.len <= MAX_PAYLOAD_LEN); + assert(payload_bytes.len <= MAX_PAYLOAD_LEN); const line = try std.fmt.bufPrint(&outbuf, ":{0X:0>2}{1X:0>4}{2X:0>2}{3s}{4X:0>2}" ++ linesep, .{ @intCast(u8, payload_bytes.len), From f31aeb0010c950430a6a388f6933498cd8e29cbc Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 9 Mar 2023 22:02:37 -0700 Subject: [PATCH 234/294] std.Build.WriteFileStep: add missing step dependencies --- lib/std/Build/WriteFileStep.zig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/std/Build/WriteFileStep.zig b/lib/std/Build/WriteFileStep.zig index e6ceb4777c..9b033e5ae2 100644 --- a/lib/std/Build/WriteFileStep.zig +++ b/lib/std/Build/WriteFileStep.zig @@ -83,6 +83,7 @@ pub fn addCopyFile(wf: *WriteFileStep, source: std.Build.FileSource, sub_path: [ wf.files.append(gpa, file) catch @panic("OOM"); wf.maybeUpdateName(); + source.addStepDependencies(&wf.step); } /// A path relative to the package root. @@ -97,6 +98,7 @@ pub fn addCopyFileToSource(wf: *WriteFileStep, source: std.Build.FileSource, sub .contents = .{ .copy = source }, .sub_path = sub_path, }) catch @panic("OOM"); + source.addStepDependencies(&wf.step); } /// A path relative to the package root. From 2c491d734ee82947b2a77c08c1c5328e7a5fe771 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 9 Mar 2023 22:02:53 -0700 Subject: [PATCH 235/294] docgen: don't print progress in dumb terminals --- doc/docgen.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/docgen.zig b/doc/docgen.zig index fae513f8c3..67163ca427 100644 --- a/doc/docgen.zig +++ b/doc/docgen.zig @@ -1270,7 +1270,7 @@ fn genHtml( zig_exe: []const u8, do_code_tests: bool, ) !void { - var progress = Progress{}; + var progress = Progress{ .dont_print_on_dumb = true }; const root_node = progress.start("Generating docgen examples", toc.nodes.len); defer root_node.end(); From f829f848ddc390e6f14e8da3eee452bd7ead5a3a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 9 Mar 2023 22:03:36 -0700 Subject: [PATCH 236/294] std.Build.InstallFileStep: add missing step dependencies in the creation function, which had to change from init() to create(). --- lib/std/Build.zig | 11 ++--------- lib/std/Build/InstallFileStep.zig | 11 ++++++++--- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/lib/std/Build.zig b/lib/std/Build.zig index 58cead7e7a..5f74af3db6 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -1220,12 +1220,7 @@ pub fn addInstallFileWithDir( install_dir: InstallDir, dest_rel_path: []const u8, ) *InstallFileStep { - if (dest_rel_path.len == 0) { - panic("dest_rel_path must be non-empty", .{}); - } - const install_step = self.allocator.create(InstallFileStep) catch @panic("OOM"); - install_step.* = InstallFileStep.init(self, source.dupe(self), install_dir, dest_rel_path); - return install_step; + return InstallFileStep.create(self, source.dupe(self), install_dir, dest_rel_path); } pub fn addInstallDirectory(self: *Build, options: InstallDirectoryOptions) *InstallDirStep { @@ -1685,9 +1680,7 @@ pub const InstallDir = union(enum) { /// Duplicates the install directory including the path if set to custom. pub fn dupe(self: InstallDir, builder: *Build) InstallDir { if (self == .custom) { - // Written with this temporary to avoid RLS problems - const duped_path = builder.dupe(self.custom); - return .{ .custom = duped_path }; + return .{ .custom = builder.dupe(self.custom) }; } else { return self; } diff --git a/lib/std/Build/InstallFileStep.zig b/lib/std/Build/InstallFileStep.zig index a77fa10b43..011ad48208 100644 --- a/lib/std/Build/InstallFileStep.zig +++ b/lib/std/Build/InstallFileStep.zig @@ -3,6 +3,7 @@ const Step = std.Build.Step; const FileSource = std.Build.FileSource; const InstallDir = std.Build.InstallDir; const InstallFileStep = @This(); +const assert = std.debug.assert; pub const base_id = .install_file; @@ -14,14 +15,16 @@ dest_rel_path: []const u8, /// package but is being installed by another. dest_builder: *std.Build, -pub fn init( +pub fn create( owner: *std.Build, source: FileSource, dir: InstallDir, dest_rel_path: []const u8, -) InstallFileStep { +) *InstallFileStep { + assert(dest_rel_path.len != 0); owner.pushInstalledFile(dir, dest_rel_path); - return InstallFileStep{ + const self = owner.allocator.create(InstallFileStep) catch @panic("OOM"); + self.* = .{ .step = Step.init(.{ .id = base_id, .name = owner.fmt("install {s} to {s}", .{ source.getDisplayName(), dest_rel_path }), @@ -33,6 +36,8 @@ pub fn init( .dest_rel_path = owner.dupePath(dest_rel_path), .dest_builder = owner, }; + source.addStepDependencies(&self.step); + return self; } fn make(step: *Step, prog_node: *std.Progress.Node) !void { From 857296a9f4bc56c3bf710b11a0868c4cf15b6ff3 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 9 Mar 2023 22:04:09 -0700 Subject: [PATCH 237/294] build.zig: update docgen to modern build system API it still writes the output to zig-cache/langref.html but now it does that explicitly as a legacy step with the intention of having that removed in the future. It also outputs the langref to the install prefix. --- build.zig | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/build.zig b/build.zig index 9ba2b1acef..8cf25cd6fd 100644 --- a/build.zig +++ b/build.zig @@ -40,19 +40,22 @@ pub fn build(b: *std.Build) !void { }); docgen_exe.single_threaded = single_threaded; - const langref_out_path = try b.cache_root.join(b.allocator, &.{"langref.html"}); - const docgen_cmd = docgen_exe.run(); - docgen_cmd.addArgs(&[_][]const u8{ - "--zig", - b.zig_exe, - "doc" ++ fs.path.sep_str ++ "langref.html.in", - langref_out_path, - }); - docgen_cmd.step.dependOn(&docgen_exe.step); + const docgen_cmd = b.addRunArtifact(docgen_exe); + docgen_cmd.addArgs(&.{ "--zig", b.zig_exe }); + docgen_cmd.addFileSourceArg(.{ .path = "doc/langref.html.in" }); + const langref_file = docgen_cmd.addOutputFileArg("langref.html"); + const install_langref = b.addInstallFileWithDir(langref_file, .prefix, "langref.html"); + b.getInstallStep().dependOn(&install_langref.step); const docs_step = b.step("docs", "Build documentation"); docs_step.dependOn(&docgen_cmd.step); + // This is for legacy reasons, to be removed after our CI scripts are upgraded to use + // the file from the install prefix instead. + const legacy_write_to_cache = b.addWriteFiles(); + legacy_write_to_cache.addCopyFileToSource(langref_file, "zig-cache/langref.html"); + docs_step.dependOn(&legacy_write_to_cache.step); + const check_case_exe = b.addExecutable(.{ .name = "check-case", .root_source_file = .{ .path = "test/src/Cases.zig" }, From 28bda2eab04e217fe4ba801a9710b2225d141940 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 9 Mar 2023 22:12:36 -0700 Subject: [PATCH 238/294] make -Dno-lib also skip docgen --- build.zig | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/build.zig b/build.zig index 8cf25cd6fd..21586963bd 100644 --- a/build.zig +++ b/build.zig @@ -31,6 +31,11 @@ pub fn build(b: *std.Build) !void { const use_zig_libcxx = b.option(bool, "use-zig-libcxx", "If libc++ is needed, use zig's bundled version, don't try to integrate with the system") orelse false; const test_step = b.step("test", "Run all the tests"); + const deprecated_skip_install_lib_files = b.option(bool, "skip-install-lib-files", "deprecated. see no-lib") orelse false; + if (deprecated_skip_install_lib_files) { + std.log.warn("-Dskip-install-lib-files is deprecated in favor of -Dno-lib", .{}); + } + const skip_install_lib_files = b.option(bool, "no-lib", "skip copying of lib/ files and langref to installation prefix. Useful for development") orelse deprecated_skip_install_lib_files; const docgen_exe = b.addExecutable(.{ .name = "docgen", @@ -45,7 +50,9 @@ pub fn build(b: *std.Build) !void { docgen_cmd.addFileSourceArg(.{ .path = "doc/langref.html.in" }); const langref_file = docgen_cmd.addOutputFileArg("langref.html"); const install_langref = b.addInstallFileWithDir(langref_file, .prefix, "langref.html"); - b.getInstallStep().dependOn(&install_langref.step); + if (!skip_install_lib_files) { + b.getInstallStep().dependOn(&install_langref.step); + } const docs_step = b.step("docs", "Build documentation"); docs_step.dependOn(&docgen_cmd.step); @@ -76,11 +83,6 @@ pub fn build(b: *std.Build) !void { const skip_stage1 = b.option(bool, "skip-stage1", "Main test suite skips stage1 compile error tests") orelse false; const skip_run_translated_c = b.option(bool, "skip-run-translated-c", "Main test suite skips run-translated-c tests") orelse false; const skip_stage2_tests = b.option(bool, "skip-stage2-tests", "Main test suite skips self-hosted compiler tests") orelse false; - const deprecated_skip_install_lib_files = b.option(bool, "skip-install-lib-files", "deprecated. see no-lib") orelse false; - if (deprecated_skip_install_lib_files) { - std.log.warn("-Dskip-install-lib-files is deprecated in favor of -Dno-lib", .{}); - } - const skip_install_lib_files = b.option(bool, "no-lib", "skip copying of lib/ files to installation prefix. Useful for development") orelse deprecated_skip_install_lib_files; const only_install_lib_files = b.option(bool, "lib-files-only", "Only install library files") orelse false; From bf73620cbdb4b9ae6088d44bb88daa4a7d84ed70 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 9 Mar 2023 22:53:59 -0800 Subject: [PATCH 239/294] build runner: communicate TTY conf to child procs via env vars --- lib/build_runner.zig | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/build_runner.zig b/lib/build_runner.zig index 8e0ebb4558..e1c3768048 100644 --- a/lib/build_runner.zig +++ b/lib/build_runner.zig @@ -272,6 +272,11 @@ pub fn main() !void { const stderr = std.io.getStdErr(); const ttyconf = get_tty_conf(color, stderr); + switch (ttyconf) { + .no_color => try builder.env_map.put("NO_COLOR", "1"), + .escape_codes => try builder.env_map.put("ZIG_DEBUG_COLOR", "1"), + .windows_api => {}, + } var progress: std.Progress = .{ .dont_print_on_dumb = true }; const main_progress_node = progress.start("", 0); From 097bcca069c5c7abdf77f321182eb4a0c54b3a93 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 9 Mar 2023 22:54:32 -0800 Subject: [PATCH 240/294] build.zig: fix how test-cases marked is_test=1 are handled --- test/src/Cases.zig | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/src/Cases.zig b/test/src/Cases.zig index 27b0cf6b21..5007939b14 100644 --- a/test/src/Cases.zig +++ b/test/src/Cases.zig @@ -485,7 +485,12 @@ pub fn lowerToBuildSteps( } const root_source_file = writefiles.getFileSource(update.files.items[0].path).?; - const artifact = switch (case.output_mode) { + const artifact = if (case.is_test) b.addTest(.{ + .root_source_file = root_source_file, + .name = case.name, + .target = case.target, + .optimize = case.optimize_mode, + }) else switch (case.output_mode) { .Obj => b.addObject(.{ .root_source_file = root_source_file, .name = case.name, @@ -498,12 +503,7 @@ pub fn lowerToBuildSteps( .target = case.target, .optimize = case.optimize_mode, }), - .Exe => if (case.is_test) b.addTest(.{ - .root_source_file = root_source_file, - .name = case.name, - .target = case.target, - .optimize = case.optimize_mode, - }) else b.addExecutable(.{ + .Exe => b.addExecutable(.{ .root_source_file = root_source_file, .name = case.name, .target = case.target, From 7d5bce56e16cd7661b79046d573427972bbb6cf5 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 9 Mar 2023 23:56:12 -0700 Subject: [PATCH 241/294] build runner: print to stderr in dumb terminals Terminal progress is suppressed and instead there is an explicit handling of printing to stderr, one line per step make() function call. The output looks very similar to Ninja. A future commit should add a -q to quiet the output. --- lib/build_runner.zig | 43 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/lib/build_runner.zig b/lib/build_runner.zig index e1c3768048..a1b88f8c0b 100644 --- a/lib/build_runner.zig +++ b/lib/build_runner.zig @@ -278,8 +278,12 @@ pub fn main() !void { .windows_api => {}, } - var progress: std.Progress = .{ .dont_print_on_dumb = true }; + var progress: std.Progress = .{}; const main_progress_node = progress.start("", 0); + if (ttyconf == .no_color) { + progress.timer = null; + progress.terminal = null; + } builder.debug_log_scopes = debug_log_scopes.items; builder.resolveInstallPrefix(install_prefix, dir_list); @@ -302,6 +306,9 @@ pub fn main() !void { .enable_summary = enable_summary, .ttyconf = ttyconf, .stderr = stderr, + + .step_index = 0, + .step_count = undefined, }; if (run.max_rss == 0) { @@ -332,6 +339,9 @@ const Run = struct { enable_summary: ?bool, ttyconf: std.debug.TTY.Config, stderr: std.fs.File, + + step_count: usize, + step_index: usize, }; fn runStepNames( @@ -395,7 +405,8 @@ fn runStepNames( { defer parent_prog_node.end(); - var step_prog = parent_prog_node.start("run steps", step_stack.count()); + run.step_count = step_stack.count(); + var step_prog = parent_prog_node.start("run steps", run.step_count); defer step_prog.end(); var wait_group: std.Thread.WaitGroup = .{}; @@ -739,10 +750,27 @@ fn workerMakeOneStep( sub_prog_node.activate(); defer sub_prog_node.end(); - // I suspect we will want to pass `b` to make() in a future modification. - // For example, CompileStep does some sus things with modifying the saved - // *Build object in install header steps that might be able to be removed - // by passing the *Build object through the make() functions. + const stderr = run.stderr; + const ttyconf = run.ttyconf; + + // If we are unable to print a fancy terminal progress bar, then we resort + // to 1 line printed to stderr for each step, similar to Ninja. + if (ttyconf == .no_color) { + var buf: [120]u8 = undefined; + const step_index = @atomicRmw(usize, &run.step_index, .Add, 1, .Monotonic); + const text = std.fmt.bufPrint(&buf, "[{d}/{d}] Making {s}{s}\n", .{ + step_index + 1, run.step_count, s.owner.dep_prefix, s.name, + }) catch |err| switch (err) { + error.NoSpaceLeft => blk: { + buf[buf.len - 4 ..].* = "...\n".*; + break :blk &buf; + }, + }; + std.debug.getStderrMutex().lock(); + defer std.debug.getStderrMutex().unlock(); + stderr.writeAll(text) catch {}; + } + const make_result = s.make(&sub_prog_node); // No matter the result, we want to display error/warning messages. @@ -750,9 +778,6 @@ fn workerMakeOneStep( sub_prog_node.context.lock_stderr(); defer sub_prog_node.context.unlock_stderr(); - const stderr = run.stderr; - const ttyconf = run.ttyconf; - for (s.result_error_msgs.items) |msg| { // Sometimes it feels like you just can't catch a break. Finally, // with Zig, you can. From 20b35332fec73956c97087959dbc0fa2f78e5553 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 9 Mar 2023 23:59:11 -0700 Subject: [PATCH 242/294] build.zig: bump maxrss upper bound for std lib tests --- build.zig | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/build.zig b/build.zig index 21586963bd..b2dce0ae08 100644 --- a/build.zig +++ b/build.zig @@ -471,7 +471,9 @@ pub fn build(b: *std.Build) !void { .skip_libc = skip_libc, .skip_stage1 = skip_stage1, .skip_stage2 = true, // TODO get all these passing - .max_rss = 3 * 1024 * 1024 * 1024, + // I observed a value of 3398275072 on my M1, and multiplied by 1.1 to + // get this amount: + .max_rss = 3738102579, })); try addWasiUpdateStep(b, version); From ba7795913704028f53de44f836b81437afb5e33e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 10 Mar 2023 01:02:31 -0800 Subject: [PATCH 243/294] Revert "build runner: print to stderr in dumb terminals" This reverts commit e6f759e1c64668c50d3ff2d02c64a66c871da0ac. I changed my mind. I don't like the output because it makes it harder to find the actual errors in CI logs. --- lib/build_runner.zig | 39 +++++---------------------------------- 1 file changed, 5 insertions(+), 34 deletions(-) diff --git a/lib/build_runner.zig b/lib/build_runner.zig index a1b88f8c0b..e28be8274d 100644 --- a/lib/build_runner.zig +++ b/lib/build_runner.zig @@ -278,12 +278,8 @@ pub fn main() !void { .windows_api => {}, } - var progress: std.Progress = .{}; + var progress: std.Progress = .{ .dont_print_on_dumb = true }; const main_progress_node = progress.start("", 0); - if (ttyconf == .no_color) { - progress.timer = null; - progress.terminal = null; - } builder.debug_log_scopes = debug_log_scopes.items; builder.resolveInstallPrefix(install_prefix, dir_list); @@ -306,9 +302,6 @@ pub fn main() !void { .enable_summary = enable_summary, .ttyconf = ttyconf, .stderr = stderr, - - .step_index = 0, - .step_count = undefined, }; if (run.max_rss == 0) { @@ -339,9 +332,6 @@ const Run = struct { enable_summary: ?bool, ttyconf: std.debug.TTY.Config, stderr: std.fs.File, - - step_count: usize, - step_index: usize, }; fn runStepNames( @@ -405,8 +395,7 @@ fn runStepNames( { defer parent_prog_node.end(); - run.step_count = step_stack.count(); - var step_prog = parent_prog_node.start("run steps", run.step_count); + var step_prog = parent_prog_node.start("run steps", step_stack.count()); defer step_prog.end(); var wait_group: std.Thread.WaitGroup = .{}; @@ -750,27 +739,6 @@ fn workerMakeOneStep( sub_prog_node.activate(); defer sub_prog_node.end(); - const stderr = run.stderr; - const ttyconf = run.ttyconf; - - // If we are unable to print a fancy terminal progress bar, then we resort - // to 1 line printed to stderr for each step, similar to Ninja. - if (ttyconf == .no_color) { - var buf: [120]u8 = undefined; - const step_index = @atomicRmw(usize, &run.step_index, .Add, 1, .Monotonic); - const text = std.fmt.bufPrint(&buf, "[{d}/{d}] Making {s}{s}\n", .{ - step_index + 1, run.step_count, s.owner.dep_prefix, s.name, - }) catch |err| switch (err) { - error.NoSpaceLeft => blk: { - buf[buf.len - 4 ..].* = "...\n".*; - break :blk &buf; - }, - }; - std.debug.getStderrMutex().lock(); - defer std.debug.getStderrMutex().unlock(); - stderr.writeAll(text) catch {}; - } - const make_result = s.make(&sub_prog_node); // No matter the result, we want to display error/warning messages. @@ -778,6 +746,9 @@ fn workerMakeOneStep( sub_prog_node.context.lock_stderr(); defer sub_prog_node.context.unlock_stderr(); + const stderr = run.stderr; + const ttyconf = run.ttyconf; + for (s.result_error_msgs.items) |msg| { // Sometimes it feels like you just can't catch a break. Finally, // with Zig, you can. From f4428e5804971f4bbb897b5d1d5583073bcb31aa Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 10 Mar 2023 13:41:54 -0700 Subject: [PATCH 244/294] fix wasm bootstrapping compilation errors --- build.zig | 1 + lib/std/child_process.zig | 1 + 2 files changed, 2 insertions(+) diff --git a/build.zig b/build.zig index b2dce0ae08..44d8f84c59 100644 --- a/build.zig +++ b/build.zig @@ -508,6 +508,7 @@ fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void { exe_options.addOption(bool, "enable_tracy_callstack", false); exe_options.addOption(bool, "enable_tracy_allocation", false); exe_options.addOption(bool, "value_tracing", false); + exe_options.addOption(bool, "omit_pkg_fetching_code", true); const run_opt = b.addSystemCommand(&.{ "wasm-opt", "-Oz", "--enable-bulk-memory" }); run_opt.addArtifactArg(exe); diff --git a/lib/std/child_process.zig b/lib/std/child_process.zig index f9b2007b3e..3748ca6877 100644 --- a/lib/std/child_process.zig +++ b/lib/std/child_process.zig @@ -22,6 +22,7 @@ const is_darwin = builtin.target.isDarwin(); pub const ChildProcess = struct { pub const Id = switch (builtin.os.tag) { .windows => windows.HANDLE, + .wasi => void, else => os.pid_t, }; From 2c326c87b16a0b8febf5347292ba61a592467d8c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 10 Mar 2023 14:22:54 -0700 Subject: [PATCH 245/294] build.zig: install the langref to $prefix/doc/langref.html and update the CI scripts to match. --- build.zig | 2 +- ci/aarch64-linux-debug.sh | 2 +- ci/aarch64-linux-release.sh | 2 +- ci/x86_64-linux-debug.sh | 2 +- ci/x86_64-linux-release.sh | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build.zig b/build.zig index 44d8f84c59..a0c4ca7fb4 100644 --- a/build.zig +++ b/build.zig @@ -49,7 +49,7 @@ pub fn build(b: *std.Build) !void { docgen_cmd.addArgs(&.{ "--zig", b.zig_exe }); docgen_cmd.addFileSourceArg(.{ .path = "doc/langref.html.in" }); const langref_file = docgen_cmd.addOutputFileArg("langref.html"); - const install_langref = b.addInstallFileWithDir(langref_file, .prefix, "langref.html"); + const install_langref = b.addInstallFileWithDir(langref_file, .prefix, "doc/langref.html"); if (!skip_install_lib_files) { b.getInstallStep().dependOn(&install_langref.step); } diff --git a/ci/aarch64-linux-debug.sh b/ci/aarch64-linux-debug.sh index 94f40c557b..ba4bcc0c9d 100644 --- a/ci/aarch64-linux-debug.sh +++ b/ci/aarch64-linux-debug.sh @@ -67,7 +67,7 @@ stage3-debug/bin/zig build test docs \ --zig-lib-dir "$(pwd)/../lib" # Look for HTML errors. -tidy --drop-empty-elements no -qe "$ZIG_LOCAL_CACHE_DIR/langref.html" +tidy --drop-empty-elements no -qe "stage3-debug/doc/langref.html" # Produce the experimental std lib documentation. stage3-debug/bin/zig test ../lib/std/std.zig -femit-docs -fno-emit-bin --zig-lib-dir ../lib diff --git a/ci/aarch64-linux-release.sh b/ci/aarch64-linux-release.sh index 65d6063f25..b3837c4942 100644 --- a/ci/aarch64-linux-release.sh +++ b/ci/aarch64-linux-release.sh @@ -67,7 +67,7 @@ stage3-release/bin/zig build test docs \ --zig-lib-dir "$(pwd)/../lib" # Look for HTML errors. -tidy --drop-empty-elements no -qe "$ZIG_LOCAL_CACHE_DIR/langref.html" +tidy --drop-empty-elements no -qe "stage3-release/doc/langref.html" # Produce the experimental std lib documentation. stage3-release/bin/zig test ../lib/std/std.zig -femit-docs -fno-emit-bin --zig-lib-dir ../lib diff --git a/ci/x86_64-linux-debug.sh b/ci/x86_64-linux-debug.sh index 7f2382f04a..37dcacb86d 100755 --- a/ci/x86_64-linux-debug.sh +++ b/ci/x86_64-linux-debug.sh @@ -66,7 +66,7 @@ stage3-debug/bin/zig build test docs \ --zig-lib-dir "$(pwd)/../lib" # Look for HTML errors. -tidy --drop-empty-elements no -qe "$ZIG_LOCAL_CACHE_DIR/langref.html" +tidy --drop-empty-elements no -qe "stage3-debug/doc/langref.html" # Produce the experimental std lib documentation. stage3-debug/bin/zig test ../lib/std/std.zig -femit-docs -fno-emit-bin --zig-lib-dir ../lib diff --git a/ci/x86_64-linux-release.sh b/ci/x86_64-linux-release.sh index cdb24e4a6f..01088a793c 100755 --- a/ci/x86_64-linux-release.sh +++ b/ci/x86_64-linux-release.sh @@ -67,7 +67,7 @@ stage3-release/bin/zig build test docs \ --zig-lib-dir "$(pwd)/../lib" # Look for HTML errors. -tidy --drop-empty-elements no -qe "$ZIG_LOCAL_CACHE_DIR/langref.html" +tidy --drop-empty-elements no -qe "stage3-release/doc/langref.html" # Produce the experimental std lib documentation. stage3-release/bin/zig test ../lib/std/std.zig -femit-docs -fno-emit-bin --zig-lib-dir ../lib From 2956232b427276c6ad1e52678e1cf3651976ec39 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 10 Mar 2023 14:32:49 -0700 Subject: [PATCH 246/294] standalone tests: avoid running on strange target Without this, aarch64-linux tried to compile this test for aarch64-windows with the same CPU settings, which is not an intended test combination. --- test/standalone.zig | 4 +++- test/tests.zig | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/test/standalone.zig b/test/standalone.zig index 6e0adcaa00..29a32d878f 100644 --- a/test/standalone.zig +++ b/test/standalone.zig @@ -5,6 +5,8 @@ pub const SimpleCase = struct { target: std.zig.CrossTarget = .{}, is_test: bool = false, is_exe: bool = true, + /// Run only on this OS. + os_filter: ?std.Target.Os.Tag = null, }; pub const BuildCase = struct { @@ -50,7 +52,7 @@ pub const simple_cases = [_]SimpleCase{ .{ .src_path = "test/standalone/issue_9402/main.zig", - .target = .{ .os_tag = .windows }, + .os_filter = .windows, .link_libc = true, }, diff --git a/test/tests.zig b/test/tests.zig index c07b669376..9ba9639e2a 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -571,6 +571,9 @@ pub fn addStandaloneTests( for (standalone.simple_cases) |case| { for (optimize_modes) |optimize| { if (!case.all_modes and optimize != .Debug) continue; + if (case.os_filter) |os_tag| { + if (os_tag != builtin.os.tag) continue; + } if (case.is_exe) { const exe = b.addExecutable(.{ From 1a3c1fe820ad9eeaa7b30a1fc6e1b8b8e2b871b6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 10 Mar 2023 14:46:35 -0700 Subject: [PATCH 247/294] test-link: add names to headerpad test --- test/link/macho/headerpad/build.zig | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/test/link/macho/headerpad/build.zig b/test/link/macho/headerpad/build.zig index 97161e8e60..0c9275b8d8 100644 --- a/test/link/macho/headerpad/build.zig +++ b/test/link/macho/headerpad/build.zig @@ -17,7 +17,7 @@ pub fn build(b: *std.Build) void { fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { { // Test -headerpad_max_install_names - const exe = simpleExe(b, optimize); + const exe = simpleExe(b, optimize, "headerpad_max_install_names"); exe.headerpad_max_install_names = true; const check = exe.checkObject(); @@ -42,7 +42,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize { // Test -headerpad - const exe = simpleExe(b, optimize); + const exe = simpleExe(b, optimize, "headerpad"); exe.headerpad_size = 0x10000; const check = exe.checkObject(); @@ -58,7 +58,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize { // Test both flags with -headerpad overriding -headerpad_max_install_names - const exe = simpleExe(b, optimize); + const exe = simpleExe(b, optimize, "headerpad_overriding"); exe.headerpad_max_install_names = true; exe.headerpad_size = 0x10000; @@ -75,7 +75,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize { // Test both flags with -headerpad_max_install_names overriding -headerpad - const exe = simpleExe(b, optimize); + const exe = simpleExe(b, optimize, "headerpad_max_install_names_overriding"); exe.headerpad_size = 0x1000; exe.headerpad_max_install_names = true; @@ -100,9 +100,13 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize } } -fn simpleExe(b: *std.Build, optimize: std.builtin.OptimizeMode) *std.Build.CompileStep { +fn simpleExe( + b: *std.Build, + optimize: std.builtin.OptimizeMode, + name: []const u8, +) *std.Build.CompileStep { const exe = b.addExecutable(.{ - .name = "main", + .name = name, .optimize = optimize, }); exe.addCSourceFile("main.c", &.{}); From fbce6a749da9e3714d5e0fb662a3849b34cc9f89 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 12 Mar 2023 00:24:19 -0700 Subject: [PATCH 248/294] disable failing aarch64 backend behavior tests --- test/behavior/array.zig | 1 + test/behavior/ptrcast.zig | 2 ++ test/behavior/slice.zig | 1 + 3 files changed, 4 insertions(+) diff --git a/test/behavior/array.zig b/test/behavior/array.zig index 6c68d50fda..c78bf4ab85 100644 --- a/test/behavior/array.zig +++ b/test/behavior/array.zig @@ -84,6 +84,7 @@ test "array concat with tuple" { } test "array init with concat" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO const a = 'a'; var i: [4]u8 = [2]u8{ a, 'b' } ++ [2]u8{ 'c', 'd' }; try expect(std.mem.eql(u8, &i, "abcd")); diff --git a/test/behavior/ptrcast.zig b/test/behavior/ptrcast.zig index 599d13be1d..845ea3751e 100644 --- a/test/behavior/ptrcast.zig +++ b/test/behavior/ptrcast.zig @@ -170,6 +170,7 @@ test "lower reinterpreted comptime field ptr" { test "reinterpret struct field at comptime" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO const numNative = comptime Bytes.init(0x12345678); if (native_endian != .Little) { @@ -232,6 +233,7 @@ test "ptrcast of const integer has the correct object size" { test "implicit optional pointer to optional anyopaque pointer" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO var buf: [4]u8 = "aoeu".*; var x: ?[*]u8 = &buf; diff --git a/test/behavior/slice.zig b/test/behavior/slice.zig index d749697ec5..2a0944a5b6 100644 --- a/test/behavior/slice.zig +++ b/test/behavior/slice.zig @@ -227,6 +227,7 @@ fn sliceFromLenToLen(a_slice: []u8, start: usize, end: usize) []u8 { test "C pointer" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO var buf: [*c]const u8 = "kjdhfkjdhfdkjhfkfjhdfkjdhfkdjhfdkjhf"; var len: u32 = 10; From 53fb59ea9b148a9d1f694039898eb60a1df75535 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 12 Mar 2023 00:25:00 -0700 Subject: [PATCH 249/294] std.fifo: make toOwnedSlice support head != 0 --- lib/std/fifo.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/std/fifo.zig b/lib/std/fifo.zig index ad929cde8a..5a72b56269 100644 --- a/lib/std/fifo.zig +++ b/lib/std/fifo.zig @@ -385,6 +385,7 @@ pub fn LinearFifo( } pub fn toOwnedSlice(self: *Self) Allocator.Error![]T { + if (self.head != 0) self.realign(); assert(self.head == 0); assert(self.count <= self.buf.len); const allocator = self.allocator; From 98299e7787146e1572cd2038654dbbcf84fa32d1 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 12 Mar 2023 00:34:11 -0700 Subject: [PATCH 250/294] add std.process.cleanExit --- lib/std/process.zig | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lib/std/process.zig b/lib/std/process.zig index 8652dd1bbc..d9bf09ee2a 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -1204,3 +1204,16 @@ fn totalSystemMemoryLinux() !usize { const kilobytes = try std.fmt.parseInt(usize, int_text, 10); return kilobytes * 1024; } + +/// Indicate that we are now terminating with a successful exit code. +/// In debug builds, this is a no-op, so that the calling code's +/// cleanup mechanisms are tested and so that external tools that +/// check for resource leaks can be accurate. In release builds, this +/// calls exit(0), and does not return. +pub fn cleanExit() void { + if (builtin.mode == .Debug) { + return; + } else { + exit(0); + } +} From ef5f8bd7c62f929b5cc210caa816ce4a8c8f8538 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 12 Mar 2023 00:38:01 -0700 Subject: [PATCH 251/294] getExternalExecutor: fix aarch64 windows std.zig.system.NativeTargetInfo.getExternalExecutor previously would incorrectly communicate that wine could be used to run aarch64-windows binaries on x86_64-linux, and x86_64-windows binaries on aarch64-linux. Neither of these things are true. --- lib/std/zig/system/NativeTargetInfo.zig | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/std/zig/system/NativeTargetInfo.zig b/lib/std/zig/system/NativeTargetInfo.zig index dbbebb43c9..987358ed5a 100644 --- a/lib/std/zig/system/NativeTargetInfo.zig +++ b/lib/std/zig/system/NativeTargetInfo.zig @@ -1090,6 +1090,11 @@ pub fn getExternalExecutor( switch (candidate.target.os.tag) { .windows => { if (options.allow_wine) { + // x86_64 wine does not support emulating aarch64-windows and + // vice versa. + if (candidate.target.cpu.arch != builtin.cpu.arch) { + return bad_result; + } switch (candidate.target.cpu.arch.ptrBitWidth()) { 32 => return Executor{ .wine = "wine" }, 64 => return Executor{ .wine = "wine64" }, From ede5dcffea5a3a5fc9fd14e4e180464633402fae Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 12 Mar 2023 00:39:21 -0700 Subject: [PATCH 252/294] make the build runner and test runner talk to each other std.Build.addTest creates a CompileStep as before, however, this kind of step no longer actually runs the unit tests. Instead it only compiles it, and one must additionally create a RunStep from the CompileStep in order to actually run the tests. RunStep gains integration with the default test runner, which now supports the standard --listen=- argument in order to communicate over stdin and stdout. It also reports test statistics; how many passed, failed, and leaked, as well as directly associating the relevant stderr with the particular test name that failed. This separation of CompileStep and RunStep means that `CompileStep.Kind.test_exe` is no longer needed, and therefore has been removed in this commit. * build runner: show unit test statistics in build summary * added Step.writeManifest since many steps want to treat it as a warning and emit the same message if it fails. * RunStep: fixed error message that prints the failed command printing the original argv and not the adjusted argv in case an interpreter was used. * RunStep: fixed not passing the command line arguments to the interpreter. * move src/Server.zig to std.zig.Server so that the default test runner can use it. * the simpler test runner function which is used by work-in-progress backends now no longer prints to stderr, which is necessary in order for the build runner to not print the stderr as a warning message. --- CMakeLists.txt | 2 +- lib/build_runner.zig | 59 ++- lib/std/Build.zig | 10 +- lib/std/Build/CompileStep.zig | 93 +---- lib/std/Build/InstallArtifactStep.zig | 5 +- lib/std/Build/RunStep.zig | 355 +++++++++++++++--- lib/std/Build/Step.zig | 33 +- lib/std/Build/WriteFileStep.zig | 2 +- lib/std/zig/Client.zig | 7 + lib/std/zig/Server.zig | 200 ++++++++++ lib/test_runner.zig | 172 ++++++--- src/Server.zig | 113 ------ src/main.zig | 21 +- src/objcopy.zig | 9 +- test/link/common_symbols/build.zig | 2 +- test/link/common_symbols_alignment/build.zig | 2 +- .../interdependent_static_c_libs/build.zig | 2 +- test/link/macho/tls/build.zig | 5 +- test/src/Cases.zig | 11 +- test/standalone/emit_asm_and_bin/build.zig | 2 +- test/standalone/global_linkage/build.zig | 2 +- test/standalone/issue_13970/build.zig | 6 +- test/standalone/main_pkg_path/build.zig | 2 +- test/standalone/options/build.zig | 2 +- test/standalone/pie/build.zig | 2 +- test/standalone/static_c_lib/build.zig | 2 +- .../test_runner_module_imports/build.zig | 2 +- test/standalone/test_runner_path/build.zig | 1 - test/standalone/use_alias/build.zig | 2 +- test/tests.zig | 27 +- 30 files changed, 780 insertions(+), 373 deletions(-) delete mode 100644 src/Server.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index 7ff8249f4d..b0867c220b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -518,6 +518,7 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/lib/std/zig/c_builtins.zig" "${CMAKE_SOURCE_DIR}/lib/std/zig/Parse.zig" "${CMAKE_SOURCE_DIR}/lib/std/zig/render.zig" + "${CMAKE_SOURCE_DIR}/lib/std/zig/Server.zig" "${CMAKE_SOURCE_DIR}/lib/std/zig/string_literal.zig" "${CMAKE_SOURCE_DIR}/lib/std/zig/system.zig" "${CMAKE_SOURCE_DIR}/lib/std/zig/system/NativePaths.zig" @@ -623,7 +624,6 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/src/print_targets.zig" "${CMAKE_SOURCE_DIR}/src/print_zir.zig" "${CMAKE_SOURCE_DIR}/src/register_manager.zig" - "${CMAKE_SOURCE_DIR}/src/Server.zig" "${CMAKE_SOURCE_DIR}/src/target.zig" "${CMAKE_SOURCE_DIR}/src/tracy.zig" "${CMAKE_SOURCE_DIR}/src/translate_c.zig" diff --git a/lib/build_runner.zig b/lib/build_runner.zig index e28be8274d..bb341574a7 100644 --- a/lib/build_runner.zig +++ b/lib/build_runner.zig @@ -416,6 +416,12 @@ fn runStepNames( } assert(run.memory_blocked_steps.items.len == 0); + var test_skip_count: usize = 0; + var test_fail_count: usize = 0; + var test_pass_count: usize = 0; + var test_leak_count: usize = 0; + var test_count: usize = 0; + var success_count: usize = 0; var skipped_count: usize = 0; var failure_count: usize = 0; @@ -425,6 +431,12 @@ fn runStepNames( defer compile_error_steps.deinit(gpa); for (step_stack.keys()) |s| { + test_fail_count += s.test_results.fail_count; + test_skip_count += s.test_results.skip_count; + test_leak_count += s.test_results.leak_count; + test_pass_count += s.test_results.passCount(); + test_count += s.test_results.test_count; + switch (s.state) { .precheck_unstarted => unreachable, .precheck_started => unreachable, @@ -468,6 +480,11 @@ fn runStepNames( if (skipped_count > 0) stderr.writer().print("; {d} skipped", .{skipped_count}) catch {}; if (failure_count > 0) stderr.writer().print("; {d} failed", .{failure_count}) catch {}; + if (test_count > 0) stderr.writer().print("; {d}/{d} tests passed", .{ test_pass_count, test_count }) catch {}; + if (test_skip_count > 0) stderr.writer().print("; {d} skipped", .{test_skip_count}) catch {}; + if (test_fail_count > 0) stderr.writer().print("; {d} failed", .{test_fail_count}) catch {}; + if (test_leak_count > 0) stderr.writer().print("; {d} leaked", .{test_leak_count}) catch {}; + if (run.enable_summary == null) { ttyconf.setColor(stderr, .Dim) catch {}; stderr.writeAll(" (disable with -fno-summary)") catch {}; @@ -566,6 +583,13 @@ fn printTreeStep( try ttyconf.setColor(stderr, .Green); if (s.result_cached) { try stderr.writeAll(" cached"); + } else if (s.test_results.test_count > 0) { + const pass_count = s.test_results.passCount(); + try stderr.writer().print(" {d} passed", .{pass_count}); + if (s.test_results.skip_count > 0) { + try ttyconf.setColor(stderr, .Yellow); + try stderr.writer().print(" {d} skipped", .{s.test_results.skip_count}); + } } else { try stderr.writeAll(" success"); } @@ -609,15 +633,46 @@ fn printTreeStep( }, .failure => { - try ttyconf.setColor(stderr, .Red); if (s.result_error_bundle.errorMessageCount() > 0) { + try ttyconf.setColor(stderr, .Red); try stderr.writer().print(" {d} errors\n", .{ s.result_error_bundle.errorMessageCount(), }); + try ttyconf.setColor(stderr, .Reset); + } else if (!s.test_results.isSuccess()) { + try stderr.writer().print(" {d}/{d} passed", .{ + s.test_results.passCount(), s.test_results.test_count, + }); + if (s.test_results.fail_count > 0) { + try stderr.writeAll(", "); + try ttyconf.setColor(stderr, .Red); + try stderr.writer().print("{d} failed", .{ + s.test_results.fail_count, + }); + try ttyconf.setColor(stderr, .Reset); + } + if (s.test_results.skip_count > 0) { + try stderr.writeAll(", "); + try ttyconf.setColor(stderr, .Yellow); + try stderr.writer().print("{d} skipped", .{ + s.test_results.skip_count, + }); + try ttyconf.setColor(stderr, .Reset); + } + if (s.test_results.leak_count > 0) { + try stderr.writeAll(", "); + try ttyconf.setColor(stderr, .Red); + try stderr.writer().print("{d} leaked", .{ + s.test_results.leak_count, + }); + try ttyconf.setColor(stderr, .Reset); + } + try stderr.writeAll("\n"); } else { + try ttyconf.setColor(stderr, .Red); try stderr.writeAll(" failure\n"); + try ttyconf.setColor(stderr, .Reset); } - try ttyconf.setColor(stderr, .Reset); }, } diff --git a/lib/std/Build.zig b/lib/std/Build.zig index 5f74af3db6..279dd765b5 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -531,7 +531,6 @@ pub fn addStaticLibrary(b: *Build, options: StaticLibraryOptions) *CompileStep { pub const TestOptions = struct { name: []const u8 = "test", - kind: CompileStep.Kind = .@"test", root_source_file: FileSource, target: CrossTarget = .{}, optimize: std.builtin.Mode = .Debug, @@ -542,7 +541,7 @@ pub const TestOptions = struct { pub fn addTest(b: *Build, options: TestOptions) *CompileStep { return CompileStep.create(b, .{ .name = options.name, - .kind = options.kind, + .kind = .@"test", .root_source_file = options.root_source_file, .target = options.target, .optimize = options.optimize, @@ -626,16 +625,15 @@ pub fn addSystemCommand(self: *Build, argv: []const []const u8) *RunStep { /// Creates a `RunStep` with an executable built with `addExecutable`. /// Add command line arguments with methods of `RunStep`. pub fn addRunArtifact(b: *Build, exe: *CompileStep) *RunStep { - assert(exe.kind == .exe or exe.kind == .test_exe); - // It doesn't have to be native. We catch that if you actually try to run it. // Consider that this is declarative; the run step may not be run unless a user // option is supplied. const run_step = RunStep.create(b, b.fmt("run {s}", .{exe.name})); run_step.addArtifactArg(exe); - if (exe.kind == .test_exe) { - run_step.addArg(b.zig_exe); + if (exe.kind == .@"test") { + run_step.stdio = .zig_test; + run_step.addArgs(&.{"--listen=-"}); } if (exe.vcpkg_bin_path) |path| { diff --git a/lib/std/Build/CompileStep.zig b/lib/std/Build/CompileStep.zig index 5c44ab82d3..5753641966 100644 --- a/lib/std/Build/CompileStep.zig +++ b/lib/std/Build/CompileStep.zig @@ -289,7 +289,6 @@ pub const Kind = enum { lib, obj, @"test", - test_exe, }; pub const Linkage = enum { dynamic, static }; @@ -328,7 +327,7 @@ pub fn create(owner: *std.Build, options: Options) *CompileStep { .exe => "zig build-exe", .lib => "zig build-lib", .obj => "zig build-obj", - .test_exe, .@"test" => "zig test", + .@"test" => "zig test", }, name_adjusted, @tagName(options.optimize), @@ -410,7 +409,7 @@ fn computeOutFileNames(self: *CompileStep) void { .output_mode = switch (self.kind) { .lib => .Lib, .obj => .Obj, - .exe, .@"test", .test_exe => .Exe, + .exe, .@"test" => .Exe, }, .link_mode = if (self.linkage) |some| @as(std.builtin.LinkMode, switch (some) { .dynamic => .Dynamic, @@ -621,7 +620,7 @@ pub fn producesPdbFile(self: *CompileStep) bool { if (!self.target.isWindows() and !self.target.isUefi()) return false; if (self.target.getObjectFormat() == .c) return false; if (self.strip == true) return false; - return self.isDynamicLibrary() or self.kind == .exe or self.kind == .test_exe; + return self.isDynamicLibrary() or self.kind == .exe or self.kind == .@"test"; } pub fn linkLibC(self: *CompileStep) void { @@ -850,19 +849,19 @@ fn linkSystemLibraryInner(self: *CompileStep, name: []const u8, opts: struct { pub fn setNamePrefix(self: *CompileStep, text: []const u8) void { const b = self.step.owner; - assert(self.kind == .@"test" or self.kind == .test_exe); + assert(self.kind == .@"test"); self.name_prefix = b.dupe(text); } pub fn setFilter(self: *CompileStep, text: ?[]const u8) void { const b = self.step.owner; - assert(self.kind == .@"test" or self.kind == .test_exe); + assert(self.kind == .@"test"); self.filter = if (text) |t| b.dupe(t) else null; } pub fn setTestRunner(self: *CompileStep, path: ?[]const u8) void { const b = self.step.owner; - assert(self.kind == .@"test" or self.kind == .test_exe); + assert(self.kind == .@"test"); self.test_runner = if (path) |p| b.dupePath(p) else null; } @@ -938,7 +937,7 @@ pub fn getOutputLibSource(self: *CompileStep) FileSource { /// Returns the generated header file. /// This function can only be called for libraries or object files which have `emit_h` set. pub fn getOutputHSource(self: *CompileStep) FileSource { - assert(self.kind != .exe and self.kind != .test_exe and self.kind != .@"test"); + assert(self.kind != .exe and self.kind != .@"test"); assert(self.emit_h); return .{ .generated = &self.output_h_path_source }; } @@ -1243,7 +1242,6 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { .exe => "build-exe", .obj => "build-obj", .@"test" => "test", - .test_exe => "test", }; try zig_args.append(cmd); @@ -1293,7 +1291,6 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { .other_step => |other| switch (other.kind) { .exe => @panic("Cannot link with an executable build artifact"), - .test_exe => @panic("Cannot link with an executable build artifact"), .@"test" => @panic("Cannot link with a test"), .obj => { try zig_args.append(other.getOutputSource().getPath(b)); @@ -1661,83 +1658,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { try zig_args.append("--test-cmd-bin"); } } - } else { - const need_cross_glibc = self.target.isGnuLibC() and transitive_deps.is_linking_libc; - - switch (b.host.getExternalExecutor(self.target_info, .{ - .qemu_fixes_dl = need_cross_glibc and b.glibc_runtimes_dir != null, - .link_libc = transitive_deps.is_linking_libc, - })) { - .native => {}, - .bad_dl, .bad_os_or_cpu => { - try zig_args.append("--test-no-exec"); - }, - .rosetta => if (b.enable_rosetta) { - try zig_args.append("--test-cmd-bin"); - } else { - try zig_args.append("--test-no-exec"); - }, - .qemu => |bin_name| ok: { - if (b.enable_qemu) qemu: { - const glibc_dir_arg = if (need_cross_glibc) - b.glibc_runtimes_dir orelse break :qemu - else - null; - try zig_args.append("--test-cmd"); - try zig_args.append(bin_name); - if (glibc_dir_arg) |dir| { - // TODO look into making this a call to `linuxTriple`. This - // needs the directory to be called "i686" rather than - // "x86" which is why we do it manually here. - const fmt_str = "{s}" ++ fs.path.sep_str ++ "{s}-{s}-{s}"; - const cpu_arch = self.target.getCpuArch(); - const os_tag = self.target.getOsTag(); - const abi = self.target.getAbi(); - const cpu_arch_name: []const u8 = if (cpu_arch == .x86) - "i686" - else - @tagName(cpu_arch); - const full_dir = try std.fmt.allocPrint(b.allocator, fmt_str, .{ - dir, cpu_arch_name, @tagName(os_tag), @tagName(abi), - }); - - try zig_args.append("--test-cmd"); - try zig_args.append("-L"); - try zig_args.append("--test-cmd"); - try zig_args.append(full_dir); - } - try zig_args.append("--test-cmd-bin"); - break :ok; - } - try zig_args.append("--test-no-exec"); - }, - .wine => |bin_name| if (b.enable_wine) { - try zig_args.append("--test-cmd"); - try zig_args.append(bin_name); - try zig_args.append("--test-cmd-bin"); - } else { - try zig_args.append("--test-no-exec"); - }, - .wasmtime => |bin_name| if (b.enable_wasmtime) { - try zig_args.append("--test-cmd"); - try zig_args.append(bin_name); - try zig_args.append("--test-cmd"); - try zig_args.append("--dir=."); - try zig_args.append("--test-cmd-bin"); - } else { - try zig_args.append("--test-no-exec"); - }, - .darling => |bin_name| if (b.enable_darling) { - try zig_args.append("--test-cmd"); - try zig_args.append(bin_name); - try zig_args.append("--test-cmd-bin"); - } else { - try zig_args.append("--test-no-exec"); - }, - } } - } else if (self.kind == .test_exe) { - try zig_args.append("--test-no-exec"); } try self.appendModuleArgs(&zig_args); diff --git a/lib/std/Build/InstallArtifactStep.zig b/lib/std/Build/InstallArtifactStep.zig index 803998a619..445f1e8ea8 100644 --- a/lib/std/Build/InstallArtifactStep.zig +++ b/lib/std/Build/InstallArtifactStep.zig @@ -32,12 +32,11 @@ pub fn create(owner: *std.Build, artifact: *CompileStep) *InstallArtifactStep { .artifact = artifact, .dest_dir = artifact.override_dest_dir orelse switch (artifact.kind) { .obj => @panic("Cannot install a .obj build artifact."), - .@"test" => @panic("Cannot install a .test build artifact, use .test_exe instead."), - .exe, .test_exe => InstallDir{ .bin = {} }, + .exe, .@"test" => InstallDir{ .bin = {} }, .lib => InstallDir{ .lib = {} }, }, .pdb_dir = if (artifact.producesPdbFile()) blk: { - if (artifact.kind == .exe or artifact.kind == .test_exe) { + if (artifact.kind == .exe or artifact.kind == .@"test") { break :blk InstallDir{ .bin = {} }; } else { break :blk InstallDir{ .lib = {} }; diff --git a/lib/std/Build/RunStep.zig b/lib/std/Build/RunStep.zig index 9a1c887d7d..484600bf9b 100644 --- a/lib/std/Build/RunStep.zig +++ b/lib/std/Build/RunStep.zig @@ -92,6 +92,9 @@ pub const StdIo = union(enum) { /// Note that an explicit check for exit code 0 needs to be added to this /// list if such a check is desireable. check: std.ArrayList(Check), + /// This RunStep is running a zig unit test binary and will communicate + /// extra metadata over the IPC protocol. + zig_test, pub const Check = union(enum) { expect_stderr_exact: []const u8, @@ -324,6 +327,7 @@ fn hasSideEffects(self: RunStep) bool { .infer_from_args => !self.hasAnyOutputArgs(), .inherit => true, .check => false, + .zig_test => false, }; } @@ -366,11 +370,6 @@ fn checksContainStderr(checks: []const StdIo.Check) bool { } fn make(step: *Step, prog_node: *std.Progress.Node) !void { - // Unfortunately we have no way to collect progress from arbitrary programs. - // Perhaps in the future Zig could offer some kind of opt-in IPC mechanism that - // processes could use to supply progress updates. - _ = prog_node; - const b = step.owner; const arena = b.allocator; const self = @fieldParentPtr(RunStep, "step", step); @@ -439,7 +438,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { hashStdIo(&man.hash, self.stdio); if (has_side_effects) { - try runCommand(self, argv_list.items, has_side_effects, null); + try runCommand(self, argv_list.items, has_side_effects, null, prog_node); return; } @@ -492,8 +491,9 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { argv_list.items[placeholder.index] = cli_arg; } - try runCommand(self, argv_list.items, has_side_effects, &digest); - try man.writeManifest(); + try runCommand(self, argv_list.items, has_side_effects, &digest, prog_node); + + try step.writeManifest(&man); } fn formatTerm( @@ -546,6 +546,7 @@ fn runCommand( argv: []const []const u8, has_side_effects: bool, digest: ?*const [std.Build.Cache.hex_digest_len]u8, + prog_node: *std.Progress.Node, ) !void { const step = &self.step; const b = step.owner; @@ -554,7 +555,15 @@ fn runCommand( try step.handleChildProcUnsupported(self.cwd, argv); try Step.handleVerbose(step.owner, self.cwd, argv); - const result = spawnChildAndCollect(self, argv, has_side_effects) catch |err| term: { + const allow_skip = switch (self.stdio) { + .check, .zig_test => self.skip_foreign_checks, + else => false, + }; + + var interp_argv = std.ArrayList([]const u8).init(b.allocator); + defer interp_argv.deinit(); + + const result = spawnChildAndCollect(self, argv, has_side_effects, prog_node) catch |err| term: { // InvalidExe: cpu arch mismatch // FileNotFound: can happen with a wrong dynamic linker path if (err == error.InvalidExe or err == error.FileNotFound) interpret: { @@ -566,10 +575,10 @@ fn runCommand( .artifact => |exe| exe, else => break :interpret, }; - if (exe.kind != .exe) break :interpret; - - var interp_argv = std.ArrayList([]const u8).init(b.allocator); - defer interp_argv.deinit(); + switch (exe.kind) { + .exe, .@"test" => {}, + else => break :interpret, + } const need_cross_glibc = exe.target.isGnuLibC() and exe.is_linking_libc; switch (b.host.getExternalExecutor(exe.target_info, .{ @@ -577,14 +586,13 @@ fn runCommand( .link_libc = exe.is_linking_libc, })) { .native, .rosetta => { - if (self.stdio == .check and self.skip_foreign_checks) - return error.MakeSkipped; - + if (allow_skip) return error.MakeSkipped; break :interpret; }, .wine => |bin_name| { if (b.enable_wine) { try interp_argv.append(bin_name); + try interp_argv.appendSlice(argv); } else { return failForeign(self, "-fwine", argv[0], exe); } @@ -617,6 +625,8 @@ fn runCommand( try interp_argv.append("-L"); try interp_argv.append(full_dir); } + + try interp_argv.appendSlice(argv); } else { return failForeign(self, "-fqemu", argv[0], exe); } @@ -624,6 +634,7 @@ fn runCommand( .darling => |bin_name| { if (b.enable_darling) { try interp_argv.append(bin_name); + try interp_argv.appendSlice(argv); } else { return failForeign(self, "-fdarling", argv[0], exe); } @@ -632,13 +643,15 @@ fn runCommand( if (b.enable_wasmtime) { try interp_argv.append(bin_name); try interp_argv.append("--dir=."); + try interp_argv.append(argv[0]); + try interp_argv.append("--"); + try interp_argv.appendSlice(argv[1..]); } else { return failForeign(self, "-fwasmtime", argv[0], exe); } }, .bad_dl => |foreign_dl| { - if (self.stdio == .check and self.skip_foreign_checks) - return error.MakeSkipped; + if (allow_skip) return error.MakeSkipped; const host_dl = b.host.dynamic_linker.get() orelse "(none)"; @@ -650,8 +663,7 @@ fn runCommand( , .{ host_dl, foreign_dl }); }, .bad_os_or_cpu => { - if (self.stdio == .check and self.skip_foreign_checks) - return error.MakeSkipped; + if (allow_skip) return error.MakeSkipped; const host_name = try b.host.target.zigTriple(b.allocator); const foreign_name = try exe.target.zigTriple(b.allocator); @@ -667,11 +679,9 @@ fn runCommand( RunStep.addPathForDynLibsInternal(&self.step, b, exe); } - try interp_argv.append(argv[0]); - try Step.handleVerbose(step.owner, self.cwd, interp_argv.items); - break :term spawnChildAndCollect(self, interp_argv.items, has_side_effects) catch |e| { + break :term spawnChildAndCollect(self, interp_argv.items, has_side_effects, prog_node) catch |e| { return step.fail("unable to spawn {s}: {s}", .{ interp_argv.items[0], @errorName(e), }); @@ -683,6 +693,7 @@ fn runCommand( step.result_duration_ns = result.elapsed_ns; step.result_peak_rss = result.peak_rss; + step.test_results = result.stdio.test_results; // Capture stdout and stderr to GeneratedFile objects. const Stream = struct { @@ -693,13 +704,13 @@ fn runCommand( for ([_]Stream{ .{ .captured = self.captured_stdout, - .is_null = result.stdout_null, - .bytes = result.stdout, + .is_null = result.stdio.stdout_null, + .bytes = result.stdio.stdout, }, .{ .captured = self.captured_stderr, - .is_null = result.stderr_null, - .bytes = result.stderr, + .is_null = result.stdio.stderr_null, + .bytes = result.stdio.stderr, }, }) |stream| { if (stream.captured) |output| { @@ -724,11 +735,13 @@ fn runCommand( } } + const final_argv = if (interp_argv.items.len == 0) argv else interp_argv.items; + switch (self.stdio) { .check => |checks| for (checks.items) |check| switch (check) { .expect_stderr_exact => |expected_bytes| { - assert(!result.stderr_null); - if (!mem.eql(u8, expected_bytes, result.stderr)) { + assert(!result.stdio.stderr_null); + if (!mem.eql(u8, expected_bytes, result.stdio.stderr)) { return step.fail( \\ \\========= expected this stderr: ========= @@ -739,14 +752,14 @@ fn runCommand( \\{s} , .{ expected_bytes, - result.stderr, - try Step.allocPrintCmd(arena, self.cwd, argv), + result.stdio.stderr, + try Step.allocPrintCmd(arena, self.cwd, final_argv), }); } }, .expect_stderr_match => |match| { - assert(!result.stderr_null); - if (mem.indexOf(u8, result.stderr, match) == null) { + assert(!result.stdio.stderr_null); + if (mem.indexOf(u8, result.stdio.stderr, match) == null) { return step.fail( \\ \\========= expected to find in stderr: ========= @@ -757,14 +770,14 @@ fn runCommand( \\{s} , .{ match, - result.stderr, - try Step.allocPrintCmd(arena, self.cwd, argv), + result.stdio.stderr, + try Step.allocPrintCmd(arena, self.cwd, final_argv), }); } }, .expect_stdout_exact => |expected_bytes| { - assert(!result.stdout_null); - if (!mem.eql(u8, expected_bytes, result.stdout)) { + assert(!result.stdio.stdout_null); + if (!mem.eql(u8, expected_bytes, result.stdio.stdout)) { return step.fail( \\ \\========= expected this stdout: ========= @@ -775,14 +788,14 @@ fn runCommand( \\{s} , .{ expected_bytes, - result.stdout, - try Step.allocPrintCmd(arena, self.cwd, argv), + result.stdio.stdout, + try Step.allocPrintCmd(arena, self.cwd, final_argv), }); } }, .expect_stdout_match => |match| { - assert(!result.stdout_null); - if (mem.indexOf(u8, result.stdout, match) == null) { + assert(!result.stdio.stdout_null); + if (mem.indexOf(u8, result.stdio.stdout, match) == null) { return step.fail( \\ \\========= expected to find in stdout: ========= @@ -793,8 +806,8 @@ fn runCommand( \\{s} , .{ match, - result.stdout, - try Step.allocPrintCmd(arena, self.cwd, argv), + result.stdio.stdout, + try Step.allocPrintCmd(arena, self.cwd, final_argv), }); } }, @@ -803,33 +816,46 @@ fn runCommand( return step.fail("the following command {} (expected {}):\n{s}", .{ fmtTerm(result.term), fmtTerm(expected_term), - try Step.allocPrintCmd(arena, self.cwd, argv), + try Step.allocPrintCmd(arena, self.cwd, final_argv), }); } }, }, + .zig_test => { + const expected_term: std.process.Child.Term = .{ .Exited = 0 }; + if (!termMatches(expected_term, result.term)) { + return step.fail("the following command {} (expected {}):\n{s}", .{ + fmtTerm(result.term), + fmtTerm(expected_term), + try Step.allocPrintCmd(arena, self.cwd, final_argv), + }); + } + if (!result.stdio.test_results.isSuccess()) { + return step.fail( + "the following test command failed:\n{s}", + .{try Step.allocPrintCmd(arena, self.cwd, final_argv)}, + ); + } + }, else => { - try step.handleChildProcessTerm(result.term, self.cwd, argv); + try step.handleChildProcessTerm(result.term, self.cwd, final_argv); }, } } const ChildProcResult = struct { - // These use boolean flags instead of optionals as a workaround for - // https://github.com/ziglang/zig/issues/14783 - stdout: []const u8, - stderr: []const u8, - stdout_null: bool, - stderr_null: bool, term: std.process.Child.Term, elapsed_ns: u64, peak_rss: usize, + + stdio: StdIoResult, }; fn spawnChildAndCollect( self: *RunStep, argv: []const []const u8, has_side_effects: bool, + prog_node: *std.Progress.Node, ) !ChildProcResult { const b = self.step.owner; const arena = b.allocator; @@ -848,16 +874,19 @@ fn spawnChildAndCollect( .infer_from_args => if (has_side_effects) .Inherit else .Close, .inherit => .Inherit, .check => .Close, + .zig_test => .Pipe, }; child.stdout_behavior = switch (self.stdio) { .infer_from_args => if (has_side_effects) .Inherit else .Ignore, .inherit => .Inherit, .check => |checks| if (checksContainStdout(checks.items)) .Pipe else .Ignore, + .zig_test => .Pipe, }; child.stderr_behavior = switch (self.stdio) { .infer_from_args => if (has_side_effects) .Inherit else .Pipe, .inherit => .Inherit, .check => .Pipe, + .zig_test => .Pipe, }; if (self.captured_stdout != null) child.stdout_behavior = .Pipe; if (self.captured_stderr != null) child.stderr_behavior = .Pipe; @@ -871,6 +900,219 @@ fn spawnChildAndCollect( }); var timer = try std.time.Timer.start(); + const result = if (self.stdio == .zig_test) + evalZigTest(self, &child, prog_node) + else + evalGeneric(self, &child); + + const term = try child.wait(); + const elapsed_ns = timer.read(); + + return .{ + .stdio = try result, + .term = term, + .elapsed_ns = elapsed_ns, + .peak_rss = child.resource_usage_statistics.getMaxRss() orelse 0, + }; +} + +const StdIoResult = struct { + // These use boolean flags instead of optionals as a workaround for + // https://github.com/ziglang/zig/issues/14783 + stdout: []const u8, + stderr: []const u8, + stdout_null: bool, + stderr_null: bool, + test_results: Step.TestResults, +}; + +fn evalZigTest( + self: *RunStep, + child: *std.process.Child, + prog_node: *std.Progress.Node, +) !StdIoResult { + const gpa = self.step.owner.allocator; + const arena = self.step.owner.allocator; + + var poller = std.io.poll(gpa, enum { stdout, stderr }, .{ + .stdout = child.stdout.?, + .stderr = child.stderr.?, + }); + defer poller.deinit(); + + try sendMessage(child.stdin.?, .query_test_metadata); + + const Header = std.zig.Server.Message.Header; + + const stdout = poller.fifo(.stdout); + const stderr = poller.fifo(.stderr); + + var fail_count: u32 = 0; + var skip_count: u32 = 0; + var leak_count: u32 = 0; + var test_count: u32 = 0; + + var metadata: ?TestMetadata = null; + + var sub_prog_node: ?std.Progress.Node = null; + defer if (sub_prog_node) |*n| n.end(); + + poll: while (try poller.poll()) { + while (true) { + const buf = stdout.readableSlice(0); + assert(stdout.readableLength() == buf.len); + if (buf.len < @sizeOf(Header)) continue :poll; + const header = @ptrCast(*align(1) const Header, buf[0..@sizeOf(Header)]); + const header_and_msg_len = header.bytes_len + @sizeOf(Header); + if (buf.len < header_and_msg_len) continue :poll; + const body = buf[@sizeOf(Header)..][0..header.bytes_len]; + switch (header.tag) { + .zig_version => { + if (!std.mem.eql(u8, builtin.zig_version_string, body)) { + return self.step.fail( + "zig version mismatch build runner vs compiler: '{s}' vs '{s}'", + .{ builtin.zig_version_string, body }, + ); + } + }, + .test_metadata => { + const TmHdr = std.zig.Server.Message.TestMetadata; + const tm_hdr = @ptrCast(*align(1) const TmHdr, body); + test_count = tm_hdr.tests_len; + + const names_bytes = body[@sizeOf(TmHdr)..][0 .. test_count * @sizeOf(u32)]; + const async_frame_lens_bytes = body[@sizeOf(TmHdr) + names_bytes.len ..][0 .. test_count * @sizeOf(u32)]; + const expected_panic_msgs_bytes = body[@sizeOf(TmHdr) + names_bytes.len + async_frame_lens_bytes.len ..][0 .. test_count * @sizeOf(u32)]; + const string_bytes = body[@sizeOf(TmHdr) + names_bytes.len + async_frame_lens_bytes.len + expected_panic_msgs_bytes.len ..][0..tm_hdr.string_bytes_len]; + + const names = std.mem.bytesAsSlice(u32, names_bytes); + const async_frame_lens = std.mem.bytesAsSlice(u32, async_frame_lens_bytes); + const expected_panic_msgs = std.mem.bytesAsSlice(u32, expected_panic_msgs_bytes); + const names_aligned = try arena.alloc(u32, names.len); + for (names_aligned, names) |*dest, src| dest.* = src; + + const async_frame_lens_aligned = try arena.alloc(u32, async_frame_lens.len); + for (async_frame_lens_aligned, async_frame_lens) |*dest, src| dest.* = src; + + const expected_panic_msgs_aligned = try arena.alloc(u32, expected_panic_msgs.len); + for (expected_panic_msgs_aligned, expected_panic_msgs) |*dest, src| dest.* = src; + + prog_node.setEstimatedTotalItems(names.len); + metadata = .{ + .string_bytes = try arena.dupe(u8, string_bytes), + .names = names_aligned, + .async_frame_lens = async_frame_lens_aligned, + .expected_panic_msgs = expected_panic_msgs_aligned, + .next_index = 0, + .prog_node = prog_node, + }; + + try requestNextTest(child.stdin.?, &metadata.?, &sub_prog_node); + }, + .test_results => { + const md = metadata.?; + + const TrHdr = std.zig.Server.Message.TestResults; + const tr_hdr = @ptrCast(*align(1) const TrHdr, body); + fail_count += @boolToInt(tr_hdr.flags.fail); + skip_count += @boolToInt(tr_hdr.flags.skip); + leak_count += @boolToInt(tr_hdr.flags.leak); + + if (tr_hdr.flags.fail or tr_hdr.flags.leak) { + const name = std.mem.sliceTo(md.string_bytes[md.names[tr_hdr.index]..], 0); + const msg = std.mem.trim(u8, stderr.readableSlice(0), "\n"); + const label = if (tr_hdr.flags.fail) "failed" else "leaked"; + if (msg.len > 0) { + try self.step.addError("'{s}' {s}: {s}", .{ name, label, msg }); + } else { + try self.step.addError("'{s}' {s}", .{ name, label }); + } + stderr.discard(msg.len); + } + + try requestNextTest(child.stdin.?, &metadata.?, &sub_prog_node); + }, + else => {}, // ignore other messages + } + stdout.discard(header_and_msg_len); + } + } + + if (stderr.readableLength() > 0) { + const msg = std.mem.trim(u8, try stderr.toOwnedSlice(), "\n"); + if (msg.len > 0) try self.step.result_error_msgs.append(arena, msg); + } + + // Send EOF to stdin. + child.stdin.?.close(); + child.stdin = null; + + return .{ + .stdout = &.{}, + .stderr = &.{}, + .stdout_null = true, + .stderr_null = true, + .test_results = .{ + .test_count = test_count, + .fail_count = fail_count, + .skip_count = skip_count, + .leak_count = leak_count, + }, + }; +} + +const TestMetadata = struct { + names: []const u32, + async_frame_lens: []const u32, + expected_panic_msgs: []const u32, + string_bytes: []const u8, + next_index: u32, + prog_node: *std.Progress.Node, + + fn testName(tm: TestMetadata, index: u32) []const u8 { + return std.mem.sliceTo(tm.string_bytes[tm.names[index]..], 0); + } +}; + +fn requestNextTest(in: fs.File, metadata: *TestMetadata, sub_prog_node: *?std.Progress.Node) !void { + while (metadata.next_index < metadata.names.len) { + const i = metadata.next_index; + metadata.next_index += 1; + + if (metadata.async_frame_lens[i] != 0) continue; + if (metadata.expected_panic_msgs[i] != 0) continue; + + const name = metadata.testName(i); + if (sub_prog_node.*) |*n| n.end(); + sub_prog_node.* = metadata.prog_node.start(name, 0); + + try sendRunTestMessage(in, i); + return; + } else { + try sendMessage(in, .exit); + } +} + +fn sendMessage(file: std.fs.File, tag: std.zig.Client.Message.Tag) !void { + const header: std.zig.Client.Message.Header = .{ + .tag = tag, + .bytes_len = 0, + }; + try file.writeAll(std.mem.asBytes(&header)); +} + +fn sendRunTestMessage(file: std.fs.File, index: u32) !void { + const header: std.zig.Client.Message.Header = .{ + .tag = .run_test, + .bytes_len = 4, + }; + const full_msg = std.mem.asBytes(&header) ++ std.mem.asBytes(&index); + try file.writeAll(full_msg); +} + +fn evalGeneric(self: *RunStep, child: *std.process.Child) !StdIoResult { + const arena = self.step.owner.allocator; + if (self.stdin) |stdin| { child.stdin.?.writeAll(stdin) catch |err| { return self.step.fail("unable to write stdin: {s}", .{@errorName(err)}); @@ -925,17 +1167,12 @@ fn spawnChildAndCollect( } } - const term = try child.wait(); - const elapsed_ns = timer.read(); - return .{ .stdout = stdout_bytes, .stderr = stderr_bytes, .stdout_null = stdout_null, .stderr_null = stderr_null, - .term = term, - .elapsed_ns = elapsed_ns, - .peak_rss = child.resource_usage_statistics.getMaxRss() orelse 0, + .test_results = .{}, }; } @@ -966,7 +1203,7 @@ fn failForeign( exe: *CompileStep, ) error{ MakeFailed, MakeSkipped, OutOfMemory } { switch (self.stdio) { - .check => { + .check, .zig_test => { if (self.skip_foreign_checks) return error.MakeSkipped; @@ -987,7 +1224,7 @@ fn failForeign( fn hashStdIo(hh: *std.Build.Cache.HashHelper, stdio: StdIo) void { switch (stdio) { - .infer_from_args, .inherit => {}, + .infer_from_args, .inherit, .zig_test => {}, .check => |checks| for (checks.items) |check| { hh.add(@as(std.meta.Tag(StdIo.Check), check)); switch (check) { diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig index 45aa635972..05c4faa52d 100644 --- a/lib/std/Build/Step.zig +++ b/lib/std/Build/Step.zig @@ -35,11 +35,27 @@ result_cached: bool, result_duration_ns: ?u64, /// 0 means unavailable or not reported. result_peak_rss: usize, +test_results: TestResults, /// The return addresss associated with creation of this step that can be useful /// to print along with debugging messages. debug_stack_trace: [n_debug_stack_frames]usize, +pub const TestResults = struct { + fail_count: u32 = 0, + skip_count: u32 = 0, + leak_count: u32 = 0, + test_count: u32 = 0, + + pub fn isSuccess(tr: TestResults) bool { + return tr.fail_count == 0 and tr.leak_count == 0; + } + + pub fn passCount(tr: TestResults) u32 { + return tr.test_count - tr.fail_count - tr.skip_count; + } +}; + pub const MakeFn = *const fn (self: *Step, prog_node: *std.Progress.Node) anyerror!void; const n_debug_stack_frames = 4; @@ -134,6 +150,7 @@ pub fn init(options: Options) Step { .result_cached = false, .result_duration_ns = null, .result_peak_rss = 0, + .test_results = .{}, }; } @@ -152,6 +169,10 @@ pub fn make(s: *Step, prog_node: *std.Progress.Node) error{ MakeFailed, MakeSkip }, }; + if (!s.test_results.isSuccess()) { + return error.MakeFailed; + } + if (s.max_rss != 0 and s.result_peak_rss > s.max_rss) { const msg = std.fmt.allocPrint(arena, "memory usage peaked at {d} bytes, exceeding the declared upper bound of {d}", .{ s.result_peak_rss, s.max_rss, @@ -346,9 +367,7 @@ pub fn evalZigProcess( s.result_cached = ebp_hdr.flags.cache_hit; result = try arena.dupe(u8, body[@sizeOf(EbpHdr)..]); }, - _ => { - // Unrecognized message. - }, + else => {}, // ignore other messages } stdout.discard(header_and_msg_len); } @@ -475,3 +494,11 @@ fn failWithCacheError(s: *Step, man: *const std.Build.Cache.Manifest, err: anyer const prefix = man.cache.prefixes()[pp.prefix].path orelse ""; return s.fail("{s}: {s}/{s}", .{ @errorName(err), prefix, pp.sub_path }); } + +pub fn writeManifest(s: *Step, man: *std.Build.Cache.Manifest) !void { + if (s.test_results.isSuccess()) { + man.writeManifest() catch |err| { + try s.addError("unable to write cache manifest: {s}", .{@errorName(err)}); + }; + } +} diff --git a/lib/std/Build/WriteFileStep.zig b/lib/std/Build/WriteFileStep.zig index 9b033e5ae2..dee79af5be 100644 --- a/lib/std/Build/WriteFileStep.zig +++ b/lib/std/Build/WriteFileStep.zig @@ -282,7 +282,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { }); } - try man.writeManifest(); + try step.writeManifest(&man); } const std = @import("../std.zig"); diff --git a/lib/std/zig/Client.zig b/lib/std/zig/Client.zig index a68c189e57..af4c29d37d 100644 --- a/lib/std/zig/Client.zig +++ b/lib/std/zig/Client.zig @@ -26,6 +26,13 @@ pub const Message = struct { /// swap. /// No body. hot_update, + /// Ask the test runner for metadata about all the unit tests that can + /// be run. Server will respond with a `test_metadata` message. + /// No body. + query_test_metadata, + /// Ask the test runner to run a particular test. + /// The message body is a u32 test index. + run_test, _, }; diff --git a/lib/std/zig/Server.zig b/lib/std/zig/Server.zig index d34b2193e9..3238f22043 100644 --- a/lib/std/zig/Server.zig +++ b/lib/std/zig/Server.zig @@ -1,3 +1,7 @@ +in: std.fs.File, +out: std.fs.File, +receive_fifo: std.fifo.LinearFifo(u8, .Dynamic), + pub const Message = struct { pub const Header = extern struct { tag: Tag, @@ -14,6 +18,11 @@ pub const Message = struct { progress, /// Body is a EmitBinPath. emit_bin_path, + /// Body is a TestMetadata + test_metadata, + /// Body is a TestResults + test_results, + _, }; @@ -26,6 +35,33 @@ pub const Message = struct { string_bytes_len: u32, }; + /// Trailing: + /// * name: [tests_len]u32 + /// - null-terminated string_bytes index + /// * async_frame_len: [tests_len]u32, + /// - 0 means not async + /// * expected_panic_msg: [tests_len]u32, + /// - null-terminated string_bytes index + /// - 0 means does not expect pani + /// * string_bytes: [string_bytes_len]u8, + pub const TestMetadata = extern struct { + string_bytes_len: u32, + tests_len: u32, + }; + + pub const TestResults = extern struct { + index: u32, + flags: Flags, + + pub const Flags = packed struct(u8) { + fail: bool, + skip: bool, + leak: bool, + + reserved: u5 = 0, + }; + }; + /// Trailing: /// * the file system path the emitted binary can be found pub const EmitBinPath = extern struct { @@ -37,3 +73,167 @@ pub const Message = struct { }; }; }; + +pub const Options = struct { + gpa: Allocator, + in: std.fs.File, + out: std.fs.File, + zig_version: []const u8, +}; + +pub fn init(options: Options) !Server { + var s: Server = .{ + .in = options.in, + .out = options.out, + .receive_fifo = std.fifo.LinearFifo(u8, .Dynamic).init(options.gpa), + }; + try s.serveStringMessage(.zig_version, options.zig_version); + return s; +} + +pub fn deinit(s: *Server) void { + s.receive_fifo.deinit(); + s.* = undefined; +} + +pub fn receiveMessage(s: *Server) !InMessage.Header { + const Header = InMessage.Header; + const fifo = &s.receive_fifo; + + while (true) { + const buf = fifo.readableSlice(0); + assert(fifo.readableLength() == buf.len); + if (buf.len >= @sizeOf(Header)) { + const header = @ptrCast(*align(1) const Header, buf[0..@sizeOf(Header)]); + + if (buf.len - @sizeOf(Header) >= header.bytes_len) { + const result = header.*; + fifo.discard(@sizeOf(Header)); + return result; + } else { + const needed = header.bytes_len - (buf.len - @sizeOf(Header)); + const write_buffer = try fifo.writableWithSize(needed); + const amt = try s.in.read(write_buffer); + fifo.update(amt); + continue; + } + } + + const write_buffer = try fifo.writableWithSize(256); + const amt = try s.in.read(write_buffer); + fifo.update(amt); + } +} + +pub fn receiveBody_u32(s: *Server) !u32 { + const fifo = &s.receive_fifo; + const buf = fifo.readableSlice(0); + const result = @ptrCast(*align(1) const u32, buf[0..4]).*; + fifo.discard(4); + return result; +} + +pub fn serveStringMessage(s: *Server, tag: OutMessage.Tag, msg: []const u8) !void { + return s.serveMessage(.{ + .tag = tag, + .bytes_len = @intCast(u32, msg.len), + }, &.{msg}); +} + +pub fn serveMessage( + s: *const Server, + header: OutMessage.Header, + bufs: []const []const u8, +) !void { + var iovecs: [10]std.os.iovec_const = undefined; + iovecs[0] = .{ + .iov_base = @ptrCast([*]const u8, &header), + .iov_len = @sizeOf(OutMessage.Header), + }; + for (bufs, iovecs[1 .. bufs.len + 1]) |buf, *iovec| { + iovec.* = .{ + .iov_base = buf.ptr, + .iov_len = buf.len, + }; + } + try s.out.writevAll(iovecs[0 .. bufs.len + 1]); +} + +pub fn serveEmitBinPath( + s: *Server, + fs_path: []const u8, + header: OutMessage.EmitBinPath, +) !void { + try s.serveMessage(.{ + .tag = .emit_bin_path, + .bytes_len = @intCast(u32, fs_path.len + @sizeOf(OutMessage.EmitBinPath)), + }, &.{ + std.mem.asBytes(&header), + fs_path, + }); +} + +pub fn serveTestResults( + s: *Server, + msg: OutMessage.TestResults, +) !void { + try s.serveMessage(.{ + .tag = .test_results, + .bytes_len = @intCast(u32, @sizeOf(OutMessage.TestResults)), + }, &.{ + std.mem.asBytes(&msg), + }); +} + +pub fn serveErrorBundle(s: *Server, error_bundle: std.zig.ErrorBundle) !void { + const eb_hdr: OutMessage.ErrorBundle = .{ + .extra_len = @intCast(u32, error_bundle.extra.len), + .string_bytes_len = @intCast(u32, error_bundle.string_bytes.len), + }; + const bytes_len = @sizeOf(OutMessage.ErrorBundle) + + 4 * error_bundle.extra.len + error_bundle.string_bytes.len; + try s.serveMessage(.{ + .tag = .error_bundle, + .bytes_len = @intCast(u32, bytes_len), + }, &.{ + std.mem.asBytes(&eb_hdr), + // TODO: implement @ptrCast between slices changing the length + std.mem.sliceAsBytes(error_bundle.extra), + error_bundle.string_bytes, + }); +} + +pub const TestMetadata = struct { + names: []const u32, + async_frame_sizes: []const u32, + expected_panic_msgs: []const u32, + string_bytes: []const u8, +}; + +pub fn serveTestMetadata(s: *Server, test_metadata: TestMetadata) !void { + const header: OutMessage.TestMetadata = .{ + .tests_len = @intCast(u32, test_metadata.names.len), + .string_bytes_len = @intCast(u32, test_metadata.string_bytes.len), + }; + const bytes_len = @sizeOf(OutMessage.TestMetadata) + + 3 * 4 * test_metadata.names.len + test_metadata.string_bytes.len; + return s.serveMessage(.{ + .tag = .test_metadata, + .bytes_len = @intCast(u32, bytes_len), + }, &.{ + std.mem.asBytes(&header), + // TODO: implement @ptrCast between slices changing the length + std.mem.sliceAsBytes(test_metadata.names), + std.mem.sliceAsBytes(test_metadata.async_frame_sizes), + std.mem.sliceAsBytes(test_metadata.expected_panic_msgs), + test_metadata.string_bytes, + }); +} + +const OutMessage = std.zig.Server.Message; +const InMessage = std.zig.Client.Message; + +const Server = @This(); +const std = @import("std"); +const Allocator = std.mem.Allocator; +const assert = std.debug.assert; diff --git a/lib/test_runner.zig b/lib/test_runner.zig index 5968fdaa54..47ababbc2c 100644 --- a/lib/test_runner.zig +++ b/lib/test_runner.zig @@ -8,14 +8,130 @@ pub const std_options = struct { }; var log_err_count: usize = 0; +var cmdline_buffer: [4096]u8 = undefined; +var fba = std.heap.FixedBufferAllocator.init(&cmdline_buffer); pub fn main() void { - if (builtin.zig_backend != .stage1 and - builtin.zig_backend != .stage2_llvm and - builtin.zig_backend != .stage2_c) + if (builtin.zig_backend == .stage2_wasm or + builtin.zig_backend == .stage2_x86_64 or + builtin.zig_backend == .stage2_aarch64) { - return main2() catch @panic("test failure"); + return mainSimple() catch @panic("test failure"); } + + const args = std.process.argsAlloc(fba.allocator()) catch + @panic("unable to parse command line args"); + + var listen = false; + + for (args[1..]) |arg| { + if (std.mem.eql(u8, arg, "--listen=-")) { + listen = true; + } else { + @panic("unrecognized command line argument"); + } + } + + if (listen) { + return mainServer(); + } else { + return mainTerminal(); + } +} + +fn mainServer() void { + return mainServerFallible() catch @panic("internal test runner failure"); +} + +fn mainServerFallible() !void { + var server = try std.zig.Server.init(.{ + .gpa = fba.allocator(), + .in = std.io.getStdIn(), + .out = std.io.getStdOut(), + .zig_version = builtin.zig_version_string, + }); + defer server.deinit(); + + while (true) { + const hdr = try server.receiveMessage(); + switch (hdr.tag) { + .exit => { + return std.process.exit(0); + }, + .query_test_metadata => { + std.testing.allocator_instance = .{}; + defer if (std.testing.allocator_instance.deinit()) { + @panic("internal test runner memory leak"); + }; + + var string_bytes: std.ArrayListUnmanaged(u8) = .{}; + defer string_bytes.deinit(std.testing.allocator); + try string_bytes.append(std.testing.allocator, 0); // Reserve 0 for null. + + const test_fns = builtin.test_functions; + const names = try std.testing.allocator.alloc(u32, test_fns.len); + defer std.testing.allocator.free(names); + const async_frame_sizes = try std.testing.allocator.alloc(u32, test_fns.len); + defer std.testing.allocator.free(async_frame_sizes); + const expected_panic_msgs = try std.testing.allocator.alloc(u32, test_fns.len); + defer std.testing.allocator.free(expected_panic_msgs); + + for (test_fns, names, async_frame_sizes, expected_panic_msgs) |test_fn, *name, *async_frame_size, *expected_panic_msg| { + name.* = @intCast(u32, string_bytes.items.len); + try string_bytes.ensureUnusedCapacity(std.testing.allocator, test_fn.name.len + 1); + string_bytes.appendSliceAssumeCapacity(test_fn.name); + string_bytes.appendAssumeCapacity(0); + + async_frame_size.* = @intCast(u32, test_fn.async_frame_size orelse 0); + expected_panic_msg.* = 0; + } + + try server.serveTestMetadata(.{ + .names = names, + .async_frame_sizes = async_frame_sizes, + .expected_panic_msgs = expected_panic_msgs, + .string_bytes = string_bytes.items, + }); + }, + + .run_test => { + std.testing.allocator_instance = .{}; + const index = try server.receiveBody_u32(); + const test_fn = builtin.test_functions[index]; + if (test_fn.async_frame_size != null) + @panic("TODO test runner implement async tests"); + var fail = false; + var skip = false; + var leak = false; + test_fn.func() catch |err| switch (err) { + error.SkipZigTest => skip = true, + else => { + fail = true; + if (@errorReturnTrace()) |trace| { + std.debug.dumpStackTrace(trace.*); + } + }, + }; + leak = std.testing.allocator_instance.deinit(); + try server.serveTestResults(.{ + .index = index, + .flags = .{ + .fail = fail, + .skip = skip, + .leak = leak, + }, + }); + }, + + else => { + std.debug.print("unsupported message: {x}", .{@enumToInt(hdr.tag)}); + std.process.exit(1); + }, + } + } +} + +fn mainTerminal() void { const test_fn_list = builtin.test_functions; var ok_count: usize = 0; var skip_count: usize = 0; @@ -118,51 +234,17 @@ pub fn log( } } -pub fn main2() anyerror!void { - var skipped: usize = 0; - var failed: usize = 0; - // Simpler main(), exercising fewer language features, so that stage2 can handle it. +/// Simpler main(), exercising fewer language features, so that +/// work-in-progress backends can handle it. +pub fn mainSimple() anyerror!void { + //const stderr = std.io.getStdErr(); for (builtin.test_functions) |test_fn| { test_fn.func() catch |err| { if (err != error.SkipZigTest) { - failed += 1; - } else { - skipped += 1; + //stderr.writeAll(test_fn.name) catch {}; + //stderr.writeAll("\n") catch {}; + return err; } }; } - if (builtin.zig_backend == .stage2_wasm or - builtin.zig_backend == .stage2_x86_64 or - builtin.zig_backend == .stage2_aarch64 or - builtin.zig_backend == .stage2_llvm or - builtin.zig_backend == .stage2_c) - { - const passed = builtin.test_functions.len - skipped - failed; - const stderr = std.io.getStdErr(); - writeInt(stderr, passed) catch {}; - stderr.writeAll(" passed; ") catch {}; - writeInt(stderr, skipped) catch {}; - stderr.writeAll(" skipped; ") catch {}; - writeInt(stderr, failed) catch {}; - stderr.writeAll(" failed.\n") catch {}; - } - if (failed != 0) { - return error.TestsFailed; - } -} - -fn writeInt(stderr: std.fs.File, int: usize) anyerror!void { - const base = 10; - var buf: [100]u8 = undefined; - var a: usize = int; - var index: usize = buf.len; - while (true) { - const digit = a % base; - index -= 1; - buf[index] = std.fmt.digitToChar(@intCast(u8, digit), .lower); - a /= base; - if (a == 0) break; - } - const slice = buf[index..]; - try stderr.writeAll(slice); } diff --git a/src/Server.zig b/src/Server.zig deleted file mode 100644 index a25dc93857..0000000000 --- a/src/Server.zig +++ /dev/null @@ -1,113 +0,0 @@ -in: std.fs.File, -out: std.fs.File, -receive_fifo: std.fifo.LinearFifo(u8, .Dynamic), - -pub const Options = struct { - gpa: Allocator, - in: std.fs.File, - out: std.fs.File, -}; - -pub fn init(options: Options) !Server { - var s: Server = .{ - .in = options.in, - .out = options.out, - .receive_fifo = std.fifo.LinearFifo(u8, .Dynamic).init(options.gpa), - }; - try s.serveStringMessage(.zig_version, build_options.version); - return s; -} - -pub fn deinit(s: *Server) void { - s.receive_fifo.deinit(); - s.* = undefined; -} - -pub fn receiveMessage(s: *Server) !InMessage.Header { - const Header = InMessage.Header; - const fifo = &s.receive_fifo; - - while (true) { - const buf = fifo.readableSlice(0); - assert(fifo.readableLength() == buf.len); - if (buf.len >= @sizeOf(Header)) { - const header = @ptrCast(*align(1) const Header, buf[0..@sizeOf(Header)]); - if (header.bytes_len != 0) - return error.InvalidClientMessage; - const result = header.*; - fifo.discard(@sizeOf(Header)); - return result; - } - - const write_buffer = try fifo.writableWithSize(256); - const amt = try s.in.read(write_buffer); - fifo.update(amt); - } -} - -pub fn serveStringMessage(s: *Server, tag: OutMessage.Tag, msg: []const u8) !void { - return s.serveMessage(.{ - .tag = tag, - .bytes_len = @intCast(u32, msg.len), - }, &.{msg}); -} - -pub fn serveMessage( - s: *const Server, - header: OutMessage.Header, - bufs: []const []const u8, -) !void { - var iovecs: [10]std.os.iovec_const = undefined; - iovecs[0] = .{ - .iov_base = @ptrCast([*]const u8, &header), - .iov_len = @sizeOf(OutMessage.Header), - }; - for (bufs, iovecs[1 .. bufs.len + 1]) |buf, *iovec| { - iovec.* = .{ - .iov_base = buf.ptr, - .iov_len = buf.len, - }; - } - try s.out.writevAll(iovecs[0 .. bufs.len + 1]); -} - -pub fn serveEmitBinPath( - s: *Server, - fs_path: []const u8, - header: std.zig.Server.Message.EmitBinPath, -) !void { - try s.serveMessage(.{ - .tag = .emit_bin_path, - .bytes_len = @intCast(u32, fs_path.len + @sizeOf(std.zig.Server.Message.EmitBinPath)), - }, &.{ - std.mem.asBytes(&header), - fs_path, - }); -} - -pub fn serveErrorBundle(s: *Server, error_bundle: std.zig.ErrorBundle) !void { - const eb_hdr: std.zig.Server.Message.ErrorBundle = .{ - .extra_len = @intCast(u32, error_bundle.extra.len), - .string_bytes_len = @intCast(u32, error_bundle.string_bytes.len), - }; - const bytes_len = @sizeOf(std.zig.Server.Message.ErrorBundle) + - 4 * error_bundle.extra.len + error_bundle.string_bytes.len; - try s.serveMessage(.{ - .tag = .error_bundle, - .bytes_len = @intCast(u32, bytes_len), - }, &.{ - std.mem.asBytes(&eb_hdr), - // TODO: implement @ptrCast between slices changing the length - std.mem.sliceAsBytes(error_bundle.extra), - error_bundle.string_bytes, - }); -} - -const OutMessage = std.zig.Server.Message; -const InMessage = std.zig.Client.Message; - -const Server = @This(); -const std = @import("std"); -const build_options = @import("build_options"); -const Allocator = std.mem.Allocator; -const assert = std.debug.assert; diff --git a/src/main.zig b/src/main.zig index e0283143d0..9602a5cd31 100644 --- a/src/main.zig +++ b/src/main.zig @@ -10,6 +10,7 @@ const ArrayList = std.ArrayList; const Ast = std.zig.Ast; const warn = std.log.warn; const ThreadPool = std.Thread.Pool; +const cleanExit = std.process.cleanExit; const tracy = @import("tracy.zig"); const Compilation = @import("Compilation.zig"); @@ -26,7 +27,7 @@ const target_util = @import("target.zig"); const crash_report = @import("crash_report.zig"); const Module = @import("Module.zig"); const AstGen = @import("AstGen.zig"); -const Server = @import("Server.zig"); +const Server = std.zig.Server; pub const std_options = struct { pub const wasiCwd = wasi_cwd; @@ -3545,6 +3546,7 @@ fn serve( .gpa = gpa, .in = in, .out = out, + .zig_version = build_options.version, }); defer server.deinit(); @@ -3656,8 +3658,8 @@ fn serve( ); } }, - _ => { - @panic("TODO unrecognized message from client"); + else => { + fatal("unrecognized message from client: 0x{x}", .{@enumToInt(hdr.tag)}); }, } } @@ -5624,19 +5626,6 @@ fn detectNativeTargetInfo(cross_target: std.zig.CrossTarget) !std.zig.system.Nat return std.zig.system.NativeTargetInfo.detect(cross_target); } -/// Indicate that we are now terminating with a successful exit code. -/// In debug builds, this is a no-op, so that the calling code's -/// cleanup mechanisms are tested and so that external tools that -/// check for resource leaks can be accurate. In release builds, this -/// calls exit(0), and does not return. -pub fn cleanExit() void { - if (builtin.mode == .Debug) { - return; - } else { - process.exit(0); - } -} - const usage_ast_check = \\Usage: zig ast-check [file] \\ diff --git a/src/objcopy.zig b/src/objcopy.zig index e821a94b59..c3305e8c04 100644 --- a/src/objcopy.zig +++ b/src/objcopy.zig @@ -8,8 +8,8 @@ const assert = std.debug.assert; const main = @import("main.zig"); const fatal = main.fatal; -const cleanExit = main.cleanExit; -const Server = @import("Server.zig"); +const Server = std.zig.Server; +const build_options = @import("build_options"); pub fn cmdObjCopy( gpa: Allocator, @@ -116,6 +116,7 @@ pub fn cmdObjCopy( .gpa = gpa, .in = std.io.getStdIn(), .out = std.io.getStdOut(), + .zig_version = build_options.version, }); defer server.deinit(); @@ -124,7 +125,7 @@ pub fn cmdObjCopy( const hdr = try server.receiveMessage(); switch (hdr.tag) { .exit => { - return cleanExit(); + return std.process.cleanExit(); }, .update => { if (seen_update) { @@ -144,7 +145,7 @@ pub fn cmdObjCopy( } } } - return cleanExit(); + return std.process.cleanExit(); } const usage = diff --git a/test/link/common_symbols/build.zig b/test/link/common_symbols/build.zig index e3c302f0f7..a8c276d1f3 100644 --- a/test/link/common_symbols/build.zig +++ b/test/link/common_symbols/build.zig @@ -24,5 +24,5 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize }); test_exe.linkLibrary(lib_a); - test_step.dependOn(&test_exe.step); + test_step.dependOn(&test_exe.run().step); } diff --git a/test/link/common_symbols_alignment/build.zig b/test/link/common_symbols_alignment/build.zig index 7d1d813447..83548f2d8a 100644 --- a/test/link/common_symbols_alignment/build.zig +++ b/test/link/common_symbols_alignment/build.zig @@ -24,5 +24,5 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize }); test_exe.linkLibrary(lib_a); - test_step.dependOn(&test_exe.step); + test_step.dependOn(&test_exe.run().step); } diff --git a/test/link/interdependent_static_c_libs/build.zig b/test/link/interdependent_static_c_libs/build.zig index c4118c1ca4..0d06410a79 100644 --- a/test/link/interdependent_static_c_libs/build.zig +++ b/test/link/interdependent_static_c_libs/build.zig @@ -35,5 +35,5 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize test_exe.linkLibrary(lib_b); test_exe.addIncludePath("."); - test_step.dependOn(&test_exe.step); + test_step.dependOn(&test_exe.run().step); } diff --git a/test/link/macho/tls/build.zig b/test/link/macho/tls/build.zig index e91f40bd59..5981fea194 100644 --- a/test/link/macho/tls/build.zig +++ b/test/link/macho/tls/build.zig @@ -32,5 +32,8 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize test_exe.linkLibrary(lib); test_exe.linkLibC(); - test_step.dependOn(&test_exe.step); + const run = test_exe.run(); + run.skip_foreign_checks = true; + + test_step.dependOn(&run.step); } diff --git a/test/src/Cases.zig b/test/src/Cases.zig index 5007939b14..6affe968d1 100644 --- a/test/src/Cases.zig +++ b/test/src/Cases.zig @@ -547,15 +547,12 @@ pub fn lowerToBuildSteps( parent_step.dependOn(&artifact.step); }, .Execution => |expected_stdout| { - if (case.is_test) { - parent_step.dependOn(&artifact.step); - } else { - const run = b.addRunArtifact(artifact); - run.skip_foreign_checks = true; + const run = b.addRunArtifact(artifact); + run.skip_foreign_checks = true; + if (!case.is_test) { run.expectStdOutEqual(expected_stdout); - - parent_step.dependOn(&run.step); } + parent_step.dependOn(&run.step); }, .Header => @panic("TODO"), } diff --git a/test/standalone/emit_asm_and_bin/build.zig b/test/standalone/emit_asm_and_bin/build.zig index 9bdfadc33d..594bf6552e 100644 --- a/test/standalone/emit_asm_and_bin/build.zig +++ b/test/standalone/emit_asm_and_bin/build.zig @@ -11,5 +11,5 @@ pub fn build(b: *std.Build) void { main.emit_asm = .{ .emit_to = b.pathFromRoot("main.s") }; main.emit_bin = .{ .emit_to = b.pathFromRoot("main") }; - test_step.dependOn(&main.step); + test_step.dependOn(&main.run().step); } diff --git a/test/standalone/global_linkage/build.zig b/test/standalone/global_linkage/build.zig index 2cf1b248a5..ddcddd612a 100644 --- a/test/standalone/global_linkage/build.zig +++ b/test/standalone/global_linkage/build.zig @@ -28,5 +28,5 @@ pub fn build(b: *std.Build) void { main.linkLibrary(obj1); main.linkLibrary(obj2); - test_step.dependOn(&main.step); + test_step.dependOn(&main.run().step); } diff --git a/test/standalone/issue_13970/build.zig b/test/standalone/issue_13970/build.zig index bbaaac5886..1eb8a5a121 100644 --- a/test/standalone/issue_13970/build.zig +++ b/test/standalone/issue_13970/build.zig @@ -17,7 +17,7 @@ pub fn build(b: *std.Build) void { test2.setTestRunner("src/main.zig"); test3.setTestRunner("src/main.zig"); - test_step.dependOn(&test1.step); - test_step.dependOn(&test2.step); - test_step.dependOn(&test3.step); + test_step.dependOn(&test1.run().step); + test_step.dependOn(&test2.run().step); + test_step.dependOn(&test3.run().step); } diff --git a/test/standalone/main_pkg_path/build.zig b/test/standalone/main_pkg_path/build.zig index cd49573692..a4dd301c43 100644 --- a/test/standalone/main_pkg_path/build.zig +++ b/test/standalone/main_pkg_path/build.zig @@ -9,5 +9,5 @@ pub fn build(b: *std.Build) void { }); test_exe.setMainPkgPath("."); - test_step.dependOn(&test_exe.step); + test_step.dependOn(&test_exe.run().step); } diff --git a/test/standalone/options/build.zig b/test/standalone/options/build.zig index 3f1e823359..5e894102a7 100644 --- a/test/standalone/options/build.zig +++ b/test/standalone/options/build.zig @@ -20,5 +20,5 @@ pub fn build(b: *std.Build) void { options.addOption([]const u8, "string", b.option([]const u8, "string", "s").?); const test_step = b.step("test", "Run unit tests"); - test_step.dependOn(&main.step); + test_step.dependOn(&main.run().step); } diff --git a/test/standalone/pie/build.zig b/test/standalone/pie/build.zig index 615111b6c2..546b4a922f 100644 --- a/test/standalone/pie/build.zig +++ b/test/standalone/pie/build.zig @@ -17,5 +17,5 @@ pub fn build(b: *std.Build) void { }); main.pie = true; - test_step.dependOn(&main.step); + test_step.dependOn(&main.run().step); } diff --git a/test/standalone/static_c_lib/build.zig b/test/standalone/static_c_lib/build.zig index 5996c978d8..794b813b75 100644 --- a/test/standalone/static_c_lib/build.zig +++ b/test/standalone/static_c_lib/build.zig @@ -21,5 +21,5 @@ pub fn build(b: *std.Build) void { test_exe.linkLibrary(foo); test_exe.addIncludePath("."); - test_step.dependOn(&test_exe.step); + test_step.dependOn(&test_exe.run().step); } diff --git a/test/standalone/test_runner_module_imports/build.zig b/test/standalone/test_runner_module_imports/build.zig index 973365e495..73c5536dc6 100644 --- a/test/standalone/test_runner_module_imports/build.zig +++ b/test/standalone/test_runner_module_imports/build.zig @@ -15,5 +15,5 @@ pub fn build(b: *std.Build) void { t.addModule("module2", module2); const test_step = b.step("test", "Run unit tests"); - test_step.dependOn(&t.step); + test_step.dependOn(&t.run().step); } diff --git a/test/standalone/test_runner_path/build.zig b/test/standalone/test_runner_path/build.zig index 40aad42b21..ce5b668054 100644 --- a/test/standalone/test_runner_path/build.zig +++ b/test/standalone/test_runner_path/build.zig @@ -8,7 +8,6 @@ pub fn build(b: *std.Build) void { const test_exe = b.addTest(.{ .root_source_file = .{ .path = "test.zig" }, - .kind = .test_exe, }); test_exe.test_runner = "test_runner.zig"; diff --git a/test/standalone/use_alias/build.zig b/test/standalone/use_alias/build.zig index 947db0828d..db47fe6692 100644 --- a/test/standalone/use_alias/build.zig +++ b/test/standalone/use_alias/build.zig @@ -12,5 +12,5 @@ pub fn build(b: *std.Build) void { }); main.addIncludePath("."); - test_step.dependOn(&main.step); + test_step.dependOn(&main.run().step); } diff --git a/test/tests.zig b/test/tests.zig index 9ba9639e2a..76ea537bb2 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -596,7 +596,7 @@ pub fn addStandaloneTests( }); if (case.link_libc) exe.linkLibC(); - step.dependOn(&exe.step); + step.dependOn(&exe.run().step); } } } @@ -981,14 +981,6 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step { }); const single_threaded_txt = if (test_target.single_threaded) "single" else "multi"; const backend_txt = if (test_target.backend) |backend| @tagName(backend) else "default"; - these_tests.setNamePrefix(b.fmt("{s}-{s}-{s}-{s}-{s}-{s} ", .{ - options.name, - triple_prefix, - @tagName(test_target.optimize_mode), - libc_prefix, - single_threaded_txt, - backend_txt, - })); these_tests.single_threaded = test_target.single_threaded; these_tests.setFilter(options.test_filter); if (test_target.link_libc) { @@ -1014,7 +1006,18 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step { }, }; - step.dependOn(&these_tests.step); + const run = these_tests.run(); + run.skip_foreign_checks = true; + run.setName(b.fmt("run test {s}-{s}-{s}-{s}-{s}-{s}", .{ + options.name, + triple_prefix, + @tagName(test_target.optimize_mode), + libc_prefix, + single_threaded_txt, + backend_txt, + })); + + step.dependOn(&run.step); } return step; } @@ -1053,7 +1056,9 @@ pub fn addCAbiTests(b: *std.Build, skip_non_native: bool, skip_release: bool) *S @tagName(optimize_mode), })); - step.dependOn(&test_step.step); + const run = test_step.run(); + run.skip_foreign_checks = true; + step.dependOn(&run.step); } } return step; From 8a8f148c8cb1254a3989584c54181218434b56c9 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 13 Mar 2023 12:13:37 -0700 Subject: [PATCH 253/294] test-stack-trace: set env to disable color The tests rely on the absence of terminal escape codes. --- test/src/StackTrace.zig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/src/StackTrace.zig b/test/src/StackTrace.zig index 5709b9d29e..c32720a210 100644 --- a/test/src/StackTrace.zig +++ b/test/src/StackTrace.zig @@ -82,6 +82,8 @@ fn addExpect( }); const run = b.addRunArtifact(exe); + run.removeEnvironmentVariable("ZIG_DEBUG_COLOR"); + run.setEnvironmentVariable("NO_COLOR", "1"); run.expectExitCode(1); run.expectStdOutEqual(""); From 61d7e31078fc54010009494d894e2461b41eeee2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 13 Mar 2023 12:15:45 -0700 Subject: [PATCH 254/294] remove bad unit test from std lib This unit test tested the command line arguments which were passed to the test runner, which is not really something that unit tests are supposed to observe. The proper way to test command line argument parsing is with a standalone test, where the set of command line arguments being tested for are also being controlled by the test itself. --- lib/std/process.zig | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/lib/std/process.zig b/lib/std/process.zig index d9bf09ee2a..d06a012af2 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -828,24 +828,6 @@ pub fn argsWithAllocator(allocator: Allocator) ArgIterator.InitError!ArgIterator return ArgIterator.initWithAllocator(allocator); } -test "args iterator" { - var ga = std.testing.allocator; - var it = try argsWithAllocator(ga); - defer it.deinit(); // no-op unless WASI or Windows - - const prog_name = it.next() orelse unreachable; - const expected_suffix = switch (builtin.os.tag) { - .wasi => "test.wasm", - .windows => "test.exe", - else => "test", - }; - const given_suffix = std.fs.path.basename(prog_name); - - try testing.expect(mem.eql(u8, expected_suffix, given_suffix)); - try testing.expect(it.next() == null); - try testing.expect(!it.skip()); -} - /// Caller must call argsFree on result. pub fn argsAlloc(allocator: Allocator) ![][:0]u8 { // TODO refactor to only make 1 allocation. From a0dd2919eb3b708909275ebdd5b07c5e828e622f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 13 Mar 2023 12:21:31 -0700 Subject: [PATCH 255/294] std.build.RunStep: clean up some leftover mess * Remove some functions that are no longer needed since EmulateableRunStep is gone. * Add removeEnvironmentVariable function. * Support printing environment variables in --verbose mode. --- lib/std/Build/RunStep.zig | 53 ++++++++++++++++----------------------- lib/std/Build/Step.zig | 38 ++++++++++++++++++++++++++-- 2 files changed, 57 insertions(+), 34 deletions(-) diff --git a/lib/std/Build/RunStep.zig b/lib/std/Build/RunStep.zig index 484600bf9b..b6bc0c43a4 100644 --- a/lib/std/Build/RunStep.zig +++ b/lib/std/Build/RunStep.zig @@ -206,40 +206,30 @@ pub fn clearEnvironment(self: *RunStep) void { } pub fn addPathDir(self: *RunStep, search_path: []const u8) void { - addPathDirInternal(&self.step, self.step.owner, search_path); -} - -/// For internal use only, users of `RunStep` should use `addPathDir` directly. -pub fn addPathDirInternal(step: *Step, builder: *std.Build, search_path: []const u8) void { - const env_map = getEnvMapInternal(step, builder.allocator); + const b = self.step.owner; + const env_map = getEnvMapInternal(self); const key = "PATH"; var prev_path = env_map.get(key); if (prev_path) |pp| { - const new_path = builder.fmt("{s}" ++ [1]u8{fs.path.delimiter} ++ "{s}", .{ pp, search_path }); + const new_path = b.fmt("{s}" ++ [1]u8{fs.path.delimiter} ++ "{s}", .{ pp, search_path }); env_map.put(key, new_path) catch @panic("OOM"); } else { - env_map.put(key, builder.dupePath(search_path)) catch @panic("OOM"); + env_map.put(key, b.dupePath(search_path)) catch @panic("OOM"); } } pub fn getEnvMap(self: *RunStep) *EnvMap { - return getEnvMapInternal(&self.step, self.step.owner.allocator); + return getEnvMapInternal(self); } -fn getEnvMapInternal(step: *Step, allocator: Allocator) *EnvMap { - const maybe_env_map = switch (step.id) { - .run => step.cast(RunStep).?.env_map, - else => unreachable, - }; - return maybe_env_map orelse { - const env_map = allocator.create(EnvMap) catch @panic("OOM"); - env_map.* = process.getEnvMap(allocator) catch @panic("unhandled error"); - switch (step.id) { - .run => step.cast(RunStep).?.env_map = env_map, - else => unreachable, - } +fn getEnvMapInternal(self: *RunStep) *EnvMap { + const arena = self.step.owner.allocator; + return self.env_map orelse { + const env_map = arena.create(EnvMap) catch @panic("OOM"); + env_map.* = process.getEnvMap(arena) catch @panic("unhandled error"); + self.env_map = env_map; return env_map; }; } @@ -250,6 +240,10 @@ pub fn setEnvironmentVariable(self: *RunStep, key: []const u8, value: []const u8 env_map.put(b.dupe(key), b.dupe(value)) catch @panic("unhandled error"); } +pub fn removeEnvironmentVariable(self: *RunStep, key: []const u8) void { + self.getEnvMap().remove(key); +} + /// Adds a check for exact stderr match. Does not add any other checks. pub fn expectStdErrEqual(self: *RunStep, bytes: []const u8) void { const new_check: StdIo.Check = .{ .expect_stderr_exact = self.step.owner.dupe(bytes) }; @@ -553,7 +547,7 @@ fn runCommand( const arena = b.allocator; try step.handleChildProcUnsupported(self.cwd, argv); - try Step.handleVerbose(step.owner, self.cwd, argv); + try Step.handleVerbose2(step.owner, self.cwd, self.env_map, argv); const allow_skip = switch (self.stdio) { .check, .zig_test => self.skip_foreign_checks, @@ -676,10 +670,10 @@ fn runCommand( if (exe.target.isWindows()) { // On Windows we don't have rpaths so we have to add .dll search paths to PATH - RunStep.addPathForDynLibsInternal(&self.step, b, exe); + self.addPathForDynLibs(exe); } - try Step.handleVerbose(step.owner, self.cwd, interp_argv.items); + try Step.handleVerbose2(step.owner, self.cwd, self.env_map, interp_argv.items); break :term spawnChildAndCollect(self, interp_argv.items, has_side_effects, prog_node) catch |e| { return step.fail("unable to spawn {s}: {s}", .{ @@ -1177,18 +1171,13 @@ fn evalGeneric(self: *RunStep, child: *std.process.Child) !StdIoResult { } fn addPathForDynLibs(self: *RunStep, artifact: *CompileStep) void { - addPathForDynLibsInternal(&self.step, self.step.owner, artifact); -} - -/// This should only be used for internal usage, this is called automatically -/// for the user. -pub fn addPathForDynLibsInternal(step: *Step, builder: *std.Build, artifact: *CompileStep) void { + const b = self.step.owner; for (artifact.link_objects.items) |link_object| { switch (link_object) { .other_step => |other| { if (other.target.isWindows() and other.isDynamicLibrary()) { - addPathDirInternal(step, builder, fs.path.dirname(other.getOutputSource().getPath(builder)).?); - addPathForDynLibsInternal(step, builder, other); + addPathDir(self, fs.path.dirname(other.getOutputSource().getPath(b)).?); + addPathForDynLibs(self, other); } }, else => {}, diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig index 05c4faa52d..890babc74e 100644 --- a/lib/std/Build/Step.zig +++ b/lib/std/Build/Step.zig @@ -427,11 +427,20 @@ pub fn handleVerbose( b: *Build, opt_cwd: ?[]const u8, argv: []const []const u8, +) error{OutOfMemory}!void { + return handleVerbose2(b, opt_cwd, null, argv); +} + +pub fn handleVerbose2( + b: *Build, + opt_cwd: ?[]const u8, + opt_env: ?*const std.process.EnvMap, + argv: []const []const u8, ) error{OutOfMemory}!void { if (b.verbose) { // Intention of verbose is to print all sub-process command lines to // stderr before spawning them. - const text = try allocPrintCmd(b.allocator, opt_cwd, argv); + const text = try allocPrintCmd2(b.allocator, opt_cwd, opt_env, argv); std.debug.print("{s}\n", .{text}); } } @@ -474,9 +483,34 @@ pub fn handleChildProcessTerm( } } -pub fn allocPrintCmd(arena: Allocator, opt_cwd: ?[]const u8, argv: []const []const u8) ![]u8 { +pub fn allocPrintCmd( + arena: Allocator, + opt_cwd: ?[]const u8, + argv: []const []const u8, +) ![]u8 { + return allocPrintCmd2(arena, opt_cwd, null, argv); +} + +pub fn allocPrintCmd2( + arena: Allocator, + opt_cwd: ?[]const u8, + opt_env: ?*const std.process.EnvMap, + argv: []const []const u8, +) ![]u8 { var buf: std.ArrayListUnmanaged(u8) = .{}; if (opt_cwd) |cwd| try buf.writer(arena).print("cd {s} && ", .{cwd}); + if (opt_env) |env| { + const process_env_map = try std.process.getEnvMap(arena); + var it = env.iterator(); + while (it.next()) |entry| { + const key = entry.key_ptr.*; + const value = entry.value_ptr.*; + if (process_env_map.get(key)) |process_value| { + if (std.mem.eql(u8, value, process_value)) continue; + } + try buf.writer(arena).print("{s}={s} ", .{ key, value }); + } + } for (argv) |arg| { try buf.writer(arena).print("{s} ", .{arg}); } From 8e6d46bca593088af7c7ec078f5534631e2a937d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 13 Mar 2023 12:21:42 -0700 Subject: [PATCH 256/294] test runner: remove one superfluous stack frame --- lib/test_runner.zig | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/test_runner.zig b/lib/test_runner.zig index 47ababbc2c..3d87264851 100644 --- a/lib/test_runner.zig +++ b/lib/test_runner.zig @@ -33,17 +33,13 @@ pub fn main() void { } if (listen) { - return mainServer(); + return mainServer() catch @panic("internal test runner failure"); } else { return mainTerminal(); } } -fn mainServer() void { - return mainServerFallible() catch @panic("internal test runner failure"); -} - -fn mainServerFallible() !void { +fn mainServer() !void { var server = try std.zig.Server.init(.{ .gpa = fba.allocator(), .in = std.io.getStdIn(), From bc79328dcf3a8653e26c7fe7b4cd8fc7fff6c421 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 13 Mar 2023 13:03:58 -0700 Subject: [PATCH 257/294] fix endianness when using test-runner in qemu --- lib/std/zig/Server.zig | 75 +++++++++++++++++++++++++++++++++++------- 1 file changed, 63 insertions(+), 12 deletions(-) diff --git a/lib/std/zig/Server.zig b/lib/std/zig/Server.zig index 3238f22043..011139f7c6 100644 --- a/lib/std/zig/Server.zig +++ b/lib/std/zig/Server.zig @@ -105,13 +105,17 @@ pub fn receiveMessage(s: *Server) !InMessage.Header { assert(fifo.readableLength() == buf.len); if (buf.len >= @sizeOf(Header)) { const header = @ptrCast(*align(1) const Header, buf[0..@sizeOf(Header)]); + const bytes_len = bswap(header.bytes_len); + const tag = bswap(header.tag); - if (buf.len - @sizeOf(Header) >= header.bytes_len) { - const result = header.*; + if (buf.len - @sizeOf(Header) >= bytes_len) { fifo.discard(@sizeOf(Header)); - return result; + return .{ + .tag = tag, + .bytes_len = bytes_len, + }; } else { - const needed = header.bytes_len - (buf.len - @sizeOf(Header)); + const needed = bytes_len - (buf.len - @sizeOf(Header)); const write_buffer = try fifo.writableWithSize(needed); const amt = try s.in.read(write_buffer); fifo.update(amt); @@ -130,7 +134,7 @@ pub fn receiveBody_u32(s: *Server) !u32 { const buf = fifo.readableSlice(0); const result = @ptrCast(*align(1) const u32, buf[0..4]).*; fifo.discard(4); - return result; + return bswap(result); } pub fn serveStringMessage(s: *Server, tag: OutMessage.Tag, msg: []const u8) !void { @@ -146,8 +150,9 @@ pub fn serveMessage( bufs: []const []const u8, ) !void { var iovecs: [10]std.os.iovec_const = undefined; + const header_le = bswap(header); iovecs[0] = .{ - .iov_base = @ptrCast([*]const u8, &header), + .iov_base = @ptrCast([*]const u8, &header_le), .iov_len = @sizeOf(OutMessage.Header), }; for (bufs, iovecs[1 .. bufs.len + 1]) |buf, *iovec| { @@ -177,11 +182,12 @@ pub fn serveTestResults( s: *Server, msg: OutMessage.TestResults, ) !void { + const msg_le = bswap(msg); try s.serveMessage(.{ .tag = .test_results, .bytes_len = @intCast(u32, @sizeOf(OutMessage.TestResults)), }, &.{ - std.mem.asBytes(&msg), + std.mem.asBytes(&msg_le), }); } @@ -204,19 +210,31 @@ pub fn serveErrorBundle(s: *Server, error_bundle: std.zig.ErrorBundle) !void { } pub const TestMetadata = struct { - names: []const u32, - async_frame_sizes: []const u32, - expected_panic_msgs: []const u32, + names: []u32, + async_frame_sizes: []u32, + expected_panic_msgs: []u32, string_bytes: []const u8, }; pub fn serveTestMetadata(s: *Server, test_metadata: TestMetadata) !void { const header: OutMessage.TestMetadata = .{ - .tests_len = @intCast(u32, test_metadata.names.len), - .string_bytes_len = @intCast(u32, test_metadata.string_bytes.len), + .tests_len = bswap(@intCast(u32, test_metadata.names.len)), + .string_bytes_len = bswap(@intCast(u32, test_metadata.string_bytes.len)), }; const bytes_len = @sizeOf(OutMessage.TestMetadata) + 3 * 4 * test_metadata.names.len + test_metadata.string_bytes.len; + + if (need_bswap) { + bswap_u32_array(test_metadata.names); + bswap_u32_array(test_metadata.async_frame_sizes); + bswap_u32_array(test_metadata.expected_panic_msgs); + } + defer if (need_bswap) { + bswap_u32_array(test_metadata.names); + bswap_u32_array(test_metadata.async_frame_sizes); + bswap_u32_array(test_metadata.expected_panic_msgs); + }; + return s.serveMessage(.{ .tag = .test_metadata, .bytes_len = @intCast(u32, bytes_len), @@ -230,10 +248,43 @@ pub fn serveTestMetadata(s: *Server, test_metadata: TestMetadata) !void { }); } +fn bswap(x: anytype) @TypeOf(x) { + if (!need_bswap) return x; + + const T = @TypeOf(x); + switch (@typeInfo(T)) { + .Enum => return @intToEnum(T, @byteSwap(@enumToInt(x))), + .Int => return @byteSwap(x), + .Struct => |info| switch (info.layout) { + .Extern => { + var result: T = undefined; + inline for (info.fields) |field| { + @field(result, field.name) = bswap(@field(x, field.name)); + } + return result; + }, + .Packed => { + const I = info.backing_integer.?; + return @bitCast(T, @byteSwap(@bitCast(I, x))); + }, + .Auto => @compileError("auto layout struct"), + }, + else => @compileError("bswap on type " ++ @typeName(T)), + } +} + +fn bswap_u32_array(slice: []u32) void { + comptime assert(need_bswap); + for (slice) |*elem| elem.* = @byteSwap(elem.*); +} + const OutMessage = std.zig.Server.Message; const InMessage = std.zig.Client.Message; const Server = @This(); +const builtin = @import("builtin"); const std = @import("std"); const Allocator = std.mem.Allocator; const assert = std.debug.assert; +const native_endian = builtin.target.cpu.arch.endian(); +const need_bswap = native_endian != .Little; From 149aa9afb7b57340c5dd7be8032b843fe439b668 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 13 Mar 2023 14:35:59 -0700 Subject: [PATCH 258/294] add a workaround for miscompilation regarding alignment See tracking issue #14904 --- lib/std/zig/Server.zig | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/lib/std/zig/Server.zig b/lib/std/zig/Server.zig index 011139f7c6..788e361782 100644 --- a/lib/std/zig/Server.zig +++ b/lib/std/zig/Server.zig @@ -105,8 +105,10 @@ pub fn receiveMessage(s: *Server) !InMessage.Header { assert(fifo.readableLength() == buf.len); if (buf.len >= @sizeOf(Header)) { const header = @ptrCast(*align(1) const Header, buf[0..@sizeOf(Header)]); - const bytes_len = bswap(header.bytes_len); - const tag = bswap(header.tag); + // workaround for https://github.com/ziglang/zig/issues/14904 + const bytes_len = bswap_and_workaround_u32(&header.bytes_len); + // workaround for https://github.com/ziglang/zig/issues/14904 + const tag = bswap_and_workaround_tag(&header.tag); if (buf.len - @sizeOf(Header) >= bytes_len) { fifo.discard(@sizeOf(Header)); @@ -278,6 +280,19 @@ fn bswap_u32_array(slice: []u32) void { for (slice) |*elem| elem.* = @byteSwap(elem.*); } +/// workaround for https://github.com/ziglang/zig/issues/14904 +fn bswap_and_workaround_u32(x: *align(1) const u32) u32 { + const bytes_ptr = @ptrCast(*const [4]u8, x); + return std.mem.readIntLittle(u32, bytes_ptr); +} + +/// workaround for https://github.com/ziglang/zig/issues/14904 +fn bswap_and_workaround_tag(x: *align(1) const InMessage.Tag) InMessage.Tag { + const bytes_ptr = @ptrCast(*const [4]u8, x); + const int = std.mem.readIntLittle(u32, bytes_ptr); + return @intToEnum(InMessage.Tag, int); +} + const OutMessage = std.zig.Server.Message; const InMessage = std.zig.Client.Message; From c5cdc0262b727e87d81fe4a92780234bf8125bd9 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 13 Mar 2023 14:43:21 -0700 Subject: [PATCH 259/294] add the new extern test to standalone tests This was from master branch commit c93e0d86187cb589d6726acd36f741f3d87a96be. Since standalone test are completely reworked, I had to resolve the merge conflict later, in this commit. --- test/standalone.zig | 4 ++++ test/standalone/extern/build.zig | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/test/standalone.zig b/test/standalone.zig index 29a32d878f..4cf795a85f 100644 --- a/test/standalone.zig +++ b/test/standalone.zig @@ -147,6 +147,10 @@ pub const build_cases = [_]BuildCase{ .build_root = "test/standalone/embed_generated_file", .import = @import("standalone/embed_generated_file/build.zig"), }, + .{ + .build_root = "test/standalone/extern", + .import = @import("standalone/extern/build.zig"), + }, .{ .build_root = "test/standalone/dep_diamond", .import = @import("standalone/dep_diamond/build.zig"), diff --git a/test/standalone/extern/build.zig b/test/standalone/extern/build.zig index 8a44a6ca8f..153380e91d 100644 --- a/test/standalone/extern/build.zig +++ b/test/standalone/extern/build.zig @@ -1,7 +1,7 @@ const std = @import("std"); pub fn build(b: *std.Build) void { - const optimize = b.standardOptimizeOption(.{}); + const optimize: std.builtin.OptimizeMode = .Debug; const obj = b.addObject(.{ .name = "exports", From 66eb910fe44af7f16046bd14de76f2356f565992 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 13 Mar 2023 17:26:26 -0700 Subject: [PATCH 260/294] remove redundant link test --- test/link.zig | 4 ---- 1 file changed, 4 deletions(-) diff --git a/test/link.zig b/test/link.zig index 610cbd4a3d..aa0ed4817e 100644 --- a/test/link.zig +++ b/test/link.zig @@ -84,10 +84,6 @@ pub const cases = [_]Case{ .build_root = "test/link/macho/bugs/13457", .import = @import("link/macho/bugs/13457/build.zig"), }, - .{ - .build_root = "test/link/macho/bugs/13457", - .import = @import("link/macho/bugs/13457/build.zig"), - }, .{ .build_root = "test/link/macho/dead_strip", .import = @import("link/macho/dead_strip/build.zig"), From fa9108c3d4cb279e2ce1412fc75eb694b5c6baaf Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 13 Mar 2023 17:26:38 -0700 Subject: [PATCH 261/294] add skip_foreign_checks=true on a standalone test --- test/standalone/pie/build.zig | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/standalone/pie/build.zig b/test/standalone/pie/build.zig index 546b4a922f..e7ef5f97cc 100644 --- a/test/standalone/pie/build.zig +++ b/test/standalone/pie/build.zig @@ -17,5 +17,8 @@ pub fn build(b: *std.Build) void { }); main.pie = true; - test_step.dependOn(&main.run().step); + const run = main.run(); + run.skip_foreign_checks = true; + + test_step.dependOn(&run.step); } From a26a2e1a178b2f2f971bbe3ed8873ac77319befe Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 13 Mar 2023 17:27:05 -0700 Subject: [PATCH 262/294] build runner: fix compilation errors on windows --- lib/std/Build/Step.zig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig index 890babc74e..123122a74b 100644 --- a/lib/std/Build/Step.zig +++ b/lib/std/Build/Step.zig @@ -487,7 +487,7 @@ pub fn allocPrintCmd( arena: Allocator, opt_cwd: ?[]const u8, argv: []const []const u8, -) ![]u8 { +) Allocator.Error![]u8 { return allocPrintCmd2(arena, opt_cwd, null, argv); } @@ -496,11 +496,11 @@ pub fn allocPrintCmd2( opt_cwd: ?[]const u8, opt_env: ?*const std.process.EnvMap, argv: []const []const u8, -) ![]u8 { +) Allocator.Error![]u8 { var buf: std.ArrayListUnmanaged(u8) = .{}; if (opt_cwd) |cwd| try buf.writer(arena).print("cd {s} && ", .{cwd}); if (opt_env) |env| { - const process_env_map = try std.process.getEnvMap(arena); + const process_env_map = std.process.getEnvMap(arena) catch std.process.EnvMap.init(arena); var it = env.iterator(); while (it.next()) |entry| { const key = entry.key_ptr.*; From b1299d515351acbfa1f169c8e65a3fa2b3e39f1a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 13 Mar 2023 18:21:37 -0700 Subject: [PATCH 263/294] build runner: tweak progress bar display --- lib/build_runner.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/build_runner.zig b/lib/build_runner.zig index bb341574a7..5009bd6cf0 100644 --- a/lib/build_runner.zig +++ b/lib/build_runner.zig @@ -395,7 +395,7 @@ fn runStepNames( { defer parent_prog_node.end(); - var step_prog = parent_prog_node.start("run steps", step_stack.count()); + var step_prog = parent_prog_node.start("steps", step_stack.count()); defer step_prog.end(); var wait_group: std.Thread.WaitGroup = .{}; From e098b287e18b8a7a4df0fdb48d32fb4376daba07 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 13 Mar 2023 18:23:17 -0700 Subject: [PATCH 264/294] std.fs.File.writevAll: fix behavior for 0-length vectors The OS layer expects pointer addresses to be inside the application's address space even if the length is zero. Meanwhile, in Zig, slices may have undefined pointer addresses when the length is zero. So this function now modifies the iov_base fields when the length is zero. This is a companion commit to b4893eb05565b2cb033c6ed88617d73faf878455. --- lib/std/fs/file.zig | 17 +++++++++++++++-- lib/std/os.zig | 7 +++++-- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/lib/std/fs/file.zig b/lib/std/fs/file.zig index f81841a662..8235b8aecf 100644 --- a/lib/std/fs/file.zig +++ b/lib/std/fs/file.zig @@ -1196,13 +1196,26 @@ pub const File = struct { } } - /// The `iovecs` parameter is mutable because this function needs to mutate the fields in - /// order to handle partial writes from the underlying OS layer. + /// The `iovecs` parameter is mutable because: + /// * This function needs to mutate the fields in order to handle partial + /// writes from the underlying OS layer. + /// * The OS layer expects pointer addresses to be inside the application's address space + /// even if the length is zero. Meanwhile, in Zig, slices may have undefined pointer + /// addresses when the length is zero. So this function modifies the iov_base fields + /// when the length is zero. /// See https://github.com/ziglang/zig/issues/7699 /// See equivalent function: `std.net.Stream.writevAll`. pub fn writevAll(self: File, iovecs: []os.iovec_const) WriteError!void { if (iovecs.len == 0) return; + // We use the address of this local variable for all zero-length + // vectors so that the OS does not complain that we are giving it + // addresses outside the application's address space. + var garbage: [1]u8 = undefined; + for (iovecs) |*v| { + if (v.iov_len == 0) v.iov_base = &garbage; + } + var i: usize = 0; while (true) { var amt = try self.writev(iovecs[i..]); diff --git a/lib/std/os.zig b/lib/std/os.zig index b11aac493f..722028b3f8 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -767,8 +767,8 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize { /// * Windows /// On these systems, the read races with concurrent writes to the same file descriptor. /// -/// This function assumes that all zero-length vectors have a pointer within the address -/// space of the application. +/// This function assumes that all vectors, including zero-length vectors, have +/// a pointer within the address space of the application. pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize { if (builtin.os.tag == .windows) { // TODO improve this to use ReadFileScatter @@ -1170,6 +1170,9 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize { /// used to perform the I/O. `error.WouldBlock` is not possible on Windows. /// /// If `iov.len` is larger than `IOV_MAX`, a partial write will occur. +/// +/// This function assumes that all vectors, including zero-length vectors, have +/// a pointer within the address space of the application. pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!usize { if (builtin.os.tag == .windows) { // TODO improve this to use WriteFileScatter From 1dbb616e73404b789b5c85491eb905b193b9b4cb Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 13 Mar 2023 18:23:53 -0700 Subject: [PATCH 265/294] Module: handle incremental update from ZIR with AST errors --- src/Module.zig | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Module.zig b/src/Module.zig index 1520a7d1b2..c47e4fc234 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -3867,6 +3867,9 @@ fn updateZirRefs(mod: *Module, file: *File, old_zir: Zir) !void { const gpa = mod.gpa; const new_zir = file.zir; + // The root decl will be null if the previous ZIR had AST errors. + const root_decl = file.root_decl.unwrap() orelse return; + // Maps from old ZIR to new ZIR, struct_decl, enum_decl, etc. Any instruction which // creates a namespace, gets mapped from old to new here. var inst_map: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .{}; @@ -3884,7 +3887,6 @@ fn updateZirRefs(mod: *Module, file: *File, old_zir: Zir) !void { var decl_stack: ArrayListUnmanaged(Decl.Index) = .{}; defer decl_stack.deinit(gpa); - const root_decl = file.root_decl.unwrap().?; try decl_stack.append(gpa, root_decl); file.deleted_decls.clearRetainingCapacity(); From 171977dc1c9d15a405b70bfa980c7d188410979e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 13 Mar 2023 18:24:17 -0700 Subject: [PATCH 266/294] test-cases: fix incorrectly linking libc when backend is llvm Now link_libc=1 must be used to link with libc, instead of the test harness assuming that using the llvm backend means additionally linking with libc. --- test/cases/f32_passed_to_variadic_fn.zig | 3 ++- test/cases/fn_typeinfo_passed_to_comptime_fn.zig | 1 + test/cases/llvm/hello_world.zig | 1 + test/src/Cases.zig | 5 ++++- 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/test/cases/f32_passed_to_variadic_fn.zig b/test/cases/f32_passed_to_variadic_fn.zig index c029b4b69f..4ae1d2cf08 100644 --- a/test/cases/f32_passed_to_variadic_fn.zig +++ b/test/cases/f32_passed_to_variadic_fn.zig @@ -9,7 +9,8 @@ pub fn main() void { // run // backend=llvm // target=x86_64-linux-gnu +// link_libc=1 // // f64: 2.000000 // f32: 10.000000 -// \ No newline at end of file +// diff --git a/test/cases/fn_typeinfo_passed_to_comptime_fn.zig b/test/cases/fn_typeinfo_passed_to_comptime_fn.zig index 31673e5b81..fb64788126 100644 --- a/test/cases/fn_typeinfo_passed_to_comptime_fn.zig +++ b/test/cases/fn_typeinfo_passed_to_comptime_fn.zig @@ -14,4 +14,5 @@ fn foo(comptime info: std.builtin.Type) !void { // run // is_test=1 +// backend=llvm // diff --git a/test/cases/llvm/hello_world.zig b/test/cases/llvm/hello_world.zig index 4243191b0f..0f75f624ec 100644 --- a/test/cases/llvm/hello_world.zig +++ b/test/cases/llvm/hello_world.zig @@ -7,6 +7,7 @@ pub fn main() void { // run // backend=llvm // target=x86_64-linux,x86_64-macos +// link_libc=1 // // hello world! // diff --git a/test/src/Cases.zig b/test/src/Cases.zig index 6affe968d1..c3a4c1df47 100644 --- a/test/src/Cases.zig +++ b/test/src/Cases.zig @@ -382,6 +382,7 @@ fn addFromDirInner( const backends = try manifest.getConfigForKeyAlloc(ctx.arena, "backend", Backend); const targets = try manifest.getConfigForKeyAlloc(ctx.arena, "target", CrossTarget); const is_test = try manifest.getConfigForKeyAssertSingle("is_test", bool); + const link_libc = try manifest.getConfigForKeyAssertSingle("link_libc", bool); const output_mode = try manifest.getConfigForKeyAssertSingle("output_mode", std.builtin.OutputMode); var cases = std.ArrayList(usize).init(ctx.arena); @@ -397,7 +398,7 @@ fn addFromDirInner( .updates = std.ArrayList(Cases.Update).init(ctx.cases.allocator), .is_test = is_test, .output_mode = output_mode, - .link_libc = backend == .llvm, + .link_libc = link_libc, .deps = std.ArrayList(DepModule).init(ctx.cases.allocator), }); try cases.append(next); @@ -745,6 +746,8 @@ const TestManifestConfigDefaults = struct { }; } else if (std.mem.eql(u8, key, "is_test")) { return "0"; + } else if (std.mem.eql(u8, key, "link_libc")) { + return "0"; } else unreachable; } }; From 0f88ad8c72797b1bc8899e2eb1d1c1c07a39addf Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 13 Mar 2023 18:52:18 -0700 Subject: [PATCH 267/294] std.Build.CompileStep: proper step dependency on headers Rather than calling make() from within make(). --- lib/std/Build/CompileStep.zig | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/std/Build/CompileStep.zig b/lib/std/Build/CompileStep.zig index 5753641966..92492e9593 100644 --- a/lib/std/Build/CompileStep.zig +++ b/lib/std/Build/CompileStep.zig @@ -1112,6 +1112,10 @@ fn linkLibraryOrObject(self: *CompileStep, other: *CompileStep) void { self.step.dependOn(&other.step); self.link_objects.append(.{ .other_step = other }) catch @panic("OOM"); self.include_dirs.append(.{ .other_step = other }) catch @panic("OOM"); + + for (other.installed_headers.items) |install_step| { + self.step.dependOn(install_step); + } } fn appendModuleArgs( @@ -1699,9 +1703,6 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { try zig_args.append(fs.path.dirname(h_path).?); } if (other.installed_headers.items.len > 0) { - for (other.installed_headers.items) |install_step| { - try install_step.make(prog_node); - } try zig_args.append("-I"); try zig_args.append(b.pathJoin(&.{ other.step.owner.install_prefix, "include", From bde12930939c50fbfa344c3701a01885e6dd06d4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 13 Mar 2023 19:11:36 -0700 Subject: [PATCH 268/294] CLI: remove the experimental --watch flag The compiler REPL will move to an external process that communicates with the compiler over the binary protocol. --- src/main.zig | 142 ++++----------------------------------------------- 1 file changed, 9 insertions(+), 133 deletions(-) diff --git a/src/main.zig b/src/main.zig index 9602a5cd31..19f1a6e52c 100644 --- a/src/main.zig +++ b/src/main.zig @@ -365,7 +365,6 @@ const usage_build_generic = \\ \\General Options: \\ -h, --help Print this help and exit - \\ --watch Enable compiler REPL \\ --color [auto|off|on] Enable or disable colored error messages \\ -femit-bin[=path] (default) Output machine code \\ -fno-emit-bin Do not output machine code @@ -700,7 +699,6 @@ fn buildOutputType( var formatted_panics: ?bool = null; var function_sections = false; var no_builtin = false; - var watch = false; var listen: Listen = .none; var debug_compile_errors = false; var verbose_link = (builtin.os.tag != .wasi or builtin.link_libc) and std.process.hasEnvVarConstant("ZIG_VERBOSE_LINK"); @@ -1163,7 +1161,6 @@ fn buildOutputType( const next_arg = args_iter.nextOrFatal(); if (mem.eql(u8, next_arg, "-")) { listen = .stdio; - watch = true; } else { if (build_options.omit_pkg_fetching_code) unreachable; // example: --listen 127.0.0.1:9000 @@ -1174,11 +1171,9 @@ fn buildOutputType( fatal("invalid port number: '{s}': {s}", .{ port_text, @errorName(err) }); listen = .{ .ip4 = std.net.Ip4Address.parse(host, port) catch |err| fatal("invalid host: '{s}': {s}", .{ host, @errorName(err) }) }; - watch = true; } } else if (mem.eql(u8, arg, "--listen=-")) { listen = .stdio; - watch = true; } else if (mem.eql(u8, arg, "--debug-link-snapshot")) { if (!build_options.enable_link_snapshots) { std.log.warn("Zig was compiled without linker snapshots enabled (-Dlink-snapshot). --debug-link-snapshot has no effect.", .{}); @@ -1207,8 +1202,6 @@ fn buildOutputType( test_evented_io = true; } else if (mem.eql(u8, arg, "--test-no-exec")) { test_no_exec = true; - } else if (mem.eql(u8, arg, "--watch")) { - watch = true; } else if (mem.eql(u8, arg, "-ftime-report")) { time_report = true; } else if (mem.eql(u8, arg, "-fstack-report")) { @@ -3355,7 +3348,7 @@ fn buildOutputType( }; updateModule(gpa, comp, hook) catch |err| switch (err) { - error.SemanticAnalyzeFail => if (!watch) process.exit(1), + error.SemanticAnalyzeFail => if (listen == .none) process.exit(1), else => |e| return e, }; if (build_options.only_c) return cleanExit(); @@ -3411,7 +3404,6 @@ fn buildOutputType( self_exe_path.?, arg_mode, target_info, - watch, &comp_destroyed, all_args, runtime_args_start, @@ -3419,113 +3411,6 @@ fn buildOutputType( ); } - // TODO move this REPL implementation to the standard library / build - // system and have it be a CLI abstraction layer on top of the real, actual - // binary protocol of the compiler. Make it actually interface through the - // server protocol. This way the REPL does not have any special powers that - // an IDE couldn't also have. - - const stdin = std.io.getStdIn().reader(); - const stderr = std.io.getStdErr().writer(); - var repl_buf: [1024]u8 = undefined; - - const ReplCmd = enum { - update, - help, - run, - update_and_run, - }; - - var last_cmd: ReplCmd = .help; - - while (watch) { - try stderr.print("(zig) ", .{}); - try comp.makeBinFileExecutable(); - if (stdin.readUntilDelimiterOrEof(&repl_buf, '\n') catch |err| { - try stderr.print("\nUnable to parse command: {s}\n", .{@errorName(err)}); - continue; - }) |line| { - const actual_line = mem.trimRight(u8, line, "\r\n "); - const cmd: ReplCmd = blk: { - if (mem.eql(u8, actual_line, "update")) { - break :blk .update; - } else if (mem.eql(u8, actual_line, "exit")) { - break; - } else if (mem.eql(u8, actual_line, "help")) { - break :blk .help; - } else if (mem.eql(u8, actual_line, "run")) { - break :blk .run; - } else if (mem.eql(u8, actual_line, "update-and-run")) { - break :blk .update_and_run; - } else if (actual_line.len == 0) { - break :blk last_cmd; - } else { - try stderr.print("unknown command: {s}\n", .{actual_line}); - continue; - } - }; - last_cmd = cmd; - switch (cmd) { - .update => { - tracy.frameMark(); - if (output_mode == .Exe) { - try comp.makeBinFileWritable(); - } - updateModule(gpa, comp, hook) catch |err| switch (err) { - error.SemanticAnalyzeFail => continue, - else => |e| return e, - }; - }, - .help => { - try stderr.writeAll(repl_help); - }, - .run => { - tracy.frameMark(); - try runOrTest( - comp, - gpa, - arena, - test_exec_args.items, - self_exe_path.?, - arg_mode, - target_info, - watch, - &comp_destroyed, - all_args, - runtime_args_start, - link_libc, - ); - }, - .update_and_run => { - tracy.frameMark(); - if (output_mode == .Exe) { - try comp.makeBinFileWritable(); - } - updateModule(gpa, comp, hook) catch |err| switch (err) { - error.SemanticAnalyzeFail => continue, - else => |e| return e, - }; - try comp.makeBinFileExecutable(); - try runOrTest( - comp, - gpa, - arena, - test_exec_args.items, - self_exe_path.?, - arg_mode, - target_info, - watch, - &comp_destroyed, - all_args, - runtime_args_start, - link_libc, - ); - }, - } - } else { - break; - } - } // Skip resource deallocation in release builds; let the OS do it. return cleanExit(); } @@ -3822,7 +3707,6 @@ fn runOrTest( self_exe_path: []const u8, arg_mode: ArgMode, target_info: std.zig.system.NativeTargetInfo, - watch: bool, comp_destroyed: *bool, all_args: []const []const u8, runtime_args_start: ?usize, @@ -3853,7 +3737,7 @@ fn runOrTest( // We do not execve for tests because if the test fails we want to print // the error message and invocation below. - if (std.process.can_execv and arg_mode == .run and !watch) { + if (std.process.can_execv and arg_mode == .run) { // execv releases the locks; no need to destroy the Compilation here. const err = std.process.execve(gpa, argv.items, &env_map); try warnAboutForeignBinaries(arena, arg_mode, target_info, link_libc); @@ -3866,12 +3750,10 @@ fn runOrTest( child.stdout_behavior = .Inherit; child.stderr_behavior = .Inherit; - if (!watch) { - // Here we release all the locks associated with the Compilation so - // that whatever this child process wants to do won't deadlock. - comp.destroy(); - comp_destroyed.* = true; - } + // Here we release all the locks associated with the Compilation so + // that whatever this child process wants to do won't deadlock. + comp.destroy(); + comp_destroyed.* = true; const term = child.spawnAndWait() catch |err| { try warnAboutForeignBinaries(arena, arg_mode, target_info, link_libc); @@ -3883,19 +3765,13 @@ fn runOrTest( switch (term) { .Exited => |code| { if (code == 0) { - if (!watch) return cleanExit(); - } else if (watch) { - warn("process exited with code {d}", .{code}); + return cleanExit(); } else { process.exit(code); } }, else => { - if (watch) { - warn("process aborted abnormally", .{}); - } else { - process.exit(1); - } + process.exit(1); }, } }, @@ -3903,7 +3779,7 @@ fn runOrTest( switch (term) { .Exited => |code| { if (code == 0) { - if (!watch) return cleanExit(); + return cleanExit(); } else { const cmd = try std.mem.join(arena, " ", argv.items); fatal("the following test command failed with exit code {d}:\n{s}", .{ code, cmd }); From 2b0929929d67e222ca6a9523a3a594ed456c4a51 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 13 Mar 2023 19:23:08 -0700 Subject: [PATCH 269/294] std.Build.Cache: handle ENOENT on createFile race There are no dir components, so you would think that this was unreachable, however we have observed on macOS two processes racing to do openat() with O_CREAT manifest in ENOENT. --- lib/std/Build/Cache.zig | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/std/Build/Cache.zig b/lib/std/Build/Cache.zig index bd2b8a8927..6c5c876c8b 100644 --- a/lib/std/Build/Cache.zig +++ b/lib/std/Build/Cache.zig @@ -439,7 +439,10 @@ pub const Manifest = struct { self.manifest_file = manifest_file; self.have_exclusive_lock = true; } else |err| switch (err) { - error.WouldBlock => { + // There are no dir components, so you would think that this was + // unreachable, however we have observed on macOS two processes racing + // to do openat() with O_CREAT manifest in ENOENT. + error.WouldBlock, error.FileNotFound => { self.manifest_file = try self.cache.manifest_dir.openFile(&manifest_file_path, .{ .lock = .Shared, }); From 22d94eaf32bc303ff757f442991f570b264b24c3 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 13 Mar 2023 23:16:28 -0700 Subject: [PATCH 270/294] disable std lib unit tests that hard code port numbers See tracking issue #14907 --- lib/std/os/linux/io_uring.zig | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/lib/std/os/linux/io_uring.zig b/lib/std/os/linux/io_uring.zig index 61bf39105f..a302f2cdf1 100644 --- a/lib/std/os/linux/io_uring.zig +++ b/lib/std/os/linux/io_uring.zig @@ -1976,6 +1976,11 @@ test "close" { test "accept/connect/send/recv" { if (builtin.os.tag != .linux) return error.SkipZigTest; + if (true) { + // https://github.com/ziglang/zig/issues/14907 + return error.SkipZigTest; + } + var ring = IO_Uring.init(16, 0) catch |err| switch (err) { error.SystemOutdated => return error.SkipZigTest, error.PermissionDenied => return error.SkipZigTest, @@ -2017,6 +2022,11 @@ test "accept/connect/send/recv" { test "sendmsg/recvmsg" { if (builtin.os.tag != .linux) return error.SkipZigTest; + if (true) { + // https://github.com/ziglang/zig/issues/14907 + return error.SkipZigTest; + } + var ring = IO_Uring.init(2, 0) catch |err| switch (err) { error.SystemOutdated => return error.SkipZigTest, error.PermissionDenied => return error.SkipZigTest, @@ -2024,6 +2034,7 @@ test "sendmsg/recvmsg" { }; defer ring.deinit(); + if (true) @compileError("don't hard code port numbers in unit tests"); // https://github.com/ziglang/zig/issues/14907 const address_server = try net.Address.parseIp4("127.0.0.1", 3131); const server = try os.socket(address_server.any.family, os.SOCK.DGRAM, 0); @@ -2223,6 +2234,11 @@ test "timeout_remove" { test "accept/connect/recv/link_timeout" { if (builtin.os.tag != .linux) return error.SkipZigTest; + if (true) { + // https://github.com/ziglang/zig/issues/14907 + return error.SkipZigTest; + } + var ring = IO_Uring.init(16, 0) catch |err| switch (err) { error.SystemOutdated => return error.SkipZigTest, error.PermissionDenied => return error.SkipZigTest, @@ -2372,6 +2388,11 @@ test "statx" { test "accept/connect/recv/cancel" { if (builtin.os.tag != .linux) return error.SkipZigTest; + if (true) { + // https://github.com/ziglang/zig/issues/14907 + return error.SkipZigTest; + } + var ring = IO_Uring.init(16, 0) catch |err| switch (err) { error.SystemOutdated => return error.SkipZigTest, error.PermissionDenied => return error.SkipZigTest, @@ -2509,6 +2530,11 @@ test "register_files_update" { test "shutdown" { if (builtin.os.tag != .linux) return error.SkipZigTest; + if (true) { + // https://github.com/ziglang/zig/issues/14907 + return error.SkipZigTest; + } + var ring = IO_Uring.init(16, 0) catch |err| switch (err) { error.SystemOutdated => return error.SkipZigTest, error.PermissionDenied => return error.SkipZigTest, @@ -2516,6 +2542,7 @@ test "shutdown" { }; defer ring.deinit(); + if (true) @compileError("don't hard code port numbers in unit tests"); // https://github.com/ziglang/zig/issues/14907 const address = try net.Address.parseIp4("127.0.0.1", 3131); // Socket bound, expect shutdown to work @@ -3060,6 +3087,11 @@ test "remove_buffers" { test "provide_buffers: accept/connect/send/recv" { if (builtin.os.tag != .linux) return error.SkipZigTest; + if (true) { + // https://github.com/ziglang/zig/issues/14907 + return error.SkipZigTest; + } + var ring = IO_Uring.init(16, 0) catch |err| switch (err) { error.SystemOutdated => return error.SkipZigTest, error.PermissionDenied => return error.SkipZigTest, @@ -3236,6 +3268,7 @@ const SocketTestHarness = struct { fn createSocketTestHarness(ring: *IO_Uring) !SocketTestHarness { // Create a TCP server socket + if (true) @compileError("don't hard code port numbers in unit tests"); // https://github.com/ziglang/zig/issues/14907 const address = try net.Address.parseIp4("127.0.0.1", 3131); const kernel_backlog = 1; const listener_socket = try os.socket(address.any.family, os.SOCK.STREAM | os.SOCK.CLOEXEC, 0); From 941cae4331ff4922ccef08a5d3d5879a0d1fa786 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 13 Mar 2023 23:41:41 -0700 Subject: [PATCH 271/294] revert a change to C ABI tests See tracking issue #14908 --- test/tests.zig | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/test/tests.zig b/test/tests.zig index 76ea537bb2..7af1ba6c3b 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -1045,9 +1045,13 @@ pub fn addCAbiTests(b: *std.Build, skip_non_native: bool, skip_release: bool) *S } test_step.linkLibC(); test_step.addCSourceFile("test/c_abi/cfuncs.c", &.{"-std=c99"}); - // This test is intentionally trying to check if the external ABI is - // done properly. LTO would be a hindrance to this. - test_step.want_lto = false; + + // test-c-abi should test both with LTO on and with LTO off. Only + // some combinations are passing currently: + // https://github.com/ziglang/zig/issues/14908 + if (c_abi_target.isWindows() and (c_abi_target.getCpuArch() == .x86 or builtin.target.os.tag == .linux)) { + test_step.want_lto = false; + } const triple_prefix = c_abi_target.zigTriple(b.allocator) catch @panic("OOM"); test_step.setNamePrefix(b.fmt("{s}-{s}-{s} ", .{ From 3e328c89b7016cb688c88ddb11a8949851500928 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 13 Mar 2023 23:42:15 -0700 Subject: [PATCH 272/294] std.Build.CompileStep: remove setNamePrefix and add setName --- lib/std/Build/CompileStep.zig | 11 ++--------- test/tests.zig | 6 ++---- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/lib/std/Build/CompileStep.zig b/lib/std/Build/CompileStep.zig index 92492e9593..d73e5d3b41 100644 --- a/lib/std/Build/CompileStep.zig +++ b/lib/std/Build/CompileStep.zig @@ -83,7 +83,6 @@ c_std: std.Build.CStd, zig_lib_dir: ?[]const u8, main_pkg_path: ?[]const u8, exec_cmd_args: ?[]const ?[]const u8, -name_prefix: []const u8, filter: ?[]const u8, test_evented_io: bool = false, test_runner: ?[]const u8, @@ -374,7 +373,6 @@ pub fn create(owner: *std.Build, options: Options) *CompileStep { .zig_lib_dir = null, .main_pkg_path = null, .exec_cmd_args = null, - .name_prefix = "", .filter = null, .test_runner = null, .disable_stack_probing = false, @@ -847,10 +845,10 @@ fn linkSystemLibraryInner(self: *CompileStep, name: []const u8, opts: struct { }) catch @panic("OOM"); } -pub fn setNamePrefix(self: *CompileStep, text: []const u8) void { +pub fn setName(self: *CompileStep, text: []const u8) void { const b = self.step.owner; assert(self.kind == .@"test"); - self.name_prefix = b.dupe(text); + self.name = b.dupe(text); } pub fn setFilter(self: *CompileStep, text: ?[]const u8) void { @@ -1424,11 +1422,6 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { try zig_args.append("--test-evented-io"); } - if (self.name_prefix.len != 0) { - try zig_args.append("--test-name-prefix"); - try zig_args.append(self.name_prefix); - } - if (self.test_runner) |test_runner| { try zig_args.append("--test-runner"); try zig_args.append(b.pathFromRoot(test_runner)); diff --git a/test/tests.zig b/test/tests.zig index 7af1ba6c3b..737cf5e291 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -1054,10 +1054,8 @@ pub fn addCAbiTests(b: *std.Build, skip_non_native: bool, skip_release: bool) *S } const triple_prefix = c_abi_target.zigTriple(b.allocator) catch @panic("OOM"); - test_step.setNamePrefix(b.fmt("{s}-{s}-{s} ", .{ - "test-c-abi", - triple_prefix, - @tagName(optimize_mode), + test_step.setName(b.fmt("test-c-abi-{s}-{s} ", .{ + triple_prefix, @tagName(optimize_mode), })); const run = test_step.run(); From 63bd0fe58e80742d273a962c03145ce598f060b6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 14 Mar 2023 11:41:37 -0700 Subject: [PATCH 273/294] use DEC graphics instead of Unicode for box drawing --- lib/build_runner.zig | 21 +++++++++++++++------ lib/std/debug.zig | 30 ++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/lib/build_runner.zig b/lib/build_runner.zig index 5009bd6cf0..5f5601a68d 100644 --- a/lib/build_runner.zig +++ b/lib/build_runner.zig @@ -532,14 +532,17 @@ const PrintNode = struct { last: bool = false, }; -fn printPrefix(node: *PrintNode, stderr: std.fs.File) !void { +fn printPrefix(node: *PrintNode, stderr: std.fs.File, ttyconf: std.debug.TTY.Config) !void { const parent = node.parent orelse return; if (parent.parent == null) return; - try printPrefix(parent, stderr); + try printPrefix(parent, stderr, ttyconf); if (parent.last) { try stderr.writeAll(" "); } else { - try stderr.writeAll("│ "); + try stderr.writeAll(switch (ttyconf) { + .no_color, .windows_api => "| ", + .escape_codes => "\x1B\x28\x30\x78\x1B\x28\x42 ", // │ + }); } } @@ -552,14 +555,20 @@ fn printTreeStep( step_stack: *std.AutoArrayHashMapUnmanaged(*Step, void), ) !void { const first = step_stack.swapRemove(s); - try printPrefix(parent_node, stderr); + try printPrefix(parent_node, stderr, ttyconf); if (!first) try ttyconf.setColor(stderr, .Dim); if (parent_node.parent != null) { if (parent_node.last) { - try stderr.writeAll("└─ "); + try stderr.writeAll(switch (ttyconf) { + .no_color, .windows_api => "+- ", + .escape_codes => "\x1B\x28\x30\x6d\x71\x1B\x28\x42 ", // └─ + }); } else { - try stderr.writeAll("├─ "); + try stderr.writeAll(switch (ttyconf) { + .no_color, .windows_api => "+- ", + .escape_codes => "\x1B\x28\x30\x74\x71\x1B\x28\x42 ", // ├─ + }); } } diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 3c5f6d2edf..6abceed9b8 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -685,6 +685,36 @@ pub const TTY = struct { }, }; } + + pub fn writeDEC(conf: Config, writer: anytype, codepoint: u8) !void { + const bytes = switch (conf) { + .no_color, .windows_api => switch (codepoint) { + 0x50...0x5e => @as(*const [1]u8, &codepoint), + 0x6a => "+", // ┘ + 0x6b => "+", // ┐ + 0x6c => "+", // ┌ + 0x6d => "+", // └ + 0x6e => "+", // ┼ + 0x71 => "-", // ─ + 0x74 => "+", // ├ + 0x75 => "+", // ┤ + 0x76 => "+", // ┴ + 0x77 => "+", // ┬ + 0x78 => "|", // │ + else => " ", // TODO + }, + .escape_codes => switch (codepoint) { + // Here we avoid writing the DEC beginning sequence and + // ending sequence in separate syscalls by putting the + // beginning and ending sequence into the same string + // literals, to prevent terminals ending up in bad states + // in case a crash happens between syscalls. + inline 0x50...0x7f => |x| "\x1B\x28\x30" ++ [1]u8{x} ++ "\x1B\x28\x42", + else => unreachable, + }, + }; + return writer.writeAll(bytes); + } }; }; From 11de55d0ddd54afabb3e26a85adba0a1f0bbe35b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 14 Mar 2023 11:42:00 -0700 Subject: [PATCH 274/294] std.Build.Cache: handle ENOENT on createFile race Companion commit to 628fec41593a2d2eca8b504e4fe90de9823aeded --- lib/std/Build/Cache.zig | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/std/Build/Cache.zig b/lib/std/Build/Cache.zig index 6c5c876c8b..92e20b1f9d 100644 --- a/lib/std/Build/Cache.zig +++ b/lib/std/Build/Cache.zig @@ -422,7 +422,11 @@ pub const Manifest = struct { self.have_exclusive_lock = true; return false; // cache miss; exclusive lock already held } else |err| switch (err) { - error.WouldBlock => continue, + // There are no dir components, so you would think + // that this was unreachable, however we have + // observed on macOS two processes racing to do + // openat() with O_CREAT manifest in ENOENT. + error.WouldBlock, error.FileNotFound => continue, else => |e| return e, } }, From 37a7d2c78d27a327c70bb6865e1c6d114459a3af Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 14 Mar 2023 14:36:34 -0700 Subject: [PATCH 275/294] std.Build.RunStep: fix handling spawn failure The error was caught and created a Step failure rather than bubbling up so that the interpreter logic could handle it. Fixes hundreds of test failures on Windows. --- lib/std/Build/RunStep.zig | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/std/Build/RunStep.zig b/lib/std/Build/RunStep.zig index b6bc0c43a4..d2fb20bdae 100644 --- a/lib/std/Build/RunStep.zig +++ b/lib/std/Build/RunStep.zig @@ -676,7 +676,7 @@ fn runCommand( try Step.handleVerbose2(step.owner, self.cwd, self.env_map, interp_argv.items); break :term spawnChildAndCollect(self, interp_argv.items, has_side_effects, prog_node) catch |e| { - return step.fail("unable to spawn {s}: {s}", .{ + return step.fail("unable to spawn interpreter {s}: {s}", .{ interp_argv.items[0], @errorName(e), }); }; @@ -889,9 +889,7 @@ fn spawnChildAndCollect( child.stdin_behavior = .Pipe; } - child.spawn() catch |err| return self.step.fail("unable to spawn {s}: {s}", .{ - argv[0], @errorName(err), - }); + try child.spawn(); var timer = try std.time.Timer.start(); const result = if (self.stdio == .zig_test) From ed33901218363b9d1baee4c37e85f573dc034e06 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 14 Mar 2023 14:44:38 -0700 Subject: [PATCH 276/294] test-cli: fix expected stderr on windows Needed to account for backward slashes in file system paths. --- test/tests.zig | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/test/tests.zig b/test/tests.zig index 737cf5e291..af0fcea2d5 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -660,20 +660,19 @@ pub fn addLinkTests( pub fn addCliTests(b: *std.Build) *Step { const step = b.step("test-cli", "Test the command line interface"); + const s = std.fs.path.sep_str; { + // Test `zig init-lib`. const tmp_path = b.makeTempPath(); const init_lib = b.addSystemCommand(&.{ b.zig_exe, "init-lib" }); init_lib.cwd = tmp_path; init_lib.setName("zig init-lib"); init_lib.expectStdOutEqual(""); - init_lib.expectStdErrEqual( - \\info: Created build.zig - \\info: Created src/main.zig - \\info: Next, try `zig build --help` or `zig build test` - \\ - ); + init_lib.expectStdErrEqual("info: Created build.zig\n" ++ + "info: Created src" ++ s ++ "main.zig\n" ++ + "info: Next, try `zig build --help` or `zig build test`\n"); const run_test = b.addSystemCommand(&.{ b.zig_exe, "build", "test" }); run_test.cwd = tmp_path; @@ -694,15 +693,11 @@ pub fn addCliTests(b: *std.Build) *Step { init_exe.cwd = tmp_path; init_exe.setName("zig init-exe"); init_exe.expectStdOutEqual(""); - init_exe.expectStdErrEqual( - \\info: Created build.zig - \\info: Created src/main.zig - \\info: Next, try `zig build --help` or `zig build run` - \\ - ); + init_exe.expectStdErrEqual("info: Created build.zig\n" ++ + "info: Created src" ++ s ++ "main.zig\n" ++ + "info: Next, try `zig build --help` or `zig build run`\n"); // Test missing output path. - const s = std.fs.path.sep_str; const bad_out_arg = "-femit-bin=does" ++ s ++ "not" ++ s ++ "exist" ++ s ++ "foo.exe"; const ok_src_arg = "src" ++ s ++ "main.zig"; const expected = "error: unable to open output directory 'does" ++ s ++ "not" ++ s ++ "exist': FileNotFound\n"; @@ -785,7 +780,6 @@ pub fn addCliTests(b: *std.Build) *Step { // owners. const tmp_path = b.makeTempPath(); const unformatted_code = " // no reason for indent"; - const s = std.fs.path.sep_str; var dir = std.fs.cwd().openDir(tmp_path, .{}) catch @panic("unhandled"); defer dir.close(); From 363d4a107de3af95620f82e851e8777aa9f576c9 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 14 Mar 2023 16:12:24 -0700 Subject: [PATCH 277/294] add compile log output to build runner --- lib/std/zig/ErrorBundle.zig | 29 ++++++++++++++++++++++++++++- src/Compilation.zig | 8 ++------ src/Sema.zig | 2 +- src/main.zig | 18 +++++++----------- 4 files changed, 38 insertions(+), 19 deletions(-) diff --git a/lib/std/zig/ErrorBundle.zig b/lib/std/zig/ErrorBundle.zig index 845e9d8ff5..ffe748203e 100644 --- a/lib/std/zig/ErrorBundle.zig +++ b/lib/std/zig/ErrorBundle.zig @@ -32,6 +32,8 @@ pub const SourceLocationIndex = enum(u32) { pub const ErrorMessageList = struct { len: u32, start: u32, + /// null-terminated string index. 0 means no compile log text. + compile_log_text: u32, }; /// Trailing: @@ -110,6 +112,10 @@ pub fn getNotes(eb: ErrorBundle, index: MessageIndex) []const MessageIndex { return @ptrCast([]const MessageIndex, eb.extra[start..][0..notes_len]); } +pub fn getCompileLogOutput(eb: ErrorBundle) [:0]const u8 { + return nullTerminatedString(eb, getErrorMessageList(eb).compile_log_text); +} + /// Returns the requested data, as well as the new index which is at the start of the /// trailers for the object. fn extraData(eb: ErrorBundle, comptime T: type, index: usize) struct { data: T, end: usize } { @@ -145,6 +151,7 @@ pub const RenderOptions = struct { ttyconf: std.debug.TTY.Config, include_reference_trace: bool = true, include_source_line: bool = true, + include_log_text: bool = true, }; pub fn renderToStdErr(eb: ErrorBundle, options: RenderOptions) void { @@ -158,6 +165,14 @@ pub fn renderToWriter(eb: ErrorBundle, options: RenderOptions, writer: anytype) for (eb.getMessages()) |err_msg| { try renderErrorMessageToWriter(eb, options, err_msg, writer, "error", .Red, 0); } + + if (options.include_log_text) { + const log_text = eb.getCompileLogOutput(); + if (log_text.len != 0) { + try writer.writeAll("\nCompile Log Output:\n"); + try writer.writeAll(log_text); + } + } } fn renderErrorMessageToWriter( @@ -314,6 +329,7 @@ pub const Wip = struct { assert(0 == try addExtra(wip, ErrorMessageList{ .len = 0, .start = 0, + .compile_log_text = 0, })); } @@ -325,9 +341,10 @@ pub const Wip = struct { wip.* = undefined; } - pub fn toOwnedBundle(wip: *Wip) !ErrorBundle { + pub fn toOwnedBundle(wip: *Wip, compile_log_text: []const u8) !ErrorBundle { const gpa = wip.gpa; if (wip.root_list.items.len == 0) { + assert(compile_log_text.len == 0); // Special encoding when there are no errors. wip.deinit(); wip.* = .{ @@ -338,9 +355,19 @@ pub const Wip = struct { }; return empty; } + + const compile_log_str_index = if (compile_log_text.len == 0) 0 else str: { + const str = @intCast(u32, wip.string_bytes.items.len); + try wip.string_bytes.ensureUnusedCapacity(gpa, compile_log_text.len + 1); + wip.string_bytes.appendSliceAssumeCapacity(compile_log_text); + wip.string_bytes.appendAssumeCapacity(0); + break :str str; + }; + wip.setExtra(0, ErrorMessageList{ .len = @intCast(u32, wip.root_list.items.len), .start = @intCast(u32, wip.extra.items.len), + .compile_log_text = compile_log_str_index, }); try wip.extra.appendSlice(gpa, @ptrCast([]const u32, wip.root_list.items)); wip.root_list.clearAndFree(gpa); diff --git a/src/Compilation.zig b/src/Compilation.zig index ec21d2c483..89512ce744 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -2705,7 +2705,8 @@ pub fn getAllErrorsAlloc(self: *Compilation) !ErrorBundle { assert(self.totalErrorCount() == bundle.root_list.items.len); - return bundle.toOwnedBundle(); + const compile_log_text = if (self.bin_file.options.module) |m| m.compile_log_text.items else ""; + return bundle.toOwnedBundle(compile_log_text); } pub const ErrorNoteHashContext = struct { @@ -2954,11 +2955,6 @@ pub fn addZirErrorMessages(eb: *ErrorBundle.Wip, file: *Module.File) !void { } } -pub fn getCompileLogOutput(self: *Compilation) []const u8 { - const module = self.bin_file.options.module orelse return &[0]u8{}; - return module.compile_log_text.items; -} - pub fn performAllTheWork( comp: *Compilation, main_progress_node: *std.Progress.Node, diff --git a/src/Sema.zig b/src/Sema.zig index c9f4f27fe2..fc39fbb9fc 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -2219,7 +2219,7 @@ fn failWithOwnedErrorMsg(sema: *Sema, err_msg: *Module.ErrorMsg) CompileError { wip_errors.init(gpa) catch unreachable; Compilation.addModuleErrorMsg(&wip_errors, err_msg.*) catch unreachable; std.debug.print("compile error during Sema:\n", .{}); - var error_bundle = wip_errors.toOwnedBundle() catch unreachable; + var error_bundle = wip_errors.toOwnedBundle("") catch unreachable; error_bundle.renderToStdErr(.{ .ttyconf = .no_color }); crash_report.compilerPanic("unexpected compile error occurred", null, null); } diff --git a/src/main.zig b/src/main.zig index 19f1a6e52c..e7d5c647b5 100644 --- a/src/main.zig +++ b/src/main.zig @@ -3886,10 +3886,6 @@ fn updateModule(gpa: Allocator, comp: *Compilation, hook: AfterUpdateHook) !void if (errors.errorMessageCount() > 0) { errors.renderToStdErr(renderOptions(comp.color)); - const log_text = comp.getCompileLogOutput(); - if (log_text.len != 0) { - std.debug.print("\nCompile Log Output:\n{s}", .{log_text}); - } return error.SemanticAnalyzeFail; } else switch (hook) { .none => {}, @@ -4512,7 +4508,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi &all_modules, ); if (wip_errors.root_list.items.len > 0) { - var errors = try wip_errors.toOwnedBundle(); + var errors = try wip_errors.toOwnedBundle(""); defer errors.deinit(gpa); errors.renderToStdErr(renderOptions(color)); process.exit(1); @@ -4775,7 +4771,7 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void try wip_errors.init(gpa); defer wip_errors.deinit(); try Compilation.addZirErrorMessages(&wip_errors, &file); - var error_bundle = try wip_errors.toOwnedBundle(); + var error_bundle = try wip_errors.toOwnedBundle(""); defer error_bundle.deinit(gpa); error_bundle.renderToStdErr(renderOptions(color)); process.exit(2); @@ -4981,7 +4977,7 @@ fn fmtPathFile( try wip_errors.init(gpa); defer wip_errors.deinit(); try Compilation.addZirErrorMessages(&wip_errors, &file); - var error_bundle = try wip_errors.toOwnedBundle(); + var error_bundle = try wip_errors.toOwnedBundle(""); defer error_bundle.deinit(gpa); error_bundle.renderToStdErr(renderOptions(fmt.color)); fmt.any_error = true; @@ -5018,7 +5014,7 @@ fn printAstErrorsToStderr(gpa: Allocator, tree: Ast, path: []const u8, color: Co try putAstErrorsIntoBundle(gpa, tree, path, &wip_errors); - var error_bundle = try wip_errors.toOwnedBundle(); + var error_bundle = try wip_errors.toOwnedBundle(""); defer error_bundle.deinit(gpa); error_bundle.renderToStdErr(renderOptions(color)); } @@ -5622,7 +5618,7 @@ pub fn cmdAstCheck( try wip_errors.init(gpa); defer wip_errors.deinit(); try Compilation.addZirErrorMessages(&wip_errors, &file); - var error_bundle = try wip_errors.toOwnedBundle(); + var error_bundle = try wip_errors.toOwnedBundle(""); defer error_bundle.deinit(gpa); error_bundle.renderToStdErr(renderOptions(color)); process.exit(1); @@ -5739,7 +5735,7 @@ pub fn cmdChangelist( try wip_errors.init(gpa); defer wip_errors.deinit(); try Compilation.addZirErrorMessages(&wip_errors, &file); - var error_bundle = try wip_errors.toOwnedBundle(); + var error_bundle = try wip_errors.toOwnedBundle(""); defer error_bundle.deinit(gpa); error_bundle.renderToStdErr(renderOptions(color)); process.exit(1); @@ -5774,7 +5770,7 @@ pub fn cmdChangelist( try wip_errors.init(gpa); defer wip_errors.deinit(); try Compilation.addZirErrorMessages(&wip_errors, &file); - var error_bundle = try wip_errors.toOwnedBundle(); + var error_bundle = try wip_errors.toOwnedBundle(""); defer error_bundle.deinit(gpa); error_bundle.renderToStdErr(renderOptions(color)); process.exit(1); From 717e2c8718a3161d6b86317b6132fa296325a88c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 14 Mar 2023 16:38:14 -0700 Subject: [PATCH 278/294] std.Build.Cache: make unit tests not depend on cwd This makes them more resilient to being run multiple times by multiple different processes at the same time. --- lib/std/Build/Cache.zig | 78 ++++++++++++++++++----------------------- 1 file changed, 35 insertions(+), 43 deletions(-) diff --git a/lib/std/Build/Cache.zig b/lib/std/Build/Cache.zig index 92e20b1f9d..b25e349168 100644 --- a/lib/std/Build/Cache.zig +++ b/lib/std/Build/Cache.zig @@ -976,16 +976,16 @@ fn hashFile(file: fs.File, bin_digest: *[Hasher.mac_length]u8) !void { } // Create/Write a file, close it, then grab its stat.mtime timestamp. -fn testGetCurrentFileTimestamp() !i128 { +fn testGetCurrentFileTimestamp(dir: fs.Dir) !i128 { const test_out_file = "test-filetimestamp.tmp"; - var file = try fs.cwd().createFile(test_out_file, .{ + var file = try dir.createFile(test_out_file, .{ .read = true, .truncate = true, }); defer { file.close(); - fs.cwd().deleteFile(test_out_file) catch {}; + dir.deleteFile(test_out_file) catch {}; } return (try file.stat()).mtime; @@ -997,16 +997,17 @@ test "cache file and then recall it" { return error.SkipZigTest; } - const cwd = fs.cwd(); + var tmp = testing.tmpDir(.{}); + defer tmp.cleanup(); const temp_file = "test.txt"; const temp_manifest_dir = "temp_manifest_dir"; - try cwd.writeFile(temp_file, "Hello, world!\n"); + try tmp.dir.writeFile(temp_file, "Hello, world!\n"); // Wait for file timestamps to tick - const initial_time = try testGetCurrentFileTimestamp(); - while ((try testGetCurrentFileTimestamp()) == initial_time) { + const initial_time = try testGetCurrentFileTimestamp(tmp.dir); + while ((try testGetCurrentFileTimestamp(tmp.dir)) == initial_time) { std.time.sleep(1); } @@ -1016,9 +1017,9 @@ test "cache file and then recall it" { { var cache = Cache{ .gpa = testing.allocator, - .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}), + .manifest_dir = try tmp.dir.makeOpenPath(temp_manifest_dir, .{}), }; - cache.addPrefix(.{ .path = null, .handle = fs.cwd() }); + cache.addPrefix(.{ .path = null, .handle = tmp.dir }); defer cache.manifest_dir.close(); { @@ -1054,9 +1055,6 @@ test "cache file and then recall it" { try testing.expectEqual(digest1, digest2); } - - try cwd.deleteTree(temp_manifest_dir); - try cwd.deleteFile(temp_file); } test "check that changing a file makes cache fail" { @@ -1064,21 +1062,19 @@ test "check that changing a file makes cache fail" { // https://github.com/ziglang/zig/issues/5437 return error.SkipZigTest; } - const cwd = fs.cwd(); + var tmp = testing.tmpDir(.{}); + defer tmp.cleanup(); const temp_file = "cache_hash_change_file_test.txt"; const temp_manifest_dir = "cache_hash_change_file_manifest_dir"; const original_temp_file_contents = "Hello, world!\n"; const updated_temp_file_contents = "Hello, world; but updated!\n"; - try cwd.deleteTree(temp_manifest_dir); - try cwd.deleteTree(temp_file); - - try cwd.writeFile(temp_file, original_temp_file_contents); + try tmp.dir.writeFile(temp_file, original_temp_file_contents); // Wait for file timestamps to tick - const initial_time = try testGetCurrentFileTimestamp(); - while ((try testGetCurrentFileTimestamp()) == initial_time) { + const initial_time = try testGetCurrentFileTimestamp(tmp.dir); + while ((try testGetCurrentFileTimestamp(tmp.dir)) == initial_time) { std.time.sleep(1); } @@ -1088,9 +1084,9 @@ test "check that changing a file makes cache fail" { { var cache = Cache{ .gpa = testing.allocator, - .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}), + .manifest_dir = try tmp.dir.makeOpenPath(temp_manifest_dir, .{}), }; - cache.addPrefix(.{ .path = null, .handle = fs.cwd() }); + cache.addPrefix(.{ .path = null, .handle = tmp.dir }); defer cache.manifest_dir.close(); { @@ -1110,7 +1106,7 @@ test "check that changing a file makes cache fail" { try ch.writeManifest(); } - try cwd.writeFile(temp_file, updated_temp_file_contents); + try tmp.dir.writeFile(temp_file, updated_temp_file_contents); { var ch = cache.obtain(); @@ -1132,9 +1128,6 @@ test "check that changing a file makes cache fail" { try testing.expect(!mem.eql(u8, digest1[0..], digest2[0..])); } - - try cwd.deleteTree(temp_manifest_dir); - try cwd.deleteTree(temp_file); } test "no file inputs" { @@ -1142,18 +1135,20 @@ test "no file inputs" { // https://github.com/ziglang/zig/issues/5437 return error.SkipZigTest; } - const cwd = fs.cwd(); + + var tmp = testing.tmpDir(.{}); + defer tmp.cleanup(); + const temp_manifest_dir = "no_file_inputs_manifest_dir"; - defer cwd.deleteTree(temp_manifest_dir) catch {}; var digest1: [hex_digest_len]u8 = undefined; var digest2: [hex_digest_len]u8 = undefined; var cache = Cache{ .gpa = testing.allocator, - .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}), + .manifest_dir = try tmp.dir.makeOpenPath(temp_manifest_dir, .{}), }; - cache.addPrefix(.{ .path = null, .handle = fs.cwd() }); + cache.addPrefix(.{ .path = null, .handle = tmp.dir }); defer cache.manifest_dir.close(); { @@ -1188,18 +1183,19 @@ test "Manifest with files added after initial hash work" { // https://github.com/ziglang/zig/issues/5437 return error.SkipZigTest; } - const cwd = fs.cwd(); + var tmp = testing.tmpDir(.{}); + defer tmp.cleanup(); const temp_file1 = "cache_hash_post_file_test1.txt"; const temp_file2 = "cache_hash_post_file_test2.txt"; const temp_manifest_dir = "cache_hash_post_file_manifest_dir"; - try cwd.writeFile(temp_file1, "Hello, world!\n"); - try cwd.writeFile(temp_file2, "Hello world the second!\n"); + try tmp.dir.writeFile(temp_file1, "Hello, world!\n"); + try tmp.dir.writeFile(temp_file2, "Hello world the second!\n"); // Wait for file timestamps to tick - const initial_time = try testGetCurrentFileTimestamp(); - while ((try testGetCurrentFileTimestamp()) == initial_time) { + const initial_time = try testGetCurrentFileTimestamp(tmp.dir); + while ((try testGetCurrentFileTimestamp(tmp.dir)) == initial_time) { std.time.sleep(1); } @@ -1210,9 +1206,9 @@ test "Manifest with files added after initial hash work" { { var cache = Cache{ .gpa = testing.allocator, - .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}), + .manifest_dir = try tmp.dir.makeOpenPath(temp_manifest_dir, .{}), }; - cache.addPrefix(.{ .path = null, .handle = fs.cwd() }); + cache.addPrefix(.{ .path = null, .handle = tmp.dir }); defer cache.manifest_dir.close(); { @@ -1245,11 +1241,11 @@ test "Manifest with files added after initial hash work" { try testing.expect(mem.eql(u8, &digest1, &digest2)); // Modify the file added after initial hash - try cwd.writeFile(temp_file2, "Hello world the second, updated\n"); + try tmp.dir.writeFile(temp_file2, "Hello world the second, updated\n"); // Wait for file timestamps to tick - const initial_time2 = try testGetCurrentFileTimestamp(); - while ((try testGetCurrentFileTimestamp()) == initial_time2) { + const initial_time2 = try testGetCurrentFileTimestamp(tmp.dir); + while ((try testGetCurrentFileTimestamp(tmp.dir)) == initial_time2) { std.time.sleep(1); } @@ -1272,8 +1268,4 @@ test "Manifest with files added after initial hash work" { try testing.expect(!mem.eql(u8, &digest1, &digest3)); } - - try cwd.deleteTree(temp_manifest_dir); - try cwd.deleteFile(temp_file1); - try cwd.deleteFile(temp_file2); } From 6d6f6a4ac6f1f2666fa2d4500ef4634fdfe0d947 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 14 Mar 2023 17:21:29 -0700 Subject: [PATCH 279/294] std.os.windows.OpenFile: handle DELETE_PENDING This error means that there *was* a file in this location on the file system, but it was deleted. However, the OS is not finished with the deletion operation, and so this CreateFile call has failed. There is not really a sane way to handle this other than retrying the creation after the OS finishes the deletion. --- lib/std/os/windows.zig | 82 ++++++++++++++++++++++++------------------ 1 file changed, 47 insertions(+), 35 deletions(-) diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig index b63fdb9f92..5576200ea5 100644 --- a/lib/std/os/windows.zig +++ b/lib/std/os/windows.zig @@ -105,41 +105,53 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN // If we're not following symlinks, we need to ensure we don't pass in any synchronization flags such as FILE_SYNCHRONOUS_IO_NONALERT. const flags: ULONG = if (options.follow_symlinks) file_or_dir_flag | blocking_flag else file_or_dir_flag | FILE_OPEN_REPARSE_POINT; - const rc = ntdll.NtCreateFile( - &result, - options.access_mask, - &attr, - &io, - null, - FILE_ATTRIBUTE_NORMAL, - options.share_access, - options.creation, - flags, - null, - 0, - ); - switch (rc) { - .SUCCESS => { - if (std.io.is_async and options.io_mode == .evented) { - _ = CreateIoCompletionPort(result, std.event.Loop.instance.?.os_data.io_port, undefined, undefined) catch undefined; - } - return result; - }, - .OBJECT_NAME_INVALID => unreachable, - .OBJECT_NAME_NOT_FOUND => return error.FileNotFound, - .OBJECT_PATH_NOT_FOUND => return error.FileNotFound, - .NO_MEDIA_IN_DEVICE => return error.NoDevice, - .INVALID_PARAMETER => unreachable, - .SHARING_VIOLATION => return error.AccessDenied, - .ACCESS_DENIED => return error.AccessDenied, - .PIPE_BUSY => return error.PipeBusy, - .OBJECT_PATH_SYNTAX_BAD => unreachable, - .OBJECT_NAME_COLLISION => return error.PathAlreadyExists, - .FILE_IS_A_DIRECTORY => return error.IsDir, - .NOT_A_DIRECTORY => return error.NotDir, - .USER_MAPPED_FILE => return error.AccessDenied, - .INVALID_HANDLE => unreachable, - else => return unexpectedStatus(rc), + while (true) { + const rc = ntdll.NtCreateFile( + &result, + options.access_mask, + &attr, + &io, + null, + FILE_ATTRIBUTE_NORMAL, + options.share_access, + options.creation, + flags, + null, + 0, + ); + switch (rc) { + .SUCCESS => { + if (std.io.is_async and options.io_mode == .evented) { + _ = CreateIoCompletionPort(result, std.event.Loop.instance.?.os_data.io_port, undefined, undefined) catch undefined; + } + return result; + }, + .OBJECT_NAME_INVALID => unreachable, + .OBJECT_NAME_NOT_FOUND => return error.FileNotFound, + .OBJECT_PATH_NOT_FOUND => return error.FileNotFound, + .NO_MEDIA_IN_DEVICE => return error.NoDevice, + .INVALID_PARAMETER => unreachable, + .SHARING_VIOLATION => return error.AccessDenied, + .ACCESS_DENIED => return error.AccessDenied, + .PIPE_BUSY => return error.PipeBusy, + .OBJECT_PATH_SYNTAX_BAD => unreachable, + .OBJECT_NAME_COLLISION => return error.PathAlreadyExists, + .FILE_IS_A_DIRECTORY => return error.IsDir, + .NOT_A_DIRECTORY => return error.NotDir, + .USER_MAPPED_FILE => return error.AccessDenied, + .INVALID_HANDLE => unreachable, + .DELETE_PENDING => { + // This error means that there *was* a file in this location on + // the file system, but it was deleted. However, the OS is not + // finished with the deletion operation, and so this CreateFile + // call has failed. There is not really a sane way to handle + // this other than retrying the creation after the OS finishes + // the deletion. + std.time.sleep(std.time.ns_per_ms); + continue; + }, + else => return unexpectedStatus(rc), + } } } From 6664d2418d0348c6432a1c93c96b4412f80036a8 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 14 Mar 2023 18:02:33 -0700 Subject: [PATCH 280/294] test-cases: add missing compile log output The new testing harness is not bound by previous limitations; it can now test compile log output as well. --- ...mpileLog_of_tagged_enum_doesnt_crash_the_compiler.zig | 4 ++++ test/cases/compile_errors/compile_log.zig | 9 +++++++++ .../compile_log_a_pointer_to_an_opaque_value.zig | 5 ++++- ..._inside_function_which_must_be_comptime_evaluated.zig | 3 +++ ...log_statement_warning_deduplication_in_generic_fn.zig | 5 +++++ test/cases/compile_log.0.zig | 5 +++++ test/cases/compile_log.1.zig | 4 ++++ 7 files changed, 34 insertions(+), 1 deletion(-) diff --git a/test/cases/compile_errors/compileLog_of_tagged_enum_doesnt_crash_the_compiler.zig b/test/cases/compile_errors/compileLog_of_tagged_enum_doesnt_crash_the_compiler.zig index 9189eeb48d..55676f9230 100644 --- a/test/cases/compile_errors/compileLog_of_tagged_enum_doesnt_crash_the_compiler.zig +++ b/test/cases/compile_errors/compileLog_of_tagged_enum_doesnt_crash_the_compiler.zig @@ -15,3 +15,7 @@ pub export fn entry() void { // target=native // // :6:5: error: found compile log statement +// +// Compile Log Output: +// @as(tmp.Bar, .{ .X = 123 }) +// @as(tmp.Bar, [runtime value]) diff --git a/test/cases/compile_errors/compile_log.zig b/test/cases/compile_errors/compile_log.zig index 772853b023..e1ea460dc3 100644 --- a/test/cases/compile_errors/compile_log.zig +++ b/test/cases/compile_errors/compile_log.zig @@ -17,3 +17,12 @@ export fn baz() void { // // :5:5: error: found compile log statement // :11:5: note: also here +// +// Compile Log Output: +// @as(*const [5:0]u8, "begin") +// @as(*const [1:0]u8, "a"), @as(i32, 12), @as(*const [1:0]u8, "b"), @as([]const u8, "hi") +// @as(*const [3:0]u8, "end") +// @as(comptime_int, 4) +// @as(*const [5:0]u8, "begin") +// @as(*const [1:0]u8, "a"), @as(i32, [runtime value]), @as(*const [1:0]u8, "b"), @as([]const u8, [runtime value]) +// @as(*const [3:0]u8, "end") diff --git a/test/cases/compile_errors/compile_log_a_pointer_to_an_opaque_value.zig b/test/cases/compile_errors/compile_log_a_pointer_to_an_opaque_value.zig index 252b6e5f14..0162860525 100644 --- a/test/cases/compile_errors/compile_log_a_pointer_to_an_opaque_value.zig +++ b/test/cases/compile_errors/compile_log_a_pointer_to_an_opaque_value.zig @@ -1,5 +1,5 @@ export fn entry() void { - @compileLog(@ptrCast(*const anyopaque, &entry)); + @compileLog(@ptrCast(*align(1) const anyopaque, &entry)); } // error @@ -7,3 +7,6 @@ export fn entry() void { // target=native // // :2:5: error: found compile log statement +// +// Compile Log Output: +// @as(*const anyopaque, (function 'entry')) diff --git a/test/cases/compile_errors/compile_log_statement_inside_function_which_must_be_comptime_evaluated.zig b/test/cases/compile_errors/compile_log_statement_inside_function_which_must_be_comptime_evaluated.zig index 0bc45eae0a..8a39fdec46 100644 --- a/test/cases/compile_errors/compile_log_statement_inside_function_which_must_be_comptime_evaluated.zig +++ b/test/cases/compile_errors/compile_log_statement_inside_function_which_must_be_comptime_evaluated.zig @@ -12,3 +12,6 @@ export fn entry() void { // target=native // // :2:5: error: found compile log statement +// +// Compile Log Output: +// @as(*const [3:0]u8, "i32\x00") diff --git a/test/cases/compile_errors/compile_log_statement_warning_deduplication_in_generic_fn.zig b/test/cases/compile_errors/compile_log_statement_warning_deduplication_in_generic_fn.zig index 76e1c80cf9..4b31d9924a 100644 --- a/test/cases/compile_errors/compile_log_statement_warning_deduplication_in_generic_fn.zig +++ b/test/cases/compile_errors/compile_log_statement_warning_deduplication_in_generic_fn.zig @@ -13,3 +13,8 @@ fn inner(comptime n: usize) void { // // :7:39: error: found compile log statement // :7:39: note: also here +// +// Compile Log Output: +// @as(*const [4:0]u8, "!@#$") +// @as(*const [4:0]u8, "!@#$") +// @as(*const [4:0]u8, "!@#$") diff --git a/test/cases/compile_log.0.zig b/test/cases/compile_log.0.zig index 161a6f37ca..27dfbc706d 100644 --- a/test/cases/compile_log.0.zig +++ b/test/cases/compile_log.0.zig @@ -15,3 +15,8 @@ fn x() void {} // error // // :6:23: error: expected type 'usize', found 'bool' +// +// Compile Log Output: +// @as(bool, true), @as(comptime_int, 20), @as(u32, [runtime value]), @as(fn() void, (function 'x')) +// @as(comptime_int, 1000) +// @as(comptime_int, 1234) diff --git a/test/cases/compile_log.1.zig b/test/cases/compile_log.1.zig index 12e6641542..286722653c 100644 --- a/test/cases/compile_log.1.zig +++ b/test/cases/compile_log.1.zig @@ -14,3 +14,7 @@ fn x() void {} // // :9:5: error: found compile log statement // :4:5: note: also here +// +// Compile Log Output: +// @as(bool, true), @as(comptime_int, 20), @as(u32, [runtime value]), @as(fn() void, (function 'x')) +// @as(comptime_int, 1000) From 4f1382e58166c4324f842450fdad4d134d553085 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 14 Mar 2023 19:47:42 -0700 Subject: [PATCH 281/294] add std.LinearFifo.readableSliceOfLen --- lib/std/fifo.zig | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/std/fifo.zig b/lib/std/fifo.zig index 5a72b56269..eddbff5af0 100644 --- a/lib/std/fifo.zig +++ b/lib/std/fifo.zig @@ -164,6 +164,17 @@ pub fn LinearFifo( return self.readableSliceMut(offset); } + pub fn readableSliceOfLen(self: *Self, len: usize) []const T { + assert(len <= self.count); + const buf = self.readableSlice(0); + if (buf.len >= len) { + return buf[0..len]; + } else { + self.realign(); + return self.readableSlice(0)[0..len]; + } + } + /// Discard first `count` items in the fifo pub fn discard(self: *Self, count: usize) void { assert(count <= self.count); From 4aa5895d322c58cfa654e890beccc1009d49c6e5 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 14 Mar 2023 19:50:52 -0700 Subject: [PATCH 282/294] std.Build: fix invalid assumption about fifos Previously this code asserted that a fifo's readable length was greater than or equal to the length of its readable slice, which was an invalid assertion. This code avoids making that assumption. --- lib/std/Build/RunStep.zig | 157 +++++++++++++++++++------------------- lib/std/Build/Step.zig | 99 ++++++++++++------------ 2 files changed, 129 insertions(+), 127 deletions(-) diff --git a/lib/std/Build/RunStep.zig b/lib/std/Build/RunStep.zig index d2fb20bdae..feeb64f6ca 100644 --- a/lib/std/Build/RunStep.zig +++ b/lib/std/Build/RunStep.zig @@ -949,85 +949,86 @@ fn evalZigTest( var sub_prog_node: ?std.Progress.Node = null; defer if (sub_prog_node) |*n| n.end(); - poll: while (try poller.poll()) { - while (true) { - const buf = stdout.readableSlice(0); - assert(stdout.readableLength() == buf.len); - if (buf.len < @sizeOf(Header)) continue :poll; - const header = @ptrCast(*align(1) const Header, buf[0..@sizeOf(Header)]); - const header_and_msg_len = header.bytes_len + @sizeOf(Header); - if (buf.len < header_and_msg_len) continue :poll; - const body = buf[@sizeOf(Header)..][0..header.bytes_len]; - switch (header.tag) { - .zig_version => { - if (!std.mem.eql(u8, builtin.zig_version_string, body)) { - return self.step.fail( - "zig version mismatch build runner vs compiler: '{s}' vs '{s}'", - .{ builtin.zig_version_string, body }, - ); - } - }, - .test_metadata => { - const TmHdr = std.zig.Server.Message.TestMetadata; - const tm_hdr = @ptrCast(*align(1) const TmHdr, body); - test_count = tm_hdr.tests_len; - - const names_bytes = body[@sizeOf(TmHdr)..][0 .. test_count * @sizeOf(u32)]; - const async_frame_lens_bytes = body[@sizeOf(TmHdr) + names_bytes.len ..][0 .. test_count * @sizeOf(u32)]; - const expected_panic_msgs_bytes = body[@sizeOf(TmHdr) + names_bytes.len + async_frame_lens_bytes.len ..][0 .. test_count * @sizeOf(u32)]; - const string_bytes = body[@sizeOf(TmHdr) + names_bytes.len + async_frame_lens_bytes.len + expected_panic_msgs_bytes.len ..][0..tm_hdr.string_bytes_len]; - - const names = std.mem.bytesAsSlice(u32, names_bytes); - const async_frame_lens = std.mem.bytesAsSlice(u32, async_frame_lens_bytes); - const expected_panic_msgs = std.mem.bytesAsSlice(u32, expected_panic_msgs_bytes); - const names_aligned = try arena.alloc(u32, names.len); - for (names_aligned, names) |*dest, src| dest.* = src; - - const async_frame_lens_aligned = try arena.alloc(u32, async_frame_lens.len); - for (async_frame_lens_aligned, async_frame_lens) |*dest, src| dest.* = src; - - const expected_panic_msgs_aligned = try arena.alloc(u32, expected_panic_msgs.len); - for (expected_panic_msgs_aligned, expected_panic_msgs) |*dest, src| dest.* = src; - - prog_node.setEstimatedTotalItems(names.len); - metadata = .{ - .string_bytes = try arena.dupe(u8, string_bytes), - .names = names_aligned, - .async_frame_lens = async_frame_lens_aligned, - .expected_panic_msgs = expected_panic_msgs_aligned, - .next_index = 0, - .prog_node = prog_node, - }; - - try requestNextTest(child.stdin.?, &metadata.?, &sub_prog_node); - }, - .test_results => { - const md = metadata.?; - - const TrHdr = std.zig.Server.Message.TestResults; - const tr_hdr = @ptrCast(*align(1) const TrHdr, body); - fail_count += @boolToInt(tr_hdr.flags.fail); - skip_count += @boolToInt(tr_hdr.flags.skip); - leak_count += @boolToInt(tr_hdr.flags.leak); - - if (tr_hdr.flags.fail or tr_hdr.flags.leak) { - const name = std.mem.sliceTo(md.string_bytes[md.names[tr_hdr.index]..], 0); - const msg = std.mem.trim(u8, stderr.readableSlice(0), "\n"); - const label = if (tr_hdr.flags.fail) "failed" else "leaked"; - if (msg.len > 0) { - try self.step.addError("'{s}' {s}: {s}", .{ name, label, msg }); - } else { - try self.step.addError("'{s}' {s}", .{ name, label }); - } - stderr.discard(msg.len); - } - - try requestNextTest(child.stdin.?, &metadata.?, &sub_prog_node); - }, - else => {}, // ignore other messages - } - stdout.discard(header_and_msg_len); + poll: while (true) { + while (stdout.readableLength() < @sizeOf(Header)) { + if (!(try poller.poll())) break :poll; } + const header = stdout.reader().readStruct(Header) catch unreachable; + while (stdout.readableLength() < header.bytes_len) { + if (!(try poller.poll())) break :poll; + } + const body = stdout.readableSliceOfLen(header.bytes_len); + + switch (header.tag) { + .zig_version => { + if (!std.mem.eql(u8, builtin.zig_version_string, body)) { + return self.step.fail( + "zig version mismatch build runner vs compiler: '{s}' vs '{s}'", + .{ builtin.zig_version_string, body }, + ); + } + }, + .test_metadata => { + const TmHdr = std.zig.Server.Message.TestMetadata; + const tm_hdr = @ptrCast(*align(1) const TmHdr, body); + test_count = tm_hdr.tests_len; + + const names_bytes = body[@sizeOf(TmHdr)..][0 .. test_count * @sizeOf(u32)]; + const async_frame_lens_bytes = body[@sizeOf(TmHdr) + names_bytes.len ..][0 .. test_count * @sizeOf(u32)]; + const expected_panic_msgs_bytes = body[@sizeOf(TmHdr) + names_bytes.len + async_frame_lens_bytes.len ..][0 .. test_count * @sizeOf(u32)]; + const string_bytes = body[@sizeOf(TmHdr) + names_bytes.len + async_frame_lens_bytes.len + expected_panic_msgs_bytes.len ..][0..tm_hdr.string_bytes_len]; + + const names = std.mem.bytesAsSlice(u32, names_bytes); + const async_frame_lens = std.mem.bytesAsSlice(u32, async_frame_lens_bytes); + const expected_panic_msgs = std.mem.bytesAsSlice(u32, expected_panic_msgs_bytes); + const names_aligned = try arena.alloc(u32, names.len); + for (names_aligned, names) |*dest, src| dest.* = src; + + const async_frame_lens_aligned = try arena.alloc(u32, async_frame_lens.len); + for (async_frame_lens_aligned, async_frame_lens) |*dest, src| dest.* = src; + + const expected_panic_msgs_aligned = try arena.alloc(u32, expected_panic_msgs.len); + for (expected_panic_msgs_aligned, expected_panic_msgs) |*dest, src| dest.* = src; + + prog_node.setEstimatedTotalItems(names.len); + metadata = .{ + .string_bytes = try arena.dupe(u8, string_bytes), + .names = names_aligned, + .async_frame_lens = async_frame_lens_aligned, + .expected_panic_msgs = expected_panic_msgs_aligned, + .next_index = 0, + .prog_node = prog_node, + }; + + try requestNextTest(child.stdin.?, &metadata.?, &sub_prog_node); + }, + .test_results => { + const md = metadata.?; + + const TrHdr = std.zig.Server.Message.TestResults; + const tr_hdr = @ptrCast(*align(1) const TrHdr, body); + fail_count += @boolToInt(tr_hdr.flags.fail); + skip_count += @boolToInt(tr_hdr.flags.skip); + leak_count += @boolToInt(tr_hdr.flags.leak); + + if (tr_hdr.flags.fail or tr_hdr.flags.leak) { + const name = std.mem.sliceTo(md.string_bytes[md.names[tr_hdr.index]..], 0); + const msg = std.mem.trim(u8, stderr.readableSlice(0), "\n"); + const label = if (tr_hdr.flags.fail) "failed" else "leaked"; + if (msg.len > 0) { + try self.step.addError("'{s}' {s}: {s}", .{ name, label, msg }); + } else { + try self.step.addError("'{s}' {s}", .{ name, label }); + } + stderr.discard(msg.len); + } + + try requestNextTest(child.stdin.?, &metadata.?, &sub_prog_node); + }, + else => {}, // ignore other messages + } + + stdout.discard(body.len); } if (stderr.readableLength() > 0) { diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig index 123122a74b..88580a6cbc 100644 --- a/lib/std/Build/Step.zig +++ b/lib/std/Build/Step.zig @@ -321,56 +321,57 @@ pub fn evalZigProcess( const stdout = poller.fifo(.stdout); - poll: while (try poller.poll()) { - while (true) { - const buf = stdout.readableSlice(0); - assert(stdout.readableLength() == buf.len); - if (buf.len < @sizeOf(Header)) continue :poll; - const header = @ptrCast(*align(1) const Header, buf[0..@sizeOf(Header)]); - const header_and_msg_len = header.bytes_len + @sizeOf(Header); - if (buf.len < header_and_msg_len) continue :poll; - const body = buf[@sizeOf(Header)..][0..header.bytes_len]; - switch (header.tag) { - .zig_version => { - if (!std.mem.eql(u8, builtin.zig_version_string, body)) { - return s.fail( - "zig version mismatch build runner vs compiler: '{s}' vs '{s}'", - .{ builtin.zig_version_string, body }, - ); - } - }, - .error_bundle => { - const EbHdr = std.zig.Server.Message.ErrorBundle; - const eb_hdr = @ptrCast(*align(1) const EbHdr, body); - const extra_bytes = - body[@sizeOf(EbHdr)..][0 .. @sizeOf(u32) * eb_hdr.extra_len]; - const string_bytes = - body[@sizeOf(EbHdr) + extra_bytes.len ..][0..eb_hdr.string_bytes_len]; - // TODO: use @ptrCast when the compiler supports it - const unaligned_extra = std.mem.bytesAsSlice(u32, extra_bytes); - const extra_array = try arena.alloc(u32, unaligned_extra.len); - // TODO: use @memcpy when it supports slices - for (extra_array, unaligned_extra) |*dst, src| dst.* = src; - s.result_error_bundle = .{ - .string_bytes = try arena.dupe(u8, string_bytes), - .extra = extra_array, - }; - }, - .progress => { - node_name.clearRetainingCapacity(); - try node_name.appendSlice(gpa, body); - sub_prog_node.setName(node_name.items); - }, - .emit_bin_path => { - const EbpHdr = std.zig.Server.Message.EmitBinPath; - const ebp_hdr = @ptrCast(*align(1) const EbpHdr, body); - s.result_cached = ebp_hdr.flags.cache_hit; - result = try arena.dupe(u8, body[@sizeOf(EbpHdr)..]); - }, - else => {}, // ignore other messages - } - stdout.discard(header_and_msg_len); + poll: while (true) { + while (stdout.readableLength() < @sizeOf(Header)) { + if (!(try poller.poll())) break :poll; } + const header = stdout.reader().readStruct(Header) catch unreachable; + while (stdout.readableLength() < header.bytes_len) { + if (!(try poller.poll())) break :poll; + } + const body = stdout.readableSliceOfLen(header.bytes_len); + + switch (header.tag) { + .zig_version => { + if (!std.mem.eql(u8, builtin.zig_version_string, body)) { + return s.fail( + "zig version mismatch build runner vs compiler: '{s}' vs '{s}'", + .{ builtin.zig_version_string, body }, + ); + } + }, + .error_bundle => { + const EbHdr = std.zig.Server.Message.ErrorBundle; + const eb_hdr = @ptrCast(*align(1) const EbHdr, body); + const extra_bytes = + body[@sizeOf(EbHdr)..][0 .. @sizeOf(u32) * eb_hdr.extra_len]; + const string_bytes = + body[@sizeOf(EbHdr) + extra_bytes.len ..][0..eb_hdr.string_bytes_len]; + // TODO: use @ptrCast when the compiler supports it + const unaligned_extra = std.mem.bytesAsSlice(u32, extra_bytes); + const extra_array = try arena.alloc(u32, unaligned_extra.len); + // TODO: use @memcpy when it supports slices + for (extra_array, unaligned_extra) |*dst, src| dst.* = src; + s.result_error_bundle = .{ + .string_bytes = try arena.dupe(u8, string_bytes), + .extra = extra_array, + }; + }, + .progress => { + node_name.clearRetainingCapacity(); + try node_name.appendSlice(gpa, body); + sub_prog_node.setName(node_name.items); + }, + .emit_bin_path => { + const EbpHdr = std.zig.Server.Message.EmitBinPath; + const ebp_hdr = @ptrCast(*align(1) const EbpHdr, body); + s.result_cached = ebp_hdr.flags.cache_hit; + result = try arena.dupe(u8, body[@sizeOf(EbpHdr)..]); + }, + else => {}, // ignore other messages + } + + stdout.discard(body.len); } const stderr = poller.fifo(.stderr); From a1058dd27bd32fed2fba14732b8a6c7009fd1116 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 14 Mar 2023 20:22:31 -0700 Subject: [PATCH 283/294] fix std.fs unit test to not be racey --- lib/std/fs/test.zig | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig index 16458d7dc4..1fbd7dbd63 100644 --- a/lib/std/fs/test.zig +++ b/lib/std/fs/test.zig @@ -1124,17 +1124,31 @@ test "open file with exclusive lock twice, make sure second lock waits" { test "open file with exclusive nonblocking lock twice (absolute paths)" { if (builtin.os.tag == .wasi) return error.SkipZigTest; - const allocator = testing.allocator; + var random_bytes: [12]u8 = undefined; + std.crypto.random.bytes(&random_bytes); - const cwd = try std.process.getCwdAlloc(allocator); - defer allocator.free(cwd); - const file_paths: [2][]const u8 = .{ cwd, "zig-test-absolute-paths.txt" }; - const filename = try fs.path.resolve(allocator, &file_paths); - defer allocator.free(filename); + var random_b64: [fs.base64_encoder.calcSize(random_bytes.len)]u8 = undefined; + _ = fs.base64_encoder.encode(&random_b64, &random_bytes); - const file1 = try fs.createFileAbsolute(filename, .{ .lock = .Exclusive, .lock_nonblocking = true }); + const sub_path = random_b64 ++ "-zig-test-absolute-paths.txt"; - const file2 = fs.createFileAbsolute(filename, .{ .lock = .Exclusive, .lock_nonblocking = true }); + const gpa = testing.allocator; + + const cwd = try std.process.getCwdAlloc(gpa); + defer gpa.free(cwd); + + const filename = try fs.path.resolve(gpa, &[_][]const u8{ cwd, sub_path }); + defer gpa.free(filename); + + const file1 = try fs.createFileAbsolute(filename, .{ + .lock = .Exclusive, + .lock_nonblocking = true, + }); + + const file2 = fs.createFileAbsolute(filename, .{ + .lock = .Exclusive, + .lock_nonblocking = true, + }); file1.close(); try testing.expectError(error.WouldBlock, file2); From 1a70ea0576ce9bbdfb02f4cbd8eb42efb099d71c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 14 Mar 2023 20:24:46 -0700 Subject: [PATCH 284/294] windows_spawn standalone test: test on native OS In master branch this test tests native Windows. In this branch, I accidentally made aarch64-windows test x86_64-windows which caused some subtle behavior that we aren't ready to add test coverage for yet. --- test/standalone/windows_spawn/build.zig | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/test/standalone/windows_spawn/build.zig b/test/standalone/windows_spawn/build.zig index 8cc6e18599..6c865f0a9f 100644 --- a/test/standalone/windows_spawn/build.zig +++ b/test/standalone/windows_spawn/build.zig @@ -6,10 +6,9 @@ pub fn build(b: *std.Build) void { b.default_step = test_step; const optimize: std.builtin.OptimizeMode = .Debug; - const target: std.zig.CrossTarget = .{ - .os_tag = .windows, - .cpu_arch = .x86_64, - }; + const target: std.zig.CrossTarget = .{}; + + if (builtin.os.tag != .windows) return; const hello = b.addExecutable(.{ .name = "hello", From 21b544a90a45cbac02c226ff7082241c2237ffdf Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 14 Mar 2023 21:02:30 -0700 Subject: [PATCH 285/294] fix compile log test case expected output --- .../compile_errors/compile_log_a_pointer_to_an_opaque_value.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cases/compile_errors/compile_log_a_pointer_to_an_opaque_value.zig b/test/cases/compile_errors/compile_log_a_pointer_to_an_opaque_value.zig index 0162860525..73de52fc97 100644 --- a/test/cases/compile_errors/compile_log_a_pointer_to_an_opaque_value.zig +++ b/test/cases/compile_errors/compile_log_a_pointer_to_an_opaque_value.zig @@ -1,5 +1,5 @@ export fn entry() void { - @compileLog(@ptrCast(*align(1) const anyopaque, &entry)); + @compileLog(@as(*align(1) const anyopaque, @ptrCast(*const anyopaque, &entry))); } // error From 5c6adbeb3931c209c651239cffb60831bcf02949 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 14 Mar 2023 21:02:52 -0700 Subject: [PATCH 286/294] test-c-abi: disable LTO on more targets --- test/tests.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/tests.zig b/test/tests.zig index af0fcea2d5..a9da9b41c7 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -1043,7 +1043,7 @@ pub fn addCAbiTests(b: *std.Build, skip_non_native: bool, skip_release: bool) *S // test-c-abi should test both with LTO on and with LTO off. Only // some combinations are passing currently: // https://github.com/ziglang/zig/issues/14908 - if (c_abi_target.isWindows() and (c_abi_target.getCpuArch() == .x86 or builtin.target.os.tag == .linux)) { + if (c_abi_target.isWindows()) { test_step.want_lto = false; } From cdda39559020b8d2c14a49d670738a3d265b496f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 15 Mar 2023 10:32:32 -0700 Subject: [PATCH 287/294] std lib tests: avoid cwd races by using std.testing.tmpDir --- lib/std/os/linux/io_uring.zig | 137 ++++++++++++++++++---------------- lib/std/os/linux/test.zig | 33 ++++---- 2 files changed, 86 insertions(+), 84 deletions(-) diff --git a/lib/std/os/linux/io_uring.zig b/lib/std/os/linux/io_uring.zig index a302f2cdf1..4dbf87c501 100644 --- a/lib/std/os/linux/io_uring.zig +++ b/lib/std/os/linux/io_uring.zig @@ -1728,10 +1728,12 @@ test "writev/fsync/readv" { }; defer ring.deinit(); + var tmp = std.testing.tmpDir(.{}); + defer tmp.cleanup(); + const path = "test_io_uring_writev_fsync_readv"; - const file = try std.fs.cwd().createFile(path, .{ .read = true, .truncate = true }); + const file = try tmp.dir.createFile(path, .{ .read = true, .truncate = true }); defer file.close(); - defer std.fs.cwd().deleteFile(path) catch {}; const fd = file.handle; const buffer_write = [_]u8{42} ** 128; @@ -1796,10 +1798,11 @@ test "write/read" { }; defer ring.deinit(); + var tmp = std.testing.tmpDir(.{}); + defer tmp.cleanup(); const path = "test_io_uring_write_read"; - const file = try std.fs.cwd().createFile(path, .{ .read = true, .truncate = true }); + const file = try tmp.dir.createFile(path, .{ .read = true, .truncate = true }); defer file.close(); - defer std.fs.cwd().deleteFile(path) catch {}; const fd = file.handle; const buffer_write = [_]u8{97} ** 20; @@ -1842,10 +1845,12 @@ test "write_fixed/read_fixed" { }; defer ring.deinit(); + var tmp = std.testing.tmpDir(.{}); + defer tmp.cleanup(); + const path = "test_io_uring_write_read_fixed"; - const file = try std.fs.cwd().createFile(path, .{ .read = true, .truncate = true }); + const file = try tmp.dir.createFile(path, .{ .read = true, .truncate = true }); defer file.close(); - defer std.fs.cwd().deleteFile(path) catch {}; const fd = file.handle; var raw_buffers: [2][11]u8 = undefined; @@ -1899,8 +1904,10 @@ test "openat" { }; defer ring.deinit(); + var tmp = std.testing.tmpDir(.{}); + defer tmp.cleanup(); + const path = "test_io_uring_openat"; - defer std.fs.cwd().deleteFile(path) catch {}; // Workaround for LLVM bug: https://github.com/ziglang/zig/issues/12014 const path_addr = if (builtin.zig_backend == .stage2_llvm) p: { @@ -1910,12 +1917,12 @@ test "openat" { const flags: u32 = os.O.CLOEXEC | os.O.RDWR | os.O.CREAT; const mode: os.mode_t = 0o666; - const sqe_openat = try ring.openat(0x33333333, linux.AT.FDCWD, path, flags, mode); + const sqe_openat = try ring.openat(0x33333333, tmp.dir.fd, path, flags, mode); try testing.expectEqual(linux.io_uring_sqe{ .opcode = .OPENAT, .flags = 0, .ioprio = 0, - .fd = linux.AT.FDCWD, + .fd = tmp.dir.fd, .off = 0, .addr = path_addr, .len = mode, @@ -1931,12 +1938,6 @@ test "openat" { const cqe_openat = try ring.copy_cqe(); try testing.expectEqual(@as(u64, 0x33333333), cqe_openat.user_data); if (cqe_openat.err() == .INVAL) return error.SkipZigTest; - // AT.FDCWD is not fully supported before kernel 5.6: - // See https://lore.kernel.org/io-uring/20200207155039.12819-1-axboe@kernel.dk/T/ - // We use IORING_FEAT_RW_CUR_POS to know if we are pre-5.6 since that feature was added in 5.6. - if (cqe_openat.err() == .BADF and (ring.features & linux.IORING_FEAT_RW_CUR_POS) == 0) { - return error.SkipZigTest; - } if (cqe_openat.res <= 0) std.debug.print("\ncqe_openat.res={}\n", .{cqe_openat.res}); try testing.expect(cqe_openat.res > 0); try testing.expectEqual(@as(u32, 0), cqe_openat.flags); @@ -1954,10 +1955,12 @@ test "close" { }; defer ring.deinit(); + var tmp = std.testing.tmpDir(.{}); + defer tmp.cleanup(); + const path = "test_io_uring_close"; - const file = try std.fs.cwd().createFile(path, .{}); + const file = try tmp.dir.createFile(path, .{}); errdefer file.close(); - defer std.fs.cwd().deleteFile(path) catch {}; const sqe_close = try ring.close(0x44444444, file.handle); try testing.expectEqual(linux.IORING_OP.CLOSE, sqe_close.opcode); @@ -2295,10 +2298,12 @@ test "fallocate" { }; defer ring.deinit(); + var tmp = std.testing.tmpDir(.{}); + defer tmp.cleanup(); + const path = "test_io_uring_fallocate"; - const file = try std.fs.cwd().createFile(path, .{ .truncate = true, .mode = 0o666 }); + const file = try tmp.dir.createFile(path, .{ .truncate = true, .mode = 0o666 }); defer file.close(); - defer std.fs.cwd().deleteFile(path) catch {}; try testing.expectEqual(@as(u64, 0), (try file.stat()).size); @@ -2339,10 +2344,11 @@ test "statx" { }; defer ring.deinit(); + var tmp = std.testing.tmpDir(.{}); + defer tmp.cleanup(); const path = "test_io_uring_statx"; - const file = try std.fs.cwd().createFile(path, .{ .truncate = true, .mode = 0o666 }); + const file = try tmp.dir.createFile(path, .{ .truncate = true, .mode = 0o666 }); defer file.close(); - defer std.fs.cwd().deleteFile(path) catch {}; try testing.expectEqual(@as(u64, 0), (try file.stat()).size); @@ -2351,14 +2357,14 @@ test "statx" { var buf: linux.Statx = undefined; const sqe = try ring.statx( 0xaaaaaaaa, - linux.AT.FDCWD, + tmp.dir.fd, path, 0, linux.STATX_SIZE, &buf, ); try testing.expectEqual(linux.IORING_OP.STATX, sqe.opcode); - try testing.expectEqual(@as(i32, linux.AT.FDCWD), sqe.fd); + try testing.expectEqual(@as(i32, tmp.dir.fd), sqe.fd); try testing.expectEqual(@as(u32, 1), try ring.submit()); const cqe = try ring.copy_cqe(); @@ -2371,8 +2377,6 @@ test "statx" { // The filesystem containing the file referred to by fd does not support this operation; // or the mode is not supported by the filesystem containing the file referred to by fd: .OPNOTSUPP => return error.SkipZigTest, - // The kernel is too old to support FDCWD for dir_fd - .BADF => return error.SkipZigTest, else => |errno| std.debug.panic("unhandled errno: {}", .{errno}), } try testing.expectEqual(linux.io_uring_cqe{ @@ -2606,28 +2610,28 @@ test "renameat" { const old_path = "test_io_uring_renameat_old"; const new_path = "test_io_uring_renameat_new"; + var tmp = std.testing.tmpDir(.{}); + defer tmp.cleanup(); + // Write old file with data - const old_file = try std.fs.cwd().createFile(old_path, .{ .truncate = true, .mode = 0o666 }); - defer { - old_file.close(); - std.fs.cwd().deleteFile(new_path) catch {}; - } + const old_file = try tmp.dir.createFile(old_path, .{ .truncate = true, .mode = 0o666 }); + defer old_file.close(); try old_file.writeAll("hello"); // Submit renameat var sqe = try ring.renameat( 0x12121212, - linux.AT.FDCWD, + tmp.dir.fd, old_path, - linux.AT.FDCWD, + tmp.dir.fd, new_path, 0, ); try testing.expectEqual(linux.IORING_OP.RENAMEAT, sqe.opcode); - try testing.expectEqual(@as(i32, linux.AT.FDCWD), sqe.fd); - try testing.expectEqual(@as(i32, linux.AT.FDCWD), @bitCast(i32, sqe.len)); + try testing.expectEqual(@as(i32, tmp.dir.fd), sqe.fd); + try testing.expectEqual(@as(i32, tmp.dir.fd), @bitCast(i32, sqe.len)); try testing.expectEqual(@as(u32, 1), try ring.submit()); const cqe = try ring.copy_cqe(); @@ -2645,7 +2649,7 @@ test "renameat" { // Validate that the old file doesn't exist anymore { - _ = std.fs.cwd().openFile(old_path, .{}) catch |err| switch (err) { + _ = tmp.dir.openFile(old_path, .{}) catch |err| switch (err) { error.FileNotFound => {}, else => std.debug.panic("unexpected error: {}", .{err}), }; @@ -2653,7 +2657,7 @@ test "renameat" { // Validate that the new file exists with the proper content { - const new_file = try std.fs.cwd().openFile(new_path, .{}); + const new_file = try tmp.dir.openFile(new_path, .{}); defer new_file.close(); var new_file_data: [16]u8 = undefined; @@ -2674,22 +2678,24 @@ test "unlinkat" { const path = "test_io_uring_unlinkat"; + var tmp = std.testing.tmpDir(.{}); + defer tmp.cleanup(); + // Write old file with data - const file = try std.fs.cwd().createFile(path, .{ .truncate = true, .mode = 0o666 }); + const file = try tmp.dir.createFile(path, .{ .truncate = true, .mode = 0o666 }); defer file.close(); - defer std.fs.cwd().deleteFile(path) catch {}; // Submit unlinkat var sqe = try ring.unlinkat( 0x12121212, - linux.AT.FDCWD, + tmp.dir.fd, path, 0, ); try testing.expectEqual(linux.IORING_OP.UNLINKAT, sqe.opcode); - try testing.expectEqual(@as(i32, linux.AT.FDCWD), sqe.fd); + try testing.expectEqual(@as(i32, tmp.dir.fd), sqe.fd); try testing.expectEqual(@as(u32, 1), try ring.submit()); const cqe = try ring.copy_cqe(); @@ -2706,7 +2712,7 @@ test "unlinkat" { }, cqe); // Validate that the file doesn't exist anymore - _ = std.fs.cwd().openFile(path, .{}) catch |err| switch (err) { + _ = tmp.dir.openFile(path, .{}) catch |err| switch (err) { error.FileNotFound => {}, else => std.debug.panic("unexpected error: {}", .{err}), }; @@ -2722,20 +2728,21 @@ test "mkdirat" { }; defer ring.deinit(); - const path = "test_io_uring_mkdirat"; + var tmp = std.testing.tmpDir(.{}); + defer tmp.cleanup(); - defer std.fs.cwd().deleteDir(path) catch {}; + const path = "test_io_uring_mkdirat"; // Submit mkdirat var sqe = try ring.mkdirat( 0x12121212, - linux.AT.FDCWD, + tmp.dir.fd, path, 0o0755, ); try testing.expectEqual(linux.IORING_OP.MKDIRAT, sqe.opcode); - try testing.expectEqual(@as(i32, linux.AT.FDCWD), sqe.fd); + try testing.expectEqual(@as(i32, tmp.dir.fd), sqe.fd); try testing.expectEqual(@as(u32, 1), try ring.submit()); const cqe = try ring.copy_cqe(); @@ -2752,7 +2759,7 @@ test "mkdirat" { }, cqe); // Validate that the directory exist - _ = try std.fs.cwd().openDir(path, .{}); + _ = try tmp.dir.openDir(path, .{}); } test "symlinkat" { @@ -2765,26 +2772,25 @@ test "symlinkat" { }; defer ring.deinit(); + var tmp = std.testing.tmpDir(.{}); + defer tmp.cleanup(); + const path = "test_io_uring_symlinkat"; const link_path = "test_io_uring_symlinkat_link"; - const file = try std.fs.cwd().createFile(path, .{ .truncate = true, .mode = 0o666 }); - defer { - file.close(); - std.fs.cwd().deleteFile(path) catch {}; - std.fs.cwd().deleteFile(link_path) catch {}; - } + const file = try tmp.dir.createFile(path, .{ .truncate = true, .mode = 0o666 }); + defer file.close(); // Submit symlinkat var sqe = try ring.symlinkat( 0x12121212, path, - linux.AT.FDCWD, + tmp.dir.fd, link_path, ); try testing.expectEqual(linux.IORING_OP.SYMLINKAT, sqe.opcode); - try testing.expectEqual(@as(i32, linux.AT.FDCWD), sqe.fd); + try testing.expectEqual(@as(i32, tmp.dir.fd), sqe.fd); try testing.expectEqual(@as(u32, 1), try ring.submit()); const cqe = try ring.copy_cqe(); @@ -2801,7 +2807,7 @@ test "symlinkat" { }, cqe); // Validate that the symlink exist - _ = try std.fs.cwd().openFile(link_path, .{}); + _ = try tmp.dir.openFile(link_path, .{}); } test "linkat" { @@ -2814,32 +2820,31 @@ test "linkat" { }; defer ring.deinit(); + var tmp = std.testing.tmpDir(.{}); + defer tmp.cleanup(); + const first_path = "test_io_uring_linkat_first"; const second_path = "test_io_uring_linkat_second"; // Write file with data - const first_file = try std.fs.cwd().createFile(first_path, .{ .truncate = true, .mode = 0o666 }); - defer { - first_file.close(); - std.fs.cwd().deleteFile(first_path) catch {}; - std.fs.cwd().deleteFile(second_path) catch {}; - } + const first_file = try tmp.dir.createFile(first_path, .{ .truncate = true, .mode = 0o666 }); + defer first_file.close(); try first_file.writeAll("hello"); // Submit linkat var sqe = try ring.linkat( 0x12121212, - linux.AT.FDCWD, + tmp.dir.fd, first_path, - linux.AT.FDCWD, + tmp.dir.fd, second_path, 0, ); try testing.expectEqual(linux.IORING_OP.LINKAT, sqe.opcode); - try testing.expectEqual(@as(i32, linux.AT.FDCWD), sqe.fd); - try testing.expectEqual(@as(i32, linux.AT.FDCWD), @bitCast(i32, sqe.len)); + try testing.expectEqual(@as(i32, tmp.dir.fd), sqe.fd); + try testing.expectEqual(@as(i32, tmp.dir.fd), @bitCast(i32, sqe.len)); try testing.expectEqual(@as(u32, 1), try ring.submit()); const cqe = try ring.copy_cqe(); @@ -2856,7 +2861,7 @@ test "linkat" { }, cqe); // Validate the second file - const second_file = try std.fs.cwd().openFile(second_path, .{}); + const second_file = try tmp.dir.openFile(second_path, .{}); defer second_file.close(); var second_file_data: [16]u8 = undefined; diff --git a/lib/std/os/linux/test.zig b/lib/std/os/linux/test.zig index 63deab3edc..e1ad36b2e5 100644 --- a/lib/std/os/linux/test.zig +++ b/lib/std/os/linux/test.zig @@ -8,10 +8,12 @@ const expectEqual = std.testing.expectEqual; const fs = std.fs; test "fallocate" { + var tmp = std.testing.tmpDir(.{}); + defer tmp.cleanup(); + const path = "test_fallocate"; - const file = try fs.cwd().createFile(path, .{ .truncate = true, .mode = 0o666 }); + const file = try tmp.dir.createFile(path, .{ .truncate = true, .mode = 0o666 }); defer file.close(); - defer fs.cwd().deleteFile(path) catch {}; try expect((try file.stat()).size == 0); @@ -67,12 +69,12 @@ test "timer" { } test "statx" { + var tmp = std.testing.tmpDir(.{}); + defer tmp.cleanup(); + const tmp_file_name = "just_a_temporary_file.txt"; - var file = try fs.cwd().createFile(tmp_file_name, .{}); - defer { - file.close(); - fs.cwd().deleteFile(tmp_file_name) catch {}; - } + var file = try tmp.dir.createFile(tmp_file_name, .{}); + defer file.close(); var statx_buf: linux.Statx = undefined; switch (linux.getErrno(linux.statx(file.handle, "", linux.AT.EMPTY_PATH, linux.STATX_BASIC_STATS, &statx_buf))) { @@ -105,21 +107,16 @@ test "user and group ids" { } test "fadvise" { + var tmp = std.testing.tmpDir(.{}); + defer tmp.cleanup(); + const tmp_file_name = "temp_posix_fadvise.txt"; - var file = try fs.cwd().createFile(tmp_file_name, .{}); - defer { - file.close(); - fs.cwd().deleteFile(tmp_file_name) catch {}; - } + var file = try tmp.dir.createFile(tmp_file_name, .{}); + defer file.close(); var buf: [2048]u8 = undefined; try file.writeAll(&buf); - const ret = linux.fadvise( - file.handle, - 0, - 0, - linux.POSIX_FADV.SEQUENTIAL, - ); + const ret = linux.fadvise(file.handle, 0, 0, linux.POSIX_FADV.SEQUENTIAL); try expectEqual(@as(usize, 0), ret); } From 1f59994a37afcfa5909440a05bcff5990b3a4651 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 15 Mar 2023 10:47:36 -0700 Subject: [PATCH 288/294] C ABI tests: don't test aarch64-windows yet because it is not passing. See tracking issue #14908 --- test/tests.zig | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/tests.zig b/test/tests.zig index a9da9b41c7..3166fbc14a 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -1027,6 +1027,11 @@ pub fn addCAbiTests(b: *std.Build, skip_non_native: bool, skip_release: bool) *S for (c_abi_targets) |c_abi_target| { if (skip_non_native and !c_abi_target.isNative()) continue; + if (c_abi_target.isWindows() and c_abi_target.getCpuArch() == .aarch64) { + // https://github.com/ziglang/zig/issues/14908 + continue; + } + const test_step = b.addTest(.{ .root_source_file = .{ .path = "test/c_abi/main.zig" }, .optimize = optimize_mode, From 7177b3994626114e57bf8df36ca84fd942bac282 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 15 Mar 2023 12:32:17 -0700 Subject: [PATCH 289/294] fix test-case copy-paste typo from earlier commit commit 3204d00a5e7fe119b690e921138a439fb84dff5b intended to move this passing test case from stage1 folder but it was accidentally changed to have identical contents as a different test case instead. Fortunately, the test case has not regressed, so I simply replaced it with the intended test from before. --- .../undefined_as_field_type_is_rejected.zig | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/test/cases/compile_errors/undefined_as_field_type_is_rejected.zig b/test/cases/compile_errors/undefined_as_field_type_is_rejected.zig index e78cadc878..b6eb059661 100644 --- a/test/cases/compile_errors/undefined_as_field_type_is_rejected.zig +++ b/test/cases/compile_errors/undefined_as_field_type_is_rejected.zig @@ -1,9 +1,13 @@ -export fn a() void { - b(); +const Foo = struct { + a: undefined, +}; +export fn entry1() void { + const foo: Foo = undefined; + _ = foo; } // error -// backend=stage2 +// backend=stage1 // target=native // -// :2:5: error: use of undeclared identifier 'b' +// tmp.zig:2:8: error: use of undefined value here causes undefined behavior From e1e414e62a86cc460ef215ea8050c953b68b6080 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 15 Mar 2023 10:12:48 +0100 Subject: [PATCH 290/294] std: move os/darwin.zig and related to c/darwin.zig Move to c/darwin.zig as they really are libSystem/libc imports/wrappers. As an added bonus, get rid of the nasty `usingnamespace`s which are now unneeded. Finally, add `os.ptrace` but currently only implemented on darwin. --- CMakeLists.txt | 1 - lib/std/c/darwin.zig | 477 ++++++++++++++++++++++++++ lib/std/{os => c}/darwin/cssm.zig | 0 lib/std/os.zig | 25 +- lib/std/os/darwin.zig | 540 ------------------------------ lib/std/os/ptrace.zig | 28 -- 6 files changed, 500 insertions(+), 571 deletions(-) rename lib/std/{os => c}/darwin/cssm.zig (100%) delete mode 100644 lib/std/os/darwin.zig delete mode 100644 lib/std/os/ptrace.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index b0867c220b..3f5cf7cd6e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -296,7 +296,6 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/lib/std/meta/trait.zig" "${CMAKE_SOURCE_DIR}/lib/std/multi_array_list.zig" "${CMAKE_SOURCE_DIR}/lib/std/os.zig" - "${CMAKE_SOURCE_DIR}/lib/std/os/darwin.zig" "${CMAKE_SOURCE_DIR}/lib/std/os/linux.zig" "${CMAKE_SOURCE_DIR}/lib/std/os/linux/errno/generic.zig" "${CMAKE_SOURCE_DIR}/lib/std/os/linux/x86_64.zig" diff --git a/lib/std/c/darwin.zig b/lib/std/c/darwin.zig index 9c5ac1e93a..75267cc171 100644 --- a/lib/std/c/darwin.zig +++ b/lib/std/c/darwin.zig @@ -8,6 +8,7 @@ const iovec_const = std.os.iovec_const; pub const aarch64 = @import("darwin/aarch64.zig"); pub const x86_64 = @import("darwin/x86_64.zig"); +pub const cssm = @import("darwin/cssm.zig"); const arch_bits = switch (native_arch) { .aarch64 => @import("darwin/aarch64.zig"), @@ -2179,6 +2180,14 @@ pub fn getKernError(err: kern_return_t) KernE { return @intToEnum(KernE, @truncate(u32, @intCast(usize, err))); } +pub fn unexpectedKernError(err: KernE) std.os.UnexpectedError { + if (std.os.unexpected_error_tracing) { + std.debug.print("unexpected errno: {d}\n", .{@enumToInt(err)}); + std.debug.dumpCurrentStackTrace(null); + } + return error.Unexpected; +} + /// Kernel return values pub const KernE = enum(u32) { SUCCESS = 0, @@ -3085,3 +3094,471 @@ pub const PT_DENY_ATTACH = 31; pub const caddr_t = ?[*]u8; pub extern "c" fn ptrace(request: c_int, pid: pid_t, addr: caddr_t, data: c_int) c_int; + +pub const MachError = error{ + /// Not enough permissions held to perform the requested kernel + /// call. + PermissionDenied, +} || std.os.UnexpectedError; + +pub const MachTask = extern struct { + port: mach_port_name_t, + + pub fn isValid(self: MachTask) bool { + return self.port != TASK_NULL; + } + + pub fn pidForTask(self: MachTask) MachError!std.os.pid_t { + var pid: std.os.pid_t = undefined; + switch (getKernError(pid_for_task(self.port, &pid))) { + .SUCCESS => return pid, + .FAILURE => return error.PermissionDenied, + else => |err| return unexpectedKernError(err), + } + } + + pub fn allocatePort(self: MachTask, right: MACH_PORT_RIGHT) MachError!MachTask { + var out_port: mach_port_name_t = undefined; + switch (getKernError(mach_port_allocate( + self.port, + @enumToInt(right), + &out_port, + ))) { + .SUCCESS => return .{ .port = out_port }, + .FAILURE => return error.PermissionDenied, + else => |err| return unexpectedKernError(err), + } + } + + pub fn deallocatePort(self: MachTask, port: MachTask) void { + _ = getKernError(mach_port_deallocate(self.port, port.port)); + } + + pub fn insertRight(self: MachTask, port: MachTask, msg: MACH_MSG_TYPE) !void { + switch (getKernError(mach_port_insert_right( + self.port, + port.port, + port.port, + @enumToInt(msg), + ))) { + .SUCCESS => return, + .FAILURE => return error.PermissionDenied, + else => |err| return unexpectedKernError(err), + } + } + + pub const PortInfo = struct { + mask: exception_mask_t, + masks: [EXC_TYPES_COUNT]exception_mask_t, + ports: [EXC_TYPES_COUNT]mach_port_t, + behaviors: [EXC_TYPES_COUNT]exception_behavior_t, + flavors: [EXC_TYPES_COUNT]thread_state_flavor_t, + count: mach_msg_type_number_t, + }; + + pub fn getExceptionPorts(self: MachTask, mask: exception_mask_t) !PortInfo { + var info = PortInfo{ + .mask = mask, + .masks = undefined, + .ports = undefined, + .behaviors = undefined, + .flavors = undefined, + .count = 0, + }; + info.count = info.ports.len / @sizeOf(mach_port_t); + + switch (getKernError(task_get_exception_ports( + self.port, + info.mask, + &info.masks, + &info.count, + &info.ports, + &info.behaviors, + &info.flavors, + ))) { + .SUCCESS => return info, + .FAILURE => return error.PermissionDenied, + else => |err| return unexpectedKernError(err), + } + } + + pub fn setExceptionPorts( + self: MachTask, + mask: exception_mask_t, + new_port: MachTask, + behavior: exception_behavior_t, + new_flavor: thread_state_flavor_t, + ) !void { + switch (getKernError(task_set_exception_ports( + self.port, + mask, + new_port.port, + behavior, + new_flavor, + ))) { + .SUCCESS => return, + .FAILURE => return error.PermissionDenied, + else => |err| return unexpectedKernError(err), + } + } + + pub const RegionInfo = struct { + pub const Tag = enum { + basic, + extended, + top, + }; + + base_addr: u64, + tag: Tag, + info: union { + basic: vm_region_basic_info_64, + extended: vm_region_extended_info, + top: vm_region_top_info, + }, + }; + + pub fn getRegionInfo( + task: MachTask, + address: u64, + len: usize, + tag: RegionInfo.Tag, + ) MachError!RegionInfo { + var info: RegionInfo = .{ + .base_addr = address, + .tag = tag, + .info = undefined, + }; + switch (tag) { + .basic => info.info = .{ .basic = undefined }, + .extended => info.info = .{ .extended = undefined }, + .top => info.info = .{ .top = undefined }, + } + var base_len: mach_vm_size_t = if (len == 1) 2 else len; + var objname: mach_port_t = undefined; + var count: mach_msg_type_number_t = switch (tag) { + .basic => VM_REGION_BASIC_INFO_COUNT, + .extended => VM_REGION_EXTENDED_INFO_COUNT, + .top => VM_REGION_TOP_INFO_COUNT, + }; + switch (getKernError(mach_vm_region( + task.port, + &info.base_addr, + &base_len, + switch (tag) { + .basic => VM_REGION_BASIC_INFO_64, + .extended => VM_REGION_EXTENDED_INFO, + .top => VM_REGION_TOP_INFO, + }, + switch (tag) { + .basic => @ptrCast(vm_region_info_t, &info.info.basic), + .extended => @ptrCast(vm_region_info_t, &info.info.extended), + .top => @ptrCast(vm_region_info_t, &info.info.top), + }, + &count, + &objname, + ))) { + .SUCCESS => return info, + .FAILURE => return error.PermissionDenied, + else => |err| return unexpectedKernError(err), + } + } + + pub const RegionSubmapInfo = struct { + pub const Tag = enum { + short, + full, + }; + + tag: Tag, + base_addr: u64, + info: union { + short: vm_region_submap_short_info_64, + full: vm_region_submap_info_64, + }, + }; + + pub fn getRegionSubmapInfo( + task: MachTask, + address: u64, + len: usize, + nesting_depth: u32, + tag: RegionSubmapInfo.Tag, + ) MachError!RegionSubmapInfo { + var info: RegionSubmapInfo = .{ + .base_addr = address, + .tag = tag, + .info = undefined, + }; + switch (tag) { + .short => info.info = .{ .short = undefined }, + .full => info.info = .{ .full = undefined }, + } + var nesting = nesting_depth; + var base_len: mach_vm_size_t = if (len == 1) 2 else len; + var count: mach_msg_type_number_t = switch (tag) { + .short => VM_REGION_SUBMAP_SHORT_INFO_COUNT_64, + .full => VM_REGION_SUBMAP_INFO_COUNT_64, + }; + switch (getKernError(mach_vm_region_recurse( + task.port, + &info.base_addr, + &base_len, + &nesting, + switch (tag) { + .short => @ptrCast(vm_region_recurse_info_t, &info.info.short), + .full => @ptrCast(vm_region_recurse_info_t, &info.info.full), + }, + &count, + ))) { + .SUCCESS => return info, + .FAILURE => return error.PermissionDenied, + else => |err| return unexpectedKernError(err), + } + } + + pub fn getCurrProtection(task: MachTask, address: u64, len: usize) MachError!vm_prot_t { + const info = try task.getRegionSubmapInfo(address, len, 0, .short); + return info.info.short.protection; + } + + pub fn setMaxProtection(task: MachTask, address: u64, len: usize, prot: vm_prot_t) MachError!void { + return task.setProtectionImpl(address, len, true, prot); + } + + pub fn setCurrProtection(task: MachTask, address: u64, len: usize, prot: vm_prot_t) MachError!void { + return task.setProtectionImpl(address, len, false, prot); + } + + fn setProtectionImpl(task: MachTask, address: u64, len: usize, set_max: bool, prot: vm_prot_t) MachError!void { + switch (getKernError(mach_vm_protect(task.port, address, len, @boolToInt(set_max), prot))) { + .SUCCESS => return, + .FAILURE => return error.PermissionDenied, + else => |err| return unexpectedKernError(err), + } + } + + /// Will write to VM even if current protection attributes specifically prohibit + /// us from doing so, by temporarily setting protection level to a level with VM_PROT_COPY + /// variant, and resetting after a successful or unsuccessful write. + pub fn writeMemProtected(task: MachTask, address: u64, buf: []const u8, arch: std.Target.Cpu.Arch) MachError!usize { + const curr_prot = try task.getCurrProtection(address, buf.len); + try task.setCurrProtection( + address, + buf.len, + PROT.READ | PROT.WRITE | PROT.COPY, + ); + defer { + task.setCurrProtection(address, buf.len, curr_prot) catch {}; + } + return task.writeMem(address, buf, arch); + } + + pub fn writeMem(task: MachTask, address: u64, buf: []const u8, arch: std.Target.Cpu.Arch) MachError!usize { + const count = buf.len; + var total_written: usize = 0; + var curr_addr = address; + const page_size = try getPageSize(task); // TODO we probably can assume value here + var out_buf = buf[0..]; + + while (total_written < count) { + const curr_size = maxBytesLeftInPage(page_size, curr_addr, count - total_written); + switch (getKernError(mach_vm_write( + task.port, + curr_addr, + @ptrToInt(out_buf.ptr), + @intCast(mach_msg_type_number_t, curr_size), + ))) { + .SUCCESS => {}, + .FAILURE => return error.PermissionDenied, + else => |err| return unexpectedKernError(err), + } + + switch (arch) { + .aarch64 => { + var mattr_value: vm_machine_attribute_val_t = MATTR_VAL_CACHE_FLUSH; + switch (getKernError(vm_machine_attribute( + task.port, + curr_addr, + curr_size, + MATTR_CACHE, + &mattr_value, + ))) { + .SUCCESS => {}, + .FAILURE => return error.PermissionDenied, + else => |err| return unexpectedKernError(err), + } + }, + .x86_64 => {}, + else => unreachable, + } + + out_buf = out_buf[curr_size..]; + total_written += curr_size; + curr_addr += curr_size; + } + + return total_written; + } + + pub fn readMem(task: MachTask, address: u64, buf: []u8) MachError!usize { + const count = buf.len; + var total_read: usize = 0; + var curr_addr = address; + const page_size = try getPageSize(task); // TODO we probably can assume value here + var out_buf = buf[0..]; + + while (total_read < count) { + const curr_size = maxBytesLeftInPage(page_size, curr_addr, count - total_read); + var curr_bytes_read: mach_msg_type_number_t = 0; + var vm_memory: vm_offset_t = undefined; + switch (getKernError(mach_vm_read(task.port, curr_addr, curr_size, &vm_memory, &curr_bytes_read))) { + .SUCCESS => {}, + .FAILURE => return error.PermissionDenied, + else => |err| return unexpectedKernError(err), + } + + @memcpy(out_buf[0..].ptr, @intToPtr([*]const u8, vm_memory), curr_bytes_read); + _ = vm_deallocate(mach_task_self(), vm_memory, curr_bytes_read); + + out_buf = out_buf[curr_bytes_read..]; + curr_addr += curr_bytes_read; + total_read += curr_bytes_read; + } + + return total_read; + } + + fn maxBytesLeftInPage(page_size: usize, address: u64, count: usize) usize { + var left = count; + if (page_size > 0) { + const page_offset = address % page_size; + const bytes_left_in_page = page_size - page_offset; + if (count > bytes_left_in_page) { + left = bytes_left_in_page; + } + } + return left; + } + + fn getPageSize(task: MachTask) MachError!usize { + if (task.isValid()) { + var info_count = TASK_VM_INFO_COUNT; + var vm_info: task_vm_info_data_t = undefined; + switch (getKernError(task_info( + task.port, + TASK_VM_INFO, + @ptrCast(task_info_t, &vm_info), + &info_count, + ))) { + .SUCCESS => return @intCast(usize, vm_info.page_size), + else => {}, + } + } + var page_size: vm_size_t = undefined; + switch (getKernError(_host_page_size(mach_host_self(), &page_size))) { + .SUCCESS => return page_size, + else => |err| return unexpectedKernError(err), + } + } + + pub fn basicTaskInfo(task: MachTask) MachError!mach_task_basic_info { + var info: mach_task_basic_info = undefined; + var count = MACH_TASK_BASIC_INFO_COUNT; + switch (getKernError(task_info( + task.port, + MACH_TASK_BASIC_INFO, + @ptrCast(task_info_t, &info), + &count, + ))) { + .SUCCESS => return info, + else => |err| return unexpectedKernError(err), + } + } + + pub fn @"resume"(task: MachTask) MachError!void { + switch (getKernError(task_resume(task.port))) { + .SUCCESS => {}, + else => |err| return unexpectedKernError(err), + } + } + + pub fn @"suspend"(task: MachTask) MachError!void { + switch (getKernError(task_suspend(task.port))) { + .SUCCESS => {}, + else => |err| return unexpectedKernError(err), + } + } + + const ThreadList = struct { + buf: []MachThread, + + pub fn deinit(list: ThreadList) void { + const self_task = machTaskForSelf(); + _ = vm_deallocate( + self_task.port, + @ptrToInt(list.buf.ptr), + @intCast(vm_size_t, list.buf.len * @sizeOf(mach_port_t)), + ); + } + }; + + pub fn getThreads(task: MachTask) MachError!ThreadList { + var thread_list: mach_port_array_t = undefined; + var thread_count: mach_msg_type_number_t = undefined; + switch (getKernError(task_threads(task.port, &thread_list, &thread_count))) { + .SUCCESS => return ThreadList{ .buf = @ptrCast([*]MachThread, thread_list)[0..thread_count] }, + else => |err| return unexpectedKernError(err), + } + } +}; + +pub const MachThread = extern struct { + port: mach_port_t, + + pub fn isValid(thread: MachThread) bool { + return thread.port != THREAD_NULL; + } + + pub fn getBasicInfo(thread: MachThread) MachError!thread_basic_info { + var info: thread_basic_info = undefined; + var count = THREAD_BASIC_INFO_COUNT; + switch (getKernError(thread_info( + thread.port, + THREAD_BASIC_INFO, + @ptrCast(thread_info_t, &info), + &count, + ))) { + .SUCCESS => return info, + else => |err| return unexpectedKernError(err), + } + } + + pub fn getIdentifierInfo(thread: MachThread) MachError!thread_identifier_info { + var info: thread_identifier_info = undefined; + var count = THREAD_IDENTIFIER_INFO_COUNT; + switch (getKernError(thread_info( + thread.port, + THREAD_IDENTIFIER_INFO, + @ptrCast(thread_info_t, &info), + &count, + ))) { + .SUCCESS => return info, + else => |err| return unexpectedKernError(err), + } + } +}; + +pub fn machTaskForPid(pid: std.os.pid_t) MachError!MachTask { + var port: mach_port_name_t = undefined; + switch (getKernError(task_for_pid(mach_task_self(), pid, &port))) { + .SUCCESS => {}, + .FAILURE => return error.PermissionDenied, + else => |err| return unexpectedKernError(err), + } + return MachTask{ .port = port }; +} + +pub fn machTaskForSelf() MachTask { + return .{ .port = mach_task_self() }; +} diff --git a/lib/std/os/darwin/cssm.zig b/lib/std/c/darwin/cssm.zig similarity index 100% rename from lib/std/os/darwin/cssm.zig rename to lib/std/c/darwin/cssm.zig diff --git a/lib/std/os.zig b/lib/std/os.zig index 722028b3f8..77995da034 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -29,7 +29,7 @@ const Allocator = std.mem.Allocator; const Preopen = std.fs.wasi.Preopen; const PreopenList = std.fs.wasi.PreopenList; -pub const darwin = @import("os/darwin.zig"); +pub const darwin = std.c; pub const dragonfly = std.c; pub const freebsd = std.c; pub const haiku = std.c; @@ -41,7 +41,6 @@ pub const plan9 = @import("os/plan9.zig"); pub const uefi = @import("os/uefi.zig"); pub const wasi = @import("os/wasi.zig"); pub const windows = @import("os/windows.zig"); -pub const ptrace = @import("os/ptrace.zig"); comptime { assert(@import("std") == std); // std lib tests require --zig-lib-dir @@ -7127,3 +7126,25 @@ pub fn timerfd_gettime(fd: i32) TimerFdGetError!linux.itimerspec { else => |err| return unexpectedErrno(err), }; } + +pub const PtraceError = error{ + DeviceBusy, + ProcessNotFound, + PermissionDenied, +} || UnexpectedError; + +/// TODO on other OSes +pub fn ptrace(request: i32, pid: pid_t, addr: ?[*]u8, signal: i32) PtraceError!void { + switch (builtin.os.tag) { + .macos, .ios, .tvos, .watchos => {}, + else => @compileError("TODO implement ptrace"), + } + return switch (errno(system.ptrace(request, pid, addr, signal))) { + .SUCCESS => {}, + .SRCH => error.ProcessNotFound, + .INVAL => unreachable, + .PERM => error.PermissionDenied, + .BUSY => error.DeviceBusy, + else => |err| return unexpectedErrno(err), + }; +} diff --git a/lib/std/os/darwin.zig b/lib/std/os/darwin.zig deleted file mode 100644 index 164a0e06c2..0000000000 --- a/lib/std/os/darwin.zig +++ /dev/null @@ -1,540 +0,0 @@ -const std = @import("std"); -const builtin = @import("builtin"); -const log = std.log; -const mem = std.mem; - -pub const cssm = @import("darwin/cssm.zig"); - -pub usingnamespace std.c; -pub usingnamespace mach_task; - -const mach_task = if (builtin.target.isDarwin()) struct { - pub const MachError = error{ - /// Not enough permissions held to perform the requested kernel - /// call. - PermissionDenied, - /// Kernel returned an unhandled and unexpected error code. - /// This is a catch-all for any yet unobserved kernel response - /// to some Mach message. - Unexpected, - }; - - pub const MachTask = extern struct { - port: std.c.mach_port_name_t, - - pub fn isValid(self: MachTask) bool { - return self.port != std.c.TASK_NULL; - } - - pub fn pidForTask(self: MachTask) MachError!std.os.pid_t { - var pid: std.os.pid_t = undefined; - switch (std.c.getKernError(std.c.pid_for_task(self.port, &pid))) { - .SUCCESS => return pid, - .FAILURE => return error.PermissionDenied, - else => |err| { - log.err("pid_for_task kernel call failed with error code: {s}", .{@tagName(err)}); - return error.Unexpected; - }, - } - } - - pub fn allocatePort(self: MachTask, right: std.c.MACH_PORT_RIGHT) MachError!MachTask { - var out_port: std.c.mach_port_name_t = undefined; - switch (std.c.getKernError(std.c.mach_port_allocate( - self.port, - @enumToInt(right), - &out_port, - ))) { - .SUCCESS => return .{ .port = out_port }, - .FAILURE => return error.PermissionDenied, - else => |err| { - log.err("mach_task_allocate kernel call failed with error code: {s}", .{@tagName(err)}); - return error.Unexpected; - }, - } - } - - pub fn deallocatePort(self: MachTask, port: MachTask) void { - _ = std.c.getKernError(std.c.mach_port_deallocate(self.port, port.port)); - } - - pub fn insertRight(self: MachTask, port: MachTask, msg: std.c.MACH_MSG_TYPE) !void { - switch (std.c.getKernError(std.c.mach_port_insert_right( - self.port, - port.port, - port.port, - @enumToInt(msg), - ))) { - .SUCCESS => return, - .FAILURE => return error.PermissionDenied, - else => |err| { - log.err("mach_port_insert_right kernel call failed with error code: {s}", .{@tagName(err)}); - return error.Unexpected; - }, - } - } - - pub const PortInfo = struct { - mask: std.c.exception_mask_t, - masks: [std.c.EXC_TYPES_COUNT]std.c.exception_mask_t, - ports: [std.c.EXC_TYPES_COUNT]std.c.mach_port_t, - behaviors: [std.c.EXC_TYPES_COUNT]std.c.exception_behavior_t, - flavors: [std.c.EXC_TYPES_COUNT]std.c.thread_state_flavor_t, - count: std.c.mach_msg_type_number_t, - }; - - pub fn getExceptionPorts(self: MachTask, mask: std.c.exception_mask_t) !PortInfo { - var info = PortInfo{ - .mask = mask, - .masks = undefined, - .ports = undefined, - .behaviors = undefined, - .flavors = undefined, - .count = 0, - }; - info.count = info.ports.len / @sizeOf(std.c.mach_port_t); - - switch (std.c.getKernError(std.c.task_get_exception_ports( - self.port, - info.mask, - &info.masks, - &info.count, - &info.ports, - &info.behaviors, - &info.flavors, - ))) { - .SUCCESS => return info, - .FAILURE => return error.PermissionDenied, - else => |err| { - log.err("task_get_exception_ports kernel call failed with error code: {s}", .{@tagName(err)}); - return error.Unexpected; - }, - } - } - - pub fn setExceptionPorts( - self: MachTask, - mask: std.c.exception_mask_t, - new_port: MachTask, - behavior: std.c.exception_behavior_t, - new_flavor: std.c.thread_state_flavor_t, - ) !void { - switch (std.c.getKernError(std.c.task_set_exception_ports( - self.port, - mask, - new_port.port, - behavior, - new_flavor, - ))) { - .SUCCESS => return, - .FAILURE => return error.PermissionDenied, - else => |err| { - log.err("task_set_exception_ports kernel call failed with error code: {s}", .{@tagName(err)}); - return error.Unexpected; - }, - } - } - - pub const RegionInfo = struct { - pub const Tag = enum { - basic, - extended, - top, - }; - - base_addr: u64, - tag: Tag, - info: union { - basic: std.c.vm_region_basic_info_64, - extended: std.c.vm_region_extended_info, - top: std.c.vm_region_top_info, - }, - }; - - pub fn getRegionInfo( - task: MachTask, - address: u64, - len: usize, - tag: RegionInfo.Tag, - ) MachError!RegionInfo { - var info: RegionInfo = .{ - .base_addr = address, - .tag = tag, - .info = undefined, - }; - switch (tag) { - .basic => info.info = .{ .basic = undefined }, - .extended => info.info = .{ .extended = undefined }, - .top => info.info = .{ .top = undefined }, - } - var base_len: std.c.mach_vm_size_t = if (len == 1) 2 else len; - var objname: std.c.mach_port_t = undefined; - var count: std.c.mach_msg_type_number_t = switch (tag) { - .basic => std.c.VM_REGION_BASIC_INFO_COUNT, - .extended => std.c.VM_REGION_EXTENDED_INFO_COUNT, - .top => std.c.VM_REGION_TOP_INFO_COUNT, - }; - switch (std.c.getKernError(std.c.mach_vm_region( - task.port, - &info.base_addr, - &base_len, - switch (tag) { - .basic => std.c.VM_REGION_BASIC_INFO_64, - .extended => std.c.VM_REGION_EXTENDED_INFO, - .top => std.c.VM_REGION_TOP_INFO, - }, - switch (tag) { - .basic => @ptrCast(std.c.vm_region_info_t, &info.info.basic), - .extended => @ptrCast(std.c.vm_region_info_t, &info.info.extended), - .top => @ptrCast(std.c.vm_region_info_t, &info.info.top), - }, - &count, - &objname, - ))) { - .SUCCESS => return info, - .FAILURE => return error.PermissionDenied, - else => |err| { - log.err("mach_vm_region kernel call failed with error code: {s}", .{@tagName(err)}); - return error.Unexpected; - }, - } - } - - pub const RegionSubmapInfo = struct { - pub const Tag = enum { - short, - full, - }; - - tag: Tag, - base_addr: u64, - info: union { - short: std.c.vm_region_submap_short_info_64, - full: std.c.vm_region_submap_info_64, - }, - }; - - pub fn getRegionSubmapInfo( - task: MachTask, - address: u64, - len: usize, - nesting_depth: u32, - tag: RegionSubmapInfo.Tag, - ) MachError!RegionSubmapInfo { - var info: RegionSubmapInfo = .{ - .base_addr = address, - .tag = tag, - .info = undefined, - }; - switch (tag) { - .short => info.info = .{ .short = undefined }, - .full => info.info = .{ .full = undefined }, - } - var nesting = nesting_depth; - var base_len: std.c.mach_vm_size_t = if (len == 1) 2 else len; - var count: std.c.mach_msg_type_number_t = switch (tag) { - .short => std.c.VM_REGION_SUBMAP_SHORT_INFO_COUNT_64, - .full => std.c.VM_REGION_SUBMAP_INFO_COUNT_64, - }; - switch (std.c.getKernError(std.c.mach_vm_region_recurse( - task.port, - &info.base_addr, - &base_len, - &nesting, - switch (tag) { - .short => @ptrCast(std.c.vm_region_recurse_info_t, &info.info.short), - .full => @ptrCast(std.c.vm_region_recurse_info_t, &info.info.full), - }, - &count, - ))) { - .SUCCESS => return info, - .FAILURE => return error.PermissionDenied, - else => |err| { - log.err("mach_vm_region kernel call failed with error code: {s}", .{@tagName(err)}); - return error.Unexpected; - }, - } - } - - pub fn getCurrProtection(task: MachTask, address: u64, len: usize) MachError!std.c.vm_prot_t { - const info = try task.getRegionSubmapInfo(address, len, 0, .short); - return info.info.short.protection; - } - - pub fn setMaxProtection(task: MachTask, address: u64, len: usize, prot: std.c.vm_prot_t) MachError!void { - return task.setProtectionImpl(address, len, true, prot); - } - - pub fn setCurrProtection(task: MachTask, address: u64, len: usize, prot: std.c.vm_prot_t) MachError!void { - return task.setProtectionImpl(address, len, false, prot); - } - - fn setProtectionImpl(task: MachTask, address: u64, len: usize, set_max: bool, prot: std.c.vm_prot_t) MachError!void { - switch (std.c.getKernError(std.c.mach_vm_protect(task.port, address, len, @boolToInt(set_max), prot))) { - .SUCCESS => return, - .FAILURE => return error.PermissionDenied, - else => |err| { - log.err("mach_vm_protect kernel call failed with error code: {s}", .{@tagName(err)}); - return error.Unexpected; - }, - } - } - - /// Will write to VM even if current protection attributes specifically prohibit - /// us from doing so, by temporarily setting protection level to a level with VM_PROT_COPY - /// variant, and resetting after a successful or unsuccessful write. - pub fn writeMemProtected(task: MachTask, address: u64, buf: []const u8, arch: std.Target.Cpu.Arch) MachError!usize { - const curr_prot = try task.getCurrProtection(address, buf.len); - try task.setCurrProtection( - address, - buf.len, - std.c.PROT.READ | std.c.PROT.WRITE | std.c.PROT.COPY, - ); - defer { - task.setCurrProtection(address, buf.len, curr_prot) catch {}; - } - return task.writeMem(address, buf, arch); - } - - pub fn writeMem(task: MachTask, address: u64, buf: []const u8, arch: std.Target.Cpu.Arch) MachError!usize { - const count = buf.len; - var total_written: usize = 0; - var curr_addr = address; - const page_size = try getPageSize(task); // TODO we probably can assume value here - var out_buf = buf[0..]; - - while (total_written < count) { - const curr_size = maxBytesLeftInPage(page_size, curr_addr, count - total_written); - switch (std.c.getKernError(std.c.mach_vm_write( - task.port, - curr_addr, - @ptrToInt(out_buf.ptr), - @intCast(std.c.mach_msg_type_number_t, curr_size), - ))) { - .SUCCESS => {}, - .FAILURE => return error.PermissionDenied, - else => |err| { - log.err("mach_vm_write kernel call failed with error code: {s}", .{@tagName(err)}); - return error.Unexpected; - }, - } - - switch (arch) { - .aarch64 => { - var mattr_value: std.c.vm_machine_attribute_val_t = std.c.MATTR_VAL_CACHE_FLUSH; - switch (std.c.getKernError(std.c.vm_machine_attribute( - task.port, - curr_addr, - curr_size, - std.c.MATTR_CACHE, - &mattr_value, - ))) { - .SUCCESS => {}, - .FAILURE => return error.PermissionDenied, - else => |err| { - log.err("vm_machine_attribute kernel call failed with error code: {s}", .{@tagName(err)}); - return error.Unexpected; - }, - } - }, - .x86_64 => {}, - else => unreachable, - } - - out_buf = out_buf[curr_size..]; - total_written += curr_size; - curr_addr += curr_size; - } - - return total_written; - } - - pub fn readMem(task: MachTask, address: u64, buf: []u8) MachError!usize { - const count = buf.len; - var total_read: usize = 0; - var curr_addr = address; - const page_size = try getPageSize(task); // TODO we probably can assume value here - var out_buf = buf[0..]; - - while (total_read < count) { - const curr_size = maxBytesLeftInPage(page_size, curr_addr, count - total_read); - var curr_bytes_read: std.c.mach_msg_type_number_t = 0; - var vm_memory: std.c.vm_offset_t = undefined; - switch (std.c.getKernError(std.c.mach_vm_read(task.port, curr_addr, curr_size, &vm_memory, &curr_bytes_read))) { - .SUCCESS => {}, - .FAILURE => return error.PermissionDenied, - else => |err| { - log.err("mach_vm_read kernel call failed with error code: {s}", .{@tagName(err)}); - return error.Unexpected; - }, - } - - @memcpy(out_buf[0..].ptr, @intToPtr([*]const u8, vm_memory), curr_bytes_read); - _ = std.c.vm_deallocate(std.c.mach_task_self(), vm_memory, curr_bytes_read); - - out_buf = out_buf[curr_bytes_read..]; - curr_addr += curr_bytes_read; - total_read += curr_bytes_read; - } - - return total_read; - } - - fn maxBytesLeftInPage(page_size: usize, address: u64, count: usize) usize { - var left = count; - if (page_size > 0) { - const page_offset = address % page_size; - const bytes_left_in_page = page_size - page_offset; - if (count > bytes_left_in_page) { - left = bytes_left_in_page; - } - } - return left; - } - - fn getPageSize(task: MachTask) MachError!usize { - if (task.isValid()) { - var info_count = std.c.TASK_VM_INFO_COUNT; - var vm_info: std.c.task_vm_info_data_t = undefined; - switch (std.c.getKernError(std.c.task_info( - task.port, - std.c.TASK_VM_INFO, - @ptrCast(std.c.task_info_t, &vm_info), - &info_count, - ))) { - .SUCCESS => return @intCast(usize, vm_info.page_size), - else => {}, - } - } - var page_size: std.c.vm_size_t = undefined; - switch (std.c.getKernError(std.c._host_page_size(std.c.mach_host_self(), &page_size))) { - .SUCCESS => return page_size, - else => |err| { - log.err("_host_page_size kernel call failed with error code: {s}", .{@tagName(err)}); - return error.Unexpected; - }, - } - } - - pub fn basicTaskInfo(task: MachTask) MachError!std.c.mach_task_basic_info { - var info: std.c.mach_task_basic_info = undefined; - var count = std.c.MACH_TASK_BASIC_INFO_COUNT; - switch (std.c.getKernError(std.c.task_info( - task.port, - std.c.MACH_TASK_BASIC_INFO, - @ptrCast(std.c.task_info_t, &info), - &count, - ))) { - .SUCCESS => return info, - else => |err| { - log.err("task_info kernel call failed with error code: {s}", .{@tagName(err)}); - return error.Unexpected; - }, - } - } - - pub fn @"resume"(task: MachTask) MachError!void { - switch (std.c.getKernError(std.c.task_resume(task.port))) { - .SUCCESS => {}, - else => |err| { - log.err("task_resume kernel call failed with error code: {s}", .{@tagName(err)}); - return error.Unexpected; - }, - } - } - - pub fn @"suspend"(task: MachTask) MachError!void { - switch (std.c.getKernError(std.c.task_suspend(task.port))) { - .SUCCESS => {}, - else => |err| { - log.err("task_suspend kernel call failed with error code: {s}", .{@tagName(err)}); - return error.Unexpected; - }, - } - } - - const ThreadList = struct { - buf: []MachThread, - - pub fn deinit(list: ThreadList) void { - const self_task = machTaskForSelf(); - _ = std.c.vm_deallocate( - self_task.port, - @ptrToInt(list.buf.ptr), - @intCast(std.c.vm_size_t, list.buf.len * @sizeOf(std.c.mach_port_t)), - ); - } - }; - - pub fn getThreads(task: MachTask) MachError!ThreadList { - var thread_list: std.c.mach_port_array_t = undefined; - var thread_count: std.c.mach_msg_type_number_t = undefined; - switch (std.c.getKernError(std.c.task_threads(task.port, &thread_list, &thread_count))) { - .SUCCESS => return ThreadList{ .buf = @ptrCast([*]MachThread, thread_list)[0..thread_count] }, - else => |err| { - log.err("task_threads kernel call failed with error code: {s}", .{@tagName(err)}); - return error.Unexpected; - }, - } - } - }; - - pub const MachThread = extern struct { - port: std.c.mach_port_t, - - pub fn isValid(thread: MachThread) bool { - return thread.port != std.c.THREAD_NULL; - } - - pub fn getBasicInfo(thread: MachThread) MachError!std.c.thread_basic_info { - var info: std.c.thread_basic_info = undefined; - var count = std.c.THREAD_BASIC_INFO_COUNT; - switch (std.c.getKernError(std.c.thread_info( - thread.port, - std.c.THREAD_BASIC_INFO, - @ptrCast(std.c.thread_info_t, &info), - &count, - ))) { - .SUCCESS => return info, - else => |err| { - log.err("thread_info kernel call failed with error code: {s}", .{@tagName(err)}); - return error.Unexpected; - }, - } - } - - pub fn getIdentifierInfo(thread: MachThread) MachError!std.c.thread_identifier_info { - var info: std.c.thread_identifier_info = undefined; - var count = std.c.THREAD_IDENTIFIER_INFO_COUNT; - switch (std.c.getKernError(std.c.thread_info( - thread.port, - std.c.THREAD_IDENTIFIER_INFO, - @ptrCast(std.c.thread_info_t, &info), - &count, - ))) { - .SUCCESS => return info, - else => |err| { - log.err("thread_info kernel call failed with error code: {s}", .{@tagName(err)}); - return error.Unexpected; - }, - } - } - }; - - pub fn machTaskForPid(pid: std.os.pid_t) MachError!MachTask { - var port: std.c.mach_port_name_t = undefined; - switch (std.c.getKernError(std.c.task_for_pid(std.c.mach_task_self(), pid, &port))) { - .SUCCESS => {}, - .FAILURE => return error.PermissionDenied, - else => |err| { - log.err("task_for_pid kernel call failed with error code: {s}", .{@tagName(err)}); - return error.Unexpected; - }, - } - return MachTask{ .port = port }; - } - - pub fn machTaskForSelf() MachTask { - return .{ .port = std.c.mach_task_self() }; - } -} else struct {}; diff --git a/lib/std/os/ptrace.zig b/lib/std/os/ptrace.zig deleted file mode 100644 index afe0b51e2e..0000000000 --- a/lib/std/os/ptrace.zig +++ /dev/null @@ -1,28 +0,0 @@ -const std = @import("std"); -const builtin = @import("builtin"); - -const os = @import("../os.zig"); -const system = os.system; -const errno = system.getErrno; -const pid_t = system.pid_t; -const unexpectedErrno = os.unexpectedErrno; -const UnexpectedError = os.UnexpectedError; - -pub usingnamespace ptrace; - -const ptrace = if (builtin.target.isDarwin()) struct { - pub const PtraceError = error{ - ProcessNotFound, - PermissionDenied, - } || UnexpectedError; - - pub fn ptrace(request: i32, pid: pid_t, addr: ?[*]u8, signal: i32) PtraceError!void { - switch (errno(system.ptrace(request, pid, addr, signal))) { - .SUCCESS => return, - .SRCH => return error.ProcessNotFound, - .INVAL => unreachable, - .BUSY, .PERM => return error.PermissionDenied, - else => |err| return unexpectedErrno(err), - } - } -} else struct {}; From 9964f1c160acd0bf994708333cd69bc070d6c77e Mon Sep 17 00:00:00 2001 From: InKryption Date: Thu, 16 Mar 2023 01:02:10 +0100 Subject: [PATCH 291/294] Add error for bad cast from `*T` to `*[n]T` Casting `*T` to `*[1]T` should still work, but every other length will now be a compiler error instead of a potential OOB access. --- src/Sema.zig | 1 + ..._implicit_cast_from_T_to_long_array_ptr.zig | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 test/cases/compile_errors/attempted_implicit_cast_from_T_to_long_array_ptr.zig diff --git a/src/Sema.zig b/src/Sema.zig index fc39fbb9fc..8b476d4542 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -24853,6 +24853,7 @@ fn coerceExtra( const array_ty = dest_info.pointee_type; if (array_ty.zigTypeTag() != .Array) break :single_item; const array_elem_ty = array_ty.childType(); + if (array_ty.arrayLen() != 1) break :single_item; const dest_is_mut = dest_info.mutable; switch (try sema.coerceInMemoryAllowed(block, array_elem_ty, ptr_elem_ty, dest_is_mut, target, dest_ty_src, inst_src)) { .ok => {}, diff --git a/test/cases/compile_errors/attempted_implicit_cast_from_T_to_long_array_ptr.zig b/test/cases/compile_errors/attempted_implicit_cast_from_T_to_long_array_ptr.zig new file mode 100644 index 0000000000..135081c15c --- /dev/null +++ b/test/cases/compile_errors/attempted_implicit_cast_from_T_to_long_array_ptr.zig @@ -0,0 +1,18 @@ +export fn entry0(single: *u32) void { + _ = @as(*const [0]u32, single); +} +export fn entry1(single: *u32) void { + _ = @as(*const [1]u32, single); +} +export fn entry2(single: *u32) void { + _ = @as(*const [2]u32, single); +} + +// error +// backend=stage2 +// target=native +// +// :2:28: error: expected type '*const [0]u32', found '*u32' +// :2:28: note: pointer type child 'u32' cannot cast into pointer type child '[0]u32' +// :8:28: error: expected type '*const [2]u32', found '*u32' +// :8:28: note: pointer type child 'u32' cannot cast into pointer type child '[2]u32' From da0509750a332806cfddad24b88ae8900782185d Mon Sep 17 00:00:00 2001 From: mllken Date: Tue, 14 Mar 2023 14:50:38 +0700 Subject: [PATCH 292/294] std.os: handle EPERM errno for bind --- lib/std/os.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/os.zig b/lib/std/os.zig index 77995da034..25cc4e34c4 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -3474,7 +3474,7 @@ pub fn bind(sock: socket_t, addr: *const sockaddr, len: socklen_t) BindError!voi const rc = system.bind(sock, addr, len); switch (errno(rc)) { .SUCCESS => return, - .ACCES => return error.AccessDenied, + .ACCES, .PERM => return error.AccessDenied, .ADDRINUSE => return error.AddressInUse, .BADF => unreachable, // always a race condition if this error is returned .INVAL => unreachable, // invalid parameters From b3af5d076c24744bdd100c25eabfea2a1a4688cf Mon Sep 17 00:00:00 2001 From: Evin Yulo Date: Wed, 15 Mar 2023 12:53:53 -0400 Subject: [PATCH 293/294] Fix #14901: parseFloat parsing `0x` successfully --- lib/std/fmt/parse_float.zig | 1 + lib/std/fmt/parse_float/parse.zig | 2 ++ 2 files changed, 3 insertions(+) diff --git a/lib/std/fmt/parse_float.zig b/lib/std/fmt/parse_float.zig index 427ac727c9..e92564ef01 100644 --- a/lib/std/fmt/parse_float.zig +++ b/lib/std/fmt/parse_float.zig @@ -119,6 +119,7 @@ test "fmt.parseFloat hex.f16" { } test "fmt.parseFloat hex.f32" { + try testing.expectError(error.InvalidCharacter, parseFloat(f32, "0x")); try testing.expectEqual(try parseFloat(f32, "0x1p0"), 1.0); try testing.expectEqual(try parseFloat(f32, "-0x1p-1"), -0.5); try testing.expectEqual(try parseFloat(f32, "0x10p+10"), 16384.0); diff --git a/lib/std/fmt/parse_float/parse.zig b/lib/std/fmt/parse_float/parse.zig index 3b757c7c41..9f6e75b29a 100644 --- a/lib/std/fmt/parse_float/parse.zig +++ b/lib/std/fmt/parse_float/parse.zig @@ -107,6 +107,8 @@ fn parsePartialNumberBase(comptime T: type, stream: *FloatStream, negative: bool tryParseDigits(MantissaT, stream, &mantissa, info.base); var int_end = stream.offsetTrue(); var n_digits = @intCast(isize, stream.offsetTrue()); + // the base being 16 implies a 0x prefix, which shouldn't be included in the digit count + if (info.base == 16) n_digits -= 2; // handle dot with the following digits var exponent: i64 = 0; From b4d58e93ea4d0bbfe674f80d301279d302fe8fc8 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 16 Mar 2023 15:43:51 -0700 Subject: [PATCH 294/294] make docgen accept --zig-lib-dir --- build.zig | 3 ++ doc/docgen.zig | 87 +++++++++++++++++++++++++++++++++++--------------- 2 files changed, 64 insertions(+), 26 deletions(-) diff --git a/build.zig b/build.zig index a0c4ca7fb4..303495f6ba 100644 --- a/build.zig +++ b/build.zig @@ -47,6 +47,9 @@ pub fn build(b: *std.Build) !void { const docgen_cmd = b.addRunArtifact(docgen_exe); docgen_cmd.addArgs(&.{ "--zig", b.zig_exe }); + if (b.zig_lib_dir) |p| { + docgen_cmd.addArgs(&.{ "--zig-lib-dir", p }); + } docgen_cmd.addFileSourceArg(.{ .path = "doc/langref.html.in" }); const langref_file = docgen_cmd.addOutputFileArg("langref.html"); const install_langref = b.addInstallFileWithDir(langref_file, .prefix, "doc/langref.html"); diff --git a/doc/docgen.zig b/doc/docgen.zig index 67163ca427..277316dd37 100644 --- a/doc/docgen.zig +++ b/doc/docgen.zig @@ -28,10 +28,10 @@ const usage = \\ ; -fn errorf(comptime format: []const u8, args: anytype) noreturn { +fn fatal(comptime format: []const u8, args: anytype) noreturn { const stderr = io.getStdErr().writer(); - stderr.print("error: " ++ format, args) catch {}; + stderr.print("error: " ++ format ++ "\n", args) catch {}; process.exit(1); } @@ -45,6 +45,7 @@ pub fn main() !void { if (!args_it.skip()) @panic("expected self arg"); var zig_exe: []const u8 = "zig"; + var opt_zig_lib_dir: ?[]const u8 = null; var do_code_tests = true; var files = [_][]const u8{ "", "" }; @@ -59,24 +60,29 @@ pub fn main() !void { if (args_it.next()) |param| { zig_exe = param; } else { - errorf("expected parameter after --zig\n", .{}); + fatal("expected parameter after --zig", .{}); + } + } else if (mem.eql(u8, arg, "--zig-lib-dir")) { + if (args_it.next()) |param| { + opt_zig_lib_dir = param; + } else { + fatal("expected parameter after --zig-lib-dir", .{}); } } else if (mem.eql(u8, arg, "--skip-code-tests")) { do_code_tests = false; } else { - errorf("unrecognized option: '{s}'\n", .{arg}); + fatal("unrecognized option: '{s}'", .{arg}); } } else { if (i > 1) { - errorf("too many arguments\n", .{}); + fatal("too many arguments", .{}); } files[i] = arg; i += 1; } } if (i < 2) { - errorf("not enough arguments\n", .{}); - process.exit(1); + fatal("not enough arguments", .{}); } var in_file = try fs.cwd().openFile(files[0], .{ .mode = .read_only }); @@ -95,7 +101,7 @@ pub fn main() !void { try fs.cwd().makePath(tmp_dir_name); defer fs.cwd().deleteTree(tmp_dir_name) catch {}; - try genHtml(allocator, &tokenizer, &toc, buffered_writer.writer(), zig_exe, do_code_tests); + try genHtml(allocator, &tokenizer, &toc, buffered_writer.writer(), zig_exe, opt_zig_lib_dir, do_code_tests); try buffered_writer.flush(); } @@ -1268,6 +1274,7 @@ fn genHtml( toc: *Toc, out: anytype, zig_exe: []const u8, + opt_zig_lib_dir: ?[]const u8, do_code_tests: bool, ) !void { var progress = Progress{ .dont_print_on_dumb = true }; @@ -1278,7 +1285,7 @@ fn genHtml( try env_map.put("ZIG_DEBUG_COLOR", "1"); const host = try std.zig.system.NativeTargetInfo.detect(.{}); - const builtin_code = try getBuiltinCode(allocator, &env_map, zig_exe); + const builtin_code = try getBuiltinCode(allocator, &env_map, zig_exe, opt_zig_lib_dir); for (toc.nodes) |node| { defer root_node.completeOne(); @@ -1370,6 +1377,9 @@ fn genHtml( "--color", "on", "--enable-cache", tmp_source_file_name, }); + if (opt_zig_lib_dir) |zig_lib_dir| { + try build_args.appendSlice(&.{ "--zig-lib-dir", zig_lib_dir }); + } try shell_out.print("$ zig build-exe {s} ", .{name_plus_ext}); @@ -1512,8 +1522,12 @@ fn genHtml( defer test_args.deinit(); try test_args.appendSlice(&[_][]const u8{ - zig_exe, "test", tmp_source_file_name, + zig_exe, "test", + tmp_source_file_name, }); + if (opt_zig_lib_dir) |zig_lib_dir| { + try test_args.appendSlice(&.{ "--zig-lib-dir", zig_lib_dir }); + } try shell_out.print("$ zig test {s}.zig ", .{code.name}); switch (code.mode) { @@ -1564,12 +1578,13 @@ fn genHtml( defer test_args.deinit(); try test_args.appendSlice(&[_][]const u8{ - zig_exe, - "test", - "--color", - "on", + zig_exe, "test", + "--color", "on", tmp_source_file_name, }); + if (opt_zig_lib_dir) |zig_lib_dir| { + try test_args.appendSlice(&.{ "--zig-lib-dir", zig_lib_dir }); + } try shell_out.print("$ zig test {s}.zig ", .{code.name}); switch (code.mode) { @@ -1624,8 +1639,12 @@ fn genHtml( defer test_args.deinit(); try test_args.appendSlice(&[_][]const u8{ - zig_exe, "test", tmp_source_file_name, + zig_exe, "test", + tmp_source_file_name, }); + if (opt_zig_lib_dir) |zig_lib_dir| { + try test_args.appendSlice(&.{ "--zig-lib-dir", zig_lib_dir }); + } var mode_arg: []const u8 = ""; switch (code.mode) { .Debug => {}, @@ -1684,17 +1703,17 @@ fn genHtml( defer build_args.deinit(); try build_args.appendSlice(&[_][]const u8{ - zig_exe, - "build-obj", + zig_exe, "build-obj", + "--color", "on", + "--name", code.name, tmp_source_file_name, - "--color", - "on", - "--name", - code.name, try std.fmt.allocPrint(allocator, "-femit-bin={s}{c}{s}", .{ tmp_dir_name, fs.path.sep, name_plus_obj_ext, }), }); + if (opt_zig_lib_dir) |zig_lib_dir| { + try build_args.appendSlice(&.{ "--zig-lib-dir", zig_lib_dir }); + } try shell_out.print("$ zig build-obj {s}.zig ", .{code.name}); @@ -1758,13 +1777,15 @@ fn genHtml( defer test_args.deinit(); try test_args.appendSlice(&[_][]const u8{ - zig_exe, - "build-lib", + zig_exe, "build-lib", tmp_source_file_name, try std.fmt.allocPrint(allocator, "-femit-bin={s}{s}{s}", .{ tmp_dir_name, fs.path.sep_str, bin_basename, }), }); + if (opt_zig_lib_dir) |zig_lib_dir| { + try test_args.appendSlice(&.{ "--zig-lib-dir", zig_lib_dir }); + } try shell_out.print("$ zig build-lib {s}.zig ", .{code.name}); switch (code.mode) { @@ -1829,9 +1850,23 @@ fn exec(allocator: Allocator, env_map: *process.EnvMap, args: []const []const u8 return result; } -fn getBuiltinCode(allocator: Allocator, env_map: *process.EnvMap, zig_exe: []const u8) ![]const u8 { - const result = try exec(allocator, env_map, &[_][]const u8{ zig_exe, "build-obj", "--show-builtin" }); - return result.stdout; +fn getBuiltinCode( + allocator: Allocator, + env_map: *process.EnvMap, + zig_exe: []const u8, + opt_zig_lib_dir: ?[]const u8, +) ![]const u8 { + if (opt_zig_lib_dir) |zig_lib_dir| { + const result = try exec(allocator, env_map, &.{ + zig_exe, "build-obj", "--show-builtin", "--zig-lib-dir", zig_lib_dir, + }); + return result.stdout; + } else { + const result = try exec(allocator, env_map, &.{ + zig_exe, "build-obj", "--show-builtin", + }); + return result.stdout; + } } fn dumpArgs(args: []const []const u8) void {