authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-07-24 21:28:54-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-07-24 21:28:54-04:00
log02713e8d8aa9641616bd85e77dda784009c96113
tree675d61a60cbe732d2c61290b33db560552b16e05
parentadefd1a52b812813dd3e3590d398f927ffc5b9af

fix race conditions in self-hosted compiler; add test

* 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 safe

6 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);
5051
...@@ -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 option229 // TODO turn this into -Dextra-lib-path=/lib option
229 exe.addLibPath("/lib");230 exe.addLibPath("/lib");
230231
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;
35pub const EventLoopLocal = struct {35pub 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,
3839
39 /// TODO pool these so that it doesn't have to lock40 /// 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 {
5556
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 {
6365
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 {
8080
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 the86 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}
94101
...@@ -672,7 +679,13 @@ const DarwinPlatform = struct {...@@ -672,7 +679,13 @@ const DarwinPlatform = struct {
672 };679 };
673680
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;
714727
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,
810811
811 // zig only stuff812 // 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 {
874875
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 {
914916
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 }
955958
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 }
13081315
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());
13101321
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,
16011613
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 }
16201633
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 }
17631780
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 the71 /// 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);
9388
94 self.loop.onNextTick(dispatch_tick_node_ptr);89 self.dispatch();
95 }90 }
96 }91 }
9792
98 /// await this function to get an item from the channel. If the buffer is empty, the promise will93 /// 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 values96 // TODO integrate this function with named return values
107 // so we can get rid of this extra result copy97 // 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);
123113
124 self.loop.onNextTick(dispatch_tick_node_ptr);114 self.dispatch();
125 }115 }
126 return result;116 return result;
127 }117 }
128118
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" flag120 // 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);
141122
test/stage2/compare_output.zig+13
...@@ -2,6 +2,7 @@ const std = @import("std");...@@ -2,6 +2,7 @@ const std = @import("std");
2const TestContext = @import("../../src-self-hosted/test.zig").TestContext;2const TestContext = @import("../../src-self-hosted/test.zig").TestContext;
33
4pub fn addCases(ctx: *TestContext) !void {4pub 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}