| author | |
| committer | |
| log | 02713e8d8aa9641616bd85e77dda784009c96113 |
| tree | 675d61a60cbe732d2c61290b33db560552b16e05 |
| parent | adefd1a52b812813dd3e3590d398f927ffc5b9af |
* fix race condition in std.event.Channel deinit
* add support to zig build for --no-rosegment
* add passing self-hosted compare-output test for calling a function
* put a global lock on LLD linking because it's not thread safe6 files changed, 67 insertions(+), 32 deletions(-)
build.zig+4| ... | @@ -45,6 +45,7 @@ pub fn build(b: *Builder) !void { | ... | @@ -45,6 +45,7 @@ pub fn build(b: *Builder) !void { |
| 45 | .c_header_files = nextValue(&index, build_info), | 45 | .c_header_files = nextValue(&index, build_info), |
| 46 | .dia_guids_lib = nextValue(&index, build_info), | 46 | .dia_guids_lib = nextValue(&index, build_info), |
| 47 | .llvm = undefined, | 47 | .llvm = undefined, |
| 48 | .no_rosegment = b.option(bool, "no-rosegment", "Workaround to enable valgrind builds") orelse false, | ||
| 48 | }; | 49 | }; |
| 49 | ctx.llvm = try findLLVM(b, ctx.llvm_config_exe); | 50 | ctx.llvm = try findLLVM(b, ctx.llvm_config_exe); |
| 50 | 51 | ||
| ... | @@ -228,6 +229,8 @@ fn configureStage2(b: *Builder, exe: var, ctx: Context) !void { | ... | @@ -228,6 +229,8 @@ fn configureStage2(b: *Builder, exe: var, ctx: Context) !void { |
| 228 | // TODO turn this into -Dextra-lib-path=/lib option | 229 | // TODO turn this into -Dextra-lib-path=/lib option |
| 229 | exe.addLibPath("/lib"); | 230 | exe.addLibPath("/lib"); |
| 230 | 231 | ||
| 232 | exe.setNoRoSegment(ctx.no_rosegment); | ||
| 233 | |||
| 231 | exe.addIncludeDir("src"); | 234 | exe.addIncludeDir("src"); |
| 232 | exe.addIncludeDir(ctx.cmake_binary_dir); | 235 | exe.addIncludeDir(ctx.cmake_binary_dir); |
| 233 | addCppLib(b, exe, ctx.cmake_binary_dir, "zig_cpp"); | 236 | addCppLib(b, exe, ctx.cmake_binary_dir, "zig_cpp"); |
| ... | @@ -286,4 +289,5 @@ const Context = struct { | ... | @@ -286,4 +289,5 @@ const Context = struct { |
| 286 | c_header_files: []const u8, | 289 | c_header_files: []const u8, |
| 287 | dia_guids_lib: []const u8, | 290 | dia_guids_lib: []const u8, |
| 288 | llvm: LibraryDep, | 291 | llvm: LibraryDep, |
| 292 | no_rosegment: bool, | ||
| 289 | }; | 293 | }; |
src-self-hosted/compilation.zig+3| ... | @@ -35,6 +35,7 @@ const CInt = @import("c_int.zig").CInt; | ... | @@ -35,6 +35,7 @@ const CInt = @import("c_int.zig").CInt; |
| 35 | pub const EventLoopLocal = struct { | 35 | pub const EventLoopLocal = struct { |
| 36 | loop: *event.Loop, | 36 | loop: *event.Loop, |
| 37 | llvm_handle_pool: std.atomic.Stack(llvm.ContextRef), | 37 | llvm_handle_pool: std.atomic.Stack(llvm.ContextRef), |
| 38 | lld_lock: event.Lock, | ||
| 38 | 39 | ||
| 39 | /// TODO pool these so that it doesn't have to lock | 40 | /// TODO pool these so that it doesn't have to lock |
| 40 | prng: event.Locked(std.rand.DefaultPrng), | 41 | prng: event.Locked(std.rand.DefaultPrng), |
| ... | @@ -55,6 +56,7 @@ pub const EventLoopLocal = struct { | ... | @@ -55,6 +56,7 @@ pub const EventLoopLocal = struct { |
| 55 | 56 | ||
| 56 | return EventLoopLocal{ | 57 | return EventLoopLocal{ |
| 57 | .loop = loop, | 58 | .loop = loop, |
| 59 | .lld_lock = event.Lock.init(loop), | ||
| 58 | .llvm_handle_pool = std.atomic.Stack(llvm.ContextRef).init(), | 60 | .llvm_handle_pool = std.atomic.Stack(llvm.ContextRef).init(), |
| 59 | .prng = event.Locked(std.rand.DefaultPrng).init(loop, std.rand.DefaultPrng.init(seed)), | 61 | .prng = event.Locked(std.rand.DefaultPrng).init(loop, std.rand.DefaultPrng.init(seed)), |
| 60 | .native_libc = event.Future(LibCInstallation).init(loop), | 62 | .native_libc = event.Future(LibCInstallation).init(loop), |
| ... | @@ -63,6 +65,7 @@ pub const EventLoopLocal = struct { | ... | @@ -63,6 +65,7 @@ pub const EventLoopLocal = struct { |
| 63 | 65 | ||
| 64 | /// Must be called only after EventLoop.run completes. | 66 | /// Must be called only after EventLoop.run completes. |
| 65 | fn deinit(self: *EventLoopLocal) void { | 67 | fn deinit(self: *EventLoopLocal) void { |
| 68 | self.lld_lock.deinit(); | ||
| 66 | while (self.llvm_handle_pool.pop()) |node| { | 69 | while (self.llvm_handle_pool.pop()) |node| { |
| 67 | c.LLVMContextDispose(node.data); | 70 | c.LLVMContextDispose(node.data); |
| 68 | self.loop.allocator.destroy(node); | 71 | self.loop.allocator.destroy(node); |
src-self-hosted/link.zig+23-10| ... | @@ -80,15 +80,22 @@ pub async fn link(comp: *Compilation) !void { | ... | @@ -80,15 +80,22 @@ pub async fn link(comp: *Compilation) !void { |
| 80 | 80 | ||
| 81 | const extern_ofmt = toExternObjectFormatType(comp.target.getObjectFormat()); | 81 | const extern_ofmt = toExternObjectFormatType(comp.target.getObjectFormat()); |
| 82 | const args_slice = ctx.args.toSlice(); | 82 | const args_slice = ctx.args.toSlice(); |
| 83 | // Not evented I/O. LLD does its own multithreading internally. | 83 | |
| 84 | if (!ZigLLDLink(extern_ofmt, args_slice.ptr, args_slice.len, linkDiagCallback, @ptrCast(*c_void, &ctx))) { | 84 | { |
| 85 | if (!ctx.link_msg.isNull()) { | 85 | // LLD is not thread-safe, so we grab a global lock. |
| 86 | // TODO capture these messages and pass them through the system, reporting them through the | 86 | const held = await (async comp.event_loop_local.lld_lock.acquire() catch unreachable); |
| 87 | // event system instead of printing them directly here. | 87 | defer held.release(); |
| 88 | // perhaps try to parse and understand them. | 88 | |
| 89 | std.debug.warn("{}\n", ctx.link_msg.toSliceConst()); | 89 | // Not evented I/O. LLD does its own multithreading internally. |
| 90 | if (!ZigLLDLink(extern_ofmt, args_slice.ptr, args_slice.len, linkDiagCallback, @ptrCast(*c_void, &ctx))) { | ||
| 91 | if (!ctx.link_msg.isNull()) { | ||
| 92 | // TODO capture these messages and pass them through the system, reporting them through the | ||
| 93 | // event system instead of printing them directly here. | ||
| 94 | // perhaps try to parse and understand them. | ||
| 95 | std.debug.warn("{}\n", ctx.link_msg.toSliceConst()); | ||
| 96 | } | ||
| 97 | return error.LinkFailed; | ||
| 90 | } | 98 | } |
| 91 | return error.LinkFailed; | ||
| 92 | } | 99 | } |
| 93 | } | 100 | } |
| 94 | 101 | ||
| ... | @@ -672,7 +679,13 @@ const DarwinPlatform = struct { | ... | @@ -672,7 +679,13 @@ const DarwinPlatform = struct { |
| 672 | }; | 679 | }; |
| 673 | 680 | ||
| 674 | var had_extra: bool = undefined; | 681 | var had_extra: bool = undefined; |
| 675 | try darwinGetReleaseVersion(ver_str, &result.major, &result.minor, &result.micro, &had_extra,); | 682 | try darwinGetReleaseVersion( |
| 683 | ver_str, | ||
| 684 | &result.major, | ||
| 685 | &result.minor, | ||
| 686 | &result.micro, | ||
| 687 | &had_extra, | ||
| 688 | ); | ||
| 676 | if (had_extra or result.major != 10 or result.minor >= 100 or result.micro >= 100) { | 689 | if (had_extra or result.major != 10 or result.minor >= 100 or result.micro >= 100) { |
| 677 | return error.InvalidDarwinVersionString; | 690 | return error.InvalidDarwinVersionString; |
| 678 | } | 691 | } |
| ... | @@ -713,7 +726,7 @@ fn darwinGetReleaseVersion(str: []const u8, major: *u32, minor: *u32, micro: *u3 | ... | @@ -713,7 +726,7 @@ fn darwinGetReleaseVersion(str: []const u8, major: *u32, minor: *u32, micro: *u3 |
| 713 | return error.InvalidDarwinVersionString; | 726 | return error.InvalidDarwinVersionString; |
| 714 | 727 | ||
| 715 | var start_pos: usize = 0; | 728 | var start_pos: usize = 0; |
| 716 | for ([]*u32{major, minor, micro}) |v| { | 729 | for ([]*u32{ major, minor, micro }) |v| { |
| 717 | const dot_pos = mem.indexOfScalarPos(u8, str, start_pos, '.'); | 730 | const dot_pos = mem.indexOfScalarPos(u8, str, start_pos, '.'); |
| 718 | const end_pos = dot_pos orelse str.len; | 731 | const end_pos = dot_pos orelse str.len; |
| 719 | v.* = std.fmt.parseUnsigned(u32, str[start_pos..end_pos], 10) catch return error.InvalidDarwinVersionString; | 732 | v.* = std.fmt.parseUnsigned(u32, str[start_pos..end_pos], 10) catch return error.InvalidDarwinVersionString; |
std/build.zig+21| ... | @@ -807,6 +807,7 @@ pub const LibExeObjStep = struct { | ... | @@ -807,6 +807,7 @@ pub const LibExeObjStep = struct { |
| 807 | disable_libc: bool, | 807 | disable_libc: bool, |
| 808 | frameworks: BufSet, | 808 | frameworks: BufSet, |
| 809 | verbose_link: bool, | 809 | verbose_link: bool, |
| 810 | no_rosegment: bool, | ||
| 810 | 811 | ||
| 811 | // zig only stuff | 812 | // zig only stuff |
| 812 | root_src: ?[]const u8, | 813 | root_src: ?[]const u8, |
| ... | @@ -874,6 +875,7 @@ pub const LibExeObjStep = struct { | ... | @@ -874,6 +875,7 @@ pub const LibExeObjStep = struct { |
| 874 | 875 | ||
| 875 | fn initExtraArgs(builder: *Builder, name: []const u8, root_src: ?[]const u8, kind: Kind, static: bool, ver: *const Version) LibExeObjStep { | 876 | fn initExtraArgs(builder: *Builder, name: []const u8, root_src: ?[]const u8, kind: Kind, static: bool, ver: *const Version) LibExeObjStep { |
| 876 | var self = LibExeObjStep{ | 877 | var self = LibExeObjStep{ |
| 878 | .no_rosegment = false, | ||
| 877 | .strip = false, | 879 | .strip = false, |
| 878 | .builder = builder, | 880 | .builder = builder, |
| 879 | .verbose_link = false, | 881 | .verbose_link = false, |
| ... | @@ -914,6 +916,7 @@ pub const LibExeObjStep = struct { | ... | @@ -914,6 +916,7 @@ pub const LibExeObjStep = struct { |
| 914 | 916 | ||
| 915 | fn initC(builder: *Builder, name: []const u8, kind: Kind, version: *const Version, static: bool) LibExeObjStep { | 917 | fn initC(builder: *Builder, name: []const u8, kind: Kind, version: *const Version, static: bool) LibExeObjStep { |
| 916 | var self = LibExeObjStep{ | 918 | var self = LibExeObjStep{ |
| 919 | .no_rosegment = false, | ||
| 917 | .builder = builder, | 920 | .builder = builder, |
| 918 | .name = name, | 921 | .name = name, |
| 919 | .kind = kind, | 922 | .kind = kind, |
| ... | @@ -953,6 +956,10 @@ pub const LibExeObjStep = struct { | ... | @@ -953,6 +956,10 @@ pub const LibExeObjStep = struct { |
| 953 | return self; | 956 | return self; |
| 954 | } | 957 | } |
| 955 | 958 | ||
| 959 | pub fn setNoRoSegment(self: *LibExeObjStep, value: bool) void { | ||
| 960 | self.no_rosegment = value; | ||
| 961 | } | ||
| 962 | |||
| 956 | fn computeOutFileNames(self: *LibExeObjStep) void { | 963 | fn computeOutFileNames(self: *LibExeObjStep) void { |
| 957 | switch (self.kind) { | 964 | switch (self.kind) { |
| 958 | Kind.Obj => { | 965 | Kind.Obj => { |
| ... | @@ -1306,6 +1313,10 @@ pub const LibExeObjStep = struct { | ... | @@ -1306,6 +1313,10 @@ pub const LibExeObjStep = struct { |
| 1306 | } | 1313 | } |
| 1307 | } | 1314 | } |
| 1308 | 1315 | ||
| 1316 | if (self.no_rosegment) { | ||
| 1317 | try zig_args.append("--no-rosegment"); | ||
| 1318 | } | ||
| 1319 | |||
| 1309 | try builder.spawnChild(zig_args.toSliceConst()); | 1320 | try builder.spawnChild(zig_args.toSliceConst()); |
| 1310 | 1321 | ||
| 1311 | if (self.kind == Kind.Lib and !self.static and self.target.wantSharedLibSymLinks()) { | 1322 | if (self.kind == Kind.Lib and !self.static and self.target.wantSharedLibSymLinks()) { |
| ... | @@ -1598,6 +1609,7 @@ pub const TestStep = struct { | ... | @@ -1598,6 +1609,7 @@ pub const TestStep = struct { |
| 1598 | include_dirs: ArrayList([]const u8), | 1609 | include_dirs: ArrayList([]const u8), |
| 1599 | lib_paths: ArrayList([]const u8), | 1610 | lib_paths: ArrayList([]const u8), |
| 1600 | object_files: ArrayList([]const u8), | 1611 | object_files: ArrayList([]const u8), |
| 1612 | no_rosegment: bool, | ||
| 1601 | 1613 | ||
| 1602 | pub fn init(builder: *Builder, root_src: []const u8) TestStep { | 1614 | pub fn init(builder: *Builder, root_src: []const u8) TestStep { |
| 1603 | const step_name = builder.fmt("test {}", root_src); | 1615 | const step_name = builder.fmt("test {}", root_src); |
| ... | @@ -1615,9 +1627,14 @@ pub const TestStep = struct { | ... | @@ -1615,9 +1627,14 @@ pub const TestStep = struct { |
| 1615 | .include_dirs = ArrayList([]const u8).init(builder.allocator), | 1627 | .include_dirs = ArrayList([]const u8).init(builder.allocator), |
| 1616 | .lib_paths = ArrayList([]const u8).init(builder.allocator), | 1628 | .lib_paths = ArrayList([]const u8).init(builder.allocator), |
| 1617 | .object_files = ArrayList([]const u8).init(builder.allocator), | 1629 | .object_files = ArrayList([]const u8).init(builder.allocator), |
| 1630 | .no_rosegment = false, | ||
| 1618 | }; | 1631 | }; |
| 1619 | } | 1632 | } |
| 1620 | 1633 | ||
| 1634 | pub fn setNoRoSegment(self: *TestStep, value: bool) void { | ||
| 1635 | self.no_rosegment = value; | ||
| 1636 | } | ||
| 1637 | |||
| 1621 | pub fn addLibPath(self: *TestStep, path: []const u8) void { | 1638 | pub fn addLibPath(self: *TestStep, path: []const u8) void { |
| 1622 | self.lib_paths.append(path) catch unreachable; | 1639 | self.lib_paths.append(path) catch unreachable; |
| 1623 | } | 1640 | } |
| ... | @@ -1761,6 +1778,10 @@ pub const TestStep = struct { | ... | @@ -1761,6 +1778,10 @@ pub const TestStep = struct { |
| 1761 | try zig_args.append(lib_path); | 1778 | try zig_args.append(lib_path); |
| 1762 | } | 1779 | } |
| 1763 | 1780 | ||
| 1781 | if (self.no_rosegment) { | ||
| 1782 | try zig_args.append("--no-rosegment"); | ||
| 1783 | } | ||
| 1784 | |||
| 1764 | try builder.spawnChild(zig_args.toSliceConst()); | 1785 | try builder.spawnChild(zig_args.toSliceConst()); |
| 1765 | } | 1786 | } |
| 1766 | }; | 1787 | }; |
std/event/channel.zig+3-22| ... | @@ -71,11 +71,6 @@ pub fn Channel(comptime T: type) type { | ... | @@ -71,11 +71,6 @@ pub fn Channel(comptime T: type) type { |
| 71 | /// puts a data item in the channel. The promise completes when the value has been added to the | 71 | /// puts a data item in the channel. The promise completes when the value has been added to the |
| 72 | /// buffer, or in the case of a zero size buffer, when the item has been retrieved by a getter. | 72 | /// buffer, or in the case of a zero size buffer, when the item has been retrieved by a getter. |
| 73 | pub async fn put(self: *SelfChannel, data: T) void { | 73 | pub async fn put(self: *SelfChannel, data: T) void { |
| 74 | // TODO should be able to group memory allocation failure before first suspend point | ||
| 75 | // so that the async invocation catches it | ||
| 76 | var dispatch_tick_node_ptr: *Loop.NextTickNode = undefined; | ||
| 77 | _ = async self.dispatch(&dispatch_tick_node_ptr) catch unreachable; | ||
| 78 | |||
| 79 | suspend |handle| { | 74 | suspend |handle| { |
| 80 | var my_tick_node = Loop.NextTickNode{ | 75 | var my_tick_node = Loop.NextTickNode{ |
| 81 | .next = undefined, | 76 | .next = undefined, |
| ... | @@ -91,18 +86,13 @@ pub fn Channel(comptime T: type) type { | ... | @@ -91,18 +86,13 @@ pub fn Channel(comptime T: type) type { |
| 91 | self.putters.put(&queue_node); | 86 | self.putters.put(&queue_node); |
| 92 | _ = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); | 87 | _ = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); |
| 93 | 88 | ||
| 94 | self.loop.onNextTick(dispatch_tick_node_ptr); | 89 | self.dispatch(); |
| 95 | } | 90 | } |
| 96 | } | 91 | } |
| 97 | 92 | ||
| 98 | /// await this function to get an item from the channel. If the buffer is empty, the promise will | 93 | /// await this function to get an item from the channel. If the buffer is empty, the promise will |
| 99 | /// complete when the next item is put in the channel. | 94 | /// complete when the next item is put in the channel. |
| 100 | pub async fn get(self: *SelfChannel) T { | 95 | pub async fn get(self: *SelfChannel) T { |
| 101 | // TODO should be able to group memory allocation failure before first suspend point | ||
| 102 | // so that the async invocation catches it | ||
| 103 | var dispatch_tick_node_ptr: *Loop.NextTickNode = undefined; | ||
| 104 | _ = async self.dispatch(&dispatch_tick_node_ptr) catch unreachable; | ||
| 105 | |||
| 106 | // TODO integrate this function with named return values | 96 | // TODO integrate this function with named return values |
| 107 | // so we can get rid of this extra result copy | 97 | // so we can get rid of this extra result copy |
| 108 | var result: T = undefined; | 98 | var result: T = undefined; |
| ... | @@ -121,21 +111,12 @@ pub fn Channel(comptime T: type) type { | ... | @@ -121,21 +111,12 @@ pub fn Channel(comptime T: type) type { |
| 121 | self.getters.put(&queue_node); | 111 | self.getters.put(&queue_node); |
| 122 | _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); | 112 | _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); |
| 123 | 113 | ||
| 124 | self.loop.onNextTick(dispatch_tick_node_ptr); | 114 | self.dispatch(); |
| 125 | } | 115 | } |
| 126 | return result; | 116 | return result; |
| 127 | } | 117 | } |
| 128 | 118 | ||
| 129 | async fn dispatch(self: *SelfChannel, tick_node_ptr: **Loop.NextTickNode) void { | 119 | fn dispatch(self: *SelfChannel) void { |
| 130 | // resumed by onNextTick | ||
| 131 | suspend |handle| { | ||
| 132 | var tick_node = Loop.NextTickNode{ | ||
| 133 | .data = handle, | ||
| 134 | .next = undefined, | ||
| 135 | }; | ||
| 136 | tick_node_ptr.* = &tick_node; | ||
| 137 | } | ||
| 138 | |||
| 139 | // set the "need dispatch" flag | 120 | // set the "need dispatch" flag |
| 140 | _ = @atomicRmw(u8, &self.need_dispatch, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); | 121 | _ = @atomicRmw(u8, &self.need_dispatch, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); |
| 141 | 122 |
test/stage2/compare_output.zig+13| ... | @@ -2,6 +2,7 @@ const std = @import("std"); | ... | @@ -2,6 +2,7 @@ const std = @import("std"); |
| 2 | const TestContext = @import("../../src-self-hosted/test.zig").TestContext; | 2 | const TestContext = @import("../../src-self-hosted/test.zig").TestContext; |
| 3 | 3 | ||
| 4 | pub fn addCases(ctx: *TestContext) !void { | 4 | pub fn addCases(ctx: *TestContext) !void { |
| 5 | // hello world | ||
| 5 | try ctx.testCompareOutputLibC( | 6 | try ctx.testCompareOutputLibC( |
| 6 | \\extern fn puts([*]const u8) void; | 7 | \\extern fn puts([*]const u8) void; |
| 7 | \\export fn main() c_int { | 8 | \\export fn main() c_int { |
| ... | @@ -9,4 +10,16 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -9,4 +10,16 @@ pub fn addCases(ctx: *TestContext) !void { |
| 9 | \\ return 0; | 10 | \\ return 0; |
| 10 | \\} | 11 | \\} |
| 11 | , "Hello, world!" ++ std.cstr.line_sep); | 12 | , "Hello, world!" ++ std.cstr.line_sep); |
| 13 | |||
| 14 | // function calling another function | ||
| 15 | try ctx.testCompareOutputLibC( | ||
| 16 | \\extern fn puts(s: [*]const u8) void; | ||
| 17 | \\export fn main() c_int { | ||
| 18 | \\ return foo(c"OK"); | ||
| 19 | \\} | ||
| 20 | \\fn foo(s: [*]const u8) c_int { | ||
| 21 | \\ puts(s); | ||
| 22 | \\ return 0; | ||
| 23 | \\} | ||
| 24 | , "OK" ++ std.cstr.line_sep); | ||
| 12 | } | 25 | } |