| 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 | 45 | .c_header_files = nextValue(&index, build_info), |
| 46 | 46 | .dia_guids_lib = nextValue(&index, build_info), |
| 47 | 47 | .llvm = undefined, |
| 48 | .no_rosegment = b.option(bool, "no-rosegment", "Workaround to enable valgrind builds") orelse false, | |
| 48 | 49 | }; |
| 49 | 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 | 229 | // TODO turn this into -Dextra-lib-path=/lib option |
| 229 | 230 | exe.addLibPath("/lib"); |
| 230 | 231 | |
| 232 | exe.setNoRoSegment(ctx.no_rosegment); | |
| 233 | ||
| 231 | 234 | exe.addIncludeDir("src"); |
| 232 | 235 | exe.addIncludeDir(ctx.cmake_binary_dir); |
| 233 | 236 | addCppLib(b, exe, ctx.cmake_binary_dir, "zig_cpp"); |
| ... | ... | @@ -286,4 +289,5 @@ const Context = struct { |
| 286 | 289 | c_header_files: []const u8, |
| 287 | 290 | dia_guids_lib: []const u8, |
| 288 | 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 | 35 | pub const EventLoopLocal = struct { |
| 36 | 36 | loop: *event.Loop, |
| 37 | 37 | llvm_handle_pool: std.atomic.Stack(llvm.ContextRef), |
| 38 | lld_lock: event.Lock, | |
| 38 | 39 | |
| 39 | 40 | /// TODO pool these so that it doesn't have to lock |
| 40 | 41 | prng: event.Locked(std.rand.DefaultPrng), |
| ... | ... | @@ -55,6 +56,7 @@ pub const EventLoopLocal = struct { |
| 55 | 56 | |
| 56 | 57 | return EventLoopLocal{ |
| 57 | 58 | .loop = loop, |
| 59 | .lld_lock = event.Lock.init(loop), | |
| 58 | 60 | .llvm_handle_pool = std.atomic.Stack(llvm.ContextRef).init(), |
| 59 | 61 | .prng = event.Locked(std.rand.DefaultPrng).init(loop, std.rand.DefaultPrng.init(seed)), |
| 60 | 62 | .native_libc = event.Future(LibCInstallation).init(loop), |
| ... | ... | @@ -63,6 +65,7 @@ pub const EventLoopLocal = struct { |
| 63 | 65 | |
| 64 | 66 | /// Must be called only after EventLoop.run completes. |
| 65 | 67 | fn deinit(self: *EventLoopLocal) void { |
| 68 | self.lld_lock.deinit(); | |
| 66 | 69 | while (self.llvm_handle_pool.pop()) |node| { |
| 67 | 70 | c.LLVMContextDispose(node.data); |
| 68 | 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 | 80 | |
| 81 | 81 | const extern_ofmt = toExternObjectFormatType(comp.target.getObjectFormat()); |
| 82 | 82 | const args_slice = ctx.args.toSlice(); |
| 83 | // Not evented I/O. LLD does its own multithreading internally. | |
| 84 | if (!ZigLLDLink(extern_ofmt, args_slice.ptr, args_slice.len, linkDiagCallback, @ptrCast(*c_void, &ctx))) { | |
| 85 | if (!ctx.link_msg.isNull()) { | |
| 86 | // TODO capture these messages and pass them through the system, reporting them through the | |
| 87 | // event system instead of printing them directly here. | |
| 88 | // perhaps try to parse and understand them. | |
| 89 | std.debug.warn("{}\n", ctx.link_msg.toSliceConst()); | |
| 83 | ||
| 84 | { | |
| 85 | // LLD is not thread-safe, so we grab a global lock. | |
| 86 | const held = await (async comp.event_loop_local.lld_lock.acquire() catch unreachable); | |
| 87 | defer held.release(); | |
| 88 | ||
| 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 | 679 | }; |
| 673 | 680 | |
| 674 | 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 | 689 | if (had_extra or result.major != 10 or result.minor >= 100 or result.micro >= 100) { |
| 677 | 690 | return error.InvalidDarwinVersionString; |
| 678 | 691 | } |
| ... | ... | @@ -713,7 +726,7 @@ fn darwinGetReleaseVersion(str: []const u8, major: *u32, minor: *u32, micro: *u3 |
| 713 | 726 | return error.InvalidDarwinVersionString; |
| 714 | 727 | |
| 715 | 728 | var start_pos: usize = 0; |
| 716 | for ([]*u32{major, minor, micro}) |v| { | |
| 729 | for ([]*u32{ major, minor, micro }) |v| { | |
| 717 | 730 | const dot_pos = mem.indexOfScalarPos(u8, str, start_pos, '.'); |
| 718 | 731 | const end_pos = dot_pos orelse str.len; |
| 719 | 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 | 807 | disable_libc: bool, |
| 808 | 808 | frameworks: BufSet, |
| 809 | 809 | verbose_link: bool, |
| 810 | no_rosegment: bool, | |
| 810 | 811 | |
| 811 | 812 | // zig only stuff |
| 812 | 813 | root_src: ?[]const u8, |
| ... | ... | @@ -874,6 +875,7 @@ pub const LibExeObjStep = struct { |
| 874 | 875 | |
| 875 | 876 | fn initExtraArgs(builder: *Builder, name: []const u8, root_src: ?[]const u8, kind: Kind, static: bool, ver: *const Version) LibExeObjStep { |
| 876 | 877 | var self = LibExeObjStep{ |
| 878 | .no_rosegment = false, | |
| 877 | 879 | .strip = false, |
| 878 | 880 | .builder = builder, |
| 879 | 881 | .verbose_link = false, |
| ... | ... | @@ -914,6 +916,7 @@ pub const LibExeObjStep = struct { |
| 914 | 916 | |
| 915 | 917 | fn initC(builder: *Builder, name: []const u8, kind: Kind, version: *const Version, static: bool) LibExeObjStep { |
| 916 | 918 | var self = LibExeObjStep{ |
| 919 | .no_rosegment = false, | |
| 917 | 920 | .builder = builder, |
| 918 | 921 | .name = name, |
| 919 | 922 | .kind = kind, |
| ... | ... | @@ -953,6 +956,10 @@ pub const LibExeObjStep = struct { |
| 953 | 956 | return self; |
| 954 | 957 | } |
| 955 | 958 | |
| 959 | pub fn setNoRoSegment(self: *LibExeObjStep, value: bool) void { | |
| 960 | self.no_rosegment = value; | |
| 961 | } | |
| 962 | ||
| 956 | 963 | fn computeOutFileNames(self: *LibExeObjStep) void { |
| 957 | 964 | switch (self.kind) { |
| 958 | 965 | Kind.Obj => { |
| ... | ... | @@ -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 | 1320 | try builder.spawnChild(zig_args.toSliceConst()); |
| 1310 | 1321 | |
| 1311 | 1322 | if (self.kind == Kind.Lib and !self.static and self.target.wantSharedLibSymLinks()) { |
| ... | ... | @@ -1598,6 +1609,7 @@ pub const TestStep = struct { |
| 1598 | 1609 | include_dirs: ArrayList([]const u8), |
| 1599 | 1610 | lib_paths: ArrayList([]const u8), |
| 1600 | 1611 | object_files: ArrayList([]const u8), |
| 1612 | no_rosegment: bool, | |
| 1601 | 1613 | |
| 1602 | 1614 | pub fn init(builder: *Builder, root_src: []const u8) TestStep { |
| 1603 | 1615 | const step_name = builder.fmt("test {}", root_src); |
| ... | ... | @@ -1615,9 +1627,14 @@ pub const TestStep = struct { |
| 1615 | 1627 | .include_dirs = ArrayList([]const u8).init(builder.allocator), |
| 1616 | 1628 | .lib_paths = ArrayList([]const u8).init(builder.allocator), |
| 1617 | 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 | 1638 | pub fn addLibPath(self: *TestStep, path: []const u8) void { |
| 1622 | 1639 | self.lib_paths.append(path) catch unreachable; |
| 1623 | 1640 | } |
| ... | ... | @@ -1761,6 +1778,10 @@ pub const TestStep = struct { |
| 1761 | 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 | 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 | 71 | /// puts a data item in the channel. The promise completes when the value has been added to the |
| 72 | 72 | /// buffer, or in the case of a zero size buffer, when the item has been retrieved by a getter. |
| 73 | 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 | 74 | suspend |handle| { |
| 80 | 75 | var my_tick_node = Loop.NextTickNode{ |
| 81 | 76 | .next = undefined, |
| ... | ... | @@ -91,18 +86,13 @@ pub fn Channel(comptime T: type) type { |
| 91 | 86 | self.putters.put(&queue_node); |
| 92 | 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 | 93 | /// await this function to get an item from the channel. If the buffer is empty, the promise will |
| 99 | 94 | /// complete when the next item is put in the channel. |
| 100 | 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 | 96 | // TODO integrate this function with named return values |
| 107 | 97 | // so we can get rid of this extra result copy |
| 108 | 98 | var result: T = undefined; |
| ... | ... | @@ -121,21 +111,12 @@ pub fn Channel(comptime T: type) type { |
| 121 | 111 | self.getters.put(&queue_node); |
| 122 | 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 | 116 | return result; |
| 127 | 117 | } |
| 128 | 118 | |
| 129 | async fn dispatch(self: *SelfChannel, tick_node_ptr: **Loop.NextTickNode) 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 | ||
| 119 | fn dispatch(self: *SelfChannel) void { | |
| 139 | 120 | // set the "need dispatch" flag |
| 140 | 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 | 2 | const TestContext = @import("../../src-self-hosted/test.zig").TestContext; |
| 3 | 3 | |
| 4 | 4 | pub fn addCases(ctx: *TestContext) !void { |
| 5 | // hello world | |
| 5 | 6 | try ctx.testCompareOutputLibC( |
| 6 | 7 | \\extern fn puts([*]const u8) void; |
| 7 | 8 | \\export fn main() c_int { |
| ... | ... | @@ -9,4 +10,16 @@ pub fn addCases(ctx: *TestContext) !void { |
| 9 | 10 | \\ return 0; |
| 10 | 11 | \\} |
| 11 | 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 | } |