authorgravatar for alex.slesarev@gmail.comAlexander Slesarev <alex.slesarev@gmail.com> 2021-10-31 22:36:30-06:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-05-10 16:40:48-07:00
log3997828a6176203b25b541c6e450f5e4bda82ce4
treedfa5c6bc07cb5be758d997c9c1c893c5f942cc73
parent67c4b16d6e27f1d81e2e2f837bd53d958b9baa33

Added _LIBCPP_HAS_NO_THREADS for single_threaded binaries linked with libcxx.

Fixed single-threaded mode for Windows.

6 files changed, 189 insertions(+), 45 deletions(-)

lib/std/build/RunStep.zig+72
......@@ -1,6 +1,7 @@
11const std = @import("../std.zig");
22const builtin = @import("builtin");
33const build = std.build;
4const CrossTarget = std.zig.CrossTarget;
45const Step = build.Step;
56const Builder = build.Builder;
67const LibExeObjStep = build.LibExeObjStep;
......@@ -142,6 +143,23 @@ pub fn expectStdErrEqual(self: *RunStep, bytes: []const u8) void {
142143 self.stderr_action = .{ .expect_exact = self.builder.dupe(bytes) };
143144}
144145
146/// Returns true if the step could be run, otherwise false
147pub fn isRunnable(
148 self: *RunStep,
149) bool {
150 for (self.argv.items) |arg| {
151 switch (arg) {
152 .artifact => |artifact| {
153 _ = self.getExternalExecutor(artifact) catch {
154 return false;
155 };
156 },
157 else => {},
158 }
159 }
160 return true;
161}
162
145163pub fn expectStdOutEqual(self: *RunStep, bytes: []const u8) void {
146164 self.stdout_action = .{ .expect_exact = self.builder.dupe(bytes) };
147165}
......@@ -154,6 +172,57 @@ fn stdIoActionToBehavior(action: StdIoAction) std.ChildProcess.StdIo {
154172 };
155173}
156174
175fn getExternalExecutor(self: *RunStep, artifact: *LibExeObjStep) !?[]const u8 {
176 const need_cross_glibc = artifact.target.isGnuLibC() and artifact.is_linking_libc;
177 const executor = self.builder.host.getExternalExecutor(artifact.target_info, .{
178 .qemu_fixes_dl = need_cross_glibc and self.builder.glibc_runtimes_dir != null,
179 .link_libc = artifact.is_linking_libc,
180 });
181 switch (executor) {
182 .bad_dl, .bad_os_or_cpu => {
183 return error.NoExecutable;
184 },
185 .native => {
186 return null;
187 },
188 .rosetta => {
189 if (self.builder.enable_rosetta) {
190 return null;
191 } else {
192 return error.RosettaNotEnabled;
193 }
194 },
195 .qemu => |bin_name| {
196 if (self.builder.enable_qemu) {
197 return bin_name;
198 } else {
199 return error.QemuNotEnabled;
200 }
201 },
202 .wine => |bin_name| {
203 if (self.builder.enable_wine) {
204 return bin_name;
205 } else {
206 return error.WineNotEnabled;
207 }
208 },
209 .wasmtime => |bin_name| {
210 if (self.builder.enable_wasmtime) {
211 return bin_name;
212 } else {
213 return error.WasmtimeNotEnabled;
214 }
215 },
216 .darling => |bin_name| {
217 if (self.builder.enable_darling) {
218 return bin_name;
219 } else {
220 return error.DarlingNotEnabled;
221 }
222 },
223 }
224}
225
157226fn make(step: *Step) !void {
158227 const self = @fieldParentPtr(RunStep, "step", step);
159228
......@@ -169,6 +238,9 @@ fn make(step: *Step) !void {
169238 // On Windows we don't have rpaths so we have to add .dll search paths to PATH
170239 self.addPathForDynLibs(artifact);
171240 }
241 if (try self.getExternalExecutor(artifact)) |executor| {
242 try argv_list.append(executor);
243 }
172244 const executable_path = artifact.installed_path orelse artifact.getOutputSource().getPath(self.builder);
173245 try argv_list.append(executable_path);
174246 },
src/Compilation.zig+13
......@@ -1180,6 +1180,15 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
11801180 if (must_single_thread and !single_threaded) {
11811181 return error.TargetRequiresSingleThreaded;
11821182 }
1183 if (!single_threaded and options.link_libcpp) {
1184 if (options.target.cpu.arch.isARM()) {
1185 log.warn(
1186 \\libc++ does not work on multi-threaded ARM yet.
1187 \\For more details: https://github.com/ziglang/zig/issues/6573
1188 , .{});
1189 return error.TargetRequiresSingleThreaded;
1190 }
1191 }
11831192
11841193 const llvm_cpu_features: ?[*:0]const u8 = if (build_options.have_llvm and use_llvm) blk: {
11851194 var buf = std.ArrayList(u8).init(arena);
......@@ -3803,6 +3812,10 @@ pub fn addCCArgs(
38033812 try argv.append("-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS");
38043813 try argv.append("-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS");
38053814 try argv.append("-D_LIBCPP_HAS_NO_VENDOR_AVAILABILITY_ANNOTATIONS");
3815
3816 if (comp.bin_file.options.single_threaded) {
3817 try argv.append("-D_LIBCPP_HAS_NO_THREADS");
3818 } else {}
38063819 }
38073820
38083821 if (comp.bin_file.options.link_libunwind) {
src/libcxx.zig+18-6
......@@ -128,6 +128,12 @@ pub fn buildLibCXX(comp: *Compilation) !void {
128128 continue;
129129 if (std.mem.startsWith(u8, cxx_src, "src/support/ibm/") and target.os.tag != .zos)
130130 continue;
131 if (comp.bin_file.options.single_threaded) {
132 if (std.mem.startsWith(u8, cxx_src, "src/support/win32/thread_win32.cpp")) {
133 continue;
134 }
135 try cflags.append("-D_LIBCPP_HAS_NO_THREADS");
136 }
131137
132138 try cflags.append("-DNDEBUG");
133139 try cflags.append("-D_LIBCPP_BUILDING_LIBRARY");
......@@ -145,8 +151,7 @@ pub fn buildLibCXX(comp: *Compilation) !void {
145151 }
146152
147153 if (target.os.tag == .wasi) {
148 // WASI doesn't support thread and exception yet.
149 try cflags.append("-D_LIBCPP_HAS_NO_THREADS");
154 // WASI doesn't support exceptions yet.
150155 try cflags.append("-fno-exceptions");
151156 }
152157
......@@ -264,13 +269,20 @@ pub fn buildLibCXXABI(comp: *Compilation) !void {
264269 var cflags = std.ArrayList([]const u8).init(arena);
265270
266271 if (target.os.tag == .wasi) {
267 // WASI doesn't support thread and exception yet.
268 if (std.mem.startsWith(u8, cxxabi_src, "src/cxa_thread_atexit.cpp") or
269 std.mem.startsWith(u8, cxxabi_src, "src/cxa_exception.cpp") or
272 // WASI doesn't support exceptions yet.
273 if (std.mem.startsWith(u8, cxxabi_src, "src/cxa_exception.cpp") or
270274 std.mem.startsWith(u8, cxxabi_src, "src/cxa_personality.cpp"))
271275 continue;
272 try cflags.append("-D_LIBCXXABI_HAS_NO_THREADS");
273276 try cflags.append("-fno-exceptions");
277 }
278
279 // WASM targets are single threaded.
280 if (comp.bin_file.options.single_threaded) {
281 if (std.mem.startsWith(u8, cxxabi_src, "src/cxa_thread_atexit.cpp")) {
282 continue;
283 }
284 try cflags.append("-D_LIBCXXABI_HAS_NO_THREADS");
285 try cflags.append("-D_LIBCPP_HAS_NO_THREADS");
274286 } else {
275287 try cflags.append("-DHAVE___CXA_THREAD_ATEXIT_IMPL");
276288 }
test/standalone/c_compiler/build.zig+27-15
......@@ -1,20 +1,21 @@
11const std = @import("std");
2const builtin = @import("builtin");
32const Builder = std.build.Builder;
43const CrossTarget = std.zig.CrossTarget;
54
6// TODO integrate this with the std.build executor API
7fn isRunnableTarget(t: CrossTarget) bool {
8 if (t.isNative()) return true;
9
10 return (t.getOsTag() == builtin.os.tag and
11 t.getCpuArch() == builtin.cpu.arch);
12}
13
145pub fn build(b: *Builder) void {
156 const mode = b.standardReleaseOptions();
167 const target = b.standardTargetOptions(.{});
178
9 const is_wine_enabled = b.option(bool, "enable-wine", "Use Wine to run cross compiled Windows tests") orelse false;
10 const is_qemu_enabled = b.option(bool, "enable-qemu", "Use QEMU to run cross compiled foreign architecture tests") orelse false;
11 const is_wasmtime_enabled = b.option(bool, "enable-wasmtime", "Use Wasmtime to enable and run WASI libstd tests") orelse false;
12 const is_darling_enabled = b.option(bool, "enable-darling", "[Experimental] Use Darling to run cross compiled macOS tests") orelse false;
13 const single_threaded = b.option(bool, "single-threaded", "Test single threaded mode") orelse false;
14 b.enable_wine = is_wine_enabled;
15 b.enable_qemu = is_qemu_enabled;
16 b.enable_wasmtime = is_wasmtime_enabled;
17 b.enable_darling = is_darling_enabled;
18
1819 const test_step = b.step("test", "Test the program");
1920
2021 const exe_c = b.addExecutable("test_c", null);
......@@ -29,9 +30,16 @@ pub fn build(b: *Builder) void {
2930 exe_cpp.addCSourceFile("test.cpp", &[0][]const u8{});
3031 exe_cpp.setBuildMode(mode);
3132 exe_cpp.setTarget(target);
32 exe_cpp.linkSystemLibrary("c++");
33 exe_cpp.linkLibCpp();
34 exe_cpp.single_threaded = single_threaded;
35 const os_tag = target.getOsTag();
36 // macos C++ exceptions could be compiled, but not being catched,
37 // additional support is required, possibly unwind + DWARF CFI
38 if (target.getCpuArch().isWasm() or os_tag == .macos) {
39 exe_cpp.defineCMacro("_LIBCPP_NO_EXCEPTIONS", null);
40 }
3341
34 switch (target.getOsTag()) {
42 switch (os_tag) {
3543 .windows => {
3644 // https://github.com/ziglang/zig/issues/8531
3745 exe_cpp.want_lto = false;
......@@ -44,13 +52,17 @@ pub fn build(b: *Builder) void {
4452 else => {},
4553 }
4654
47 if (isRunnableTarget(target)) {
48 const run_c_cmd = exe_c.run();
55 const run_c_cmd = exe_c.run();
56 if (run_c_cmd.isRunnable()) {
4957 test_step.dependOn(&run_c_cmd.step);
50 const run_cpp_cmd = exe_cpp.run();
51 test_step.dependOn(&run_cpp_cmd.step);
5258 } else {
5359 test_step.dependOn(&exe_c.step);
60 }
61
62 const run_cpp_cmd = exe_cpp.run();
63 if (run_cpp_cmd.isRunnable()) {
64 test_step.dependOn(&run_cpp_cmd.step);
65 } else {
5466 test_step.dependOn(&exe_cpp.step);
5567 }
5668}
test/standalone/c_compiler/test.c+13-10
......@@ -1,25 +1,28 @@
11#include <assert.h>
22#include <stdio.h>
3#include <stdlib.h>
34
4typedef struct {
5 int val;
5typedef struct {
6 int val;
67} STest;
78
89int getVal(STest* data) { return data->val; }
910
1011int main (int argc, char *argv[])
1112{
12 STest* data = (STest*)malloc(sizeof(STest));
13 data->val = 123;
13 STest* data = (STest*)malloc(sizeof(STest));
14 data->val = 123;
1415
15 assert(getVal(data) != 456);
16 int ok = (getVal(data) == 123);
16 assert(getVal(data) != 456);
17 int ok = (getVal(data) == 123);
1718
18 if (argc>1) fprintf(stdout, "val=%d\n", data->val);
19 if (argc > 1) {
20 fprintf(stdout, "val=%d\n", data->val);
21 }
1922
20 free(data);
23 free(data);
2124
22 if (!ok) abort();
25 if (!ok) abort();
2326
24 return 0;
27 return EXIT_SUCCESS;
2528}
test/standalone/c_compiler/test.cpp+46-14
......@@ -1,15 +1,33 @@
1#include <iostream>
21#include <cassert>
2#include <iostream>
3
4#ifndef _LIBCPP_HAS_NO_THREADS
5#include <future>
6#endif
7
8thread_local unsigned int tls_counter = 1;
9
10// a non-optimized way of checking for prime numbers:
11bool is_prime(int x) {
12 for (int i = 2; i <x ; ++i) {
13 if (x % i == 0) {
14 return false;
15 }
16 }
17 return true;
18}
319
420class CTest {
521public:
6 CTest(int val) : m_val(val) {};
7 virtual ~CTest() {}
22 CTest(int val) : m_val(val) {
23 tls_counter++;
24 };
25 virtual ~CTest() {}
826
9 virtual int getVal() const { return m_val; }
10 virtual void printVal() { std::cout << "val=" << m_val << std::endl; }
27 virtual int getVal() const { return m_val; }
28 virtual void printVal() { std::cout << "val=" << m_val << std::endl; }
1129private:
12 int m_val;
30 int m_val;
1331};
1432
1533
......@@ -18,16 +36,30 @@ CTest global(runtime_val); // test if global initializers are called.
1836
1937int main (int argc, char *argv[])
2038{
21 assert(global.getVal() == 456);
39 assert(global.getVal() == 456);
40 auto t = std::make_unique<CTest>(123);
41 assert(t->getVal() != 456);
42 assert(tls_counter == 2);
43 if (argc > 1) {
44 t->printVal();
45 }
46 bool ok = t->getVal() == 123;
2247
23 auto* t = new CTest(123);
24 assert(t->getVal()!=456);
48 if (!ok) abort();
2549
26 if (argc>1) t->printVal();
27 bool ok = t->getVal() == 123;
28 delete t;
50#ifndef _LIBCPP_HAS_NO_THREADS
51 std::future<bool> fut = std::async(is_prime, 313);
52 bool ret = fut.get();
53 assert(ret);
54#endif
2955
30 if (!ok) abort();
56#ifndef _LIBCPP_NO_EXCEPTIONS
57 try {
58 throw 20;
59 } catch (int e) {
60 assert(e == 20);
61 }
62#endif
3163
32 return 0;
64 return EXIT_SUCCESS;
3365}