authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-05-13 08:24:21+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-05-13 08:24:21+02:00
log1aee896cae0747263e607de125a93023b5a9b320
tree49841fe55b55de0058176b025ec015ba990c1b61
parent6c3050e3c3eee19a9de2c912dc653062e5597b67
parent4b59f564344598b4d1f1a51839a4dc5cbf357012

Merge branch 'master' into streamline-stage2-build-script


13 files changed, 274 insertions(+), 166 deletions(-)

lib/std/build.zig+13-3
...@@ -2480,9 +2480,19 @@ pub const LibExeObjStep = struct {...@@ -2480,9 +2480,19 @@ pub const LibExeObjStep = struct {
2480 try zig_args.append("--test-cmd");2480 try zig_args.append("--test-cmd");
2481 try zig_args.append(bin_name);2481 try zig_args.append(bin_name);
2482 if (glibc_dir_arg) |dir| {2482 if (glibc_dir_arg) |dir| {
2483 const full_dir = try fs.path.join(builder.allocator, &[_][]const u8{2483 // TODO look into making this a call to `linuxTriple`. This
2484 dir,2484 // needs the directory to be called "i686" rather than
2485 try self.target.linuxTriple(builder.allocator),2485 // "i386" which is why we do it manually here.
2486 const fmt_str = "{s}" ++ fs.path.sep_str ++ "{s}-{s}-{s}";
2487 const cpu_arch = self.target.getCpuArch();
2488 const os_tag = self.target.getOsTag();
2489 const abi = self.target.getAbi();
2490 const cpu_arch_name: []const u8 = if (cpu_arch == .i386)
2491 "i686"
2492 else
2493 @tagName(cpu_arch);
2494 const full_dir = try std.fmt.allocPrint(builder.allocator, fmt_str, .{
2495 dir, cpu_arch_name, @tagName(os_tag), @tagName(abi),
2486 });2496 });
24872497
2488 try zig_args.append("--test-cmd");2498 try zig_args.append("--test-cmd");
lib/std/mem.zig+188
...@@ -602,6 +602,7 @@ test "span" {...@@ -602,6 +602,7 @@ test "span" {
602 try testing.expectEqual(@as(?[:0]u16, null), span(@as(?[*:0]u16, null)));602 try testing.expectEqual(@as(?[:0]u16, null), span(@as(?[*:0]u16, null)));
603}603}
604604
605/// Deprecated: use std.mem.span() or std.mem.sliceTo()
605/// Same as `span`, except when there is both a sentinel and an array606/// Same as `span`, except when there is both a sentinel and an array
606/// length or slice length, scans the memory for the sentinel value607/// length or slice length, scans the memory for the sentinel value
607/// rather than using the length.608/// rather than using the length.
...@@ -630,6 +631,192 @@ test "spanZ" {...@@ -630,6 +631,192 @@ test "spanZ" {
630 try testing.expectEqual(@as(?[:0]u16, null), spanZ(@as(?[*:0]u16, null)));631 try testing.expectEqual(@as(?[:0]u16, null), spanZ(@as(?[*:0]u16, null)));
631}632}
632633
634/// Helper for the return type of sliceTo()
635fn SliceTo(comptime T: type, comptime end: meta.Elem(T)) type {
636 switch (@typeInfo(T)) {
637 .Optional => |optional_info| {
638 return ?SliceTo(optional_info.child, end);
639 },
640 .Pointer => |ptr_info| {
641 var new_ptr_info = ptr_info;
642 new_ptr_info.size = .Slice;
643 switch (ptr_info.size) {
644 .One => switch (@typeInfo(ptr_info.child)) {
645 .Array => |array_info| {
646 new_ptr_info.child = array_info.child;
647 // The return type must only be sentinel terminated if we are guaranteed
648 // to find the value searched for, which is only the case if it matches
649 // the sentinel of the type passed.
650 if (array_info.sentinel) |sentinel| {
651 if (end == sentinel) {
652 new_ptr_info.sentinel = end;
653 } else {
654 new_ptr_info.sentinel = null;
655 }
656 }
657 },
658 else => {},
659 },
660 .Many, .Slice => {
661 // The return type must only be sentinel terminated if we are guaranteed
662 // to find the value searched for, which is only the case if it matches
663 // the sentinel of the type passed.
664 if (ptr_info.sentinel) |sentinel| {
665 if (end == sentinel) {
666 new_ptr_info.sentinel = end;
667 } else {
668 new_ptr_info.sentinel = null;
669 }
670 }
671 },
672 .C => {
673 new_ptr_info.sentinel = end;
674 // C pointers are always allowzero, but we don't want the return type to be.
675 assert(new_ptr_info.is_allowzero);
676 new_ptr_info.is_allowzero = false;
677 },
678 }
679 return @Type(std.builtin.TypeInfo{ .Pointer = new_ptr_info });
680 },
681 else => {},
682 }
683 @compileError("invalid type given to std.mem.sliceTo: " ++ @typeName(T));
684}
685
686/// Takes a pointer to an array, an array, a sentinel-terminated pointer, or a slice and
687/// iterates searching for the first occurrence of `end`, returning the scanned slice.
688/// If `end` is not found, the full length of the array/slice/sentinel terminated pointer is returned.
689/// If the pointer type is sentinel terminated and `end` matches that terminator, the
690/// resulting slice is also sentinel terminated.
691/// Pointer properties such as mutability and alignment are preserved.
692/// C pointers are assumed to be non-null.
693pub fn sliceTo(ptr: anytype, comptime end: meta.Elem(@TypeOf(ptr))) SliceTo(@TypeOf(ptr), end) {
694 if (@typeInfo(@TypeOf(ptr)) == .Optional) {
695 const non_null = ptr orelse return null;
696 return sliceTo(non_null, end);
697 }
698 const Result = SliceTo(@TypeOf(ptr), end);
699 const length = lenSliceTo(ptr, end);
700 if (@typeInfo(Result).Pointer.sentinel) |s| {
701 return ptr[0..length :s];
702 } else {
703 return ptr[0..length];
704 }
705}
706
707test "sliceTo" {
708 try testing.expectEqualSlices(u8, "aoeu", sliceTo("aoeu", 0));
709
710 {
711 var array: [5]u16 = [_]u16{ 1, 2, 3, 4, 5 };
712 try testing.expectEqualSlices(u16, &array, sliceTo(&array, 0));
713 try testing.expectEqualSlices(u16, array[0..3], sliceTo(array[0..3], 0));
714 try testing.expectEqualSlices(u16, array[0..2], sliceTo(&array, 3));
715 try testing.expectEqualSlices(u16, array[0..2], sliceTo(array[0..3], 3));
716
717 const sentinel_ptr = @ptrCast([*:5]u16, &array);
718 try testing.expectEqualSlices(u16, array[0..2], sliceTo(sentinel_ptr, 3));
719 try testing.expectEqualSlices(u16, array[0..4], sliceTo(sentinel_ptr, 99));
720
721 const optional_sentinel_ptr = @ptrCast(?[*:5]u16, &array);
722 try testing.expectEqualSlices(u16, array[0..2], sliceTo(optional_sentinel_ptr, 3).?);
723 try testing.expectEqualSlices(u16, array[0..4], sliceTo(optional_sentinel_ptr, 99).?);
724
725 const c_ptr = @as([*c]u16, &array);
726 try testing.expectEqualSlices(u16, array[0..2], sliceTo(c_ptr, 3));
727
728 const slice: []u16 = &array;
729 try testing.expectEqualSlices(u16, array[0..2], sliceTo(slice, 3));
730 try testing.expectEqualSlices(u16, &array, sliceTo(slice, 99));
731
732 const sentinel_slice: [:5]u16 = array[0..4 :5];
733 try testing.expectEqualSlices(u16, array[0..2], sliceTo(sentinel_slice, 3));
734 try testing.expectEqualSlices(u16, array[0..4], sliceTo(sentinel_slice, 99));
735 }
736 {
737 var sentinel_array: [5:0]u16 = [_:0]u16{ 1, 2, 3, 4, 5 };
738 try testing.expectEqualSlices(u16, sentinel_array[0..2], sliceTo(&sentinel_array, 3));
739 try testing.expectEqualSlices(u16, &sentinel_array, sliceTo(&sentinel_array, 0));
740 try testing.expectEqualSlices(u16, &sentinel_array, sliceTo(&sentinel_array, 99));
741 }
742
743 try testing.expectEqual(@as(?[]u8, null), sliceTo(@as(?[]u8, null), 0));
744}
745
746/// Private helper for sliceTo(). If you want the length, use sliceTo(foo, x).len
747fn lenSliceTo(ptr: anytype, comptime end: meta.Elem(@TypeOf(ptr))) usize {
748 switch (@typeInfo(@TypeOf(ptr))) {
749 .Pointer => |ptr_info| switch (ptr_info.size) {
750 .One => switch (@typeInfo(ptr_info.child)) {
751 .Array => |array_info| {
752 if (array_info.sentinel) |sentinel| {
753 if (sentinel == end) {
754 return indexOfSentinel(array_info.child, end, ptr);
755 }
756 }
757 return indexOfScalar(array_info.child, ptr, end) orelse array_info.len;
758 },
759 else => {},
760 },
761 .Many => if (ptr_info.sentinel) |sentinel| {
762 // We may be looking for something other than the sentinel,
763 // but iterating past the sentinel would be a bug so we need
764 // to check for both.
765 var i: usize = 0;
766 while (ptr[i] != end and ptr[i] != sentinel) i += 1;
767 return i;
768 },
769 .C => {
770 assert(ptr != null);
771 return indexOfSentinel(ptr_info.child, end, ptr);
772 },
773 .Slice => {
774 if (ptr_info.sentinel) |sentinel| {
775 if (sentinel == end) {
776 return indexOfSentinel(ptr_info.child, sentinel, ptr);
777 }
778 }
779 return indexOfScalar(ptr_info.child, ptr, end) orelse ptr.len;
780 },
781 },
782 else => {},
783 }
784 @compileError("invalid type given to std.mem.sliceTo: " ++ @typeName(@TypeOf(ptr)));
785}
786
787test "lenSliceTo" {
788 try testing.expect(lenSliceTo("aoeu", 0) == 4);
789
790 {
791 var array: [5]u16 = [_]u16{ 1, 2, 3, 4, 5 };
792 try testing.expectEqual(@as(usize, 5), lenSliceTo(&array, 0));
793 try testing.expectEqual(@as(usize, 3), lenSliceTo(array[0..3], 0));
794 try testing.expectEqual(@as(usize, 2), lenSliceTo(&array, 3));
795 try testing.expectEqual(@as(usize, 2), lenSliceTo(array[0..3], 3));
796
797 const sentinel_ptr = @ptrCast([*:5]u16, &array);
798 try testing.expectEqual(@as(usize, 2), lenSliceTo(sentinel_ptr, 3));
799 try testing.expectEqual(@as(usize, 4), lenSliceTo(sentinel_ptr, 99));
800
801 const c_ptr = @as([*c]u16, &array);
802 try testing.expectEqual(@as(usize, 2), lenSliceTo(c_ptr, 3));
803
804 const slice: []u16 = &array;
805 try testing.expectEqual(@as(usize, 2), lenSliceTo(slice, 3));
806 try testing.expectEqual(@as(usize, 5), lenSliceTo(slice, 99));
807
808 const sentinel_slice: [:5]u16 = array[0..4 :5];
809 try testing.expectEqual(@as(usize, 2), lenSliceTo(sentinel_slice, 3));
810 try testing.expectEqual(@as(usize, 4), lenSliceTo(sentinel_slice, 99));
811 }
812 {
813 var sentinel_array: [5:0]u16 = [_:0]u16{ 1, 2, 3, 4, 5 };
814 try testing.expectEqual(@as(usize, 2), lenSliceTo(&sentinel_array, 3));
815 try testing.expectEqual(@as(usize, 5), lenSliceTo(&sentinel_array, 0));
816 try testing.expectEqual(@as(usize, 5), lenSliceTo(&sentinel_array, 99));
817 }
818}
819
633/// Takes a pointer to an array, an array, a vector, a sentinel-terminated pointer,820/// Takes a pointer to an array, an array, a vector, a sentinel-terminated pointer,
634/// a slice or a tuple, and returns the length.821/// a slice or a tuple, and returns the length.
635/// In the case of a sentinel-terminated array, it uses the array length.822/// In the case of a sentinel-terminated array, it uses the array length.
...@@ -688,6 +875,7 @@ test "len" {...@@ -688,6 +875,7 @@ test "len" {
688 }875 }
689}876}
690877
878/// Deprecated: use std.mem.len() or std.mem.sliceTo().len
691/// Takes a pointer to an array, an array, a sentinel-terminated pointer,879/// Takes a pointer to an array, an array, a sentinel-terminated pointer,
692/// or a slice, and returns the length.880/// or a slice, and returns the length.
693/// In the case of a sentinel-terminated array, it scans the array881/// In the case of a sentinel-terminated array, it scans the array
lib/std/meta.zig+1-7
...@@ -175,13 +175,7 @@ pub fn Elem(comptime T: type) type {...@@ -175,13 +175,7 @@ pub fn Elem(comptime T: type) type {
175 },175 },
176 .Many, .C, .Slice => return info.child,176 .Many, .C, .Slice => return info.child,
177 },177 },
178 .Optional => |info| switch (@typeInfo(info.child)) {178 .Optional => |info| return Elem(info.child),
179 .Pointer => |ptr_info| switch (ptr_info.size) {
180 .Many => return ptr_info.child,
181 else => {},
182 },
183 else => {},
184 },
185 else => {},179 else => {},
186 }180 }
187 @compileError("Expected pointer, slice, array or vector type, found '" ++ @typeName(T) ++ "'");181 @compileError("Expected pointer, slice, array or vector type, found '" ++ @typeName(T) ++ "'");
lib/std/priority_dequeue.zig+1-16
...@@ -387,17 +387,6 @@ pub fn PriorityDequeue(comptime T: type) type {...@@ -387,17 +387,6 @@ pub fn PriorityDequeue(comptime T: type) type {
387 return;387 return;
388 },388 },
389 };389 };
390 self.len = new_len;
391 }
392
393 /// Reduce length to `new_len`.
394 pub fn shrinkRetainingCapacity(self: *Self, new_len: usize) void {
395 assert(new_len <= self.items.len);
396
397 // Cannot shrink to smaller than the current queue size without invalidating the heap property
398 assert(new_len >= self.len);
399
400 self.len = new_len;
401 }390 }
402391
403 pub fn update(self: *Self, elem: T, new_elem: T) !void {392 pub fn update(self: *Self, elem: T, new_elem: T) !void {
...@@ -836,7 +825,7 @@ test "std.PriorityDequeue: iterator while empty" {...@@ -836,7 +825,7 @@ test "std.PriorityDequeue: iterator while empty" {
836 try expectEqual(it.next(), null);825 try expectEqual(it.next(), null);
837}826}
838827
839test "std.PriorityDequeue: shrinkRetainingCapacity and shrinkAndFree" {828test "std.PriorityDequeue: shrinkAndFree" {
840 var queue = PDQ.init(testing.allocator, lessThanComparison);829 var queue = PDQ.init(testing.allocator, lessThanComparison);
841 defer queue.deinit();830 defer queue.deinit();
842831
...@@ -849,10 +838,6 @@ test "std.PriorityDequeue: shrinkRetainingCapacity and shrinkAndFree" {...@@ -849,10 +838,6 @@ test "std.PriorityDequeue: shrinkRetainingCapacity and shrinkAndFree" {
849 try expect(queue.capacity() >= 4);838 try expect(queue.capacity() >= 4);
850 try expectEqual(@as(usize, 3), queue.len);839 try expectEqual(@as(usize, 3), queue.len);
851840
852 queue.shrinkRetainingCapacity(3);
853 try expect(queue.capacity() >= 4);
854 try expectEqual(@as(usize, 3), queue.len);
855
856 queue.shrinkAndFree(3);841 queue.shrinkAndFree(3);
857 try expectEqual(@as(usize, 3), queue.capacity());842 try expectEqual(@as(usize, 3), queue.capacity());
858 try expectEqual(@as(usize, 3), queue.len);843 try expectEqual(@as(usize, 3), queue.len);
lib/std/priority_queue.zig+1-16
...@@ -203,17 +203,6 @@ pub fn PriorityQueue(comptime T: type) type {...@@ -203,17 +203,6 @@ pub fn PriorityQueue(comptime T: type) type {
203 return;203 return;
204 },204 },
205 };205 };
206 self.len = new_len;
207 }
208
209 /// Reduce length to `new_len`.
210 pub fn shrinkRetainingCapacity(self: *Self, new_len: usize) void {
211 assert(new_len <= self.items.len);
212
213 // Cannot shrink to smaller than the current queue size without invalidating the heap property
214 assert(new_len >= self.len);
215
216 self.len = new_len;
217 }206 }
218207
219 pub fn update(self: *Self, elem: T, new_elem: T) !void {208 pub fn update(self: *Self, elem: T, new_elem: T) !void {
...@@ -495,7 +484,7 @@ test "std.PriorityQueue: iterator while empty" {...@@ -495,7 +484,7 @@ test "std.PriorityQueue: iterator while empty" {
495 try expectEqual(it.next(), null);484 try expectEqual(it.next(), null);
496}485}
497486
498test "std.PriorityQueue: shrinkRetainingCapacity and shrinkAndFree" {487test "std.PriorityQueue: shrinkAndFree" {
499 var queue = PQ.init(testing.allocator, lessThan);488 var queue = PQ.init(testing.allocator, lessThan);
500 defer queue.deinit();489 defer queue.deinit();
501490
...@@ -508,10 +497,6 @@ test "std.PriorityQueue: shrinkRetainingCapacity and shrinkAndFree" {...@@ -508,10 +497,6 @@ test "std.PriorityQueue: shrinkRetainingCapacity and shrinkAndFree" {
508 try expect(queue.capacity() >= 4);497 try expect(queue.capacity() >= 4);
509 try expectEqual(@as(usize, 3), queue.len);498 try expectEqual(@as(usize, 3), queue.len);
510499
511 queue.shrinkRetainingCapacity(3);
512 try expect(queue.capacity() >= 4);
513 try expectEqual(@as(usize, 3), queue.len);
514
515 queue.shrinkAndFree(3);500 queue.shrinkAndFree(3);
516 try expectEqual(@as(usize, 3), queue.capacity());501 try expectEqual(@as(usize, 3), queue.capacity());
517 try expectEqual(@as(usize, 3), queue.len);502 try expectEqual(@as(usize, 3), queue.len);
lib/std/zig/system.zig-9
...@@ -208,11 +208,6 @@ pub const NativeTargetInfo = struct {...@@ -208,11 +208,6 @@ pub const NativeTargetInfo = struct {
208208
209 dynamic_linker: DynamicLinker = DynamicLinker{},209 dynamic_linker: DynamicLinker = DynamicLinker{},
210210
211 /// Only some architectures have CPU detection implemented. This field reveals whether
212 /// CPU detection actually occurred. When this is `true` it means that the reported
213 /// CPU is baseline only because of a missing implementation for that architecture.
214 cpu_detection_unimplemented: bool = false,
215
216 pub const DynamicLinker = Target.DynamicLinker;211 pub const DynamicLinker = Target.DynamicLinker;
217212
218 pub const DetectError = error{213 pub const DetectError = error{
...@@ -367,8 +362,6 @@ pub const NativeTargetInfo = struct {...@@ -367,8 +362,6 @@ pub const NativeTargetInfo = struct {
367 os.version_range.linux.glibc = glibc;362 os.version_range.linux.glibc = glibc;
368 }363 }
369364
370 var cpu_detection_unimplemented = false;
371
372 // Until https://github.com/ziglang/zig/issues/4592 is implemented (support detecting the365 // Until https://github.com/ziglang/zig/issues/4592 is implemented (support detecting the
373 // native CPU architecture as being different than the current target), we use this:366 // native CPU architecture as being different than the current target), we use this:
374 const cpu_arch = cross_target.getCpuArch();367 const cpu_arch = cross_target.getCpuArch();
...@@ -382,7 +375,6 @@ pub const NativeTargetInfo = struct {...@@ -382,7 +375,6 @@ pub const NativeTargetInfo = struct {
382 Target.Cpu.baseline(cpu_arch),375 Target.Cpu.baseline(cpu_arch),
383 .explicit => |model| model.toCpu(cpu_arch),376 .explicit => |model| model.toCpu(cpu_arch),
384 } orelse backup_cpu_detection: {377 } orelse backup_cpu_detection: {
385 cpu_detection_unimplemented = true;
386 break :backup_cpu_detection Target.Cpu.baseline(cpu_arch);378 break :backup_cpu_detection Target.Cpu.baseline(cpu_arch);
387 };379 };
388 var result = try detectAbiAndDynamicLinker(allocator, cpu, os, cross_target);380 var result = try detectAbiAndDynamicLinker(allocator, cpu, os, cross_target);
...@@ -419,7 +411,6 @@ pub const NativeTargetInfo = struct {...@@ -419,7 +411,6 @@ pub const NativeTargetInfo = struct {
419 else => {},411 else => {},
420 }412 }
421 cross_target.updateCpuFeatures(&result.target.cpu.features);413 cross_target.updateCpuFeatures(&result.target.cpu.features);
422 result.cpu_detection_unimplemented = cpu_detection_unimplemented;
423 return result;414 return result;
424 }415 }
425416
src/Cache.zig+20-1
...@@ -11,6 +11,7 @@ const testing = std.testing;...@@ -11,6 +11,7 @@ const testing = std.testing;
11const mem = std.mem;11const mem = std.mem;
12const fmt = std.fmt;12const fmt = std.fmt;
13const Allocator = std.mem.Allocator;13const Allocator = std.mem.Allocator;
14const Compilation = @import("Compilation.zig");
1415
15/// Be sure to call `Manifest.deinit` after successful initialization.16/// Be sure to call `Manifest.deinit` after successful initialization.
16pub fn obtain(cache: *const Cache) Manifest {17pub fn obtain(cache: *const Cache) Manifest {
...@@ -61,7 +62,7 @@ pub const File = struct {...@@ -61,7 +62,7 @@ pub const File = struct {
61pub const HashHelper = struct {62pub const HashHelper = struct {
62 hasher: Hasher = hasher_init,63 hasher: Hasher = hasher_init,
6364
64 const EmitLoc = @import("Compilation.zig").EmitLoc;65 const EmitLoc = Compilation.EmitLoc;
6566
66 /// Record a slice of bytes as an dependency of the process being cached67 /// Record a slice of bytes as an dependency of the process being cached
67 pub fn addBytes(hh: *HashHelper, bytes: []const u8) void {68 pub fn addBytes(hh: *HashHelper, bytes: []const u8) void {
...@@ -220,6 +221,24 @@ pub const Manifest = struct {...@@ -220,6 +221,24 @@ pub const Manifest = struct {
220 return idx;221 return idx;
221 }222 }
222223
224 pub fn hashCSource(self: *Manifest, c_source: Compilation.CSourceFile) !void {
225 _ = try self.addFile(c_source.src_path, null);
226 // Hash the extra flags, with special care to call addFile for file parameters.
227 // TODO this logic can likely be improved by utilizing clang_options_data.zig.
228 const file_args = [_][]const u8{"-include"};
229 var arg_i: usize = 0;
230 while (arg_i < c_source.extra_flags.len) : (arg_i += 1) {
231 const arg = c_source.extra_flags[arg_i];
232 self.hash.addBytes(arg);
233 for (file_args) |file_arg| {
234 if (mem.eql(u8, file_arg, arg) and arg_i + 1 < c_source.extra_flags.len) {
235 arg_i += 1;
236 _ = try self.addFile(c_source.extra_flags[arg_i], null);
237 }
238 }
239 }
240 }
241
223 pub fn addOptionalFile(self: *Manifest, optional_file_path: ?[]const u8) !void {242 pub fn addOptionalFile(self: *Manifest, optional_file_path: ?[]const u8) !void {
224 self.hash.add(optional_file_path != null);243 self.hash.add(optional_file_path != null);
225 const file_path = optional_file_path orelse return;244 const file_path = optional_file_path orelse return;
src/Compilation.zig+1-18
...@@ -2260,23 +2260,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_comp_progress_node: *...@@ -2260,23 +2260,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_comp_progress_node: *
22602260
2261 man.hash.add(comp.clang_preprocessor_mode);2261 man.hash.add(comp.clang_preprocessor_mode);
22622262
2263 _ = try man.addFile(c_object.src.src_path, null);2263 try man.hashCSource(c_object.src);
2264 {
2265 // Hash the extra flags, with special care to call addFile for file parameters.
2266 // TODO this logic can likely be improved by utilizing clang_options_data.zig.
2267 const file_args = [_][]const u8{"-include"};
2268 var arg_i: usize = 0;
2269 while (arg_i < c_object.src.extra_flags.len) : (arg_i += 1) {
2270 const arg = c_object.src.extra_flags[arg_i];
2271 man.hash.addBytes(arg);
2272 for (file_args) |file_arg| {
2273 if (mem.eql(u8, file_arg, arg) and arg_i + 1 < c_object.src.extra_flags.len) {
2274 arg_i += 1;
2275 _ = try man.addFile(c_object.src.extra_flags[arg_i], null);
2276 }
2277 }
2278 }
2279 }
22802264
2281 {2265 {
2282 const is_collision = blk: {2266 const is_collision = blk: {
...@@ -3039,7 +3023,6 @@ fn wantBuildLibUnwindFromSource(comp: *Compilation) bool {...@@ -3039,7 +3023,6 @@ fn wantBuildLibUnwindFromSource(comp: *Compilation) bool {
3039 .Exe => true,3023 .Exe => true,
3040 };3024 };
3041 return comp.bin_file.options.link_libc and is_exe_or_dyn_lib and3025 return comp.bin_file.options.link_libc and is_exe_or_dyn_lib and
3042 comp.bin_file.options.libc_installation == null and
3043 target_util.libcNeedsLibUnwind(comp.getTarget());3026 target_util.libcNeedsLibUnwind(comp.getTarget());
3044}3027}
30453028
src/link/Elf.zig+4-9
...@@ -1648,17 +1648,12 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {...@@ -1648,17 +1648,12 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
1648 // libc dep1648 // libc dep
1649 if (self.base.options.link_libc) {1649 if (self.base.options.link_libc) {
1650 if (self.base.options.libc_installation != null) {1650 if (self.base.options.libc_installation != null) {
1651 if (target_util.libcNeedsLibUnwind(target)) {
1652 try argv.append(comp.libunwind_static_lib.?.full_object_path);
1653 }
1651 const needs_grouping = self.base.options.link_mode == .Static;1654 const needs_grouping = self.base.options.link_mode == .Static;
1652 if (needs_grouping) try argv.append("--start-group");1655 if (needs_grouping) try argv.append("--start-group");
1653 // This matches the order of glibc.libs1656 try argv.appendSlice(target_util.libcFullLinkFlags(target));
1654 try argv.appendSlice(&[_][]const u8{
1655 "-lm",
1656 "-lpthread",
1657 "-lc",
1658 "-ldl",
1659 "-lrt",
1660 "-lutil",
1661 });
1662 if (needs_grouping) try argv.append("--end-group");1657 if (needs_grouping) try argv.append("--end-group");
1663 } else if (target.isGnuLibC()) {1658 } else if (target.isGnuLibC()) {
1664 try argv.append(comp.libunwind_static_lib.?.full_object_path);1659 try argv.append(comp.libunwind_static_lib.?.full_object_path);
src/link/MachO.zig+4-1
...@@ -442,6 +442,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {...@@ -442,6 +442,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
442 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;442 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
443 const main_cmd = &self.load_commands.items[self.main_cmd_index.?].Main;443 const main_cmd = &self.load_commands.items[self.main_cmd_index.?].Main;
444 main_cmd.entryoff = addr - text_segment.inner.vmaddr;444 main_cmd.entryoff = addr - text_segment.inner.vmaddr;
445 main_cmd.stacksize = self.base.options.stack_size_override orelse 0;
445 self.load_commands_dirty = true;446 self.load_commands_dirty = true;
446 }447 }
447 try self.writeRebaseInfoTable();448 try self.writeRebaseInfoTable();
...@@ -695,7 +696,9 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {...@@ -695,7 +696,9 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
695 Compilation.dump_argv(argv.items);696 Compilation.dump_argv(argv.items);
696 }697 }
697698
698 try zld.link(input_files.items, full_out_path);699 try zld.link(input_files.items, full_out_path, .{
700 .stack_size = self.base.options.stack_size_override,
701 });
699702
700 break :outer;703 break :outer;
701 }704 }
src/link/MachO/Zld.zig+11-1
...@@ -29,6 +29,10 @@ page_size: ?u16 = null,...@@ -29,6 +29,10 @@ page_size: ?u16 = null,
29file: ?fs.File = null,29file: ?fs.File = null,
30out_path: ?[]const u8 = null,30out_path: ?[]const u8 = null,
3131
32// TODO these args will become obselete once Zld is coalesced with incremental
33// linker.
34stack_size: u64 = 0,
35
32objects: std.ArrayListUnmanaged(*Object) = .{},36objects: std.ArrayListUnmanaged(*Object) = .{},
33archives: std.ArrayListUnmanaged(*Archive) = .{},37archives: std.ArrayListUnmanaged(*Archive) = .{},
3438
...@@ -172,7 +176,11 @@ pub fn closeFiles(self: Zld) void {...@@ -172,7 +176,11 @@ pub fn closeFiles(self: Zld) void {
172 if (self.file) |f| f.close();176 if (self.file) |f| f.close();
173}177}
174178
175pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8) !void {179const LinkArgs = struct {
180 stack_size: ?u64 = null,
181};
182
183pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8, args: LinkArgs) !void {
176 if (files.len == 0) return error.NoInputFiles;184 if (files.len == 0) return error.NoInputFiles;
177 if (out_path.len == 0) return error.EmptyOutputPath;185 if (out_path.len == 0) return error.EmptyOutputPath;
178186
...@@ -206,6 +214,7 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8) !void {...@@ -206,6 +214,7 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8) !void {
206 .read = true,214 .read = true,
207 .mode = if (std.Target.current.os.tag == .windows) 0 else 0o777,215 .mode = if (std.Target.current.os.tag == .windows) 0 else 0o777,
208 });216 });
217 self.stack_size = args.stack_size orelse 0;
209218
210 try self.populateMetadata();219 try self.populateMetadata();
211 try self.parseInputFiles(files);220 try self.parseInputFiles(files);
...@@ -2204,6 +2213,7 @@ fn setEntryPoint(self: *Zld) !void {...@@ -2204,6 +2213,7 @@ fn setEntryPoint(self: *Zld) !void {
2204 const entry_sym = sym.cast(Symbol.Regular) orelse unreachable;2213 const entry_sym = sym.cast(Symbol.Regular) orelse unreachable;
2205 const ec = &self.load_commands.items[self.main_cmd_index.?].Main;2214 const ec = &self.load_commands.items[self.main_cmd_index.?].Main;
2206 ec.entryoff = @intCast(u32, entry_sym.address - seg.inner.vmaddr);2215 ec.entryoff = @intCast(u32, entry_sym.address - seg.inner.vmaddr);
2216 ec.stacksize = self.stack_size;
2207}2217}
22082218
2209fn writeRebaseInfoTable(self: *Zld) !void {2219fn writeRebaseInfoTable(self: *Zld) !void {
src/main.zig+9-85
...@@ -2172,7 +2172,7 @@ fn cmdTranslateC(comp: *Compilation, arena: *Allocator, enable_cache: bool) !voi...@@ -2172,7 +2172,7 @@ fn cmdTranslateC(comp: *Compilation, arena: *Allocator, enable_cache: bool) !voi
2172 defer if (enable_cache) man.deinit();2172 defer if (enable_cache) man.deinit();
21732173
2174 man.hash.add(@as(u16, 0xb945)); // Random number to distinguish translate-c from compiling C objects2174 man.hash.add(@as(u16, 0xb945)); // Random number to distinguish translate-c from compiling C objects
2175 _ = man.addFile(c_source_file.src_path, null) catch |err| {2175 man.hashCSource(c_source_file) catch |err| {
2176 fatal("unable to process '{s}': {s}", .{ c_source_file.src_path, @errorName(err) });2176 fatal("unable to process '{s}': {s}", .{ c_source_file.src_path, @errorName(err) });
2177 };2177 };
21782178
...@@ -2202,12 +2202,16 @@ fn cmdTranslateC(comp: *Compilation, arena: *Allocator, enable_cache: bool) !voi...@@ -2202,12 +2202,16 @@ fn cmdTranslateC(comp: *Compilation, arena: *Allocator, enable_cache: bool) !voi
2202 }2202 }
22032203
2204 // Convert to null terminated args.2204 // Convert to null terminated args.
2205 const new_argv_with_sentinel = try arena.alloc(?[*:0]const u8, argv.items.len + 1);2205 const clang_args_len = argv.items.len + c_source_file.extra_flags.len;
2206 new_argv_with_sentinel[argv.items.len] = null;2206 const new_argv_with_sentinel = try arena.alloc(?[*:0]const u8, clang_args_len + 1);
2207 const new_argv = new_argv_with_sentinel[0..argv.items.len :null];2207 new_argv_with_sentinel[clang_args_len] = null;
2208 const new_argv = new_argv_with_sentinel[0..clang_args_len :null];
2208 for (argv.items) |arg, i| {2209 for (argv.items) |arg, i| {
2209 new_argv[i] = try arena.dupeZ(u8, arg);2210 new_argv[i] = try arena.dupeZ(u8, arg);
2210 }2211 }
2212 for (c_source_file.extra_flags) |arg, i| {
2213 new_argv[argv.items.len + i] = try arena.dupeZ(u8, arg);
2214 }
22112215
2212 const c_headers_dir_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{"include"});2216 const c_headers_dir_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{"include"});
2213 const c_headers_dir_path_z = try arena.dupeZ(u8, c_headers_dir_path);2217 const c_headers_dir_path_z = try arena.dupeZ(u8, c_headers_dir_path);
...@@ -3396,88 +3400,8 @@ test "fds" {...@@ -3396,88 +3400,8 @@ test "fds" {
3396 gimmeMoreOfThoseSweetSweetFileDescriptors();3400 gimmeMoreOfThoseSweetSweetFileDescriptors();
3397}3401}
33983402
3399fn detectNativeCpuWithLLVM(
3400 arch: std.Target.Cpu.Arch,
3401 llvm_cpu_name_z: ?[*:0]const u8,
3402 llvm_cpu_features_opt: ?[*:0]const u8,
3403) !std.Target.Cpu {
3404 var result = std.Target.Cpu.baseline(arch);
3405
3406 if (llvm_cpu_name_z) |cpu_name_z| {
3407 const llvm_cpu_name = mem.spanZ(cpu_name_z);
3408
3409 for (arch.allCpuModels()) |model| {
3410 const this_llvm_name = model.llvm_name orelse continue;
3411 if (mem.eql(u8, this_llvm_name, llvm_cpu_name)) {
3412 // Here we use the non-dependencies-populated set,
3413 // so that subtracting features later in this function
3414 // affect the prepopulated set.
3415 result = std.Target.Cpu{
3416 .arch = arch,
3417 .model = model,
3418 .features = model.features,
3419 };
3420 break;
3421 }
3422 }
3423 }
3424
3425 const all_features = arch.allFeaturesList();
3426
3427 if (llvm_cpu_features_opt) |llvm_cpu_features| {
3428 var it = mem.tokenize(mem.spanZ(llvm_cpu_features), ",");
3429 while (it.next()) |decorated_llvm_feat| {
3430 var op: enum {
3431 add,
3432 sub,
3433 } = undefined;
3434 var llvm_feat: []const u8 = undefined;
3435 if (mem.startsWith(u8, decorated_llvm_feat, "+")) {
3436 op = .add;
3437 llvm_feat = decorated_llvm_feat[1..];
3438 } else if (mem.startsWith(u8, decorated_llvm_feat, "-")) {
3439 op = .sub;
3440 llvm_feat = decorated_llvm_feat[1..];
3441 } else {
3442 return error.InvalidLlvmCpuFeaturesFormat;
3443 }
3444 for (all_features) |feature, index_usize| {
3445 const this_llvm_name = feature.llvm_name orelse continue;
3446 if (mem.eql(u8, llvm_feat, this_llvm_name)) {
3447 const index = @intCast(std.Target.Cpu.Feature.Set.Index, index_usize);
3448 switch (op) {
3449 .add => result.features.addFeature(index),
3450 .sub => result.features.removeFeature(index),
3451 }
3452 break;
3453 }
3454 }
3455 }
3456 }
3457
3458 result.features.populateDependencies(all_features);
3459 return result;
3460}
3461
3462fn detectNativeTargetInfo(gpa: *Allocator, cross_target: std.zig.CrossTarget) !std.zig.system.NativeTargetInfo {3403fn detectNativeTargetInfo(gpa: *Allocator, cross_target: std.zig.CrossTarget) !std.zig.system.NativeTargetInfo {
3463 var info = try std.zig.system.NativeTargetInfo.detect(gpa, cross_target);3404 return std.zig.system.NativeTargetInfo.detect(gpa, cross_target);
3464 if (info.cpu_detection_unimplemented) {
3465 const arch = std.Target.current.cpu.arch;
3466
3467 // We want to just use detected_info.target but implementing
3468 // CPU model & feature detection is todo so here we rely on LLVM.
3469 // https://github.com/ziglang/zig/issues/4591
3470 if (!build_options.have_llvm)
3471 fatal("CPU features detection is not yet available for {s} without LLVM extensions", .{@tagName(arch)});
3472
3473 const llvm = @import("codegen/llvm/bindings.zig");
3474 const llvm_cpu_name = llvm.GetHostCPUName();
3475 const llvm_cpu_features = llvm.GetNativeFeatures();
3476 info.target.cpu = try detectNativeCpuWithLLVM(arch, llvm_cpu_name, llvm_cpu_features);
3477 cross_target.updateCpuFeatures(&info.target.cpu.features);
3478 info.target.cpu.arch = cross_target.getCpuArch();
3479 }
3480 return info;
3481}3405}
34823406
3483/// Indicate that we are now terminating with a successful exit code.3407/// Indicate that we are now terminating with a successful exit code.
src/target.zig+21
...@@ -374,3 +374,24 @@ pub fn hasRedZone(target: std.Target) bool {...@@ -374,3 +374,24 @@ pub fn hasRedZone(target: std.Target) bool {
374 else => false,374 else => false,
375 };375 };
376}376}
377
378pub fn libcFullLinkFlags(target: std.Target) []const []const u8 {
379 // The linking order of these is significant and should match the order other
380 // c compilers such as gcc or clang use.
381 return switch (target.os.tag) {
382 .netbsd, .openbsd => &[_][]const u8{
383 "-lm",
384 "-lpthread",
385 "-lc",
386 "-lutil",
387 },
388 else => &[_][]const u8{
389 "-lm",
390 "-lpthread",
391 "-lc",
392 "-ldl",
393 "-lrt",
394 "-lutil",
395 },
396 };
397}