From c0369575218d3dccb69f62495de31c8e22d41ae9 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Tue, 11 May 2021 22:21:22 +0200 Subject: [PATCH 1/9] std.meta.Elem: support all optional types --- lib/std/meta.zig | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/lib/std/meta.zig b/lib/std/meta.zig index 391b7259e3..473d4fcf26 100644 --- a/lib/std/meta.zig +++ b/lib/std/meta.zig @@ -175,13 +175,7 @@ pub fn Elem(comptime T: type) type { }, .Many, .C, .Slice => return info.child, }, - .Optional => |info| switch (@typeInfo(info.child)) { - .Pointer => |ptr_info| switch (ptr_info.size) { - .Many => return ptr_info.child, - else => {}, - }, - else => {}, - }, + .Optional => |info| return Elem(info.child), else => {}, } @compileError("Expected pointer, slice, array or vector type, found '" ++ @typeName(T) ++ "'"); From cd7b5a37186332e90fee91dd366cba46e3c29ca0 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Thu, 6 May 2021 18:33:21 +0200 Subject: [PATCH 2/9] std/mem: add sliceTo(), deprecate spanZ(), lenZ() The current spanZ() function will not scan for a 0 terminator if the type is not 0 terminated. This encourages using 0 terminated array types to bind C arrays which hold 0 terminated strings. However, this is a big footgun as nothing in the C type system guarantees there to be a 0 terminator at the end of the array and if there is none this becomes Illegal Behavior in Zig. To solve this, deprecate spanZ() and lenZ(), adding a new sliceTo() function that always scans for the given terminator even if the type is not sentinel terminated. --- lib/std/mem.zig | 188 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 188 insertions(+) diff --git a/lib/std/mem.zig b/lib/std/mem.zig index df91adddb2..aeec384da1 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -602,6 +602,7 @@ test "span" { try testing.expectEqual(@as(?[:0]u16, null), span(@as(?[*:0]u16, null))); } +/// Deprecated: use std.mem.span() or std.mem.sliceTo() /// Same as `span`, except when there is both a sentinel and an array /// length or slice length, scans the memory for the sentinel value /// rather than using the length. @@ -630,6 +631,192 @@ test "spanZ" { try testing.expectEqual(@as(?[:0]u16, null), spanZ(@as(?[*:0]u16, null))); } +/// Helper for the return type of sliceTo() +fn SliceTo(comptime T: type, comptime end: meta.Elem(T)) type { + switch (@typeInfo(T)) { + .Optional => |optional_info| { + return ?SliceTo(optional_info.child, end); + }, + .Pointer => |ptr_info| { + var new_ptr_info = ptr_info; + new_ptr_info.size = .Slice; + switch (ptr_info.size) { + .One => switch (@typeInfo(ptr_info.child)) { + .Array => |array_info| { + new_ptr_info.child = array_info.child; + // The return type must only be sentinel terminated if we are guaranteed + // to find the value searched for, which is only the case if it matches + // the sentinel of the type passed. + if (array_info.sentinel) |sentinel| { + if (end == sentinel) { + new_ptr_info.sentinel = end; + } else { + new_ptr_info.sentinel = null; + } + } + }, + else => {}, + }, + .Many, .Slice => { + // The return type must only be sentinel terminated if we are guaranteed + // to find the value searched for, which is only the case if it matches + // the sentinel of the type passed. + if (ptr_info.sentinel) |sentinel| { + if (end == sentinel) { + new_ptr_info.sentinel = end; + } else { + new_ptr_info.sentinel = null; + } + } + }, + .C => { + new_ptr_info.sentinel = end; + // C pointers are always allowzero, but we don't want the return type to be. + assert(new_ptr_info.is_allowzero); + new_ptr_info.is_allowzero = false; + }, + } + return @Type(std.builtin.TypeInfo{ .Pointer = new_ptr_info }); + }, + else => {}, + } + @compileError("invalid type given to std.mem.sliceTo: " ++ @typeName(T)); +} + +/// Takes a pointer to an array, an array, a sentinel-terminated pointer, or a slice and +/// iterates searching for the first occurrence of `end`, returning the scanned slice. +/// If `end` is not found, the full length of the array/slice/sentinel terminated pointer is returned. +/// If the pointer type is sentinel terminated and `end` matches that terminator, the +/// resulting slice is also sentinel terminated. +/// Pointer properties such as mutability and alignment are preserved. +/// C pointers are assumed to be non-null. +pub fn sliceTo(ptr: anytype, comptime end: meta.Elem(@TypeOf(ptr))) SliceTo(@TypeOf(ptr), end) { + if (@typeInfo(@TypeOf(ptr)) == .Optional) { + const non_null = ptr orelse return null; + return sliceTo(non_null, end); + } + const Result = SliceTo(@TypeOf(ptr), end); + const length = lenSliceTo(ptr, end); + if (@typeInfo(Result).Pointer.sentinel) |s| { + return ptr[0..length :s]; + } else { + return ptr[0..length]; + } +} + +test "sliceTo" { + try testing.expectEqualSlices(u8, "aoeu", sliceTo("aoeu", 0)); + + { + var array: [5]u16 = [_]u16{ 1, 2, 3, 4, 5 }; + try testing.expectEqualSlices(u16, &array, sliceTo(&array, 0)); + try testing.expectEqualSlices(u16, array[0..3], sliceTo(array[0..3], 0)); + try testing.expectEqualSlices(u16, array[0..2], sliceTo(&array, 3)); + try testing.expectEqualSlices(u16, array[0..2], sliceTo(array[0..3], 3)); + + const sentinel_ptr = @ptrCast([*:5]u16, &array); + try testing.expectEqualSlices(u16, array[0..2], sliceTo(sentinel_ptr, 3)); + try testing.expectEqualSlices(u16, array[0..4], sliceTo(sentinel_ptr, 99)); + + const optional_sentinel_ptr = @ptrCast(?[*:5]u16, &array); + try testing.expectEqualSlices(u16, array[0..2], sliceTo(optional_sentinel_ptr, 3).?); + try testing.expectEqualSlices(u16, array[0..4], sliceTo(optional_sentinel_ptr, 99).?); + + const c_ptr = @as([*c]u16, &array); + try testing.expectEqualSlices(u16, array[0..2], sliceTo(c_ptr, 3)); + + const slice: []u16 = &array; + try testing.expectEqualSlices(u16, array[0..2], sliceTo(slice, 3)); + try testing.expectEqualSlices(u16, &array, sliceTo(slice, 99)); + + const sentinel_slice: [:5]u16 = array[0..4 :5]; + try testing.expectEqualSlices(u16, array[0..2], sliceTo(sentinel_slice, 3)); + try testing.expectEqualSlices(u16, array[0..4], sliceTo(sentinel_slice, 99)); + } + { + var sentinel_array: [5:0]u16 = [_:0]u16{ 1, 2, 3, 4, 5 }; + try testing.expectEqualSlices(u16, sentinel_array[0..2], sliceTo(&sentinel_array, 3)); + try testing.expectEqualSlices(u16, &sentinel_array, sliceTo(&sentinel_array, 0)); + try testing.expectEqualSlices(u16, &sentinel_array, sliceTo(&sentinel_array, 99)); + } + + try testing.expectEqual(@as(?[]u8, null), sliceTo(@as(?[]u8, null), 0)); +} + +/// Private helper for sliceTo(). If you want the length, use sliceTo(foo, x).len +fn lenSliceTo(ptr: anytype, comptime end: meta.Elem(@TypeOf(ptr))) usize { + switch (@typeInfo(@TypeOf(ptr))) { + .Pointer => |ptr_info| switch (ptr_info.size) { + .One => switch (@typeInfo(ptr_info.child)) { + .Array => |array_info| { + if (array_info.sentinel) |sentinel| { + if (sentinel == end) { + return indexOfSentinel(array_info.child, end, ptr); + } + } + return indexOfScalar(array_info.child, ptr, end) orelse array_info.len; + }, + else => {}, + }, + .Many => if (ptr_info.sentinel) |sentinel| { + // We may be looking for something other than the sentinel, + // but iterating past the sentinel would be a bug so we need + // to check for both. + var i: usize = 0; + while (ptr[i] != end and ptr[i] != sentinel) i += 1; + return i; + }, + .C => { + assert(ptr != null); + return indexOfSentinel(ptr_info.child, end, ptr); + }, + .Slice => { + if (ptr_info.sentinel) |sentinel| { + if (sentinel == end) { + return indexOfSentinel(ptr_info.child, sentinel, ptr); + } + } + return indexOfScalar(ptr_info.child, ptr, end) orelse ptr.len; + }, + }, + else => {}, + } + @compileError("invalid type given to std.mem.sliceTo: " ++ @typeName(@TypeOf(ptr))); +} + +test "lenSliceTo" { + try testing.expect(lenSliceTo("aoeu", 0) == 4); + + { + var array: [5]u16 = [_]u16{ 1, 2, 3, 4, 5 }; + try testing.expectEqual(@as(usize, 5), lenSliceTo(&array, 0)); + try testing.expectEqual(@as(usize, 3), lenSliceTo(array[0..3], 0)); + try testing.expectEqual(@as(usize, 2), lenSliceTo(&array, 3)); + try testing.expectEqual(@as(usize, 2), lenSliceTo(array[0..3], 3)); + + const sentinel_ptr = @ptrCast([*:5]u16, &array); + try testing.expectEqual(@as(usize, 2), lenSliceTo(sentinel_ptr, 3)); + try testing.expectEqual(@as(usize, 4), lenSliceTo(sentinel_ptr, 99)); + + const c_ptr = @as([*c]u16, &array); + try testing.expectEqual(@as(usize, 2), lenSliceTo(c_ptr, 3)); + + const slice: []u16 = &array; + try testing.expectEqual(@as(usize, 2), lenSliceTo(slice, 3)); + try testing.expectEqual(@as(usize, 5), lenSliceTo(slice, 99)); + + const sentinel_slice: [:5]u16 = array[0..4 :5]; + try testing.expectEqual(@as(usize, 2), lenSliceTo(sentinel_slice, 3)); + try testing.expectEqual(@as(usize, 4), lenSliceTo(sentinel_slice, 99)); + } + { + var sentinel_array: [5:0]u16 = [_:0]u16{ 1, 2, 3, 4, 5 }; + try testing.expectEqual(@as(usize, 2), lenSliceTo(&sentinel_array, 3)); + try testing.expectEqual(@as(usize, 5), lenSliceTo(&sentinel_array, 0)); + try testing.expectEqual(@as(usize, 5), lenSliceTo(&sentinel_array, 99)); + } +} + /// Takes a pointer to an array, an array, a vector, a sentinel-terminated pointer, /// a slice or a tuple, and returns the length. /// In the case of a sentinel-terminated array, it uses the array length. @@ -688,6 +875,7 @@ test "len" { } } +/// Deprecated: use std.mem.len() or std.mem.sliceTo().len /// Takes a pointer to an array, an array, a sentinel-terminated pointer, /// or a slice, and returns the length. /// In the case of a sentinel-terminated array, it scans the array From 7b7724628974067f1a66069699fd1433d32da50d Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 12 May 2021 15:17:21 +0200 Subject: [PATCH 3/9] macho: allow overriding stack size in binary --- src/link/MachO.zig | 5 ++++- src/link/MachO/Zld.zig | 12 +++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 09414b4cdd..2ae9575b7e 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -442,6 +442,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; const main_cmd = &self.load_commands.items[self.main_cmd_index.?].Main; main_cmd.entryoff = addr - text_segment.inner.vmaddr; + main_cmd.stacksize = self.base.options.stack_size_override orelse 0; self.load_commands_dirty = true; } try self.writeRebaseInfoTable(); @@ -695,7 +696,9 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { Compilation.dump_argv(argv.items); } - try zld.link(input_files.items, full_out_path); + try zld.link(input_files.items, full_out_path, .{ + .stack_size = self.base.options.stack_size_override, + }); break :outer; } diff --git a/src/link/MachO/Zld.zig b/src/link/MachO/Zld.zig index 3904192995..c619d0634b 100644 --- a/src/link/MachO/Zld.zig +++ b/src/link/MachO/Zld.zig @@ -29,6 +29,10 @@ page_size: ?u16 = null, file: ?fs.File = null, out_path: ?[]const u8 = null, +// TODO these args will become obselete once Zld is coalesced with incremental +// linker. +stack_size: u64 = 0, + objects: std.ArrayListUnmanaged(*Object) = .{}, archives: std.ArrayListUnmanaged(*Archive) = .{}, @@ -172,7 +176,11 @@ pub fn closeFiles(self: Zld) void { if (self.file) |f| f.close(); } -pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8) !void { +const LinkArgs = struct { + stack_size: ?u64 = null, +}; + +pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8, args: LinkArgs) !void { if (files.len == 0) return error.NoInputFiles; if (out_path.len == 0) return error.EmptyOutputPath; @@ -206,6 +214,7 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8) !void { .read = true, .mode = if (std.Target.current.os.tag == .windows) 0 else 0o777, }); + self.stack_size = args.stack_size orelse 0; try self.populateMetadata(); try self.parseInputFiles(files); @@ -2204,6 +2213,7 @@ fn setEntryPoint(self: *Zld) !void { const entry_sym = sym.cast(Symbol.Regular) orelse unreachable; const ec = &self.load_commands.items[self.main_cmd_index.?].Main; ec.entryoff = @intCast(u32, entry_sym.address - seg.inner.vmaddr); + ec.stacksize = self.stack_size; } fn writeRebaseInfoTable(self: *Zld) !void { From 893a4286569c9250c29eec8d1cff386ddbf38b3c Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Wed, 12 May 2021 11:26:37 +0200 Subject: [PATCH 4/9] stage2: Drop LLVM's host CPU detection method as fallback The CPU detection code is nearly at feature parity, we do support detecting the native CPU on Sparc systems and macos, our ARM/AArch64 model list is quite comprehensive and so is our PPC one. The only missing pieces are: - ARM32 detection on Darwin hosts (I don't think anybody is planning on running the compiler on a old-ass iPhone) - s390x detection on Linux hosts, this can be easily added at a later stage. --- lib/std/zig/system.zig | 9 ----- src/main.zig | 82 +----------------------------------------- 2 files changed, 1 insertion(+), 90 deletions(-) diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index 5522367d36..c504f93efd 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -208,11 +208,6 @@ pub const NativeTargetInfo = struct { dynamic_linker: DynamicLinker = DynamicLinker{}, - /// Only some architectures have CPU detection implemented. This field reveals whether - /// CPU detection actually occurred. When this is `true` it means that the reported - /// CPU is baseline only because of a missing implementation for that architecture. - cpu_detection_unimplemented: bool = false, - pub const DynamicLinker = Target.DynamicLinker; pub const DetectError = error{ @@ -367,8 +362,6 @@ pub const NativeTargetInfo = struct { os.version_range.linux.glibc = glibc; } - var cpu_detection_unimplemented = false; - // Until https://github.com/ziglang/zig/issues/4592 is implemented (support detecting the // native CPU architecture as being different than the current target), we use this: const cpu_arch = cross_target.getCpuArch(); @@ -382,7 +375,6 @@ pub const NativeTargetInfo = struct { Target.Cpu.baseline(cpu_arch), .explicit => |model| model.toCpu(cpu_arch), } orelse backup_cpu_detection: { - cpu_detection_unimplemented = true; break :backup_cpu_detection Target.Cpu.baseline(cpu_arch); }; var result = try detectAbiAndDynamicLinker(allocator, cpu, os, cross_target); @@ -419,7 +411,6 @@ pub const NativeTargetInfo = struct { else => {}, } cross_target.updateCpuFeatures(&result.target.cpu.features); - result.cpu_detection_unimplemented = cpu_detection_unimplemented; return result; } diff --git a/src/main.zig b/src/main.zig index a0a11bbbbc..dc98044642 100644 --- a/src/main.zig +++ b/src/main.zig @@ -3396,88 +3396,8 @@ test "fds" { gimmeMoreOfThoseSweetSweetFileDescriptors(); } -fn detectNativeCpuWithLLVM( - arch: std.Target.Cpu.Arch, - llvm_cpu_name_z: ?[*:0]const u8, - llvm_cpu_features_opt: ?[*:0]const u8, -) !std.Target.Cpu { - var result = std.Target.Cpu.baseline(arch); - - if (llvm_cpu_name_z) |cpu_name_z| { - const llvm_cpu_name = mem.spanZ(cpu_name_z); - - for (arch.allCpuModels()) |model| { - const this_llvm_name = model.llvm_name orelse continue; - if (mem.eql(u8, this_llvm_name, llvm_cpu_name)) { - // Here we use the non-dependencies-populated set, - // so that subtracting features later in this function - // affect the prepopulated set. - result = std.Target.Cpu{ - .arch = arch, - .model = model, - .features = model.features, - }; - break; - } - } - } - - const all_features = arch.allFeaturesList(); - - if (llvm_cpu_features_opt) |llvm_cpu_features| { - var it = mem.tokenize(mem.spanZ(llvm_cpu_features), ","); - while (it.next()) |decorated_llvm_feat| { - var op: enum { - add, - sub, - } = undefined; - var llvm_feat: []const u8 = undefined; - if (mem.startsWith(u8, decorated_llvm_feat, "+")) { - op = .add; - llvm_feat = decorated_llvm_feat[1..]; - } else if (mem.startsWith(u8, decorated_llvm_feat, "-")) { - op = .sub; - llvm_feat = decorated_llvm_feat[1..]; - } else { - return error.InvalidLlvmCpuFeaturesFormat; - } - for (all_features) |feature, index_usize| { - const this_llvm_name = feature.llvm_name orelse continue; - if (mem.eql(u8, llvm_feat, this_llvm_name)) { - const index = @intCast(std.Target.Cpu.Feature.Set.Index, index_usize); - switch (op) { - .add => result.features.addFeature(index), - .sub => result.features.removeFeature(index), - } - break; - } - } - } - } - - result.features.populateDependencies(all_features); - return result; -} - fn detectNativeTargetInfo(gpa: *Allocator, cross_target: std.zig.CrossTarget) !std.zig.system.NativeTargetInfo { - var info = try std.zig.system.NativeTargetInfo.detect(gpa, cross_target); - if (info.cpu_detection_unimplemented) { - const arch = std.Target.current.cpu.arch; - - // We want to just use detected_info.target but implementing - // CPU model & feature detection is todo so here we rely on LLVM. - // https://github.com/ziglang/zig/issues/4591 - if (!build_options.have_llvm) - fatal("CPU features detection is not yet available for {s} without LLVM extensions", .{@tagName(arch)}); - - const llvm = @import("codegen/llvm/bindings.zig"); - const llvm_cpu_name = llvm.GetHostCPUName(); - const llvm_cpu_features = llvm.GetNativeFeatures(); - info.target.cpu = try detectNativeCpuWithLLVM(arch, llvm_cpu_name, llvm_cpu_features); - cross_target.updateCpuFeatures(&info.target.cpu.features); - info.target.cpu.arch = cross_target.getCpuArch(); - } - return info; + return std.zig.system.NativeTargetInfo.detect(gpa, cross_target); } /// Indicate that we are now terminating with a successful exit code. From 19aab5302c71edccae7959065062fd1695a396aa Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Fri, 30 Apr 2021 23:07:40 -0700 Subject: [PATCH 5/9] translate-c: Ensure extra_cflags are passed to clang Additionally ensure that the Zig cache incorporates any extra cflags when using translate-c. Fixes the issue identified in #8662 --- src/Cache.zig | 21 ++++++++++++++++++++- src/Compilation.zig | 18 +----------------- src/main.zig | 12 ++++++++---- 3 files changed, 29 insertions(+), 22 deletions(-) diff --git a/src/Cache.zig b/src/Cache.zig index 5bc32b4b68..6c17f52d69 100644 --- a/src/Cache.zig +++ b/src/Cache.zig @@ -11,6 +11,7 @@ const testing = std.testing; const mem = std.mem; const fmt = std.fmt; const Allocator = std.mem.Allocator; +const Compilation = @import("Compilation.zig"); /// Be sure to call `Manifest.deinit` after successful initialization. pub fn obtain(cache: *const Cache) Manifest { @@ -61,7 +62,7 @@ pub const File = struct { pub const HashHelper = struct { hasher: Hasher = hasher_init, - const EmitLoc = @import("Compilation.zig").EmitLoc; + const EmitLoc = Compilation.EmitLoc; /// Record a slice of bytes as an dependency of the process being cached pub fn addBytes(hh: *HashHelper, bytes: []const u8) void { @@ -220,6 +221,24 @@ pub const Manifest = struct { return idx; } + pub fn hashCSource(self: *Manifest, c_source: Compilation.CSourceFile) !void { + _ = try self.addFile(c_source.src_path, null); + // Hash the extra flags, with special care to call addFile for file parameters. + // TODO this logic can likely be improved by utilizing clang_options_data.zig. + const file_args = [_][]const u8{"-include"}; + var arg_i: usize = 0; + while (arg_i < c_source.extra_flags.len) : (arg_i += 1) { + const arg = c_source.extra_flags[arg_i]; + self.hash.addBytes(arg); + for (file_args) |file_arg| { + if (mem.eql(u8, file_arg, arg) and arg_i + 1 < c_source.extra_flags.len) { + arg_i += 1; + _ = try self.addFile(c_source.extra_flags[arg_i], null); + } + } + } + } + pub fn addOptionalFile(self: *Manifest, optional_file_path: ?[]const u8) !void { self.hash.add(optional_file_path != null); const file_path = optional_file_path orelse return; diff --git a/src/Compilation.zig b/src/Compilation.zig index 71df776855..6a9f75acf5 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -2260,23 +2260,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_comp_progress_node: * man.hash.add(comp.clang_preprocessor_mode); - _ = try man.addFile(c_object.src.src_path, null); - { - // Hash the extra flags, with special care to call addFile for file parameters. - // TODO this logic can likely be improved by utilizing clang_options_data.zig. - const file_args = [_][]const u8{"-include"}; - var arg_i: usize = 0; - while (arg_i < c_object.src.extra_flags.len) : (arg_i += 1) { - const arg = c_object.src.extra_flags[arg_i]; - man.hash.addBytes(arg); - for (file_args) |file_arg| { - if (mem.eql(u8, file_arg, arg) and arg_i + 1 < c_object.src.extra_flags.len) { - arg_i += 1; - _ = try man.addFile(c_object.src.extra_flags[arg_i], null); - } - } - } - } + try man.hashCSource(c_object.src); { const is_collision = blk: { diff --git a/src/main.zig b/src/main.zig index dc98044642..bd57c1f14f 100644 --- a/src/main.zig +++ b/src/main.zig @@ -2172,7 +2172,7 @@ fn cmdTranslateC(comp: *Compilation, arena: *Allocator, enable_cache: bool) !voi defer if (enable_cache) man.deinit(); man.hash.add(@as(u16, 0xb945)); // Random number to distinguish translate-c from compiling C objects - _ = man.addFile(c_source_file.src_path, null) catch |err| { + man.hashCSource(c_source_file) catch |err| { fatal("unable to process '{s}': {s}", .{ c_source_file.src_path, @errorName(err) }); }; @@ -2202,12 +2202,16 @@ fn cmdTranslateC(comp: *Compilation, arena: *Allocator, enable_cache: bool) !voi } // Convert to null terminated args. - const new_argv_with_sentinel = try arena.alloc(?[*:0]const u8, argv.items.len + 1); - new_argv_with_sentinel[argv.items.len] = null; - const new_argv = new_argv_with_sentinel[0..argv.items.len :null]; + const clang_args_len = argv.items.len + c_source_file.extra_flags.len; + const new_argv_with_sentinel = try arena.alloc(?[*:0]const u8, clang_args_len + 1); + new_argv_with_sentinel[clang_args_len] = null; + const new_argv = new_argv_with_sentinel[0..clang_args_len :null]; for (argv.items) |arg, i| { new_argv[i] = try arena.dupeZ(u8, arg); } + for (c_source_file.extra_flags) |arg, i| { + new_argv[argv.items.len + i] = try arena.dupeZ(u8, arg); + } const c_headers_dir_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{"include"}); const c_headers_dir_path_z = try arena.dupeZ(u8, c_headers_dir_path); From 40a47eae65b918866abc9d745f89d837f6a1e591 Mon Sep 17 00:00:00 2001 From: Matthew Borkowski Date: Sun, 9 May 2021 01:13:18 -0400 Subject: [PATCH 6/9] fix shrinkAndFree and remove shrinkRetainingCapacity in PriorityQueue and PriorityDequeue --- lib/std/priority_dequeue.zig | 17 +---------------- lib/std/priority_queue.zig | 17 +---------------- 2 files changed, 2 insertions(+), 32 deletions(-) diff --git a/lib/std/priority_dequeue.zig b/lib/std/priority_dequeue.zig index f0f8d7f6f7..0bb1f13503 100644 --- a/lib/std/priority_dequeue.zig +++ b/lib/std/priority_dequeue.zig @@ -387,17 +387,6 @@ pub fn PriorityDequeue(comptime T: type) type { return; }, }; - self.len = new_len; - } - - /// Reduce length to `new_len`. - pub fn shrinkRetainingCapacity(self: *Self, new_len: usize) void { - assert(new_len <= self.items.len); - - // Cannot shrink to smaller than the current queue size without invalidating the heap property - assert(new_len >= self.len); - - self.len = new_len; } pub fn update(self: *Self, elem: T, new_elem: T) !void { @@ -836,7 +825,7 @@ test "std.PriorityDequeue: iterator while empty" { try expectEqual(it.next(), null); } -test "std.PriorityDequeue: shrinkRetainingCapacity and shrinkAndFree" { +test "std.PriorityDequeue: shrinkAndFree" { var queue = PDQ.init(testing.allocator, lessThanComparison); defer queue.deinit(); @@ -849,10 +838,6 @@ test "std.PriorityDequeue: shrinkRetainingCapacity and shrinkAndFree" { try expect(queue.capacity() >= 4); try expectEqual(@as(usize, 3), queue.len); - queue.shrinkRetainingCapacity(3); - try expect(queue.capacity() >= 4); - try expectEqual(@as(usize, 3), queue.len); - queue.shrinkAndFree(3); try expectEqual(@as(usize, 3), queue.capacity()); try expectEqual(@as(usize, 3), queue.len); diff --git a/lib/std/priority_queue.zig b/lib/std/priority_queue.zig index 621af4e97f..4e5320a92b 100644 --- a/lib/std/priority_queue.zig +++ b/lib/std/priority_queue.zig @@ -203,17 +203,6 @@ pub fn PriorityQueue(comptime T: type) type { return; }, }; - self.len = new_len; - } - - /// Reduce length to `new_len`. - pub fn shrinkRetainingCapacity(self: *Self, new_len: usize) void { - assert(new_len <= self.items.len); - - // Cannot shrink to smaller than the current queue size without invalidating the heap property - assert(new_len >= self.len); - - self.len = new_len; } pub fn update(self: *Self, elem: T, new_elem: T) !void { @@ -495,7 +484,7 @@ test "std.PriorityQueue: iterator while empty" { try expectEqual(it.next(), null); } -test "std.PriorityQueue: shrinkRetainingCapacity and shrinkAndFree" { +test "std.PriorityQueue: shrinkAndFree" { var queue = PQ.init(testing.allocator, lessThan); defer queue.deinit(); @@ -508,10 +497,6 @@ test "std.PriorityQueue: shrinkRetainingCapacity and shrinkAndFree" { try expect(queue.capacity() >= 4); try expectEqual(@as(usize, 3), queue.len); - queue.shrinkRetainingCapacity(3); - try expect(queue.capacity() >= 4); - try expectEqual(@as(usize, 3), queue.len); - queue.shrinkAndFree(3); try expectEqual(@as(usize, 3), queue.capacity()); try expectEqual(@as(usize, 3), queue.len); From 799d1f0d36f69c8aead5184a76f87b557d690d7b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 12 May 2021 20:34:54 -0700 Subject: [PATCH 7/9] build system: fix wrong glibc dir passed to qemu for i386 Without this, and with `-Denable-qemu -Denable-foreign-glibc`, i386-linux-gnu tests failed because the `-L` path passed to qemu was "i386-linux-gnu" (nonexistent) rather than "i686-linux-gnu". Fixes std lib and behavior tests when using these options to enable test coverage with cross compiled glibcs. --- lib/std/build.zig | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/lib/std/build.zig b/lib/std/build.zig index 1bdd97b253..9d12a4de51 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -2480,9 +2480,19 @@ pub const LibExeObjStep = struct { try zig_args.append("--test-cmd"); try zig_args.append(bin_name); if (glibc_dir_arg) |dir| { - const full_dir = try fs.path.join(builder.allocator, &[_][]const u8{ - dir, - try self.target.linuxTriple(builder.allocator), + // TODO look into making this a call to `linuxTriple`. This + // needs the directory to be called "i686" rather than + // "i386" 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 == .i386) + "i686" + else + @tagName(cpu_arch); + const full_dir = try std.fmt.allocPrint(builder.allocator, fmt_str, .{ + dir, cpu_arch_name, @tagName(os_tag), @tagName(abi), }); try zig_args.append("--test-cmd"); From 459c9f05359e3405f39e4c954c62c61a493e49ae Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Wed, 12 May 2021 22:39:10 +0200 Subject: [PATCH 8/9] stage2: fix build on OpenBSD/NetBSD Apparently these systems do not provide libdl or librt. --- src/link/Elf.zig | 10 +--------- src/target.zig | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 91ee6f3206..9a0aeabe6c 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1650,15 +1650,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { if (self.base.options.libc_installation != null) { const needs_grouping = self.base.options.link_mode == .Static; if (needs_grouping) try argv.append("--start-group"); - // This matches the order of glibc.libs - try argv.appendSlice(&[_][]const u8{ - "-lm", - "-lpthread", - "-lc", - "-ldl", - "-lrt", - "-lutil", - }); + try argv.appendSlice(target_util.libcFullLinkFlags(target)); if (needs_grouping) try argv.append("--end-group"); } else if (target.isGnuLibC()) { try argv.append(comp.libunwind_static_lib.?.full_object_path); diff --git a/src/target.zig b/src/target.zig index 1e31f99dc1..c2018db012 100644 --- a/src/target.zig +++ b/src/target.zig @@ -374,3 +374,24 @@ pub fn hasRedZone(target: std.Target) bool { else => false, }; } + +pub fn libcFullLinkFlags(target: std.Target) []const []const u8 { + // The linking order of these is significant and should match the order other + // c compilers such as gcc or clang use. + return switch (target.os.tag) { + .netbsd, .openbsd => &[_][]const u8{ + "-lm", + "-lpthread", + "-lc", + "-lutil", + }, + else => &[_][]const u8{ + "-lm", + "-lpthread", + "-lc", + "-ldl", + "-lrt", + "-lutil", + }, + }; +} From 4b59f564344598b4d1f1a51839a4dc5cbf357012 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 12 May 2021 22:25:20 -0700 Subject: [PATCH 9/9] stage2: build and provide libunwind when compiling for native libc 5ac91794cce8bd53916a378815be01e4365d53d9 made Zig link against the system libc when targeting the native C ABI. However this made it stop putting libunwind.a on the linker line when it needed to sometimes, causing undefined symbols when linking against C++ code. --- src/Compilation.zig | 1 - src/link/Elf.zig | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index 6a9f75acf5..b14e598f71 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -3023,7 +3023,6 @@ fn wantBuildLibUnwindFromSource(comp: *Compilation) bool { .Exe => true, }; return comp.bin_file.options.link_libc and is_exe_or_dyn_lib and - comp.bin_file.options.libc_installation == null and target_util.libcNeedsLibUnwind(comp.getTarget()); } diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 9a0aeabe6c..fbbe40022a 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1648,6 +1648,9 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { // libc dep if (self.base.options.link_libc) { if (self.base.options.libc_installation != null) { + if (target_util.libcNeedsLibUnwind(target)) { + try argv.append(comp.libunwind_static_lib.?.full_object_path); + } const needs_grouping = self.base.options.link_mode == .Static; if (needs_grouping) try argv.append("--start-group"); try argv.appendSlice(target_util.libcFullLinkFlags(target));