diff --git a/doc/langref.html.in b/doc/langref.html.in index 9d48238843032a0cd0dc286b2494ae9141de19ac..7ffbcc01108669087224d943b3fc9a36ceaa98ea 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -2780,10 +2780,16 @@ fn noop4() align(4) void {} test "function alignment" { try expect(derp() == 1234); - try expect(@TypeOf(noop1) == fn () align(1) void); - try expect(@TypeOf(noop4) == fn () align(4) void); + try expect(@TypeOf(derp) == fn () i32); + try expect(@TypeOf(&derp) == *align(@sizeOf(usize) * 2) const fn () i32); + noop1(); + try expect(@TypeOf(noop1) == fn () void); + try expect(@TypeOf(&noop1) == *align(1) const fn () void); + noop4(); + try expect(@TypeOf(noop4) == fn () void); + try expect(@TypeOf(&noop4) == *align(4) const fn () void); } {#code_end#}

diff --git a/lib/build_runner.zig b/lib/build_runner.zig deleted file mode 100644 index f19713776916b94baada339ef967edd0a9aa303a..0000000000000000000000000000000000000000 --- a/lib/build_runner.zig +++ /dev/null @@ -1,1293 +0,0 @@ -const root = @import("@build"); -const std = @import("std"); -const builtin = @import("builtin"); -const assert = std.debug.assert; -const io = std.io; -const fmt = std.fmt; -const mem = std.mem; -const process = std.process; -const ArrayList = std.ArrayList; -const File = std.fs.File; -const Step = std.Build.Step; - -pub const dependencies = @import("@dependencies"); - -pub fn main() !void { - // Here we use an ArenaAllocator backed by a page allocator because a build is a short-lived, - // one shot program. We don't need to waste time freeing memory and finding places to squish - // bytes into. So we free everything all at once at the very end. - var single_threaded_arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); - defer single_threaded_arena.deinit(); - - var thread_safe_arena: std.heap.ThreadSafeAllocator = .{ - .child_allocator = single_threaded_arena.allocator(), - }; - const arena = thread_safe_arena.allocator(); - - const args = try process.argsAlloc(arena); - - // skip my own exe name - var arg_idx: usize = 1; - - const zig_exe = nextArg(args, &arg_idx) orelse { - std.debug.print("Expected path to zig compiler\n", .{}); - return error.InvalidArgs; - }; - const build_root = nextArg(args, &arg_idx) orelse { - std.debug.print("Expected build root directory path\n", .{}); - return error.InvalidArgs; - }; - const cache_root = nextArg(args, &arg_idx) orelse { - std.debug.print("Expected cache root directory path\n", .{}); - return error.InvalidArgs; - }; - const global_cache_root = nextArg(args, &arg_idx) orelse { - std.debug.print("Expected global cache root directory path\n", .{}); - return error.InvalidArgs; - }; - - const build_root_directory: std.Build.Cache.Directory = .{ - .path = build_root, - .handle = try std.fs.cwd().openDir(build_root, .{}), - }; - - const local_cache_directory: std.Build.Cache.Directory = .{ - .path = cache_root, - .handle = try std.fs.cwd().makeOpenPath(cache_root, .{}), - }; - - const global_cache_directory: std.Build.Cache.Directory = .{ - .path = global_cache_root, - .handle = try std.fs.cwd().makeOpenPath(global_cache_root, .{}), - }; - - var graph: std.Build.Graph = .{ - .arena = arena, - .cache = .{ - .gpa = arena, - .manifest_dir = try local_cache_directory.handle.makeOpenPath("h", .{}), - }, - .zig_exe = zig_exe, - .env_map = try process.getEnvMap(arena), - .global_cache_root = global_cache_directory, - }; - - graph.cache.addPrefix(.{ .path = null, .handle = std.fs.cwd() }); - graph.cache.addPrefix(build_root_directory); - graph.cache.addPrefix(local_cache_directory); - graph.cache.addPrefix(global_cache_directory); - graph.cache.hash.addBytes(builtin.zig_version_string); - - const builder = try std.Build.create( - &graph, - build_root_directory, - local_cache_directory, - dependencies.root_deps, - ); - - var targets = ArrayList([]const u8).init(arena); - var debug_log_scopes = ArrayList([]const u8).init(arena); - var thread_pool_options: std.Thread.Pool.Options = .{ .allocator = arena }; - - var install_prefix: ?[]const u8 = null; - var dir_list = std.Build.DirList{}; - var summary: ?Summary = null; - var max_rss: u64 = 0; - var skip_oom_steps: bool = false; - var color: Color = .auto; - var seed: u32 = 0; - var prominent_compile_errors: bool = false; - var help_menu: bool = false; - var steps_menu: bool = false; - var output_tmp_nonce: ?[16]u8 = null; - - while (nextArg(args, &arg_idx)) |arg| { - if (mem.startsWith(u8, arg, "-Z")) { - if (arg.len != 18) fatalWithHint("bad argument: '{s}'", .{arg}); - output_tmp_nonce = arg[2..18].*; - } else if (mem.startsWith(u8, arg, "-D")) { - const option_contents = arg[2..]; - if (option_contents.len == 0) - fatalWithHint("expected option name after '-D'", .{}); - if (mem.indexOfScalar(u8, option_contents, '=')) |name_end| { - const option_name = option_contents[0..name_end]; - const option_value = option_contents[name_end + 1 ..]; - if (try builder.addUserInputOption(option_name, option_value)) - fatal(" access the help menu with 'zig build -h'", .{}); - } else { - if (try builder.addUserInputFlag(option_contents)) - fatal(" access the help menu with 'zig build -h'", .{}); - } - } else if (mem.startsWith(u8, arg, "-")) { - if (mem.eql(u8, arg, "--verbose")) { - builder.verbose = true; - } else if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) { - help_menu = true; - } else if (mem.eql(u8, arg, "-p") or mem.eql(u8, arg, "--prefix")) { - install_prefix = nextArgOrFatal(args, &arg_idx); - } else if (mem.eql(u8, arg, "-l") or mem.eql(u8, arg, "--list-steps")) { - steps_menu = true; - } else if (mem.startsWith(u8, arg, "-fsys=")) { - const name = arg["-fsys=".len..]; - graph.system_library_options.put(arena, name, .user_enabled) catch @panic("OOM"); - } else if (mem.startsWith(u8, arg, "-fno-sys=")) { - const name = arg["-fno-sys=".len..]; - graph.system_library_options.put(arena, name, .user_disabled) catch @panic("OOM"); - } else if (mem.eql(u8, arg, "--release")) { - builder.release_mode = .any; - } else if (mem.startsWith(u8, arg, "--release=")) { - const text = arg["--release=".len..]; - builder.release_mode = std.meta.stringToEnum(std.Build.ReleaseMode, text) orelse { - fatalWithHint("expected [off|any|fast|safe|small] in '{s}', found '{s}'", .{ - arg, text, - }); - }; - } else if (mem.eql(u8, arg, "--host-target")) { - graph.host_query_options.arch_os_abi = nextArgOrFatal(args, &arg_idx); - } else if (mem.eql(u8, arg, "--host-cpu")) { - graph.host_query_options.cpu_features = nextArgOrFatal(args, &arg_idx); - } else if (mem.eql(u8, arg, "--host-dynamic-linker")) { - graph.host_query_options.dynamic_linker = nextArgOrFatal(args, &arg_idx); - } else if (mem.eql(u8, arg, "--prefix-lib-dir")) { - dir_list.lib_dir = nextArgOrFatal(args, &arg_idx); - } else if (mem.eql(u8, arg, "--prefix-exe-dir")) { - dir_list.exe_dir = nextArgOrFatal(args, &arg_idx); - } else if (mem.eql(u8, arg, "--prefix-include-dir")) { - dir_list.include_dir = nextArgOrFatal(args, &arg_idx); - } else if (mem.eql(u8, arg, "--sysroot")) { - builder.sysroot = nextArgOrFatal(args, &arg_idx); - } else if (mem.eql(u8, arg, "--maxrss")) { - const max_rss_text = nextArgOrFatal(args, &arg_idx); - max_rss = std.fmt.parseIntSizeSuffix(max_rss_text, 10) catch |err| { - std.debug.print("invalid byte size: '{s}': {s}\n", .{ - max_rss_text, @errorName(err), - }); - process.exit(1); - }; - } else if (mem.eql(u8, arg, "--skip-oom-steps")) { - skip_oom_steps = true; - } else if (mem.eql(u8, arg, "--search-prefix")) { - const search_prefix = nextArgOrFatal(args, &arg_idx); - builder.addSearchPrefix(search_prefix); - } else if (mem.eql(u8, arg, "--libc")) { - builder.libc_file = nextArgOrFatal(args, &arg_idx); - } else if (mem.eql(u8, arg, "--color")) { - const next_arg = nextArg(args, &arg_idx) orelse - fatalWithHint("expected [auto|on|off] after '{s}'", .{arg}); - color = std.meta.stringToEnum(Color, next_arg) orelse { - fatalWithHint("expected [auto|on|off] after '{s}', found '{s}'", .{ - arg, next_arg, - }); - }; - } else if (mem.eql(u8, arg, "--summary")) { - const next_arg = nextArg(args, &arg_idx) orelse - fatalWithHint("expected [all|new|failures|none] after '{s}'", .{arg}); - summary = std.meta.stringToEnum(Summary, next_arg) orelse { - fatalWithHint("expected [all|failures|none] after '{s}', found '{s}'", .{ - arg, next_arg, - }); - }; - } else if (mem.eql(u8, arg, "--zig-lib-dir")) { - builder.zig_lib_dir = .{ .cwd_relative = nextArgOrFatal(args, &arg_idx) }; - } else if (mem.eql(u8, arg, "--seed")) { - const next_arg = nextArg(args, &arg_idx) orelse - fatalWithHint("expected u32 after '{s}'", .{arg}); - seed = std.fmt.parseUnsigned(u32, next_arg, 0) catch |err| { - fatal("unable to parse seed '{s}' as 32-bit integer: {s}\n", .{ - next_arg, @errorName(err), - }); - }; - } else if (mem.eql(u8, arg, "--debug-log")) { - const next_arg = nextArgOrFatal(args, &arg_idx); - try debug_log_scopes.append(next_arg); - } else if (mem.eql(u8, arg, "--debug-pkg-config")) { - builder.debug_pkg_config = true; - } else if (mem.eql(u8, arg, "--debug-compile-errors")) { - builder.debug_compile_errors = true; - } else if (mem.eql(u8, arg, "--system")) { - // The usage text shows another argument after this parameter - // but it is handled by the parent process. The build runner - // only sees this flag. - graph.system_package_mode = true; - } else if (mem.eql(u8, arg, "--glibc-runtimes")) { - builder.glibc_runtimes_dir = nextArgOrFatal(args, &arg_idx); - } else if (mem.eql(u8, arg, "--verbose-link")) { - builder.verbose_link = true; - } else if (mem.eql(u8, arg, "--verbose-air")) { - builder.verbose_air = true; - } else if (mem.eql(u8, arg, "--verbose-llvm-ir")) { - builder.verbose_llvm_ir = "-"; - } else if (mem.startsWith(u8, arg, "--verbose-llvm-ir=")) { - builder.verbose_llvm_ir = arg["--verbose-llvm-ir=".len..]; - } else if (mem.eql(u8, arg, "--verbose-llvm-bc=")) { - builder.verbose_llvm_bc = arg["--verbose-llvm-bc=".len..]; - } else if (mem.eql(u8, arg, "--verbose-cimport")) { - builder.verbose_cimport = true; - } else if (mem.eql(u8, arg, "--verbose-cc")) { - builder.verbose_cc = true; - } else if (mem.eql(u8, arg, "--verbose-llvm-cpu-features")) { - builder.verbose_llvm_cpu_features = true; - } else if (mem.eql(u8, arg, "--prominent-compile-errors")) { - prominent_compile_errors = true; - } else if (mem.eql(u8, arg, "-fwine")) { - builder.enable_wine = true; - } else if (mem.eql(u8, arg, "-fno-wine")) { - builder.enable_wine = false; - } else if (mem.eql(u8, arg, "-fqemu")) { - builder.enable_qemu = true; - } else if (mem.eql(u8, arg, "-fno-qemu")) { - builder.enable_qemu = false; - } else if (mem.eql(u8, arg, "-fwasmtime")) { - builder.enable_wasmtime = true; - } else if (mem.eql(u8, arg, "-fno-wasmtime")) { - builder.enable_wasmtime = false; - } else if (mem.eql(u8, arg, "-frosetta")) { - builder.enable_rosetta = true; - } else if (mem.eql(u8, arg, "-fno-rosetta")) { - builder.enable_rosetta = false; - } else if (mem.eql(u8, arg, "-fdarling")) { - builder.enable_darling = true; - } else if (mem.eql(u8, arg, "-fno-darling")) { - builder.enable_darling = false; - } else if (mem.eql(u8, arg, "-freference-trace")) { - builder.reference_trace = 256; - } else if (mem.startsWith(u8, arg, "-freference-trace=")) { - const num = arg["-freference-trace=".len..]; - builder.reference_trace = std.fmt.parseUnsigned(u32, num, 10) catch |err| { - std.debug.print("unable to parse reference_trace count '{s}': {s}", .{ num, @errorName(err) }); - process.exit(1); - }; - } else if (mem.eql(u8, arg, "-fno-reference-trace")) { - builder.reference_trace = null; - } else if (mem.startsWith(u8, arg, "-j")) { - const num = arg["-j".len..]; - const n_jobs = std.fmt.parseUnsigned(u32, num, 10) catch |err| { - std.debug.print("unable to parse jobs count '{s}': {s}", .{ - num, @errorName(err), - }); - process.exit(1); - }; - if (n_jobs < 1) { - std.debug.print("number of jobs must be at least 1\n", .{}); - process.exit(1); - } - thread_pool_options.n_jobs = n_jobs; - } else if (mem.eql(u8, arg, "--")) { - builder.args = argsRest(args, arg_idx); - break; - } else { - fatalWithHint("unrecognized argument: '{s}'", .{arg}); - } - } else { - try targets.append(arg); - } - } - - const host_query = std.Build.parseTargetQuery(graph.host_query_options) catch |err| switch (err) { - error.ParseFailed => process.exit(1), - }; - builder.host = .{ - .query = .{}, - .result = try std.zig.system.resolveTargetQuery(host_query), - }; - - const stderr = std.io.getStdErr(); - const ttyconf = get_tty_conf(color, stderr); - switch (ttyconf) { - .no_color => try graph.env_map.put("NO_COLOR", "1"), - .escape_codes => try graph.env_map.put("YES_COLOR", "1"), - .windows_api => {}, - } - - var progress: std.Progress = .{ .dont_print_on_dumb = true }; - const main_progress_node = progress.start("", 0); - - builder.debug_log_scopes = debug_log_scopes.items; - builder.resolveInstallPrefix(install_prefix, dir_list); - { - var prog_node = main_progress_node.start("user build.zig logic", 0); - defer prog_node.end(); - try builder.runBuild(root); - } - - if (graph.needed_lazy_dependencies.entries.len != 0) { - var buffer: std.ArrayListUnmanaged(u8) = .{}; - for (graph.needed_lazy_dependencies.keys()) |k| { - try buffer.appendSlice(arena, k); - try buffer.append(arena, '\n'); - } - const s = std.fs.path.sep_str; - const tmp_sub_path = "tmp" ++ s ++ (output_tmp_nonce orelse fatal("missing -Z arg", .{})); - local_cache_directory.handle.writeFile2(.{ - .sub_path = tmp_sub_path, - .data = buffer.items, - .flags = .{ .exclusive = true }, - }) catch |err| { - fatal("unable to write configuration results to '{}{s}': {s}", .{ - local_cache_directory, tmp_sub_path, @errorName(err), - }); - }; - process.exit(3); // Indicate configure phase failed with meaningful stdout. - } - - if (builder.validateUserInputDidItFail()) { - fatal(" access the help menu with 'zig build -h'", .{}); - } - - validateSystemLibraryOptions(builder); - - const stdout_writer = io.getStdOut().writer(); - - if (help_menu) - return usage(builder, stdout_writer); - - if (steps_menu) - return steps(builder, stdout_writer); - - var run: Run = .{ - .max_rss = max_rss, - .max_rss_is_default = false, - .max_rss_mutex = .{}, - .skip_oom_steps = skip_oom_steps, - .memory_blocked_steps = std.ArrayList(*Step).init(arena), - .prominent_compile_errors = prominent_compile_errors, - - .claimed_rss = 0, - .summary = summary, - .ttyconf = ttyconf, - .stderr = stderr, - }; - - if (run.max_rss == 0) { - run.max_rss = process.totalSystemMemory() catch std.math.maxInt(u64); - run.max_rss_is_default = true; - } - - runStepNames( - arena, - builder, - targets.items, - main_progress_node, - thread_pool_options, - &run, - seed, - ) catch |err| switch (err) { - error.UncleanExit => process.exit(1), - else => return err, - }; -} - -const Run = struct { - max_rss: u64, - max_rss_is_default: bool, - max_rss_mutex: std.Thread.Mutex, - skip_oom_steps: bool, - memory_blocked_steps: std.ArrayList(*Step), - prominent_compile_errors: bool, - - claimed_rss: usize, - summary: ?Summary, - ttyconf: std.io.tty.Config, - stderr: File, -}; - -fn runStepNames( - arena: std.mem.Allocator, - b: *std.Build, - step_names: []const []const u8, - parent_prog_node: *std.Progress.Node, - thread_pool_options: std.Thread.Pool.Options, - run: *Run, - seed: u32, -) !void { - const gpa = b.allocator; - var step_stack: std.AutoArrayHashMapUnmanaged(*Step, void) = .{}; - defer step_stack.deinit(gpa); - - if (step_names.len == 0) { - try step_stack.put(gpa, b.default_step, {}); - } else { - try step_stack.ensureUnusedCapacity(gpa, step_names.len); - for (0..step_names.len) |i| { - const step_name = step_names[step_names.len - i - 1]; - const s = b.top_level_steps.get(step_name) orelse { - std.debug.print("no step named '{s}'\n access the help menu with 'zig build -h'\n", .{step_name}); - process.exit(1); - }; - step_stack.putAssumeCapacity(&s.step, {}); - } - } - - const starting_steps = try arena.dupe(*Step, step_stack.keys()); - - var rng = std.Random.DefaultPrng.init(seed); - const rand = rng.random(); - rand.shuffle(*Step, starting_steps); - - for (starting_steps) |s| { - constructGraphAndCheckForDependencyLoop(b, s, &step_stack, rand) catch |err| switch (err) { - error.DependencyLoopDetected => return error.UncleanExit, - else => |e| return e, - }; - } - - { - // Check that we have enough memory to complete the build. - var any_problems = false; - for (step_stack.keys()) |s| { - if (s.max_rss == 0) continue; - if (s.max_rss > run.max_rss) { - if (run.skip_oom_steps) { - s.state = .skipped_oom; - } else { - std.debug.print("{s}{s}: this step declares an upper bound of {d} bytes of memory, exceeding the available {d} bytes of memory\n", .{ - s.owner.dep_prefix, s.name, s.max_rss, run.max_rss, - }); - any_problems = true; - } - } - } - if (any_problems) { - if (run.max_rss_is_default) { - std.debug.print("note: use --maxrss to override the default", .{}); - } - return error.UncleanExit; - } - } - - var thread_pool: std.Thread.Pool = undefined; - try thread_pool.init(thread_pool_options); - defer thread_pool.deinit(); - - { - defer parent_prog_node.end(); - - var step_prog = parent_prog_node.start("steps", step_stack.count()); - defer step_prog.end(); - - var wait_group: std.Thread.WaitGroup = .{}; - defer wait_group.wait(); - - // Here we spawn the initial set of tasks with a nice heuristic - - // dependency order. Each worker when it finishes a step will then - // check whether it should run any dependants. - const steps_slice = step_stack.keys(); - for (0..steps_slice.len) |i| { - const step = steps_slice[steps_slice.len - i - 1]; - if (step.state == .skipped_oom) continue; - - wait_group.start(); - thread_pool.spawn(workerMakeOneStep, .{ - &wait_group, &thread_pool, b, step, &step_prog, run, - }) catch @panic("OOM"); - } - } - assert(run.memory_blocked_steps.items.len == 0); - - var test_skip_count: usize = 0; - var test_fail_count: usize = 0; - var test_pass_count: usize = 0; - var test_leak_count: usize = 0; - var test_count: usize = 0; - - var success_count: usize = 0; - var skipped_count: usize = 0; - var failure_count: usize = 0; - var pending_count: usize = 0; - var total_compile_errors: usize = 0; - var compile_error_steps: std.ArrayListUnmanaged(*Step) = .{}; - defer compile_error_steps.deinit(gpa); - - for (step_stack.keys()) |s| { - test_fail_count += s.test_results.fail_count; - test_skip_count += s.test_results.skip_count; - test_leak_count += s.test_results.leak_count; - test_pass_count += s.test_results.passCount(); - test_count += s.test_results.test_count; - - switch (s.state) { - .precheck_unstarted => unreachable, - .precheck_started => unreachable, - .running => unreachable, - .precheck_done => { - // precheck_done is equivalent to dependency_failure in the case of - // transitive dependencies. For example: - // A -> B -> C (failure) - // B will be marked as dependency_failure, while A may never be queued, and thus - // remain in the initial state of precheck_done. - s.state = .dependency_failure; - pending_count += 1; - }, - .dependency_failure => pending_count += 1, - .success => success_count += 1, - .skipped, .skipped_oom => skipped_count += 1, - .failure => { - failure_count += 1; - const compile_errors_len = s.result_error_bundle.errorMessageCount(); - if (compile_errors_len > 0) { - total_compile_errors += compile_errors_len; - try compile_error_steps.append(gpa, s); - } - }, - } - } - - // A proper command line application defaults to silently succeeding. - // The user may request verbose mode if they have a different preference. - const failures_only = run.summary != .all and run.summary != .new; - if (failure_count == 0 and failures_only) return cleanExit(); - - const ttyconf = run.ttyconf; - const stderr = run.stderr; - - if (run.summary != Summary.none) { - const total_count = success_count + failure_count + pending_count + skipped_count; - ttyconf.setColor(stderr, .cyan) catch {}; - stderr.writeAll("Build Summary:") catch {}; - ttyconf.setColor(stderr, .reset) catch {}; - stderr.writer().print(" {d}/{d} steps succeeded", .{ success_count, total_count }) catch {}; - if (skipped_count > 0) stderr.writer().print("; {d} skipped", .{skipped_count}) catch {}; - if (failure_count > 0) stderr.writer().print("; {d} failed", .{failure_count}) catch {}; - - if (test_count > 0) stderr.writer().print("; {d}/{d} tests passed", .{ test_pass_count, test_count }) catch {}; - if (test_skip_count > 0) stderr.writer().print("; {d} skipped", .{test_skip_count}) catch {}; - if (test_fail_count > 0) stderr.writer().print("; {d} failed", .{test_fail_count}) catch {}; - if (test_leak_count > 0) stderr.writer().print("; {d} leaked", .{test_leak_count}) catch {}; - - if (run.summary == null) { - ttyconf.setColor(stderr, .dim) catch {}; - stderr.writeAll(" (disable with --summary none)") catch {}; - ttyconf.setColor(stderr, .reset) catch {}; - } - stderr.writeAll("\n") catch {}; - - // Print a fancy tree with build results. - var print_node: PrintNode = .{ .parent = null }; - if (step_names.len == 0) { - print_node.last = true; - printTreeStep(b, b.default_step, run, stderr, ttyconf, &print_node, &step_stack) catch {}; - } else { - const last_index = if (run.summary == .all) b.top_level_steps.count() else blk: { - var i: usize = step_names.len; - while (i > 0) { - i -= 1; - const step = b.top_level_steps.get(step_names[i]).?.step; - const found = switch (run.summary orelse .failures) { - .all, .none => unreachable, - .failures => step.state != .success, - .new => !step.result_cached, - }; - if (found) break :blk i; - } - break :blk b.top_level_steps.count(); - }; - for (step_names, 0..) |step_name, i| { - const tls = b.top_level_steps.get(step_name).?; - print_node.last = i + 1 == last_index; - printTreeStep(b, &tls.step, run, stderr, ttyconf, &print_node, &step_stack) catch {}; - } - } - } - - if (failure_count == 0) return cleanExit(); - - // Finally, render compile errors at the bottom of the terminal. - // We use a separate compile_error_steps array list because step_stack is destructively - // mutated in printTreeStep above. - if (run.prominent_compile_errors and total_compile_errors > 0) { - for (compile_error_steps.items) |s| { - if (s.result_error_bundle.errorMessageCount() > 0) { - s.result_error_bundle.renderToStdErr(renderOptions(ttyconf)); - } - } - - // Signal to parent process that we have printed compile errors. The - // parent process may choose to omit the "following command failed" - // line in this case. - process.exit(2); - } - - process.exit(1); -} - -const PrintNode = struct { - parent: ?*PrintNode, - last: bool = false, -}; - -fn printPrefix(node: *PrintNode, stderr: File, ttyconf: std.io.tty.Config) !void { - const parent = node.parent orelse return; - if (parent.parent == null) return; - try printPrefix(parent, stderr, ttyconf); - if (parent.last) { - try stderr.writeAll(" "); - } else { - try stderr.writeAll(switch (ttyconf) { - .no_color, .windows_api => "| ", - .escape_codes => "\x1B\x28\x30\x78\x1B\x28\x42 ", // │ - }); - } -} - -fn printChildNodePrefix(stderr: File, ttyconf: std.io.tty.Config) !void { - try stderr.writeAll(switch (ttyconf) { - .no_color, .windows_api => "+- ", - .escape_codes => "\x1B\x28\x30\x6d\x71\x1B\x28\x42 ", // └─ - }); -} - -fn printStepStatus( - s: *Step, - stderr: File, - ttyconf: std.io.tty.Config, - run: *const Run, -) !void { - switch (s.state) { - .precheck_unstarted => unreachable, - .precheck_started => unreachable, - .precheck_done => unreachable, - .running => unreachable, - - .dependency_failure => { - try ttyconf.setColor(stderr, .dim); - try stderr.writeAll(" transitive failure\n"); - try ttyconf.setColor(stderr, .reset); - }, - - .success => { - try ttyconf.setColor(stderr, .green); - if (s.result_cached) { - try stderr.writeAll(" cached"); - } else if (s.test_results.test_count > 0) { - const pass_count = s.test_results.passCount(); - try stderr.writer().print(" {d} passed", .{pass_count}); - if (s.test_results.skip_count > 0) { - try ttyconf.setColor(stderr, .yellow); - try stderr.writer().print(" {d} skipped", .{s.test_results.skip_count}); - } - } else { - try stderr.writeAll(" success"); - } - try ttyconf.setColor(stderr, .reset); - if (s.result_duration_ns) |ns| { - try ttyconf.setColor(stderr, .dim); - if (ns >= std.time.ns_per_min) { - try stderr.writer().print(" {d}m", .{ns / std.time.ns_per_min}); - } else if (ns >= std.time.ns_per_s) { - try stderr.writer().print(" {d}s", .{ns / std.time.ns_per_s}); - } else if (ns >= std.time.ns_per_ms) { - try stderr.writer().print(" {d}ms", .{ns / std.time.ns_per_ms}); - } else if (ns >= std.time.ns_per_us) { - try stderr.writer().print(" {d}us", .{ns / std.time.ns_per_us}); - } else { - try stderr.writer().print(" {d}ns", .{ns}); - } - try ttyconf.setColor(stderr, .reset); - } - if (s.result_peak_rss != 0) { - const rss = s.result_peak_rss; - try ttyconf.setColor(stderr, .dim); - if (rss >= 1000_000_000) { - try stderr.writer().print(" MaxRSS:{d}G", .{rss / 1000_000_000}); - } else if (rss >= 1000_000) { - try stderr.writer().print(" MaxRSS:{d}M", .{rss / 1000_000}); - } else if (rss >= 1000) { - try stderr.writer().print(" MaxRSS:{d}K", .{rss / 1000}); - } else { - try stderr.writer().print(" MaxRSS:{d}B", .{rss}); - } - try ttyconf.setColor(stderr, .reset); - } - try stderr.writeAll("\n"); - }, - .skipped, .skipped_oom => |skip| { - try ttyconf.setColor(stderr, .yellow); - try stderr.writeAll(" skipped"); - if (skip == .skipped_oom) { - try stderr.writeAll(" (not enough memory)"); - try ttyconf.setColor(stderr, .dim); - try stderr.writer().print(" upper bound of {d} exceeded runner limit ({d})", .{ s.max_rss, run.max_rss }); - try ttyconf.setColor(stderr, .yellow); - } - try stderr.writeAll("\n"); - try ttyconf.setColor(stderr, .reset); - }, - .failure => try printStepFailure(s, stderr, ttyconf), - } -} - -fn printStepFailure( - s: *Step, - stderr: File, - ttyconf: std.io.tty.Config, -) !void { - if (s.result_error_bundle.errorMessageCount() > 0) { - try ttyconf.setColor(stderr, .red); - try stderr.writer().print(" {d} errors\n", .{ - s.result_error_bundle.errorMessageCount(), - }); - try ttyconf.setColor(stderr, .reset); - } else if (!s.test_results.isSuccess()) { - try stderr.writer().print(" {d}/{d} passed", .{ - s.test_results.passCount(), s.test_results.test_count, - }); - if (s.test_results.fail_count > 0) { - try stderr.writeAll(", "); - try ttyconf.setColor(stderr, .red); - try stderr.writer().print("{d} failed", .{ - s.test_results.fail_count, - }); - try ttyconf.setColor(stderr, .reset); - } - if (s.test_results.skip_count > 0) { - try stderr.writeAll(", "); - try ttyconf.setColor(stderr, .yellow); - try stderr.writer().print("{d} skipped", .{ - s.test_results.skip_count, - }); - try ttyconf.setColor(stderr, .reset); - } - if (s.test_results.leak_count > 0) { - try stderr.writeAll(", "); - try ttyconf.setColor(stderr, .red); - try stderr.writer().print("{d} leaked", .{ - s.test_results.leak_count, - }); - try ttyconf.setColor(stderr, .reset); - } - try stderr.writeAll("\n"); - } else if (s.result_error_msgs.items.len > 0) { - try ttyconf.setColor(stderr, .red); - try stderr.writeAll(" failure\n"); - try ttyconf.setColor(stderr, .reset); - } else { - assert(s.result_stderr.len > 0); - try ttyconf.setColor(stderr, .red); - try stderr.writeAll(" stderr\n"); - try ttyconf.setColor(stderr, .reset); - } -} - -fn printTreeStep( - b: *std.Build, - s: *Step, - run: *const Run, - stderr: File, - ttyconf: std.io.tty.Config, - parent_node: *PrintNode, - step_stack: *std.AutoArrayHashMapUnmanaged(*Step, void), -) !void { - const first = step_stack.swapRemove(s); - const summary = run.summary orelse .failures; - const skip = switch (summary) { - .none => unreachable, - .all => false, - .new => s.result_cached, - .failures => s.state == .success, - }; - if (skip) return; - try printPrefix(parent_node, stderr, ttyconf); - - if (!first) try ttyconf.setColor(stderr, .dim); - if (parent_node.parent != null) { - if (parent_node.last) { - try printChildNodePrefix(stderr, ttyconf); - } else { - try stderr.writeAll(switch (ttyconf) { - .no_color, .windows_api => "+- ", - .escape_codes => "\x1B\x28\x30\x74\x71\x1B\x28\x42 ", // ├─ - }); - } - } - - // dep_prefix omitted here because it is redundant with the tree. - try stderr.writeAll(s.name); - - if (first) { - try printStepStatus(s, stderr, ttyconf, run); - - const last_index = if (summary == .all) s.dependencies.items.len -| 1 else blk: { - var i: usize = s.dependencies.items.len; - while (i > 0) { - i -= 1; - - const step = s.dependencies.items[i]; - const found = switch (summary) { - .all, .none => unreachable, - .failures => step.state != .success, - .new => !step.result_cached, - }; - if (found) break :blk i; - } - break :blk s.dependencies.items.len -| 1; - }; - for (s.dependencies.items, 0..) |dep, i| { - var print_node: PrintNode = .{ - .parent = parent_node, - .last = i == last_index, - }; - try printTreeStep(b, dep, run, stderr, ttyconf, &print_node, step_stack); - } - } else { - if (s.dependencies.items.len == 0) { - try stderr.writeAll(" (reused)\n"); - } else { - try stderr.writer().print(" (+{d} more reused dependencies)\n", .{ - s.dependencies.items.len, - }); - } - try ttyconf.setColor(stderr, .reset); - } -} - -/// Traverse the dependency graph depth-first and make it undirected by having -/// steps know their dependants (they only know dependencies at start). -/// Along the way, check that there is no dependency loop, and record the steps -/// in traversal order in `step_stack`. -/// Each step has its dependencies traversed in random order, this accomplishes -/// two things: -/// - `step_stack` will be in randomized-depth-first order, so the build runner -/// spawns steps in a random (but optimized) order -/// - each step's `dependants` list is also filled in a random order, so that -/// when it finishes executing in `workerMakeOneStep`, it spawns next steps -/// to run in random order -fn constructGraphAndCheckForDependencyLoop( - b: *std.Build, - s: *Step, - step_stack: *std.AutoArrayHashMapUnmanaged(*Step, void), - rand: std.Random, -) !void { - switch (s.state) { - .precheck_started => { - std.debug.print("dependency loop detected:\n {s}\n", .{s.name}); - return error.DependencyLoopDetected; - }, - .precheck_unstarted => { - s.state = .precheck_started; - - try step_stack.ensureUnusedCapacity(b.allocator, s.dependencies.items.len); - - // We dupe to avoid shuffling the steps in the summary, it depends - // on s.dependencies' order. - const deps = b.allocator.dupe(*Step, s.dependencies.items) catch @panic("OOM"); - rand.shuffle(*Step, deps); - - for (deps) |dep| { - try step_stack.put(b.allocator, dep, {}); - try dep.dependants.append(b.allocator, s); - constructGraphAndCheckForDependencyLoop(b, dep, step_stack, rand) catch |err| { - if (err == error.DependencyLoopDetected) { - std.debug.print(" {s}\n", .{s.name}); - } - return err; - }; - } - - s.state = .precheck_done; - }, - .precheck_done => {}, - - // These don't happen until we actually run the step graph. - .dependency_failure => unreachable, - .running => unreachable, - .success => unreachable, - .failure => unreachable, - .skipped => unreachable, - .skipped_oom => unreachable, - } -} - -fn workerMakeOneStep( - wg: *std.Thread.WaitGroup, - thread_pool: *std.Thread.Pool, - b: *std.Build, - s: *Step, - prog_node: *std.Progress.Node, - run: *Run, -) void { - defer wg.finish(); - - // First, check the conditions for running this step. If they are not met, - // then we return without doing the step, relying on another worker to - // queue this step up again when dependencies are met. - for (s.dependencies.items) |dep| { - switch (@atomicLoad(Step.State, &dep.state, .seq_cst)) { - .success, .skipped => continue, - .failure, .dependency_failure, .skipped_oom => { - @atomicStore(Step.State, &s.state, .dependency_failure, .seq_cst); - return; - }, - .precheck_done, .running => { - // dependency is not finished yet. - return; - }, - .precheck_unstarted => unreachable, - .precheck_started => unreachable, - } - } - - if (s.max_rss != 0) { - run.max_rss_mutex.lock(); - defer run.max_rss_mutex.unlock(); - - // Avoid running steps twice. - if (s.state != .precheck_done) { - // Another worker got the job. - return; - } - - const new_claimed_rss = run.claimed_rss + s.max_rss; - if (new_claimed_rss > run.max_rss) { - // Running this step right now could possibly exceed the allotted RSS. - // Add this step to the queue of memory-blocked steps. - run.memory_blocked_steps.append(s) catch @panic("OOM"); - return; - } - - run.claimed_rss = new_claimed_rss; - s.state = .running; - } else { - // Avoid running steps twice. - if (@cmpxchgStrong(Step.State, &s.state, .precheck_done, .running, .seq_cst, .seq_cst) != null) { - // Another worker got the job. - return; - } - } - - var sub_prog_node = prog_node.start(s.name, 0); - sub_prog_node.activate(); - defer sub_prog_node.end(); - - const make_result = s.make(&sub_prog_node); - - // No matter the result, we want to display error/warning messages. - const show_compile_errors = !run.prominent_compile_errors and - s.result_error_bundle.errorMessageCount() > 0; - const show_error_msgs = s.result_error_msgs.items.len > 0; - const show_stderr = s.result_stderr.len > 0; - - if (show_error_msgs or show_compile_errors or show_stderr) { - sub_prog_node.context.lock_stderr(); - defer sub_prog_node.context.unlock_stderr(); - - printErrorMessages(b, s, run) catch {}; - } - - handle_result: { - if (make_result) |_| { - @atomicStore(Step.State, &s.state, .success, .seq_cst); - } else |err| switch (err) { - error.MakeFailed => { - @atomicStore(Step.State, &s.state, .failure, .seq_cst); - break :handle_result; - }, - error.MakeSkipped => @atomicStore(Step.State, &s.state, .skipped, .seq_cst), - } - - // Successful completion of a step, so we queue up its dependants as well. - for (s.dependants.items) |dep| { - wg.start(); - thread_pool.spawn(workerMakeOneStep, .{ - wg, thread_pool, b, dep, prog_node, run, - }) catch @panic("OOM"); - } - } - - // If this is a step that claims resources, we must now queue up other - // steps that are waiting for resources. - if (s.max_rss != 0) { - run.max_rss_mutex.lock(); - defer run.max_rss_mutex.unlock(); - - // Give the memory back to the scheduler. - run.claimed_rss -= s.max_rss; - // Avoid kicking off too many tasks that we already know will not have - // enough resources. - var remaining = run.max_rss - run.claimed_rss; - var i: usize = 0; - var j: usize = 0; - while (j < run.memory_blocked_steps.items.len) : (j += 1) { - const dep = run.memory_blocked_steps.items[j]; - assert(dep.max_rss != 0); - if (dep.max_rss <= remaining) { - remaining -= dep.max_rss; - - wg.start(); - thread_pool.spawn(workerMakeOneStep, .{ - wg, thread_pool, b, dep, prog_node, run, - }) catch @panic("OOM"); - } else { - run.memory_blocked_steps.items[i] = dep; - i += 1; - } - } - run.memory_blocked_steps.shrinkRetainingCapacity(i); - } -} - -fn printErrorMessages(b: *std.Build, failing_step: *Step, run: *const Run) !void { - const gpa = b.allocator; - const stderr = run.stderr; - const ttyconf = run.ttyconf; - - // Provide context for where these error messages are coming from by - // printing the corresponding Step subtree. - - var step_stack: std.ArrayListUnmanaged(*Step) = .{}; - defer step_stack.deinit(gpa); - try step_stack.append(gpa, failing_step); - while (step_stack.items[step_stack.items.len - 1].dependants.items.len != 0) { - try step_stack.append(gpa, step_stack.items[step_stack.items.len - 1].dependants.items[0]); - } - - // Now, `step_stack` has the subtree that we want to print, in reverse order. - try ttyconf.setColor(stderr, .dim); - var indent: usize = 0; - while (step_stack.popOrNull()) |s| : (indent += 1) { - if (indent > 0) { - try stderr.writer().writeByteNTimes(' ', (indent - 1) * 3); - try printChildNodePrefix(stderr, ttyconf); - } - - try stderr.writeAll(s.name); - - if (s == failing_step) { - try printStepFailure(s, stderr, ttyconf); - } else { - try stderr.writeAll("\n"); - } - } - try ttyconf.setColor(stderr, .reset); - - if (failing_step.result_stderr.len > 0) { - try stderr.writeAll(failing_step.result_stderr); - if (!mem.endsWith(u8, failing_step.result_stderr, "\n")) { - try stderr.writeAll("\n"); - } - } - - if (!run.prominent_compile_errors and failing_step.result_error_bundle.errorMessageCount() > 0) - try failing_step.result_error_bundle.renderToWriter(renderOptions(ttyconf), stderr.writer()); - - for (failing_step.result_error_msgs.items) |msg| { - try ttyconf.setColor(stderr, .red); - try stderr.writeAll("error: "); - try ttyconf.setColor(stderr, .reset); - try stderr.writeAll(msg); - try stderr.writeAll("\n"); - } -} - -fn steps(builder: *std.Build, out_stream: anytype) !void { - const allocator = builder.allocator; - for (builder.top_level_steps.values()) |top_level_step| { - const name = if (&top_level_step.step == builder.default_step) - try fmt.allocPrint(allocator, "{s} (default)", .{top_level_step.step.name}) - else - top_level_step.step.name; - try out_stream.print(" {s:<28} {s}\n", .{ name, top_level_step.description }); - } -} - -fn usage(b: *std.Build, out_stream: anytype) !void { - try out_stream.print( - \\Usage: {s} build [steps] [options] - \\ - \\Steps: - \\ - , .{b.graph.zig_exe}); - try steps(b, out_stream); - - try out_stream.writeAll( - \\ - \\General Options: - \\ -p, --prefix [path] Where to install files (default: zig-out) - \\ --prefix-lib-dir [path] Where to install libraries - \\ --prefix-exe-dir [path] Where to install executables - \\ --prefix-include-dir [path] Where to install C header files - \\ - \\ --release[=mode] Request release mode, optionally specifying a - \\ preferred optimization mode: fast, safe, small - \\ - \\ -fdarling, -fno-darling Integration with system-installed Darling to - \\ execute macOS programs on Linux hosts - \\ (default: no) - \\ -fqemu, -fno-qemu Integration with system-installed QEMU to execute - \\ foreign-architecture programs on Linux hosts - \\ (default: no) - \\ --glibc-runtimes [path] Enhances QEMU integration by providing glibc built - \\ for multiple foreign architectures, allowing - \\ execution of non-native programs that link with glibc. - \\ -frosetta, -fno-rosetta Rely on Rosetta to execute x86_64 programs on - \\ ARM64 macOS hosts. (default: no) - \\ -fwasmtime, -fno-wasmtime Integration with system-installed wasmtime to - \\ execute WASI binaries. (default: no) - \\ -fwine, -fno-wine Integration with system-installed Wine to execute - \\ Windows programs on Linux hosts. (default: no) - \\ - \\ -h, --help Print this help and exit - \\ -l, --list-steps Print available steps - \\ --verbose Print commands before executing them - \\ --color [auto|off|on] Enable or disable colored error messages - \\ --prominent-compile-errors Buffer compile errors and display at end - \\ --summary [mode] Control the printing of the build summary - \\ all Print the build summary in its entirety - \\ new Omit cached steps - \\ failures (Default) Only print failed steps - \\ none Do not print the build summary - \\ -j Limit concurrent jobs (default is to use all CPU cores) - \\ --maxrss Limit memory usage (default is to use available memory) - \\ --skip-oom-steps Instead of failing, skip steps that would exceed --maxrss - \\ --fetch Exit after fetching dependency tree - \\ - \\Project-Specific Options: - \\ - ); - - const arena = b.allocator; - if (b.available_options_list.items.len == 0) { - try out_stream.print(" (none)\n", .{}); - } else { - for (b.available_options_list.items) |option| { - const name = try fmt.allocPrint(arena, " -D{s}=[{s}]", .{ - option.name, - @tagName(option.type_id), - }); - try out_stream.print("{s:<30} {s}\n", .{ name, option.description }); - if (option.enum_options) |enum_options| { - const padding = " " ** 33; - try out_stream.writeAll(padding ++ "Supported Values:\n"); - for (enum_options) |enum_option| { - try out_stream.print(padding ++ " {s}\n", .{enum_option}); - } - } - } - } - - try out_stream.writeAll( - \\ - \\System Integration Options: - \\ --search-prefix [path] Add a path to look for binaries, libraries, headers - \\ --sysroot [path] Set the system root directory (usually /) - \\ --libc [file] Provide a file which specifies libc paths - \\ - \\ --host-target [triple] Use the provided target as the host - \\ --host-cpu [cpu] Use the provided CPU as the host - \\ --host-dynamic-linker [path] Use the provided dynamic linker as the host - \\ - \\ --system [pkgdir] Disable package fetching; enable all integrations - \\ -fsys=[name] Enable a system integration - \\ -fno-sys=[name] Disable a system integration - \\ - \\ Available System Integrations: Enabled: - \\ - ); - if (b.graph.system_library_options.entries.len == 0) { - try out_stream.writeAll(" (none) -\n"); - } else { - for (b.graph.system_library_options.keys(), b.graph.system_library_options.values()) |k, v| { - const status = switch (v) { - .declared_enabled => "yes", - .declared_disabled => "no", - .user_enabled, .user_disabled => unreachable, // already emitted error - }; - try out_stream.print(" {s:<43} {s}\n", .{ k, status }); - } - } - - try out_stream.writeAll( - \\ - \\Advanced Options: - \\ -freference-trace[=num] How many lines of reference trace should be shown per compile error - \\ -fno-reference-trace Disable reference trace - \\ --build-file [file] Override path to build.zig - \\ --cache-dir [path] Override path to local Zig cache directory - \\ --global-cache-dir [path] Override path to global Zig cache directory - \\ --zig-lib-dir [arg] Override path to Zig lib directory - \\ --build-runner [file] Override path to build runner - \\ --seed [integer] For shuffling dependency traversal order (default: random) - \\ --debug-log [scope] Enable debugging the compiler - \\ --debug-pkg-config Fail if unknown pkg-config flags encountered - \\ --verbose-link Enable compiler debug output for linking - \\ --verbose-air Enable compiler debug output for Zig AIR - \\ --verbose-llvm-ir[=file] Enable compiler debug output for LLVM IR - \\ --verbose-llvm-bc=[file] Enable compiler debug output for LLVM BC - \\ --verbose-cimport Enable compiler debug output for C imports - \\ --verbose-cc Enable compiler debug output for C compilation - \\ --verbose-llvm-cpu-features Enable compiler debug output for LLVM CPU features - \\ - ); -} - -fn nextArg(args: [][:0]const u8, idx: *usize) ?[:0]const u8 { - if (idx.* >= args.len) return null; - defer idx.* += 1; - return args[idx.*]; -} - -fn nextArgOrFatal(args: [][:0]const u8, idx: *usize) [:0]const u8 { - return nextArg(args, idx) orelse { - std.debug.print("expected argument after '{s}'\n access the help menu with 'zig build -h'\n", .{args[idx.*]}); - process.exit(1); - }; -} - -fn argsRest(args: [][:0]const u8, idx: usize) ?[][:0]const u8 { - if (idx >= args.len) return null; - return args[idx..]; -} - -fn cleanExit() void { - // Perhaps in the future there could be an Advanced Options flag such as - // --debug-build-runner-leaks which would make this function return instead - // of calling exit. - process.exit(0); -} - -const Color = enum { auto, off, on }; -const Summary = enum { all, new, failures, none }; - -fn get_tty_conf(color: Color, stderr: File) std.io.tty.Config { - return switch (color) { - .auto => std.io.tty.detectConfig(stderr), - .on => .escape_codes, - .off => .no_color, - }; -} - -fn renderOptions(ttyconf: std.io.tty.Config) std.zig.ErrorBundle.RenderOptions { - return .{ - .ttyconf = ttyconf, - .include_source_line = ttyconf != .no_color, - .include_reference_trace = ttyconf != .no_color, - }; -} - -fn fatalWithHint(comptime f: []const u8, args: anytype) noreturn { - std.debug.print(f ++ "\n access the help menu with 'zig build -h'\n", args); - process.exit(1); -} - -fn fatal(comptime f: []const u8, args: anytype) noreturn { - std.debug.print(f ++ "\n", args); - process.exit(1); -} - -fn validateSystemLibraryOptions(b: *std.Build) void { - var bad = false; - for (b.graph.system_library_options.keys(), b.graph.system_library_options.values()) |k, v| { - switch (v) { - .user_disabled, .user_enabled => { - // The user tried to enable or disable a system library integration, but - // the build script did not recognize that option. - std.debug.print("system library name not recognized by build script: '{s}'\n", .{k}); - bad = true; - }, - .declared_disabled, .declared_enabled => {}, - } - } - if (bad) { - std.debug.print(" access the help menu with 'zig build -h'\n", .{}); - process.exit(1); - } -} diff --git a/lib/compiler/build_runner.zig b/lib/compiler/build_runner.zig new file mode 100644 index 0000000000000000000000000000000000000000..f19713776916b94baada339ef967edd0a9aa303a --- /dev/null +++ b/lib/compiler/build_runner.zig @@ -0,0 +1,1293 @@ +const root = @import("@build"); +const std = @import("std"); +const builtin = @import("builtin"); +const assert = std.debug.assert; +const io = std.io; +const fmt = std.fmt; +const mem = std.mem; +const process = std.process; +const ArrayList = std.ArrayList; +const File = std.fs.File; +const Step = std.Build.Step; + +pub const dependencies = @import("@dependencies"); + +pub fn main() !void { + // Here we use an ArenaAllocator backed by a page allocator because a build is a short-lived, + // one shot program. We don't need to waste time freeing memory and finding places to squish + // bytes into. So we free everything all at once at the very end. + var single_threaded_arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer single_threaded_arena.deinit(); + + var thread_safe_arena: std.heap.ThreadSafeAllocator = .{ + .child_allocator = single_threaded_arena.allocator(), + }; + const arena = thread_safe_arena.allocator(); + + const args = try process.argsAlloc(arena); + + // skip my own exe name + var arg_idx: usize = 1; + + const zig_exe = nextArg(args, &arg_idx) orelse { + std.debug.print("Expected path to zig compiler\n", .{}); + return error.InvalidArgs; + }; + const build_root = nextArg(args, &arg_idx) orelse { + std.debug.print("Expected build root directory path\n", .{}); + return error.InvalidArgs; + }; + const cache_root = nextArg(args, &arg_idx) orelse { + std.debug.print("Expected cache root directory path\n", .{}); + return error.InvalidArgs; + }; + const global_cache_root = nextArg(args, &arg_idx) orelse { + std.debug.print("Expected global cache root directory path\n", .{}); + return error.InvalidArgs; + }; + + const build_root_directory: std.Build.Cache.Directory = .{ + .path = build_root, + .handle = try std.fs.cwd().openDir(build_root, .{}), + }; + + const local_cache_directory: std.Build.Cache.Directory = .{ + .path = cache_root, + .handle = try std.fs.cwd().makeOpenPath(cache_root, .{}), + }; + + const global_cache_directory: std.Build.Cache.Directory = .{ + .path = global_cache_root, + .handle = try std.fs.cwd().makeOpenPath(global_cache_root, .{}), + }; + + var graph: std.Build.Graph = .{ + .arena = arena, + .cache = .{ + .gpa = arena, + .manifest_dir = try local_cache_directory.handle.makeOpenPath("h", .{}), + }, + .zig_exe = zig_exe, + .env_map = try process.getEnvMap(arena), + .global_cache_root = global_cache_directory, + }; + + graph.cache.addPrefix(.{ .path = null, .handle = std.fs.cwd() }); + graph.cache.addPrefix(build_root_directory); + graph.cache.addPrefix(local_cache_directory); + graph.cache.addPrefix(global_cache_directory); + graph.cache.hash.addBytes(builtin.zig_version_string); + + const builder = try std.Build.create( + &graph, + build_root_directory, + local_cache_directory, + dependencies.root_deps, + ); + + var targets = ArrayList([]const u8).init(arena); + var debug_log_scopes = ArrayList([]const u8).init(arena); + var thread_pool_options: std.Thread.Pool.Options = .{ .allocator = arena }; + + var install_prefix: ?[]const u8 = null; + var dir_list = std.Build.DirList{}; + var summary: ?Summary = null; + var max_rss: u64 = 0; + var skip_oom_steps: bool = false; + var color: Color = .auto; + var seed: u32 = 0; + var prominent_compile_errors: bool = false; + var help_menu: bool = false; + var steps_menu: bool = false; + var output_tmp_nonce: ?[16]u8 = null; + + while (nextArg(args, &arg_idx)) |arg| { + if (mem.startsWith(u8, arg, "-Z")) { + if (arg.len != 18) fatalWithHint("bad argument: '{s}'", .{arg}); + output_tmp_nonce = arg[2..18].*; + } else if (mem.startsWith(u8, arg, "-D")) { + const option_contents = arg[2..]; + if (option_contents.len == 0) + fatalWithHint("expected option name after '-D'", .{}); + if (mem.indexOfScalar(u8, option_contents, '=')) |name_end| { + const option_name = option_contents[0..name_end]; + const option_value = option_contents[name_end + 1 ..]; + if (try builder.addUserInputOption(option_name, option_value)) + fatal(" access the help menu with 'zig build -h'", .{}); + } else { + if (try builder.addUserInputFlag(option_contents)) + fatal(" access the help menu with 'zig build -h'", .{}); + } + } else if (mem.startsWith(u8, arg, "-")) { + if (mem.eql(u8, arg, "--verbose")) { + builder.verbose = true; + } else if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) { + help_menu = true; + } else if (mem.eql(u8, arg, "-p") or mem.eql(u8, arg, "--prefix")) { + install_prefix = nextArgOrFatal(args, &arg_idx); + } else if (mem.eql(u8, arg, "-l") or mem.eql(u8, arg, "--list-steps")) { + steps_menu = true; + } else if (mem.startsWith(u8, arg, "-fsys=")) { + const name = arg["-fsys=".len..]; + graph.system_library_options.put(arena, name, .user_enabled) catch @panic("OOM"); + } else if (mem.startsWith(u8, arg, "-fno-sys=")) { + const name = arg["-fno-sys=".len..]; + graph.system_library_options.put(arena, name, .user_disabled) catch @panic("OOM"); + } else if (mem.eql(u8, arg, "--release")) { + builder.release_mode = .any; + } else if (mem.startsWith(u8, arg, "--release=")) { + const text = arg["--release=".len..]; + builder.release_mode = std.meta.stringToEnum(std.Build.ReleaseMode, text) orelse { + fatalWithHint("expected [off|any|fast|safe|small] in '{s}', found '{s}'", .{ + arg, text, + }); + }; + } else if (mem.eql(u8, arg, "--host-target")) { + graph.host_query_options.arch_os_abi = nextArgOrFatal(args, &arg_idx); + } else if (mem.eql(u8, arg, "--host-cpu")) { + graph.host_query_options.cpu_features = nextArgOrFatal(args, &arg_idx); + } else if (mem.eql(u8, arg, "--host-dynamic-linker")) { + graph.host_query_options.dynamic_linker = nextArgOrFatal(args, &arg_idx); + } else if (mem.eql(u8, arg, "--prefix-lib-dir")) { + dir_list.lib_dir = nextArgOrFatal(args, &arg_idx); + } else if (mem.eql(u8, arg, "--prefix-exe-dir")) { + dir_list.exe_dir = nextArgOrFatal(args, &arg_idx); + } else if (mem.eql(u8, arg, "--prefix-include-dir")) { + dir_list.include_dir = nextArgOrFatal(args, &arg_idx); + } else if (mem.eql(u8, arg, "--sysroot")) { + builder.sysroot = nextArgOrFatal(args, &arg_idx); + } else if (mem.eql(u8, arg, "--maxrss")) { + const max_rss_text = nextArgOrFatal(args, &arg_idx); + max_rss = std.fmt.parseIntSizeSuffix(max_rss_text, 10) catch |err| { + std.debug.print("invalid byte size: '{s}': {s}\n", .{ + max_rss_text, @errorName(err), + }); + process.exit(1); + }; + } else if (mem.eql(u8, arg, "--skip-oom-steps")) { + skip_oom_steps = true; + } else if (mem.eql(u8, arg, "--search-prefix")) { + const search_prefix = nextArgOrFatal(args, &arg_idx); + builder.addSearchPrefix(search_prefix); + } else if (mem.eql(u8, arg, "--libc")) { + builder.libc_file = nextArgOrFatal(args, &arg_idx); + } else if (mem.eql(u8, arg, "--color")) { + const next_arg = nextArg(args, &arg_idx) orelse + fatalWithHint("expected [auto|on|off] after '{s}'", .{arg}); + color = std.meta.stringToEnum(Color, next_arg) orelse { + fatalWithHint("expected [auto|on|off] after '{s}', found '{s}'", .{ + arg, next_arg, + }); + }; + } else if (mem.eql(u8, arg, "--summary")) { + const next_arg = nextArg(args, &arg_idx) orelse + fatalWithHint("expected [all|new|failures|none] after '{s}'", .{arg}); + summary = std.meta.stringToEnum(Summary, next_arg) orelse { + fatalWithHint("expected [all|failures|none] after '{s}', found '{s}'", .{ + arg, next_arg, + }); + }; + } else if (mem.eql(u8, arg, "--zig-lib-dir")) { + builder.zig_lib_dir = .{ .cwd_relative = nextArgOrFatal(args, &arg_idx) }; + } else if (mem.eql(u8, arg, "--seed")) { + const next_arg = nextArg(args, &arg_idx) orelse + fatalWithHint("expected u32 after '{s}'", .{arg}); + seed = std.fmt.parseUnsigned(u32, next_arg, 0) catch |err| { + fatal("unable to parse seed '{s}' as 32-bit integer: {s}\n", .{ + next_arg, @errorName(err), + }); + }; + } else if (mem.eql(u8, arg, "--debug-log")) { + const next_arg = nextArgOrFatal(args, &arg_idx); + try debug_log_scopes.append(next_arg); + } else if (mem.eql(u8, arg, "--debug-pkg-config")) { + builder.debug_pkg_config = true; + } else if (mem.eql(u8, arg, "--debug-compile-errors")) { + builder.debug_compile_errors = true; + } else if (mem.eql(u8, arg, "--system")) { + // The usage text shows another argument after this parameter + // but it is handled by the parent process. The build runner + // only sees this flag. + graph.system_package_mode = true; + } else if (mem.eql(u8, arg, "--glibc-runtimes")) { + builder.glibc_runtimes_dir = nextArgOrFatal(args, &arg_idx); + } else if (mem.eql(u8, arg, "--verbose-link")) { + builder.verbose_link = true; + } else if (mem.eql(u8, arg, "--verbose-air")) { + builder.verbose_air = true; + } else if (mem.eql(u8, arg, "--verbose-llvm-ir")) { + builder.verbose_llvm_ir = "-"; + } else if (mem.startsWith(u8, arg, "--verbose-llvm-ir=")) { + builder.verbose_llvm_ir = arg["--verbose-llvm-ir=".len..]; + } else if (mem.eql(u8, arg, "--verbose-llvm-bc=")) { + builder.verbose_llvm_bc = arg["--verbose-llvm-bc=".len..]; + } else if (mem.eql(u8, arg, "--verbose-cimport")) { + builder.verbose_cimport = true; + } else if (mem.eql(u8, arg, "--verbose-cc")) { + builder.verbose_cc = true; + } else if (mem.eql(u8, arg, "--verbose-llvm-cpu-features")) { + builder.verbose_llvm_cpu_features = true; + } else if (mem.eql(u8, arg, "--prominent-compile-errors")) { + prominent_compile_errors = true; + } else if (mem.eql(u8, arg, "-fwine")) { + builder.enable_wine = true; + } else if (mem.eql(u8, arg, "-fno-wine")) { + builder.enable_wine = false; + } else if (mem.eql(u8, arg, "-fqemu")) { + builder.enable_qemu = true; + } else if (mem.eql(u8, arg, "-fno-qemu")) { + builder.enable_qemu = false; + } else if (mem.eql(u8, arg, "-fwasmtime")) { + builder.enable_wasmtime = true; + } else if (mem.eql(u8, arg, "-fno-wasmtime")) { + builder.enable_wasmtime = false; + } else if (mem.eql(u8, arg, "-frosetta")) { + builder.enable_rosetta = true; + } else if (mem.eql(u8, arg, "-fno-rosetta")) { + builder.enable_rosetta = false; + } else if (mem.eql(u8, arg, "-fdarling")) { + builder.enable_darling = true; + } else if (mem.eql(u8, arg, "-fno-darling")) { + builder.enable_darling = false; + } else if (mem.eql(u8, arg, "-freference-trace")) { + builder.reference_trace = 256; + } else if (mem.startsWith(u8, arg, "-freference-trace=")) { + const num = arg["-freference-trace=".len..]; + builder.reference_trace = std.fmt.parseUnsigned(u32, num, 10) catch |err| { + std.debug.print("unable to parse reference_trace count '{s}': {s}", .{ num, @errorName(err) }); + process.exit(1); + }; + } else if (mem.eql(u8, arg, "-fno-reference-trace")) { + builder.reference_trace = null; + } else if (mem.startsWith(u8, arg, "-j")) { + const num = arg["-j".len..]; + const n_jobs = std.fmt.parseUnsigned(u32, num, 10) catch |err| { + std.debug.print("unable to parse jobs count '{s}': {s}", .{ + num, @errorName(err), + }); + process.exit(1); + }; + if (n_jobs < 1) { + std.debug.print("number of jobs must be at least 1\n", .{}); + process.exit(1); + } + thread_pool_options.n_jobs = n_jobs; + } else if (mem.eql(u8, arg, "--")) { + builder.args = argsRest(args, arg_idx); + break; + } else { + fatalWithHint("unrecognized argument: '{s}'", .{arg}); + } + } else { + try targets.append(arg); + } + } + + const host_query = std.Build.parseTargetQuery(graph.host_query_options) catch |err| switch (err) { + error.ParseFailed => process.exit(1), + }; + builder.host = .{ + .query = .{}, + .result = try std.zig.system.resolveTargetQuery(host_query), + }; + + const stderr = std.io.getStdErr(); + const ttyconf = get_tty_conf(color, stderr); + switch (ttyconf) { + .no_color => try graph.env_map.put("NO_COLOR", "1"), + .escape_codes => try graph.env_map.put("YES_COLOR", "1"), + .windows_api => {}, + } + + var progress: std.Progress = .{ .dont_print_on_dumb = true }; + const main_progress_node = progress.start("", 0); + + builder.debug_log_scopes = debug_log_scopes.items; + builder.resolveInstallPrefix(install_prefix, dir_list); + { + var prog_node = main_progress_node.start("user build.zig logic", 0); + defer prog_node.end(); + try builder.runBuild(root); + } + + if (graph.needed_lazy_dependencies.entries.len != 0) { + var buffer: std.ArrayListUnmanaged(u8) = .{}; + for (graph.needed_lazy_dependencies.keys()) |k| { + try buffer.appendSlice(arena, k); + try buffer.append(arena, '\n'); + } + const s = std.fs.path.sep_str; + const tmp_sub_path = "tmp" ++ s ++ (output_tmp_nonce orelse fatal("missing -Z arg", .{})); + local_cache_directory.handle.writeFile2(.{ + .sub_path = tmp_sub_path, + .data = buffer.items, + .flags = .{ .exclusive = true }, + }) catch |err| { + fatal("unable to write configuration results to '{}{s}': {s}", .{ + local_cache_directory, tmp_sub_path, @errorName(err), + }); + }; + process.exit(3); // Indicate configure phase failed with meaningful stdout. + } + + if (builder.validateUserInputDidItFail()) { + fatal(" access the help menu with 'zig build -h'", .{}); + } + + validateSystemLibraryOptions(builder); + + const stdout_writer = io.getStdOut().writer(); + + if (help_menu) + return usage(builder, stdout_writer); + + if (steps_menu) + return steps(builder, stdout_writer); + + var run: Run = .{ + .max_rss = max_rss, + .max_rss_is_default = false, + .max_rss_mutex = .{}, + .skip_oom_steps = skip_oom_steps, + .memory_blocked_steps = std.ArrayList(*Step).init(arena), + .prominent_compile_errors = prominent_compile_errors, + + .claimed_rss = 0, + .summary = summary, + .ttyconf = ttyconf, + .stderr = stderr, + }; + + if (run.max_rss == 0) { + run.max_rss = process.totalSystemMemory() catch std.math.maxInt(u64); + run.max_rss_is_default = true; + } + + runStepNames( + arena, + builder, + targets.items, + main_progress_node, + thread_pool_options, + &run, + seed, + ) catch |err| switch (err) { + error.UncleanExit => process.exit(1), + else => return err, + }; +} + +const Run = struct { + max_rss: u64, + max_rss_is_default: bool, + max_rss_mutex: std.Thread.Mutex, + skip_oom_steps: bool, + memory_blocked_steps: std.ArrayList(*Step), + prominent_compile_errors: bool, + + claimed_rss: usize, + summary: ?Summary, + ttyconf: std.io.tty.Config, + stderr: File, +}; + +fn runStepNames( + arena: std.mem.Allocator, + b: *std.Build, + step_names: []const []const u8, + parent_prog_node: *std.Progress.Node, + thread_pool_options: std.Thread.Pool.Options, + run: *Run, + seed: u32, +) !void { + const gpa = b.allocator; + var step_stack: std.AutoArrayHashMapUnmanaged(*Step, void) = .{}; + defer step_stack.deinit(gpa); + + if (step_names.len == 0) { + try step_stack.put(gpa, b.default_step, {}); + } else { + try step_stack.ensureUnusedCapacity(gpa, step_names.len); + for (0..step_names.len) |i| { + const step_name = step_names[step_names.len - i - 1]; + const s = b.top_level_steps.get(step_name) orelse { + std.debug.print("no step named '{s}'\n access the help menu with 'zig build -h'\n", .{step_name}); + process.exit(1); + }; + step_stack.putAssumeCapacity(&s.step, {}); + } + } + + const starting_steps = try arena.dupe(*Step, step_stack.keys()); + + var rng = std.Random.DefaultPrng.init(seed); + const rand = rng.random(); + rand.shuffle(*Step, starting_steps); + + for (starting_steps) |s| { + constructGraphAndCheckForDependencyLoop(b, s, &step_stack, rand) catch |err| switch (err) { + error.DependencyLoopDetected => return error.UncleanExit, + else => |e| return e, + }; + } + + { + // Check that we have enough memory to complete the build. + var any_problems = false; + for (step_stack.keys()) |s| { + if (s.max_rss == 0) continue; + if (s.max_rss > run.max_rss) { + if (run.skip_oom_steps) { + s.state = .skipped_oom; + } else { + std.debug.print("{s}{s}: this step declares an upper bound of {d} bytes of memory, exceeding the available {d} bytes of memory\n", .{ + s.owner.dep_prefix, s.name, s.max_rss, run.max_rss, + }); + any_problems = true; + } + } + } + if (any_problems) { + if (run.max_rss_is_default) { + std.debug.print("note: use --maxrss to override the default", .{}); + } + return error.UncleanExit; + } + } + + var thread_pool: std.Thread.Pool = undefined; + try thread_pool.init(thread_pool_options); + defer thread_pool.deinit(); + + { + defer parent_prog_node.end(); + + var step_prog = parent_prog_node.start("steps", step_stack.count()); + defer step_prog.end(); + + var wait_group: std.Thread.WaitGroup = .{}; + defer wait_group.wait(); + + // Here we spawn the initial set of tasks with a nice heuristic - + // dependency order. Each worker when it finishes a step will then + // check whether it should run any dependants. + const steps_slice = step_stack.keys(); + for (0..steps_slice.len) |i| { + const step = steps_slice[steps_slice.len - i - 1]; + if (step.state == .skipped_oom) continue; + + wait_group.start(); + thread_pool.spawn(workerMakeOneStep, .{ + &wait_group, &thread_pool, b, step, &step_prog, run, + }) catch @panic("OOM"); + } + } + assert(run.memory_blocked_steps.items.len == 0); + + var test_skip_count: usize = 0; + var test_fail_count: usize = 0; + var test_pass_count: usize = 0; + var test_leak_count: usize = 0; + var test_count: usize = 0; + + var success_count: usize = 0; + var skipped_count: usize = 0; + var failure_count: usize = 0; + var pending_count: usize = 0; + var total_compile_errors: usize = 0; + var compile_error_steps: std.ArrayListUnmanaged(*Step) = .{}; + defer compile_error_steps.deinit(gpa); + + for (step_stack.keys()) |s| { + test_fail_count += s.test_results.fail_count; + test_skip_count += s.test_results.skip_count; + test_leak_count += s.test_results.leak_count; + test_pass_count += s.test_results.passCount(); + test_count += s.test_results.test_count; + + switch (s.state) { + .precheck_unstarted => unreachable, + .precheck_started => unreachable, + .running => unreachable, + .precheck_done => { + // precheck_done is equivalent to dependency_failure in the case of + // transitive dependencies. For example: + // A -> B -> C (failure) + // B will be marked as dependency_failure, while A may never be queued, and thus + // remain in the initial state of precheck_done. + s.state = .dependency_failure; + pending_count += 1; + }, + .dependency_failure => pending_count += 1, + .success => success_count += 1, + .skipped, .skipped_oom => skipped_count += 1, + .failure => { + failure_count += 1; + const compile_errors_len = s.result_error_bundle.errorMessageCount(); + if (compile_errors_len > 0) { + total_compile_errors += compile_errors_len; + try compile_error_steps.append(gpa, s); + } + }, + } + } + + // A proper command line application defaults to silently succeeding. + // The user may request verbose mode if they have a different preference. + const failures_only = run.summary != .all and run.summary != .new; + if (failure_count == 0 and failures_only) return cleanExit(); + + const ttyconf = run.ttyconf; + const stderr = run.stderr; + + if (run.summary != Summary.none) { + const total_count = success_count + failure_count + pending_count + skipped_count; + ttyconf.setColor(stderr, .cyan) catch {}; + stderr.writeAll("Build Summary:") catch {}; + ttyconf.setColor(stderr, .reset) catch {}; + stderr.writer().print(" {d}/{d} steps succeeded", .{ success_count, total_count }) catch {}; + if (skipped_count > 0) stderr.writer().print("; {d} skipped", .{skipped_count}) catch {}; + if (failure_count > 0) stderr.writer().print("; {d} failed", .{failure_count}) catch {}; + + if (test_count > 0) stderr.writer().print("; {d}/{d} tests passed", .{ test_pass_count, test_count }) catch {}; + if (test_skip_count > 0) stderr.writer().print("; {d} skipped", .{test_skip_count}) catch {}; + if (test_fail_count > 0) stderr.writer().print("; {d} failed", .{test_fail_count}) catch {}; + if (test_leak_count > 0) stderr.writer().print("; {d} leaked", .{test_leak_count}) catch {}; + + if (run.summary == null) { + ttyconf.setColor(stderr, .dim) catch {}; + stderr.writeAll(" (disable with --summary none)") catch {}; + ttyconf.setColor(stderr, .reset) catch {}; + } + stderr.writeAll("\n") catch {}; + + // Print a fancy tree with build results. + var print_node: PrintNode = .{ .parent = null }; + if (step_names.len == 0) { + print_node.last = true; + printTreeStep(b, b.default_step, run, stderr, ttyconf, &print_node, &step_stack) catch {}; + } else { + const last_index = if (run.summary == .all) b.top_level_steps.count() else blk: { + var i: usize = step_names.len; + while (i > 0) { + i -= 1; + const step = b.top_level_steps.get(step_names[i]).?.step; + const found = switch (run.summary orelse .failures) { + .all, .none => unreachable, + .failures => step.state != .success, + .new => !step.result_cached, + }; + if (found) break :blk i; + } + break :blk b.top_level_steps.count(); + }; + for (step_names, 0..) |step_name, i| { + const tls = b.top_level_steps.get(step_name).?; + print_node.last = i + 1 == last_index; + printTreeStep(b, &tls.step, run, stderr, ttyconf, &print_node, &step_stack) catch {}; + } + } + } + + if (failure_count == 0) return cleanExit(); + + // Finally, render compile errors at the bottom of the terminal. + // We use a separate compile_error_steps array list because step_stack is destructively + // mutated in printTreeStep above. + if (run.prominent_compile_errors and total_compile_errors > 0) { + for (compile_error_steps.items) |s| { + if (s.result_error_bundle.errorMessageCount() > 0) { + s.result_error_bundle.renderToStdErr(renderOptions(ttyconf)); + } + } + + // Signal to parent process that we have printed compile errors. The + // parent process may choose to omit the "following command failed" + // line in this case. + process.exit(2); + } + + process.exit(1); +} + +const PrintNode = struct { + parent: ?*PrintNode, + last: bool = false, +}; + +fn printPrefix(node: *PrintNode, stderr: File, ttyconf: std.io.tty.Config) !void { + const parent = node.parent orelse return; + if (parent.parent == null) return; + try printPrefix(parent, stderr, ttyconf); + if (parent.last) { + try stderr.writeAll(" "); + } else { + try stderr.writeAll(switch (ttyconf) { + .no_color, .windows_api => "| ", + .escape_codes => "\x1B\x28\x30\x78\x1B\x28\x42 ", // │ + }); + } +} + +fn printChildNodePrefix(stderr: File, ttyconf: std.io.tty.Config) !void { + try stderr.writeAll(switch (ttyconf) { + .no_color, .windows_api => "+- ", + .escape_codes => "\x1B\x28\x30\x6d\x71\x1B\x28\x42 ", // └─ + }); +} + +fn printStepStatus( + s: *Step, + stderr: File, + ttyconf: std.io.tty.Config, + run: *const Run, +) !void { + switch (s.state) { + .precheck_unstarted => unreachable, + .precheck_started => unreachable, + .precheck_done => unreachable, + .running => unreachable, + + .dependency_failure => { + try ttyconf.setColor(stderr, .dim); + try stderr.writeAll(" transitive failure\n"); + try ttyconf.setColor(stderr, .reset); + }, + + .success => { + try ttyconf.setColor(stderr, .green); + if (s.result_cached) { + try stderr.writeAll(" cached"); + } else if (s.test_results.test_count > 0) { + const pass_count = s.test_results.passCount(); + try stderr.writer().print(" {d} passed", .{pass_count}); + if (s.test_results.skip_count > 0) { + try ttyconf.setColor(stderr, .yellow); + try stderr.writer().print(" {d} skipped", .{s.test_results.skip_count}); + } + } else { + try stderr.writeAll(" success"); + } + try ttyconf.setColor(stderr, .reset); + if (s.result_duration_ns) |ns| { + try ttyconf.setColor(stderr, .dim); + if (ns >= std.time.ns_per_min) { + try stderr.writer().print(" {d}m", .{ns / std.time.ns_per_min}); + } else if (ns >= std.time.ns_per_s) { + try stderr.writer().print(" {d}s", .{ns / std.time.ns_per_s}); + } else if (ns >= std.time.ns_per_ms) { + try stderr.writer().print(" {d}ms", .{ns / std.time.ns_per_ms}); + } else if (ns >= std.time.ns_per_us) { + try stderr.writer().print(" {d}us", .{ns / std.time.ns_per_us}); + } else { + try stderr.writer().print(" {d}ns", .{ns}); + } + try ttyconf.setColor(stderr, .reset); + } + if (s.result_peak_rss != 0) { + const rss = s.result_peak_rss; + try ttyconf.setColor(stderr, .dim); + if (rss >= 1000_000_000) { + try stderr.writer().print(" MaxRSS:{d}G", .{rss / 1000_000_000}); + } else if (rss >= 1000_000) { + try stderr.writer().print(" MaxRSS:{d}M", .{rss / 1000_000}); + } else if (rss >= 1000) { + try stderr.writer().print(" MaxRSS:{d}K", .{rss / 1000}); + } else { + try stderr.writer().print(" MaxRSS:{d}B", .{rss}); + } + try ttyconf.setColor(stderr, .reset); + } + try stderr.writeAll("\n"); + }, + .skipped, .skipped_oom => |skip| { + try ttyconf.setColor(stderr, .yellow); + try stderr.writeAll(" skipped"); + if (skip == .skipped_oom) { + try stderr.writeAll(" (not enough memory)"); + try ttyconf.setColor(stderr, .dim); + try stderr.writer().print(" upper bound of {d} exceeded runner limit ({d})", .{ s.max_rss, run.max_rss }); + try ttyconf.setColor(stderr, .yellow); + } + try stderr.writeAll("\n"); + try ttyconf.setColor(stderr, .reset); + }, + .failure => try printStepFailure(s, stderr, ttyconf), + } +} + +fn printStepFailure( + s: *Step, + stderr: File, + ttyconf: std.io.tty.Config, +) !void { + if (s.result_error_bundle.errorMessageCount() > 0) { + try ttyconf.setColor(stderr, .red); + try stderr.writer().print(" {d} errors\n", .{ + s.result_error_bundle.errorMessageCount(), + }); + try ttyconf.setColor(stderr, .reset); + } else if (!s.test_results.isSuccess()) { + try stderr.writer().print(" {d}/{d} passed", .{ + s.test_results.passCount(), s.test_results.test_count, + }); + if (s.test_results.fail_count > 0) { + try stderr.writeAll(", "); + try ttyconf.setColor(stderr, .red); + try stderr.writer().print("{d} failed", .{ + s.test_results.fail_count, + }); + try ttyconf.setColor(stderr, .reset); + } + if (s.test_results.skip_count > 0) { + try stderr.writeAll(", "); + try ttyconf.setColor(stderr, .yellow); + try stderr.writer().print("{d} skipped", .{ + s.test_results.skip_count, + }); + try ttyconf.setColor(stderr, .reset); + } + if (s.test_results.leak_count > 0) { + try stderr.writeAll(", "); + try ttyconf.setColor(stderr, .red); + try stderr.writer().print("{d} leaked", .{ + s.test_results.leak_count, + }); + try ttyconf.setColor(stderr, .reset); + } + try stderr.writeAll("\n"); + } else if (s.result_error_msgs.items.len > 0) { + try ttyconf.setColor(stderr, .red); + try stderr.writeAll(" failure\n"); + try ttyconf.setColor(stderr, .reset); + } else { + assert(s.result_stderr.len > 0); + try ttyconf.setColor(stderr, .red); + try stderr.writeAll(" stderr\n"); + try ttyconf.setColor(stderr, .reset); + } +} + +fn printTreeStep( + b: *std.Build, + s: *Step, + run: *const Run, + stderr: File, + ttyconf: std.io.tty.Config, + parent_node: *PrintNode, + step_stack: *std.AutoArrayHashMapUnmanaged(*Step, void), +) !void { + const first = step_stack.swapRemove(s); + const summary = run.summary orelse .failures; + const skip = switch (summary) { + .none => unreachable, + .all => false, + .new => s.result_cached, + .failures => s.state == .success, + }; + if (skip) return; + try printPrefix(parent_node, stderr, ttyconf); + + if (!first) try ttyconf.setColor(stderr, .dim); + if (parent_node.parent != null) { + if (parent_node.last) { + try printChildNodePrefix(stderr, ttyconf); + } else { + try stderr.writeAll(switch (ttyconf) { + .no_color, .windows_api => "+- ", + .escape_codes => "\x1B\x28\x30\x74\x71\x1B\x28\x42 ", // ├─ + }); + } + } + + // dep_prefix omitted here because it is redundant with the tree. + try stderr.writeAll(s.name); + + if (first) { + try printStepStatus(s, stderr, ttyconf, run); + + const last_index = if (summary == .all) s.dependencies.items.len -| 1 else blk: { + var i: usize = s.dependencies.items.len; + while (i > 0) { + i -= 1; + + const step = s.dependencies.items[i]; + const found = switch (summary) { + .all, .none => unreachable, + .failures => step.state != .success, + .new => !step.result_cached, + }; + if (found) break :blk i; + } + break :blk s.dependencies.items.len -| 1; + }; + for (s.dependencies.items, 0..) |dep, i| { + var print_node: PrintNode = .{ + .parent = parent_node, + .last = i == last_index, + }; + try printTreeStep(b, dep, run, stderr, ttyconf, &print_node, step_stack); + } + } else { + if (s.dependencies.items.len == 0) { + try stderr.writeAll(" (reused)\n"); + } else { + try stderr.writer().print(" (+{d} more reused dependencies)\n", .{ + s.dependencies.items.len, + }); + } + try ttyconf.setColor(stderr, .reset); + } +} + +/// Traverse the dependency graph depth-first and make it undirected by having +/// steps know their dependants (they only know dependencies at start). +/// Along the way, check that there is no dependency loop, and record the steps +/// in traversal order in `step_stack`. +/// Each step has its dependencies traversed in random order, this accomplishes +/// two things: +/// - `step_stack` will be in randomized-depth-first order, so the build runner +/// spawns steps in a random (but optimized) order +/// - each step's `dependants` list is also filled in a random order, so that +/// when it finishes executing in `workerMakeOneStep`, it spawns next steps +/// to run in random order +fn constructGraphAndCheckForDependencyLoop( + b: *std.Build, + s: *Step, + step_stack: *std.AutoArrayHashMapUnmanaged(*Step, void), + rand: std.Random, +) !void { + switch (s.state) { + .precheck_started => { + std.debug.print("dependency loop detected:\n {s}\n", .{s.name}); + return error.DependencyLoopDetected; + }, + .precheck_unstarted => { + s.state = .precheck_started; + + try step_stack.ensureUnusedCapacity(b.allocator, s.dependencies.items.len); + + // We dupe to avoid shuffling the steps in the summary, it depends + // on s.dependencies' order. + const deps = b.allocator.dupe(*Step, s.dependencies.items) catch @panic("OOM"); + rand.shuffle(*Step, deps); + + for (deps) |dep| { + try step_stack.put(b.allocator, dep, {}); + try dep.dependants.append(b.allocator, s); + constructGraphAndCheckForDependencyLoop(b, dep, step_stack, rand) catch |err| { + if (err == error.DependencyLoopDetected) { + std.debug.print(" {s}\n", .{s.name}); + } + return err; + }; + } + + s.state = .precheck_done; + }, + .precheck_done => {}, + + // These don't happen until we actually run the step graph. + .dependency_failure => unreachable, + .running => unreachable, + .success => unreachable, + .failure => unreachable, + .skipped => unreachable, + .skipped_oom => unreachable, + } +} + +fn workerMakeOneStep( + wg: *std.Thread.WaitGroup, + thread_pool: *std.Thread.Pool, + b: *std.Build, + s: *Step, + prog_node: *std.Progress.Node, + run: *Run, +) void { + defer wg.finish(); + + // First, check the conditions for running this step. If they are not met, + // then we return without doing the step, relying on another worker to + // queue this step up again when dependencies are met. + for (s.dependencies.items) |dep| { + switch (@atomicLoad(Step.State, &dep.state, .seq_cst)) { + .success, .skipped => continue, + .failure, .dependency_failure, .skipped_oom => { + @atomicStore(Step.State, &s.state, .dependency_failure, .seq_cst); + return; + }, + .precheck_done, .running => { + // dependency is not finished yet. + return; + }, + .precheck_unstarted => unreachable, + .precheck_started => unreachable, + } + } + + if (s.max_rss != 0) { + run.max_rss_mutex.lock(); + defer run.max_rss_mutex.unlock(); + + // Avoid running steps twice. + if (s.state != .precheck_done) { + // Another worker got the job. + return; + } + + const new_claimed_rss = run.claimed_rss + s.max_rss; + if (new_claimed_rss > run.max_rss) { + // Running this step right now could possibly exceed the allotted RSS. + // Add this step to the queue of memory-blocked steps. + run.memory_blocked_steps.append(s) catch @panic("OOM"); + return; + } + + run.claimed_rss = new_claimed_rss; + s.state = .running; + } else { + // Avoid running steps twice. + if (@cmpxchgStrong(Step.State, &s.state, .precheck_done, .running, .seq_cst, .seq_cst) != null) { + // Another worker got the job. + return; + } + } + + var sub_prog_node = prog_node.start(s.name, 0); + sub_prog_node.activate(); + defer sub_prog_node.end(); + + const make_result = s.make(&sub_prog_node); + + // No matter the result, we want to display error/warning messages. + const show_compile_errors = !run.prominent_compile_errors and + s.result_error_bundle.errorMessageCount() > 0; + const show_error_msgs = s.result_error_msgs.items.len > 0; + const show_stderr = s.result_stderr.len > 0; + + if (show_error_msgs or show_compile_errors or show_stderr) { + sub_prog_node.context.lock_stderr(); + defer sub_prog_node.context.unlock_stderr(); + + printErrorMessages(b, s, run) catch {}; + } + + handle_result: { + if (make_result) |_| { + @atomicStore(Step.State, &s.state, .success, .seq_cst); + } else |err| switch (err) { + error.MakeFailed => { + @atomicStore(Step.State, &s.state, .failure, .seq_cst); + break :handle_result; + }, + error.MakeSkipped => @atomicStore(Step.State, &s.state, .skipped, .seq_cst), + } + + // Successful completion of a step, so we queue up its dependants as well. + for (s.dependants.items) |dep| { + wg.start(); + thread_pool.spawn(workerMakeOneStep, .{ + wg, thread_pool, b, dep, prog_node, run, + }) catch @panic("OOM"); + } + } + + // If this is a step that claims resources, we must now queue up other + // steps that are waiting for resources. + if (s.max_rss != 0) { + run.max_rss_mutex.lock(); + defer run.max_rss_mutex.unlock(); + + // Give the memory back to the scheduler. + run.claimed_rss -= s.max_rss; + // Avoid kicking off too many tasks that we already know will not have + // enough resources. + var remaining = run.max_rss - run.claimed_rss; + var i: usize = 0; + var j: usize = 0; + while (j < run.memory_blocked_steps.items.len) : (j += 1) { + const dep = run.memory_blocked_steps.items[j]; + assert(dep.max_rss != 0); + if (dep.max_rss <= remaining) { + remaining -= dep.max_rss; + + wg.start(); + thread_pool.spawn(workerMakeOneStep, .{ + wg, thread_pool, b, dep, prog_node, run, + }) catch @panic("OOM"); + } else { + run.memory_blocked_steps.items[i] = dep; + i += 1; + } + } + run.memory_blocked_steps.shrinkRetainingCapacity(i); + } +} + +fn printErrorMessages(b: *std.Build, failing_step: *Step, run: *const Run) !void { + const gpa = b.allocator; + const stderr = run.stderr; + const ttyconf = run.ttyconf; + + // Provide context for where these error messages are coming from by + // printing the corresponding Step subtree. + + var step_stack: std.ArrayListUnmanaged(*Step) = .{}; + defer step_stack.deinit(gpa); + try step_stack.append(gpa, failing_step); + while (step_stack.items[step_stack.items.len - 1].dependants.items.len != 0) { + try step_stack.append(gpa, step_stack.items[step_stack.items.len - 1].dependants.items[0]); + } + + // Now, `step_stack` has the subtree that we want to print, in reverse order. + try ttyconf.setColor(stderr, .dim); + var indent: usize = 0; + while (step_stack.popOrNull()) |s| : (indent += 1) { + if (indent > 0) { + try stderr.writer().writeByteNTimes(' ', (indent - 1) * 3); + try printChildNodePrefix(stderr, ttyconf); + } + + try stderr.writeAll(s.name); + + if (s == failing_step) { + try printStepFailure(s, stderr, ttyconf); + } else { + try stderr.writeAll("\n"); + } + } + try ttyconf.setColor(stderr, .reset); + + if (failing_step.result_stderr.len > 0) { + try stderr.writeAll(failing_step.result_stderr); + if (!mem.endsWith(u8, failing_step.result_stderr, "\n")) { + try stderr.writeAll("\n"); + } + } + + if (!run.prominent_compile_errors and failing_step.result_error_bundle.errorMessageCount() > 0) + try failing_step.result_error_bundle.renderToWriter(renderOptions(ttyconf), stderr.writer()); + + for (failing_step.result_error_msgs.items) |msg| { + try ttyconf.setColor(stderr, .red); + try stderr.writeAll("error: "); + try ttyconf.setColor(stderr, .reset); + try stderr.writeAll(msg); + try stderr.writeAll("\n"); + } +} + +fn steps(builder: *std.Build, out_stream: anytype) !void { + const allocator = builder.allocator; + for (builder.top_level_steps.values()) |top_level_step| { + const name = if (&top_level_step.step == builder.default_step) + try fmt.allocPrint(allocator, "{s} (default)", .{top_level_step.step.name}) + else + top_level_step.step.name; + try out_stream.print(" {s:<28} {s}\n", .{ name, top_level_step.description }); + } +} + +fn usage(b: *std.Build, out_stream: anytype) !void { + try out_stream.print( + \\Usage: {s} build [steps] [options] + \\ + \\Steps: + \\ + , .{b.graph.zig_exe}); + try steps(b, out_stream); + + try out_stream.writeAll( + \\ + \\General Options: + \\ -p, --prefix [path] Where to install files (default: zig-out) + \\ --prefix-lib-dir [path] Where to install libraries + \\ --prefix-exe-dir [path] Where to install executables + \\ --prefix-include-dir [path] Where to install C header files + \\ + \\ --release[=mode] Request release mode, optionally specifying a + \\ preferred optimization mode: fast, safe, small + \\ + \\ -fdarling, -fno-darling Integration with system-installed Darling to + \\ execute macOS programs on Linux hosts + \\ (default: no) + \\ -fqemu, -fno-qemu Integration with system-installed QEMU to execute + \\ foreign-architecture programs on Linux hosts + \\ (default: no) + \\ --glibc-runtimes [path] Enhances QEMU integration by providing glibc built + \\ for multiple foreign architectures, allowing + \\ execution of non-native programs that link with glibc. + \\ -frosetta, -fno-rosetta Rely on Rosetta to execute x86_64 programs on + \\ ARM64 macOS hosts. (default: no) + \\ -fwasmtime, -fno-wasmtime Integration with system-installed wasmtime to + \\ execute WASI binaries. (default: no) + \\ -fwine, -fno-wine Integration with system-installed Wine to execute + \\ Windows programs on Linux hosts. (default: no) + \\ + \\ -h, --help Print this help and exit + \\ -l, --list-steps Print available steps + \\ --verbose Print commands before executing them + \\ --color [auto|off|on] Enable or disable colored error messages + \\ --prominent-compile-errors Buffer compile errors and display at end + \\ --summary [mode] Control the printing of the build summary + \\ all Print the build summary in its entirety + \\ new Omit cached steps + \\ failures (Default) Only print failed steps + \\ none Do not print the build summary + \\ -j Limit concurrent jobs (default is to use all CPU cores) + \\ --maxrss Limit memory usage (default is to use available memory) + \\ --skip-oom-steps Instead of failing, skip steps that would exceed --maxrss + \\ --fetch Exit after fetching dependency tree + \\ + \\Project-Specific Options: + \\ + ); + + const arena = b.allocator; + if (b.available_options_list.items.len == 0) { + try out_stream.print(" (none)\n", .{}); + } else { + for (b.available_options_list.items) |option| { + const name = try fmt.allocPrint(arena, " -D{s}=[{s}]", .{ + option.name, + @tagName(option.type_id), + }); + try out_stream.print("{s:<30} {s}\n", .{ name, option.description }); + if (option.enum_options) |enum_options| { + const padding = " " ** 33; + try out_stream.writeAll(padding ++ "Supported Values:\n"); + for (enum_options) |enum_option| { + try out_stream.print(padding ++ " {s}\n", .{enum_option}); + } + } + } + } + + try out_stream.writeAll( + \\ + \\System Integration Options: + \\ --search-prefix [path] Add a path to look for binaries, libraries, headers + \\ --sysroot [path] Set the system root directory (usually /) + \\ --libc [file] Provide a file which specifies libc paths + \\ + \\ --host-target [triple] Use the provided target as the host + \\ --host-cpu [cpu] Use the provided CPU as the host + \\ --host-dynamic-linker [path] Use the provided dynamic linker as the host + \\ + \\ --system [pkgdir] Disable package fetching; enable all integrations + \\ -fsys=[name] Enable a system integration + \\ -fno-sys=[name] Disable a system integration + \\ + \\ Available System Integrations: Enabled: + \\ + ); + if (b.graph.system_library_options.entries.len == 0) { + try out_stream.writeAll(" (none) -\n"); + } else { + for (b.graph.system_library_options.keys(), b.graph.system_library_options.values()) |k, v| { + const status = switch (v) { + .declared_enabled => "yes", + .declared_disabled => "no", + .user_enabled, .user_disabled => unreachable, // already emitted error + }; + try out_stream.print(" {s:<43} {s}\n", .{ k, status }); + } + } + + try out_stream.writeAll( + \\ + \\Advanced Options: + \\ -freference-trace[=num] How many lines of reference trace should be shown per compile error + \\ -fno-reference-trace Disable reference trace + \\ --build-file [file] Override path to build.zig + \\ --cache-dir [path] Override path to local Zig cache directory + \\ --global-cache-dir [path] Override path to global Zig cache directory + \\ --zig-lib-dir [arg] Override path to Zig lib directory + \\ --build-runner [file] Override path to build runner + \\ --seed [integer] For shuffling dependency traversal order (default: random) + \\ --debug-log [scope] Enable debugging the compiler + \\ --debug-pkg-config Fail if unknown pkg-config flags encountered + \\ --verbose-link Enable compiler debug output for linking + \\ --verbose-air Enable compiler debug output for Zig AIR + \\ --verbose-llvm-ir[=file] Enable compiler debug output for LLVM IR + \\ --verbose-llvm-bc=[file] Enable compiler debug output for LLVM BC + \\ --verbose-cimport Enable compiler debug output for C imports + \\ --verbose-cc Enable compiler debug output for C compilation + \\ --verbose-llvm-cpu-features Enable compiler debug output for LLVM CPU features + \\ + ); +} + +fn nextArg(args: [][:0]const u8, idx: *usize) ?[:0]const u8 { + if (idx.* >= args.len) return null; + defer idx.* += 1; + return args[idx.*]; +} + +fn nextArgOrFatal(args: [][:0]const u8, idx: *usize) [:0]const u8 { + return nextArg(args, idx) orelse { + std.debug.print("expected argument after '{s}'\n access the help menu with 'zig build -h'\n", .{args[idx.*]}); + process.exit(1); + }; +} + +fn argsRest(args: [][:0]const u8, idx: usize) ?[][:0]const u8 { + if (idx >= args.len) return null; + return args[idx..]; +} + +fn cleanExit() void { + // Perhaps in the future there could be an Advanced Options flag such as + // --debug-build-runner-leaks which would make this function return instead + // of calling exit. + process.exit(0); +} + +const Color = enum { auto, off, on }; +const Summary = enum { all, new, failures, none }; + +fn get_tty_conf(color: Color, stderr: File) std.io.tty.Config { + return switch (color) { + .auto => std.io.tty.detectConfig(stderr), + .on => .escape_codes, + .off => .no_color, + }; +} + +fn renderOptions(ttyconf: std.io.tty.Config) std.zig.ErrorBundle.RenderOptions { + return .{ + .ttyconf = ttyconf, + .include_source_line = ttyconf != .no_color, + .include_reference_trace = ttyconf != .no_color, + }; +} + +fn fatalWithHint(comptime f: []const u8, args: anytype) noreturn { + std.debug.print(f ++ "\n access the help menu with 'zig build -h'\n", args); + process.exit(1); +} + +fn fatal(comptime f: []const u8, args: anytype) noreturn { + std.debug.print(f ++ "\n", args); + process.exit(1); +} + +fn validateSystemLibraryOptions(b: *std.Build) void { + var bad = false; + for (b.graph.system_library_options.keys(), b.graph.system_library_options.values()) |k, v| { + switch (v) { + .user_disabled, .user_enabled => { + // The user tried to enable or disable a system library integration, but + // the build script did not recognize that option. + std.debug.print("system library name not recognized by build script: '{s}'\n", .{k}); + bad = true; + }, + .declared_disabled, .declared_enabled => {}, + } + } + if (bad) { + std.debug.print(" access the help menu with 'zig build -h'\n", .{}); + process.exit(1); + } +} diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index d9e72f2019a6fc5e20b85a287a08d43785f9270a..0238d35c7d9dc2fec6b54cab2411a1bfd5ae1bd9 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -420,7 +420,6 @@ pub const Type = union(enum) { /// therefore must be kept in sync with the compiler implementation. pub const Fn = struct { calling_convention: CallingConvention, - alignment: comptime_int, is_generic: bool, is_var_args: bool, /// TODO change the language spec to make this not optional. diff --git a/lib/std/c/darwin.zig b/lib/std/c/darwin.zig index 8dd122c3ba676e4654ce746120e5ed43d1a6c533..8442ac9fe00ce1374a0a0f4a06f5b9bd8a26dd6a 100644 --- a/lib/std/c/darwin.zig +++ b/lib/std/c/darwin.zig @@ -1053,10 +1053,10 @@ pub const sigset_t = u32; pub const empty_sigset: sigset_t = 0; pub const SIG = struct { - pub const ERR = @as(?Sigaction.handler_fn, @ptrFromInt(maxInt(usize))); - pub const DFL = @as(?Sigaction.handler_fn, @ptrFromInt(0)); - pub const IGN = @as(?Sigaction.handler_fn, @ptrFromInt(1)); - pub const HOLD = @as(?Sigaction.handler_fn, @ptrFromInt(5)); + pub const ERR: ?Sigaction.handler_fn = @ptrFromInt(maxInt(usize)); + pub const DFL: ?Sigaction.handler_fn = @ptrFromInt(0); + pub const IGN: ?Sigaction.handler_fn = @ptrFromInt(1); + pub const HOLD: ?Sigaction.handler_fn = @ptrFromInt(5); /// block specified signal set pub const BLOCK = 1; @@ -1150,7 +1150,7 @@ pub const siginfo_t = extern struct { /// Renamed from `sigaction` to `Sigaction` to avoid conflict with function name. pub const Sigaction = extern struct { - pub const handler_fn = *const fn (c_int) align(1) callconv(.C) void; + pub const handler_fn = *align(1) const fn (c_int) callconv(.C) void; pub const sigaction_fn = *const fn (c_int, *const siginfo_t, ?*const anyopaque) callconv(.C) void; handler: extern union { diff --git a/lib/std/c/dragonfly.zig b/lib/std/c/dragonfly.zig index 8cbdd22c401f122b19294fa3844af3b83519c03c..183a81bba2a273a30f277b990d5d6ad93263a125 100644 --- a/lib/std/c/dragonfly.zig +++ b/lib/std/c/dragonfly.zig @@ -616,9 +616,9 @@ pub const S = struct { pub const BADSIG = SIG.ERR; pub const SIG = struct { - pub const DFL = @as(?Sigaction.handler_fn, @ptrFromInt(0)); - pub const IGN = @as(?Sigaction.handler_fn, @ptrFromInt(1)); - pub const ERR = @as(?Sigaction.handler_fn, @ptrFromInt(maxInt(usize))); + pub const DFL: ?Sigaction.handler_fn = @ptrFromInt(0); + pub const IGN: ?Sigaction.handler_fn = @ptrFromInt(1); + pub const ERR: ?Sigaction.handler_fn = @ptrFromInt(maxInt(usize)); pub const BLOCK = 1; pub const UNBLOCK = 2; @@ -690,7 +690,7 @@ pub const empty_sigset = sigset_t{ .__bits = [_]c_uint{0} ** _SIG_WORDS }; pub const sig_atomic_t = c_int; pub const Sigaction = extern struct { - pub const handler_fn = *const fn (c_int) align(1) callconv(.C) void; + pub const handler_fn = *align(1) const fn (c_int) callconv(.C) void; pub const sigaction_fn = *const fn (c_int, *const siginfo_t, ?*const anyopaque) callconv(.C) void; /// signal handler diff --git a/lib/std/c/freebsd.zig b/lib/std/c/freebsd.zig index 94854cf09005431900d44b10be3d6231c253ec03..a89ca30968fc8423760f8b5be273bdddf3a0d7fb 100644 --- a/lib/std/c/freebsd.zig +++ b/lib/std/c/freebsd.zig @@ -695,9 +695,9 @@ pub const SIG = struct { pub const UNBLOCK = 2; pub const SETMASK = 3; - pub const DFL = @as(?Sigaction.handler_fn, @ptrFromInt(0)); - pub const IGN = @as(?Sigaction.handler_fn, @ptrFromInt(1)); - pub const ERR = @as(?Sigaction.handler_fn, @ptrFromInt(maxInt(usize))); + pub const DFL: ?Sigaction.handler_fn = @ptrFromInt(0); + pub const IGN: ?Sigaction.handler_fn = @ptrFromInt(1); + pub const ERR: ?Sigaction.handler_fn = @ptrFromInt(maxInt(usize)); pub const WORDS = 4; pub const MAXSIG = 128; @@ -1171,7 +1171,7 @@ const NSIG = 32; /// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall. pub const Sigaction = extern struct { - pub const handler_fn = *const fn (c_int) align(1) callconv(.C) void; + pub const handler_fn = *align(1) const fn (c_int) callconv(.C) void; pub const sigaction_fn = *const fn (c_int, *const siginfo_t, ?*const anyopaque) callconv(.C) void; /// signal handler diff --git a/lib/std/c/haiku.zig b/lib/std/c/haiku.zig index 723d953d2d76c8c43e999361a1b4c3f649e4469c..12b5201acd864e2785daaa593ccdc8390ee0edad 100644 --- a/lib/std/c/haiku.zig +++ b/lib/std/c/haiku.zig @@ -441,9 +441,9 @@ pub const SA = struct { }; pub const SIG = struct { - pub const ERR = @as(?Sigaction.handler_fn, @ptrFromInt(maxInt(usize))); - pub const DFL = @as(?Sigaction.handler_fn, @ptrFromInt(0)); - pub const IGN = @as(?Sigaction.handler_fn, @ptrFromInt(1)); + pub const ERR: ?Sigaction.handler_fn = @ptrFromInt(maxInt(usize)); + pub const DFL: ?Sigaction.handler_fn = @ptrFromInt(0); + pub const IGN: ?Sigaction.handler_fn = @ptrFromInt(1); pub const HUP = 1; pub const INT = 2; @@ -690,7 +690,7 @@ const NSIG = 32; /// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall. pub const Sigaction = extern struct { - pub const handler_fn = *const fn (i32) align(1) callconv(.C) void; + pub const handler_fn = *align(1) const fn (i32) callconv(.C) void; /// signal handler __sigaction_u: extern union { diff --git a/lib/std/c/netbsd.zig b/lib/std/c/netbsd.zig index 475fd55e22bfa5a44e0bf653f8b3aec5d9c6bcf7..c06857787ae46885e804cc51629f525ccde9bfc7 100644 --- a/lib/std/c/netbsd.zig +++ b/lib/std/c/netbsd.zig @@ -800,9 +800,9 @@ pub const winsize = extern struct { const NSIG = 32; pub const SIG = struct { - pub const DFL = @as(?Sigaction.handler_fn, @ptrFromInt(0)); - pub const IGN = @as(?Sigaction.handler_fn, @ptrFromInt(1)); - pub const ERR = @as(?Sigaction.handler_fn, @ptrFromInt(maxInt(usize))); + pub const DFL: ?Sigaction.handler_fn = @ptrFromInt(0); + pub const IGN: ?Sigaction.handler_fn = @ptrFromInt(1); + pub const ERR: ?Sigaction.handler_fn = @ptrFromInt(maxInt(usize)); pub const WORDS = 4; pub const MAXSIG = 128; @@ -864,7 +864,7 @@ pub const SIG = struct { /// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall. pub const Sigaction = extern struct { - pub const handler_fn = *const fn (c_int) align(1) callconv(.C) void; + pub const handler_fn = *align(1) const fn (c_int) callconv(.C) void; pub const sigaction_fn = *const fn (c_int, *const siginfo_t, ?*const anyopaque) callconv(.C) void; /// signal handler diff --git a/lib/std/c/openbsd.zig b/lib/std/c/openbsd.zig index 75a4d6e0e8ee2c444431be2d803b4067350e03c4..4fd450cd5ce465fa6f76135060cf58786b8a4b58 100644 --- a/lib/std/c/openbsd.zig +++ b/lib/std/c/openbsd.zig @@ -795,11 +795,11 @@ pub const winsize = extern struct { const NSIG = 33; pub const SIG = struct { - pub const DFL = @as(?Sigaction.handler_fn, @ptrFromInt(0)); - pub const IGN = @as(?Sigaction.handler_fn, @ptrFromInt(1)); - pub const ERR = @as(?Sigaction.handler_fn, @ptrFromInt(maxInt(usize))); - pub const CATCH = @as(?Sigaction.handler_fn, @ptrFromInt(2)); - pub const HOLD = @as(?Sigaction.handler_fn, @ptrFromInt(3)); + pub const DFL: ?Sigaction.handler_fn = @ptrFromInt(0); + pub const IGN: ?Sigaction.handler_fn = @ptrFromInt(1); + pub const ERR: ?Sigaction.handler_fn = @ptrFromInt(maxInt(usize)); + pub const CATCH: ?Sigaction.handler_fn = @ptrFromInt(2); + pub const HOLD: ?Sigaction.handler_fn = @ptrFromInt(3); pub const HUP = 1; pub const INT = 2; @@ -842,7 +842,7 @@ pub const SIG = struct { /// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall. pub const Sigaction = extern struct { - pub const handler_fn = *const fn (c_int) align(1) callconv(.C) void; + pub const handler_fn = *align(1) const fn (c_int) callconv(.C) void; pub const sigaction_fn = *const fn (c_int, *const siginfo_t, ?*const anyopaque) callconv(.C) void; /// signal handler diff --git a/lib/std/c/solaris.zig b/lib/std/c/solaris.zig index ef64acd43b931a65a01fc57b2649906e10569172..838b6985cc0873a86ea8451a6b70733cf9e7ce5d 100644 --- a/lib/std/c/solaris.zig +++ b/lib/std/c/solaris.zig @@ -798,10 +798,10 @@ pub const winsize = extern struct { const NSIG = 75; pub const SIG = struct { - pub const DFL = @as(?Sigaction.handler_fn, @ptrFromInt(0)); - pub const ERR = @as(?Sigaction.handler_fn, @ptrFromInt(maxInt(usize))); - pub const IGN = @as(?Sigaction.handler_fn, @ptrFromInt(1)); - pub const HOLD = @as(?Sigaction.handler_fn, @ptrFromInt(2)); + pub const DFL: ?Sigaction.handler_fn = @ptrFromInt(0); + pub const ERR: ?Sigaction.handler_fn = @ptrFromInt(maxInt(usize)); + pub const IGN: ?Sigaction.handler_fn = @ptrFromInt(1); + pub const HOLD: ?Sigaction.handler_fn = @ptrFromInt(2); pub const WORDS = 4; pub const MAXSIG = 75; @@ -874,7 +874,7 @@ pub const SIG = struct { /// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall. pub const Sigaction = extern struct { - pub const handler_fn = *const fn (c_int) align(1) callconv(.C) void; + pub const handler_fn = *align(1) const fn (c_int) callconv(.C) void; pub const sigaction_fn = *const fn (c_int, *const siginfo_t, ?*const anyopaque) callconv(.C) void; /// signal options diff --git a/lib/std/meta.zig b/lib/std/meta.zig index f7b418d71d3104abfa82ff0e54ac70e650fc56d8..da0f629748ed53794585a27bba17f743802f6062 100644 --- a/lib/std/meta.zig +++ b/lib/std/meta.zig @@ -57,10 +57,9 @@ test stringToEnum { } /// Returns the alignment of type T. -/// Note that if T is a pointer or function type the result is different than -/// the one returned by @alignOf(T). +/// Note that if T is a pointer type the result is different than the one +/// returned by @alignOf(T). /// If T is a pointer type the alignment of the type it points to is returned. -/// If T is a function type the alignment a target-dependent value is returned. pub fn alignment(comptime T: type) comptime_int { return switch (@typeInfo(T)) { .Optional => |info| switch (@typeInfo(info.child)) { @@ -68,7 +67,6 @@ pub fn alignment(comptime T: type) comptime_int { else => @alignOf(T), }, .Pointer => |info| info.alignment, - .Fn => |info| info.alignment, else => @alignOf(T), }; } @@ -80,7 +78,8 @@ test alignment { try testing.expect(alignment([]align(1) u8) == 1); try testing.expect(alignment([]align(2) u8) == 2); try testing.expect(alignment(fn () void) > 0); - try testing.expect(alignment(fn () align(128) void) == 128); + try testing.expect(alignment(*const fn () void) > 0); + try testing.expect(alignment(*align(128) const fn () void) == 128); } /// Given a parameterized type (array, vector, pointer, optional), returns the "child type". diff --git a/lib/std/os/emscripten.zig b/lib/std/os/emscripten.zig index 04c3996f152f2087b8da8c90b5b01fcdba26539d..924b2dd0b21daee49444fee993176758d5e47fc6 100644 --- a/lib/std/os/emscripten.zig +++ b/lib/std/os/emscripten.zig @@ -689,13 +689,13 @@ pub const SIG = struct { pub const SYS = 31; pub const UNUSED = SIG.SYS; - pub const ERR = @as(?Sigaction.handler_fn, @ptrFromInt(std.math.maxInt(usize))); - pub const DFL = @as(?Sigaction.handler_fn, @ptrFromInt(0)); - pub const IGN = @as(?Sigaction.handler_fn, @ptrFromInt(1)); + pub const ERR: ?Sigaction.handler_fn = @ptrFromInt(std.math.maxInt(usize)); + pub const DFL: ?Sigaction.handler_fn = @ptrFromInt(0); + pub const IGN: ?Sigaction.handler_fn = @ptrFromInt(1); }; pub const Sigaction = extern struct { - pub const handler_fn = *const fn (c_int) align(1) callconv(.C) void; + pub const handler_fn = *align(1) const fn (c_int) callconv(.C) void; pub const sigaction_fn = *const fn (c_int, *const siginfo_t, ?*const anyopaque) callconv(.C) void; handler: extern union { diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index 3892e4326a558deae3c14e9ff4df6ea8ebbf4de9..a5cdab18ce8103a60c56b99994a22fe632fb15a2 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -1327,16 +1327,14 @@ pub fn flock(fd: fd_t, operation: i32) usize { return syscall2(.flock, @as(usize, @bitCast(@as(isize, fd))), @as(usize, @bitCast(@as(isize, operation)))); } -var vdso_clock_gettime = @as(?*const anyopaque, @ptrCast(&init_vdso_clock_gettime)); - // We must follow the C calling convention when we call into the VDSO -const vdso_clock_gettime_ty = *align(1) const fn (i32, *timespec) callconv(.C) usize; +const VdsoClockGettime = *align(1) const fn (i32, *timespec) callconv(.C) usize; +var vdso_clock_gettime: ?VdsoClockGettime = &init_vdso_clock_gettime; pub fn clock_gettime(clk_id: i32, tp: *timespec) usize { if (@hasDecl(VDSO, "CGT_SYM")) { - const ptr = @atomicLoad(?*const anyopaque, &vdso_clock_gettime, .unordered); - if (ptr) |fn_ptr| { - const f = @as(vdso_clock_gettime_ty, @ptrCast(fn_ptr)); + const ptr = @atomicLoad(?VdsoClockGettime, &vdso_clock_gettime, .unordered); + if (ptr) |f| { const rc = f(clk_id, tp); switch (rc) { 0, @as(usize, @bitCast(-@as(isize, @intFromEnum(E.INVAL)))) => return rc, @@ -1348,15 +1346,12 @@ pub fn clock_gettime(clk_id: i32, tp: *timespec) usize { } fn init_vdso_clock_gettime(clk: i32, ts: *timespec) callconv(.C) usize { - const ptr = @as(?*const anyopaque, @ptrFromInt(vdso.lookup(VDSO.CGT_VER, VDSO.CGT_SYM))); + const ptr: ?VdsoClockGettime = @ptrFromInt(vdso.lookup(VDSO.CGT_VER, VDSO.CGT_SYM)); // Note that we may not have a VDSO at all, update the stub address anyway // so that clock_gettime will fall back on the good old (and slow) syscall - @atomicStore(?*const anyopaque, &vdso_clock_gettime, ptr, .monotonic); + @atomicStore(?VdsoClockGettime, &vdso_clock_gettime, ptr, .monotonic); // Call into the VDSO if available - if (ptr) |fn_ptr| { - const f = @as(vdso_clock_gettime_ty, @ptrCast(fn_ptr)); - return f(clk, ts); - } + if (ptr) |f| return f(clk, ts); return @as(usize, @bitCast(-@as(isize, @intFromEnum(E.NOSYS)))); } @@ -2516,9 +2511,9 @@ pub const SIG = if (is_mips) struct { pub const SYS = 31; pub const UNUSED = SIG.SYS; - pub const ERR = @as(?Sigaction.handler_fn, @ptrFromInt(maxInt(usize))); - pub const DFL = @as(?Sigaction.handler_fn, @ptrFromInt(0)); - pub const IGN = @as(?Sigaction.handler_fn, @ptrFromInt(1)); + pub const ERR: ?Sigaction.handler_fn = @ptrFromInt(maxInt(usize)); + pub const DFL: ?Sigaction.handler_fn = @ptrFromInt(0); + pub const IGN: ?Sigaction.handler_fn = @ptrFromInt(1); } else if (is_sparc) struct { pub const BLOCK = 1; pub const UNBLOCK = 2; @@ -2560,9 +2555,9 @@ pub const SIG = if (is_mips) struct { pub const PWR = LOST; pub const IO = SIG.POLL; - pub const ERR = @as(?Sigaction.handler_fn, @ptrFromInt(maxInt(usize))); - pub const DFL = @as(?Sigaction.handler_fn, @ptrFromInt(0)); - pub const IGN = @as(?Sigaction.handler_fn, @ptrFromInt(1)); + pub const ERR: ?Sigaction.handler_fn = @ptrFromInt(maxInt(usize)); + pub const DFL: ?Sigaction.handler_fn = @ptrFromInt(0); + pub const IGN: ?Sigaction.handler_fn = @ptrFromInt(1); } else struct { pub const BLOCK = 0; pub const UNBLOCK = 1; @@ -2603,9 +2598,9 @@ pub const SIG = if (is_mips) struct { pub const SYS = 31; pub const UNUSED = SIG.SYS; - pub const ERR = @as(?Sigaction.handler_fn, @ptrFromInt(maxInt(usize))); - pub const DFL = @as(?Sigaction.handler_fn, @ptrFromInt(0)); - pub const IGN = @as(?Sigaction.handler_fn, @ptrFromInt(1)); + pub const ERR: ?Sigaction.handler_fn = @ptrFromInt(maxInt(usize)); + pub const DFL: ?Sigaction.handler_fn = @ptrFromInt(0); + pub const IGN: ?Sigaction.handler_fn = @ptrFromInt(1); }; pub const kernel_rwf = u32; @@ -3709,7 +3704,7 @@ pub const all_mask: sigset_t = [_]u32{0xffffffff} ** @typeInfo(sigset_t).Array.l pub const app_mask: sigset_t = [2]u32{ 0xfffffffc, 0x7fffffff } ++ [_]u32{0xffffffff} ** 30; const k_sigaction_funcs = struct { - const handler = ?*const fn (c_int) align(1) callconv(.C) void; + const handler = ?*align(1) const fn (c_int) callconv(.C) void; const restorer = *const fn () callconv(.C) void; }; @@ -3736,7 +3731,7 @@ pub const k_sigaction = switch (native_arch) { /// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall. pub const Sigaction = extern struct { - pub const handler_fn = *const fn (c_int) align(1) callconv(.C) void; + pub const handler_fn = *align(1) const fn (c_int) callconv(.C) void; pub const sigaction_fn = *const fn (c_int, *const siginfo_t, ?*const anyopaque) callconv(.C) void; handler: extern union { diff --git a/lib/std/zig/AstGen.zig b/lib/std/zig/AstGen.zig index 364e49ae8fba5fdea98e35009e9422efccded89f..54efe22b8d623c06bdd5b682354436bc766ba371 100644 --- a/lib/std/zig/AstGen.zig +++ b/lib/std/zig/AstGen.zig @@ -1369,16 +1369,16 @@ fn fnProtoExpr( break :is_var_args false; }; - const align_ref: Zir.Inst.Ref = if (fn_proto.ast.align_expr == 0) .none else inst: { - break :inst try expr(&block_scope, scope, coerced_align_ri, fn_proto.ast.align_expr); - }; + if (fn_proto.ast.align_expr != 0) { + return astgen.failNode(fn_proto.ast.align_expr, "function type cannot have an alignment", .{}); + } if (fn_proto.ast.addrspace_expr != 0) { - return astgen.failNode(fn_proto.ast.addrspace_expr, "addrspace not allowed on function prototypes", .{}); + return astgen.failNode(fn_proto.ast.addrspace_expr, "function type cannot have an addrspace", .{}); } if (fn_proto.ast.section_expr != 0) { - return astgen.failNode(fn_proto.ast.section_expr, "linksection not allowed on function prototypes", .{}); + return astgen.failNode(fn_proto.ast.section_expr, "function type cannot have a linksection", .{}); } const cc: Zir.Inst.Ref = if (fn_proto.ast.callconv_expr != 0) @@ -1394,7 +1394,7 @@ fn fnProtoExpr( const maybe_bang = tree.firstToken(fn_proto.ast.return_type) - 1; const is_inferred_error = token_tags[maybe_bang] == .bang; if (is_inferred_error) { - return astgen.failTok(maybe_bang, "function prototype may not have inferred error set", .{}); + return astgen.failTok(maybe_bang, "function type cannot have an inferred error set", .{}); } const ret_ty = try expr(&block_scope, scope, coerced_type_ri, fn_proto.ast.return_type); @@ -1403,7 +1403,7 @@ fn fnProtoExpr( .cc_ref = cc, .cc_gz = null, - .align_ref = align_ref, + .align_ref = .none, .align_gz = null, .ret_ref = ret_ty, .ret_gz = null, diff --git a/src/InternPool.zig b/src/InternPool.zig index 832927071e24bd66bdb609752ff7e82efff20adf..8611a6756476cd92a728254c3319b2c07706e3c7 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -765,16 +765,10 @@ pub const Key = union(enum) { /// Tells whether a parameter is noalias. See `paramIsNoalias` helper /// method for accessing this. noalias_bits: u32, - /// `none` indicates the function has the default alignment for - /// function code on the target. In this case, this field *must* be set - /// to `none`, otherwise the `InternPool` equality and hashing - /// functions will return incorrect results. - alignment: Alignment, cc: std.builtin.CallingConvention, is_var_args: bool, is_generic: bool, is_noinline: bool, - align_is_generic: bool, cc_is_generic: bool, section_is_generic: bool, addrspace_is_generic: bool, @@ -794,7 +788,6 @@ pub const Key = union(enum) { a.return_type == b.return_type and a.comptime_bits == b.comptime_bits and a.noalias_bits == b.noalias_bits and - a.alignment == b.alignment and a.cc == b.cc and a.is_var_args == b.is_var_args and a.is_generic == b.is_generic and @@ -808,7 +801,6 @@ pub const Key = union(enum) { std.hash.autoHash(hasher, self.return_type); std.hash.autoHash(hasher, self.comptime_bits); std.hash.autoHash(hasher, self.noalias_bits); - std.hash.autoHash(hasher, self.alignment); std.hash.autoHash(hasher, self.cc); std.hash.autoHash(hasher, self.is_var_args); std.hash.autoHash(hasher, self.is_generic); @@ -3587,18 +3579,16 @@ pub const Tag = enum(u8) { flags: Flags, pub const Flags = packed struct(u32) { - alignment: Alignment, cc: std.builtin.CallingConvention, is_var_args: bool, is_generic: bool, has_comptime_bits: bool, has_noalias_bits: bool, is_noinline: bool, - align_is_generic: bool, cc_is_generic: bool, section_is_generic: bool, addrspace_is_generic: bool, - _: u9 = 0, + _: u16 = 0, }; }; @@ -4918,11 +4908,9 @@ fn extraFuncType(ip: *const InternPool, extra_index: u32) Key.FuncType { .return_type = type_function.data.return_type, .comptime_bits = comptime_bits, .noalias_bits = noalias_bits, - .alignment = type_function.data.flags.alignment, .cc = type_function.data.flags.cc, .is_var_args = type_function.data.flags.is_var_args, .is_noinline = type_function.data.flags.is_noinline, - .align_is_generic = type_function.data.flags.align_is_generic, .cc_is_generic = type_function.data.flags.cc_is_generic, .section_is_generic = type_function.data.flags.section_is_generic, .addrspace_is_generic = type_function.data.flags.addrspace_is_generic, @@ -6211,8 +6199,6 @@ pub const GetFuncTypeKey = struct { comptime_bits: u32 = 0, noalias_bits: u32 = 0, /// `null` means generic. - alignment: ?Alignment = .none, - /// `null` means generic. cc: ?std.builtin.CallingConvention = .Unspecified, is_var_args: bool = false, is_generic: bool = false, @@ -6242,14 +6228,12 @@ pub fn getFuncType(ip: *InternPool, gpa: Allocator, key: GetFuncTypeKey) Allocat .params_len = params_len, .return_type = key.return_type, .flags = .{ - .alignment = key.alignment orelse .none, .cc = key.cc orelse .Unspecified, .is_var_args = key.is_var_args, .has_comptime_bits = key.comptime_bits != 0, .has_noalias_bits = key.noalias_bits != 0, .is_generic = key.is_generic, .is_noinline = key.is_noinline, - .align_is_generic = key.alignment == null, .cc_is_generic = key.cc == null, .section_is_generic = key.section_is_generic, .addrspace_is_generic = key.addrspace_is_generic, @@ -6433,14 +6417,12 @@ pub fn getFuncDeclIes(ip: *InternPool, gpa: Allocator, key: GetFuncDeclIesKey) A .params_len = params_len, .return_type = @enumFromInt(ip.items.len - 2), .flags = .{ - .alignment = key.alignment orelse .none, .cc = key.cc orelse .Unspecified, .is_var_args = key.is_var_args, .has_comptime_bits = key.comptime_bits != 0, .has_noalias_bits = key.noalias_bits != 0, .is_generic = key.is_generic, .is_noinline = key.is_noinline, - .align_is_generic = key.alignment == null, .cc_is_generic = key.cc == null, .section_is_generic = key.section_is_generic, .addrspace_is_generic = key.addrspace_is_generic, @@ -6553,7 +6535,6 @@ pub fn getFuncInstance(ip: *InternPool, gpa: Allocator, arg: GetFuncInstanceKey) .param_types = arg.param_types, .return_type = arg.bare_return_type, .noalias_bits = arg.noalias_bits, - .alignment = arg.alignment, .cc = arg.cc, .is_noinline = arg.is_noinline, }); @@ -6610,6 +6591,7 @@ pub fn getFuncInstance(ip: *InternPool, gpa: Allocator, arg: GetFuncInstanceKey) func_index, func_extra_index, func_ty, + arg.alignment, arg.section, ); } @@ -6673,14 +6655,12 @@ pub fn getFuncInstanceIes( .params_len = params_len, .return_type = error_union_type, .flags = .{ - .alignment = arg.alignment, .cc = arg.cc, .is_var_args = false, .has_comptime_bits = false, .has_noalias_bits = arg.noalias_bits != 0, .is_generic = false, .is_noinline = arg.is_noinline, - .align_is_generic = false, .cc_is_generic = false, .section_is_generic = false, .addrspace_is_generic = false, @@ -6741,6 +6721,7 @@ pub fn getFuncInstanceIes( func_index, func_extra_index, func_ty, + arg.alignment, arg.section, ); } @@ -6752,6 +6733,7 @@ fn finishFuncInstance( func_index: Index, func_extra_index: u32, func_ty: Index, + alignment: Alignment, section: OptionalNullTerminatedString, ) Allocator.Error!Index { const fn_owner_decl = ip.declPtr(ip.funcDeclOwner(generic_owner)); @@ -6764,7 +6746,7 @@ fn finishFuncInstance( .owns_tv = true, .ty = @import("type.zig").Type.fromInterned(func_ty), .val = @import("Value.zig").fromInterned(func_index), - .alignment = .none, + .alignment = alignment, .@"linksection" = section, .@"addrspace" = fn_owner_decl.@"addrspace", .analysis = .complete, diff --git a/src/Module.zig b/src/Module.zig index 8f6def21ae6d04d05c6152c6450cfbc1ad0b0c6f..bfc5a35e101ae6f3a0c099369e32f235fc42b81d 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -3596,6 +3596,18 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !SemaDeclResult { log.debug("semaDecl '{d}'", .{@intFromEnum(decl_index)}); + const old_has_tv = decl.has_tv; + // The following values are ignored if `!old_has_tv` + const old_ty = decl.ty; + const old_val = decl.val; + const old_align = decl.alignment; + const old_linksection = decl.@"linksection"; + const old_addrspace = decl.@"addrspace"; + const old_is_inline = if (decl.getOwnedFunction(mod)) |prev_func| + prev_func.analysis(ip).state == .inline_only + else + false; + const decl_inst = decl.zir_decl_index.unwrap().?.resolve(ip); const gpa = mod.gpa; @@ -3733,141 +3745,96 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !SemaDeclResult { }; } - switch (ip.indexToKey(decl_tv.val.toIntern())) { - .func => |func| { - const owns_tv = func.owner_decl == decl_index; - if (owns_tv) { - var prev_type_has_bits = false; - var prev_is_inline = false; - var type_changed = true; - - if (decl.has_tv) { - prev_type_has_bits = decl.ty.isFnOrHasRuntimeBits(mod); - type_changed = !decl.ty.eql(decl_tv.ty, mod); - if (decl.getOwnedFunction(mod)) |prev_func| { - prev_is_inline = prev_func.analysis(ip).state == .inline_only; - } - } - - decl.ty = decl_tv.ty; - decl.val = Value.fromInterned((try decl_tv.val.intern(decl_tv.ty, mod))); - // linksection, align, and addrspace were already set by Sema - decl.has_tv = true; - decl.owns_tv = owns_tv; - decl.analysis = .complete; - - const is_inline = decl.ty.fnCallingConvention(mod) == .Inline; - if (decl.is_exported) { - const export_src: LazySrcLoc = .{ .token_offset = @intFromBool(decl.is_pub) }; - if (is_inline) { - return sema.fail(&block_scope, export_src, "export of inline function", .{}); - } - // The scope needs to have the decl in it. - try sema.analyzeExport(&block_scope, export_src, .{ .name = decl.name }, decl_index); - } - // TODO: align, linksection, addrspace? - const changed = type_changed or is_inline != prev_is_inline; - return .{ - .invalidate_decl_val = changed, - .invalidate_decl_ref = changed, - }; - } - }, - else => {}, - } - - decl.owns_tv = false; - var queue_linker_work = false; - var is_extern = false; + var queue_linker_work = true; + var is_func = false; + var is_inline = false; switch (decl_tv.val.toIntern()) { .generic_poison => unreachable, .unreachable_value => unreachable, else => switch (ip.indexToKey(decl_tv.val.toIntern())) { - .variable => |variable| if (variable.decl == decl_index) { - decl.owns_tv = true; - queue_linker_work = true; + .variable => |variable| { + decl.owns_tv = variable.decl == decl_index; + queue_linker_work = decl.owns_tv; }, - .extern_func => |extern_fn| if (extern_fn.decl == decl_index) { - decl.owns_tv = true; - queue_linker_work = true; - is_extern = true; + .extern_func => |extern_func| { + decl.owns_tv = extern_func.decl == decl_index; + queue_linker_work = decl.owns_tv; + is_func = decl.owns_tv; }, - .func => {}, - - else => { - queue_linker_work = true; + .func => |func| { + decl.owns_tv = func.owner_decl == decl_index; + queue_linker_work = false; + is_inline = decl.owns_tv and decl_tv.ty.fnCallingConvention(mod) == .Inline; + is_func = decl.owns_tv; }, + + else => {}, }, } - const old_has_tv = decl.has_tv; - // The following values are ignored if `!old_has_tv` - const old_ty = decl.ty; - const old_val = decl.val; - const old_align = decl.alignment; - const old_linksection = decl.@"linksection"; - const old_addrspace = decl.@"addrspace"; - decl.ty = decl_tv.ty; decl.val = Value.fromInterned((try decl_tv.val.intern(decl_tv.ty, mod))); - decl.alignment = blk: { - const align_body = decl_bodies.align_body orelse break :blk .none; - const align_ref = try sema.resolveInlineBody(&block_scope, align_body, decl_inst); - break :blk try sema.analyzeAsAlign(&block_scope, align_src, align_ref); - }; - decl.@"linksection" = blk: { - const linksection_body = decl_bodies.linksection_body orelse break :blk .none; - const linksection_ref = try sema.resolveInlineBody(&block_scope, linksection_body, decl_inst); - const bytes = try sema.toConstString(&block_scope, section_src, linksection_ref, .{ - .needed_comptime_reason = "linksection must be comptime-known", - }); - if (mem.indexOfScalar(u8, bytes, 0) != null) { - return sema.fail(&block_scope, section_src, "linksection cannot contain null bytes", .{}); - } else if (bytes.len == 0) { - return sema.fail(&block_scope, section_src, "linksection cannot be empty", .{}); - } - const section = try ip.getOrPutString(gpa, bytes); - break :blk section.toOptional(); - }; - decl.@"addrspace" = blk: { - const addrspace_ctx: Sema.AddressSpaceContext = switch (ip.indexToKey(decl_tv.val.toIntern())) { - .variable => .variable, - .extern_func, .func => .function, - else => .constant, + // Function linksection, align, and addrspace were already set by Sema + if (!is_func) { + decl.alignment = blk: { + const align_body = decl_bodies.align_body orelse break :blk .none; + const align_ref = try sema.resolveInlineBody(&block_scope, align_body, decl_inst); + break :blk try sema.analyzeAsAlign(&block_scope, align_src, align_ref); }; + decl.@"linksection" = blk: { + const linksection_body = decl_bodies.linksection_body orelse break :blk .none; + const linksection_ref = try sema.resolveInlineBody(&block_scope, linksection_body, decl_inst); + const bytes = try sema.toConstString(&block_scope, section_src, linksection_ref, .{ + .needed_comptime_reason = "linksection must be comptime-known", + }); + if (mem.indexOfScalar(u8, bytes, 0) != null) { + return sema.fail(&block_scope, section_src, "linksection cannot contain null bytes", .{}); + } else if (bytes.len == 0) { + return sema.fail(&block_scope, section_src, "linksection cannot be empty", .{}); + } + const section = try ip.getOrPutString(gpa, bytes); + break :blk section.toOptional(); + }; + decl.@"addrspace" = blk: { + const addrspace_ctx: Sema.AddressSpaceContext = switch (ip.indexToKey(decl_tv.val.toIntern())) { + .variable => .variable, + .extern_func, .func => .function, + else => .constant, + }; - const target = sema.mod.getTarget(); + const target = sema.mod.getTarget(); - const addrspace_body = decl_bodies.addrspace_body orelse break :blk switch (addrspace_ctx) { - .function => target_util.defaultAddressSpace(target, .function), - .variable => target_util.defaultAddressSpace(target, .global_mutable), - .constant => target_util.defaultAddressSpace(target, .global_constant), - else => unreachable, + const addrspace_body = decl_bodies.addrspace_body orelse break :blk switch (addrspace_ctx) { + .function => target_util.defaultAddressSpace(target, .function), + .variable => target_util.defaultAddressSpace(target, .global_mutable), + .constant => target_util.defaultAddressSpace(target, .global_constant), + else => unreachable, + }; + const addrspace_ref = try sema.resolveInlineBody(&block_scope, addrspace_body, decl_inst); + break :blk try sema.analyzeAsAddressSpace(&block_scope, address_space_src, addrspace_ref, addrspace_ctx); }; - const addrspace_ref = try sema.resolveInlineBody(&block_scope, addrspace_body, decl_inst); - break :blk try sema.analyzeAsAddressSpace(&block_scope, address_space_src, addrspace_ref, addrspace_ctx); - }; + } decl.has_tv = true; decl.analysis = .complete; const result: SemaDeclResult = if (old_has_tv) .{ - .invalidate_decl_val = !decl.ty.eql(old_ty, mod) or !decl.val.eql(old_val, decl.ty, mod), + .invalidate_decl_val = !decl.ty.eql(old_ty, mod) or + !decl.val.eql(old_val, decl.ty, mod) or + is_inline != old_is_inline, .invalidate_decl_ref = !decl.ty.eql(old_ty, mod) or decl.alignment != old_align or decl.@"linksection" != old_linksection or - decl.@"addrspace" != old_addrspace, + decl.@"addrspace" != old_addrspace or + is_inline != old_is_inline, } else .{ .invalidate_decl_val = true, .invalidate_decl_ref = true, }; - const has_runtime_bits = is_extern or - (queue_linker_work and try sema.typeHasRuntimeBits(decl.ty)); - + const has_runtime_bits = queue_linker_work and (is_func or try sema.typeHasRuntimeBits(decl.ty)); if (has_runtime_bits) { - // Needed for codegen_decl which will call updateDecl and then the // codegen backend wants full access to the Decl Type. try sema.resolveTypeFully(decl.ty); @@ -3881,6 +3848,7 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !SemaDeclResult { if (decl.is_exported) { const export_src: LazySrcLoc = .{ .token_offset = @intFromBool(decl.is_pub) }; + if (is_inline) return sema.fail(&block_scope, export_src, "export of inline function", .{}); // The scope needs to have the decl in it. try sema.analyzeExport(&block_scope, export_src, .{ .name = decl.name }, decl_index); } diff --git a/src/Sema.zig b/src/Sema.zig index 54d5e6df7dae06ab5b8a75b3059caa4ad7350aab..cf103e7230915b248b42e519c230f275b31c62a8 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -7605,7 +7605,6 @@ fn analyzeCall( .param_types = new_param_types, .return_type = owner_info.return_type, .noalias_bits = owner_info.noalias_bits, - .alignment = if (owner_info.align_is_generic) null else owner_info.alignment, .cc = if (owner_info.cc_is_generic) null else owner_info.cc, .is_var_args = owner_info.is_var_args, .is_noinline = owner_info.is_noinline, @@ -9629,7 +9628,6 @@ fn funcCommon( .comptime_bits = comptime_bits, .return_type = bare_return_type.toIntern(), .cc = cc, - .alignment = alignment, .section_is_generic = section == .generic, .addrspace_is_generic = address_space == null, .is_var_args = var_args, @@ -9640,6 +9638,7 @@ fn funcCommon( if (is_extern) { assert(comptime_bits == 0); assert(cc != null); + assert(alignment != null); assert(section != .generic); assert(address_space != null); assert(!is_generic); @@ -17623,8 +17622,6 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const field_values = .{ // calling_convention: CallingConvention, (try mod.enumValueFieldIndex(callconv_ty, @intFromEnum(func_ty_info.cc))).toIntern(), - // alignment: comptime_int, - (try mod.intValue(Type.comptime_int, ty.abiAlignment(mod).toByteUnits(0))).toIntern(), // is_generic: bool, Value.makeBool(func_ty_info.is_generic).toIntern(), // is_var_args: bool, @@ -19701,12 +19698,6 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air if (inst_data.size != .One) { return sema.fail(block, elem_ty_src, "function pointers must be single pointers", .{}); } - const fn_align = mod.typeToFunc(elem_ty).?.alignment; - if (inst_data.flags.has_align and abi_align != .none and fn_align != .none and - abi_align != fn_align) - { - return sema.fail(block, align_src, "function pointer alignment disagrees with function alignment", .{}); - } } else if (inst_data.size == .Many and elem_ty.zigTypeTag(mod) == .Opaque) { return sema.fail(block, elem_ty_src, "unknown-length pointer to opaque not allowed", .{}); } else if (inst_data.size == .C) { @@ -21030,7 +21021,6 @@ fn zirReify( .needed_comptime_reason = "operand to @Type must be comptime-known", }); const union_val = ip.indexToKey(val.toIntern()).un; - const target = mod.getTarget(); if (try Value.fromInterned(union_val.val).anyUndef(mod)) return sema.failWithUseOfUndef(block, src); const tag_index = type_info_ty.unionTagFieldIndex(Value.fromInterned(union_val.tag), mod).?; switch (@as(std.builtin.TypeId, @enumFromInt(tag_index))) { @@ -21171,12 +21161,6 @@ fn zirReify( if (ptr_size != .One) { return sema.fail(block, src, "function pointers must be single pointers", .{}); } - const fn_align = mod.typeToFunc(elem_ty).?.alignment; - if (abi_align != .none and fn_align != .none and - abi_align != fn_align) - { - return sema.fail(block, src, "function pointer alignment disagrees with function alignment", .{}); - } } else if (ptr_size == .Many and elem_ty.zigTypeTag(mod) == .Opaque) { return sema.fail(block, src, "unknown-length pointer to opaque not allowed", .{}); } else if (ptr_size == .C) { @@ -21429,10 +21413,6 @@ fn zirReify( ip, try ip.getOrPutString(gpa, "calling_convention"), ).?); - const alignment_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, "alignment"), - ).?); const is_generic_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( ip, try ip.getOrPutString(gpa, "is_generic"), @@ -21461,11 +21441,6 @@ fn zirReify( try sema.checkCallConvSupportsVarArgs(block, src, cc); } - const alignment = alignment: { - const alignment = try sema.validateAlignAllowZero(block, src, try alignment_val.toUnsignedIntAdvanced(sema)); - const default = target_util.defaultFunctionAlignment(target); - break :alignment if (alignment == default) .none else alignment; - }; const return_type = return_type_val.optionalValue(mod) orelse return sema.fail(block, src, "Type.Fn.return_type must be non-null for @Type", .{}); @@ -21510,7 +21485,6 @@ fn zirReify( .param_types = param_types, .noalias_bits = noalias_bits, .return_type = return_type.toIntern(), - .alignment = alignment, .cc = cc, .is_var_args = is_var_args, }); @@ -32536,16 +32510,21 @@ fn analyzeDeclRefInner(sema: *Sema, decl_index: InternPool.DeclIndex, analyze_fn const mod = sema.mod; try sema.ensureDeclAnalyzed(decl_index); - const decl = mod.declPtr(decl_index); - const decl_tv = try decl.typedValue(); + const decl_tv = try mod.declPtr(decl_index).typedValue(); + const owner_decl = mod.declPtr(switch (mod.intern_pool.indexToKey(decl_tv.val.toIntern())) { + .variable => |variable| variable.decl, + .extern_func => |extern_func| extern_func.decl, + .func => |func| func.owner_decl, + else => decl_index, + }); // TODO: if this is a `decl_ref` of a non-variable decl, only depend on decl type try sema.declareDependency(.{ .decl_val = decl_index }); const ptr_ty = try sema.ptrType(.{ .child = decl_tv.ty.toIntern(), .flags = .{ - .alignment = decl.alignment, - .is_const = if (decl.val.getVariable(mod)) |variable| variable.is_const else true, - .address_space = decl.@"addrspace", + .alignment = owner_decl.alignment, + .is_const = if (decl_tv.val.getVariable(mod)) |variable| variable.is_const else true, + .address_space = owner_decl.@"addrspace", }, }); if (analyze_fn_body) { diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 8d630480e2e65080abacb9029610fd632d91d6cc..928cc995dc67595e951bd34d9c5bf04f4ae48cb1 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1635,7 +1635,7 @@ pub const DeclGen = struct { switch (kind) { .forward => {}, - .complete => if (fn_info.alignment.toByteUnitsOptional()) |a| { + .complete => if (fn_decl.alignment.toByteUnitsOptional()) |a| { try w.print("{}zig_align_fn({})", .{ trailing, a }); trailing = .maybe_space; }, @@ -1666,7 +1666,7 @@ pub const DeclGen = struct { switch (kind) { .forward => { - if (fn_info.alignment.toByteUnitsOptional()) |a| { + if (fn_decl.alignment.toByteUnitsOptional()) |a| { try w.print(" zig_align_fn({})", .{a}); } switch (name) { diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 6273ce0942e9a30549343f736cec150308776770..d159a531751859a28e64d0d420ff46d77d87814a 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -2952,8 +2952,8 @@ pub const Object = struct { else => function_index.setCallConv(toLlvmCallConv(fn_info.cc, target), &o.builder), } - if (fn_info.alignment != .none) - function_index.setAlignment(fn_info.alignment.toLlvm(), &o.builder); + if (decl.alignment != .none) + function_index.setAlignment(decl.alignment.toLlvm(), &o.builder); // Function attributes that are independent of analysis results of the function body. try o.addCommonFnAttributes(&attributes, owner_mod); diff --git a/src/main.zig b/src/main.zig index 8c445fdd1e2dd9c36212a6c16ee07d2c6e8a0f0e..db76f7605ca6ecdf4336444241579b8d3f346dca 100644 --- a/src/main.zig +++ b/src/main.zig @@ -4995,6 +4995,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { } else .{ .root = .{ .root_dir = zig_lib_directory, + .sub_path = "compiler", }, .root_src_path = "build_runner.zig", }; diff --git a/src/type.zig b/src/type.zig index 664498e353d6e6902bc0d65437b5f15a65bc872d..9ea16d6224f6196a48315544fd3b80fe023b22c8 100644 --- a/src/type.zig +++ b/src/type.zig @@ -396,9 +396,6 @@ pub const Type = struct { try writer.writeAll("..."); } try writer.writeAll(") "); - if (fn_info.alignment.toByteUnitsOptional()) |a| { - try writer.print("align({d}) ", .{a}); - } if (fn_info.cc != .Unspecified) { try writer.writeAll("callconv(."); try writer.writeAll(@tagName(fn_info.cc)); @@ -949,12 +946,7 @@ pub const Type = struct { }, // represents machine code; not a pointer - .func_type => |func_type| return .{ - .scalar = if (func_type.alignment != .none) - func_type.alignment - else - target_util.defaultFunctionAlignment(target), - }, + .func_type => return .{ .scalar = target_util.defaultFunctionAlignment(target) }, .simple_type => |t| switch (t) { .bool, diff --git a/stage1/zig.h b/stage1/zig.h index acd7b1d700ff0811b35f7dfe2755c454576f5613..7a1c69575a24928b97cb232abb04ee9cf5f26957 100644 --- a/stage1/zig.h +++ b/stage1/zig.h @@ -25,11 +25,15 @@ typedef char bool; #endif #endif +#define zig_concat(lhs, rhs) lhs##rhs +#define zig_expand_concat(lhs, rhs) zig_concat(lhs, rhs) + #if defined(__has_builtin) #define zig_has_builtin(builtin) __has_builtin(__builtin_##builtin) #else #define zig_has_builtin(builtin) 0 #endif +#define zig_expand_has_builtin(b) zig_has_builtin(b) #if defined(__has_attribute) #define zig_has_attribute(attribute) __has_attribute(attribute) @@ -112,7 +116,7 @@ typedef char bool; #define zig_never_tail zig_never_tail_unavailable #endif -#if zig_has_attribute(always_inline) +#if zig_has_attribute(musttail) #define zig_always_tail __attribute__((musttail)) #else #define zig_always_tail zig_always_tail_unavailable @@ -180,20 +184,58 @@ typedef char bool; #define zig_extern extern #endif -#if zig_has_attribute(alias) -#define zig_export(sig, symbol, name) zig_extern sig __attribute__((alias(symbol))) -#elif _MSC_VER +#if _MSC_VER #if _M_X64 -#define zig_export(sig, symbol, name) sig;\ - __pragma(comment(linker, "/alternatename:" name "=" symbol )) +#define zig_mangle_c(symbol) symbol #else /*_M_X64 */ -#define zig_export(sig, symbol, name) sig;\ - __pragma(comment(linker, "/alternatename:_" name "=_" symbol )) +#define zig_mangle_c(symbol) "_" symbol #endif /*_M_X64 */ +#else /* _MSC_VER */ +#if __APPLE__ +#define zig_mangle_c(symbol) "_" symbol +#else /* __APPLE__ */ +#define zig_mangle_c(symbol) symbol +#endif /* __APPLE__ */ +#endif /* _MSC_VER */ + +#if zig_has_attribute(alias) && !__APPLE__ +#define zig_export(symbol, name) __attribute__((alias(symbol))) +#elif _MSC_VER +#define zig_export(symbol, name) ; \ + __pragma(comment(linker, "/alternatename:" zig_mangle_c(name) "=" zig_mangle_c(symbol))) #else -#define zig_export(sig, symbol, name) __asm(name " = " symbol) +#define zig_export(symbol, name) ; \ + __asm(zig_mangle_c(name) " = " zig_mangle_c(symbol)) #endif +#if _MSC_VER +#define zig_mangled_tentative(mangled, unmangled) +#define zig_mangled_final(mangled, unmangled) ; \ + zig_export(#mangled, unmangled) +#define zig_mangled_export(mangled, unmangled, symbol) \ + zig_export(unmangled, #mangled) \ + zig_export(symbol, unmangled) +#else /* _MSC_VER */ +#define zig_mangled_tentative(mangled, unmangled) __asm(zig_mangle_c(unmangled)) +#define zig_mangled_final(mangled, unmangled) zig_mangled_tentative(mangled, unmangled) +#define zig_mangled_export(mangled, unmangled, symbol) \ + zig_mangled_final(mangled, unmangled) \ + zig_export(symbol, unmangled) +#endif /* _MSC_VER */ + +#if _MSC_VER +#define zig_import(Type, fn_name, libc_name, sig_args, call_args) zig_extern Type fn_name sig_args;\ + __pragma(comment(linker, "/alternatename:" zig_mangle_c(#fn_name) "=" zig_mangle_c(#libc_name))); +#define zig_import_builtin(Type, fn_name, libc_name, sig_args, call_args) zig_import(Type, fn_name, sig_args, call_args) +#else /* _MSC_VER */ +#define zig_import(Type, fn_name, libc_name, sig_args, call_args) zig_extern Type fn_name sig_args __asm(zig_mangle_c(#libc_name)); +#define zig_import_builtin(Type, fn_name, libc_name, sig_args, call_args) zig_extern Type libc_name sig_args; \ + static inline Type fn_name sig_args { return libc_name call_args; } +#endif + +#define zig_expand_import_0(Type, fn_name, libc_name, sig_args, call_args) zig_import(Type, fn_name, libc_name, sig_args, call_args) +#define zig_expand_import_1(Type, fn_name, libc_name, sig_args, call_args) zig_import_builtin(Type, fn_name, libc_name, sig_args, call_args) + #if zig_has_attribute(weak) || defined(zig_gnuc) #define zig_weak_linkage __attribute__((weak)) #define zig_weak_linkage_fn __attribute__((weak)) @@ -267,9 +309,6 @@ typedef char bool; #define zig_wasm_memory_grow(index, delta) zig_unimplemented() #endif -#define zig_concat(lhs, rhs) lhs##rhs -#define zig_expand_concat(lhs, rhs) zig_concat(lhs, rhs) - #if __STDC_VERSION__ >= 201112L #define zig_noreturn _Noreturn #elif zig_has_attribute(noreturn) || defined(zig_gnuc) @@ -2163,7 +2202,7 @@ static inline bool zig_addo_big(void *res, const void *lhs, const void *rhs, boo const uint8_t *rhs_bytes = rhs; uint16_t byte_offset = 0; uint16_t remaining_bytes = zig_int_bytes(bits); - uint16_t top_bits = remaining_bytes * 8 - bits; + uint8_t top_bits = (uint8_t)(remaining_bytes * 8 - bits); bool overflow = false; #if zig_big_endian @@ -2171,7 +2210,7 @@ static inline bool zig_addo_big(void *res, const void *lhs, const void *rhs, boo #endif while (remaining_bytes >= 128 / CHAR_BIT) { - uint16_t limb_bits = 128 - (remaining_bytes == 128 / CHAR_BIT ? top_bits : 0); + uint8_t limb_bits = 128 - (remaining_bytes == 128 / CHAR_BIT ? top_bits : 0); #if zig_big_endian byte_offset -= 128 / CHAR_BIT; @@ -2211,7 +2250,7 @@ static inline bool zig_addo_big(void *res, const void *lhs, const void *rhs, boo } while (remaining_bytes >= 64 / CHAR_BIT) { - uint16_t limb_bits = 64 - (remaining_bytes == 64 / CHAR_BIT ? top_bits : 0); + uint8_t limb_bits = 64 - (remaining_bytes == 64 / CHAR_BIT ? top_bits : 0); #if zig_big_endian byte_offset -= 64 / CHAR_BIT; @@ -2251,7 +2290,7 @@ static inline bool zig_addo_big(void *res, const void *lhs, const void *rhs, boo } while (remaining_bytes >= 32 / CHAR_BIT) { - uint16_t limb_bits = 32 - (remaining_bytes == 32 / CHAR_BIT ? top_bits : 0); + uint8_t limb_bits = 32 - (remaining_bytes == 32 / CHAR_BIT ? top_bits : 0); #if zig_big_endian byte_offset -= 32 / CHAR_BIT; @@ -2291,7 +2330,7 @@ static inline bool zig_addo_big(void *res, const void *lhs, const void *rhs, boo } while (remaining_bytes >= 16 / CHAR_BIT) { - uint16_t limb_bits = 16 - (remaining_bytes == 16 / CHAR_BIT ? top_bits : 0); + uint8_t limb_bits = 16 - (remaining_bytes == 16 / CHAR_BIT ? top_bits : 0); #if zig_big_endian byte_offset -= 16 / CHAR_BIT; @@ -2331,7 +2370,7 @@ static inline bool zig_addo_big(void *res, const void *lhs, const void *rhs, boo } while (remaining_bytes >= 8 / CHAR_BIT) { - uint16_t limb_bits = 8 - (remaining_bytes == 8 / CHAR_BIT ? top_bits : 0); + uint8_t limb_bits = 8 - (remaining_bytes == 8 / CHAR_BIT ? top_bits : 0); #if zig_big_endian byte_offset -= 8 / CHAR_BIT; @@ -2379,7 +2418,7 @@ static inline bool zig_subo_big(void *res, const void *lhs, const void *rhs, boo const uint8_t *rhs_bytes = rhs; uint16_t byte_offset = 0; uint16_t remaining_bytes = zig_int_bytes(bits); - uint16_t top_bits = remaining_bytes * 8 - bits; + uint8_t top_bits = (uint8_t)(remaining_bytes * 8 - bits); bool overflow = false; #if zig_big_endian @@ -2387,7 +2426,7 @@ static inline bool zig_subo_big(void *res, const void *lhs, const void *rhs, boo #endif while (remaining_bytes >= 128 / CHAR_BIT) { - uint16_t limb_bits = 128 - (remaining_bytes == 128 / CHAR_BIT ? top_bits : 0); + uint8_t limb_bits = 128 - (remaining_bytes == 128 / CHAR_BIT ? top_bits : 0); #if zig_big_endian byte_offset -= 128 / CHAR_BIT; @@ -2427,7 +2466,7 @@ static inline bool zig_subo_big(void *res, const void *lhs, const void *rhs, boo } while (remaining_bytes >= 64 / CHAR_BIT) { - uint16_t limb_bits = 64 - (remaining_bytes == 64 / CHAR_BIT ? top_bits : 0); + uint8_t limb_bits = 64 - (remaining_bytes == 64 / CHAR_BIT ? top_bits : 0); #if zig_big_endian byte_offset -= 64 / CHAR_BIT; @@ -2467,7 +2506,7 @@ static inline bool zig_subo_big(void *res, const void *lhs, const void *rhs, boo } while (remaining_bytes >= 32 / CHAR_BIT) { - uint16_t limb_bits = 32 - (remaining_bytes == 32 / CHAR_BIT ? top_bits : 0); + uint8_t limb_bits = 32 - (remaining_bytes == 32 / CHAR_BIT ? top_bits : 0); #if zig_big_endian byte_offset -= 32 / CHAR_BIT; @@ -2507,7 +2546,7 @@ static inline bool zig_subo_big(void *res, const void *lhs, const void *rhs, boo } while (remaining_bytes >= 16 / CHAR_BIT) { - uint16_t limb_bits = 16 - (remaining_bytes == 16 / CHAR_BIT ? top_bits : 0); + uint8_t limb_bits = 16 - (remaining_bytes == 16 / CHAR_BIT ? top_bits : 0); #if zig_big_endian byte_offset -= 16 / CHAR_BIT; @@ -2547,7 +2586,7 @@ static inline bool zig_subo_big(void *res, const void *lhs, const void *rhs, boo } while (remaining_bytes >= 8 / CHAR_BIT) { - uint16_t limb_bits = 8 - (remaining_bytes == 8 / CHAR_BIT ? top_bits : 0); + uint8_t limb_bits = 8 - (remaining_bytes == 8 / CHAR_BIT ? top_bits : 0); #if zig_big_endian byte_offset -= 8 / CHAR_BIT; @@ -3093,6 +3132,7 @@ ypedef uint32_t zig_f32; #define zig_has_f64 1 #define zig_libc_name_f64(name) name + #if _MSC_VER #define zig_init_special_f64(sign, name, arg, repr) sign zig_make_f64(zig_msvc_flt_##name, ) #else @@ -3336,31 +3376,31 @@ zig_float_negate_builtin(128, zig_make_u128, (UINT64_C(1) << 63, UINT64_C(0))) zig_expand_concat(zig_float_binary_builtin_, zig_has_f##w)(f##w, sub, -) \ zig_expand_concat(zig_float_binary_builtin_, zig_has_f##w)(f##w, mul, *) \ zig_expand_concat(zig_float_binary_builtin_, zig_has_f##w)(f##w, div, /) \ - zig_extern zig_f##w zig_libc_name_f##w(sqrt)(zig_f##w); \ - zig_extern zig_f##w zig_libc_name_f##w(sin)(zig_f##w); \ - zig_extern zig_f##w zig_libc_name_f##w(cos)(zig_f##w); \ - zig_extern zig_f##w zig_libc_name_f##w(tan)(zig_f##w); \ - zig_extern zig_f##w zig_libc_name_f##w(exp)(zig_f##w); \ - zig_extern zig_f##w zig_libc_name_f##w(exp2)(zig_f##w); \ - zig_extern zig_f##w zig_libc_name_f##w(log)(zig_f##w); \ - zig_extern zig_f##w zig_libc_name_f##w(log2)(zig_f##w); \ - zig_extern zig_f##w zig_libc_name_f##w(log10)(zig_f##w); \ - zig_extern zig_f##w zig_libc_name_f##w(fabs)(zig_f##w); \ - zig_extern zig_f##w zig_libc_name_f##w(floor)(zig_f##w); \ - zig_extern zig_f##w zig_libc_name_f##w(ceil)(zig_f##w); \ - zig_extern zig_f##w zig_libc_name_f##w(round)(zig_f##w); \ - zig_extern zig_f##w zig_libc_name_f##w(trunc)(zig_f##w); \ - zig_extern zig_f##w zig_libc_name_f##w(fmod)(zig_f##w, zig_f##w); \ - zig_extern zig_f##w zig_libc_name_f##w(fmin)(zig_f##w, zig_f##w); \ - zig_extern zig_f##w zig_libc_name_f##w(fmax)(zig_f##w, zig_f##w); \ - zig_extern zig_f##w zig_libc_name_f##w(fma)(zig_f##w, zig_f##w, zig_f##w); \ + zig_expand_concat(zig_expand_import_, zig_expand_has_builtin(zig_libc_name_f##w(sqrt)))(zig_f##w, zig_float_fn_f##w##_sqrt, zig_libc_name_f##w(sqrt), (zig_f##w x), (x)) \ + zig_expand_concat(zig_expand_import_, zig_expand_has_builtin(zig_libc_name_f##w(sin)))(zig_f##w, zig_float_fn_f##w##_sin, zig_libc_name_f##w(sin), (zig_f##w x), (x)) \ + zig_expand_concat(zig_expand_import_, zig_expand_has_builtin(zig_libc_name_f##w(cos)))(zig_f##w, zig_float_fn_f##w##_cos, zig_libc_name_f##w(cos), (zig_f##w x), (x)) \ + zig_expand_concat(zig_expand_import_, zig_expand_has_builtin(zig_libc_name_f##w(tan)))(zig_f##w, zig_float_fn_f##w##_tan, zig_libc_name_f##w(tan), (zig_f##w x), (x)) \ + zig_expand_concat(zig_expand_import_, zig_expand_has_builtin(zig_libc_name_f##w(exp)))(zig_f##w, zig_float_fn_f##w##_exp, zig_libc_name_f##w(exp), (zig_f##w x), (x)) \ + zig_expand_concat(zig_expand_import_, zig_expand_has_builtin(zig_libc_name_f##w(exp2)))(zig_f##w, zig_float_fn_f##w##_exp2, zig_libc_name_f##w(exp2), (zig_f##w x), (x)) \ + zig_expand_concat(zig_expand_import_, zig_expand_has_builtin(zig_libc_name_f##w(log)))(zig_f##w, zig_float_fn_f##w##_log, zig_libc_name_f##w(log), (zig_f##w x), (x)) \ + zig_expand_concat(zig_expand_import_, zig_expand_has_builtin(zig_libc_name_f##w(log2)))(zig_f##w, zig_float_fn_f##w##_log2, zig_libc_name_f##w(log2), (zig_f##w x), (x)) \ + zig_expand_concat(zig_expand_import_, zig_expand_has_builtin(zig_libc_name_f##w(log10)))(zig_f##w, zig_float_fn_f##w##_log10, zig_libc_name_f##w(log10), (zig_f##w x), (x)) \ + zig_expand_concat(zig_expand_import_, zig_expand_has_builtin(zig_libc_name_f##w(fabs)))(zig_f##w, zig_float_fn_f##w##_fabs, zig_libc_name_f##w(fabs), (zig_f##w x), (x)) \ + zig_expand_concat(zig_expand_import_, zig_expand_has_builtin(zig_libc_name_f##w(floor)))(zig_f##w, zig_float_fn_f##w##_floor, zig_libc_name_f##w(floor), (zig_f##w x), (x)) \ + zig_expand_concat(zig_expand_import_, zig_expand_has_builtin(zig_libc_name_f##w(ceil)))(zig_f##w, zig_float_fn_f##w##_ceil, zig_libc_name_f##w(ceil), (zig_f##w x), (x)) \ + zig_expand_concat(zig_expand_import_, zig_expand_has_builtin(zig_libc_name_f##w(round)))(zig_f##w, zig_float_fn_f##w##_round, zig_libc_name_f##w(round), (zig_f##w x), (x)) \ + zig_expand_concat(zig_expand_import_, zig_expand_has_builtin(zig_libc_name_f##w(trunc)))(zig_f##w, zig_float_fn_f##w##_trunc, zig_libc_name_f##w(trunc), (zig_f##w x), (x)) \ + zig_expand_concat(zig_expand_import_, zig_expand_has_builtin(zig_libc_name_f##w(fmod)))(zig_f##w, zig_float_fn_f##w##_fmod, zig_libc_name_f##w(fmod), (zig_f##w x, zig_f##w y), (x, y)) \ + zig_expand_concat(zig_expand_import_, zig_expand_has_builtin(zig_libc_name_f##w(fmin)))(zig_f##w, zig_float_fn_f##w##_fmin, zig_libc_name_f##w(fmin), (zig_f##w x, zig_f##w y), (x, y)) \ + zig_expand_concat(zig_expand_import_, zig_expand_has_builtin(zig_libc_name_f##w(fmax)))(zig_f##w, zig_float_fn_f##w##_fmax, zig_libc_name_f##w(fmax), (zig_f##w x, zig_f##w y), (x, y)) \ + zig_expand_concat(zig_expand_import_, zig_expand_has_builtin(zig_libc_name_f##w(fma)))(zig_f##w, zig_float_fn_f##w##_fma, zig_libc_name_f##w(fma), (zig_f##w x, zig_f##w y, zig_f##w z), (x, y, z)) \ \ static inline zig_f##w zig_div_trunc_f##w(zig_f##w lhs, zig_f##w rhs) { \ - return zig_libc_name_f##w(trunc)(zig_div_f##w(lhs, rhs)); \ + return zig_float_fn_f##w##_trunc(zig_div_f##w(lhs, rhs)); \ } \ \ static inline zig_f##w zig_div_floor_f##w(zig_f##w lhs, zig_f##w rhs) { \ - return zig_libc_name_f##w(floor)(zig_div_f##w(lhs, rhs)); \ + return zig_float_fn_f##w##_floor(zig_div_f##w(lhs, rhs)); \ } \ \ static inline zig_f##w zig_mod_f##w(zig_f##w lhs, zig_f##w rhs) { \ @@ -3437,129 +3477,134 @@ zig_float_builtins(64) /* Note that zig_atomicrmw_expected is needed to handle aliasing between res and arg. */ #define zig_atomicrmw_xchg_float(res, obj, arg, order, Type, ReprType) do { \ zig_##Type zig_atomicrmw_expected; \ - zig_atomic_load(zig_atomicrmw_expected, obj, memory_order_relaxed, Type, ReprType); \ - while (!zig_cmpxchg_weak(obj, zig_atomicrmw_expected, arg, order, memory_order_relaxed, Type, ReprType)); \ + zig_atomic_load(zig_atomicrmw_expected, obj, zig_memory_order_relaxed, Type, ReprType); \ + while (!zig_cmpxchg_weak(obj, zig_atomicrmw_expected, arg, order, zig_memory_order_relaxed, Type, ReprType)); \ res = zig_atomicrmw_expected; \ } while (0) #define zig_atomicrmw_add_float(res, obj, arg, order, Type, ReprType) do { \ zig_##Type zig_atomicrmw_expected; \ zig_##Type zig_atomicrmw_desired; \ - zig_atomic_load(zig_atomicrmw_expected, obj, memory_order_relaxed, Type, ReprType); \ + zig_atomic_load(zig_atomicrmw_expected, obj, zig_memory_order_relaxed, Type, ReprType); \ do { \ zig_atomicrmw_desired = zig_add_##Type(zig_atomicrmw_expected, arg); \ - } while (!zig_cmpxchg_weak(obj, zig_atomicrmw_expected, zig_atomicrmw_desired, order, memory_order_relaxed, Type, ReprType)); \ + } while (!zig_cmpxchg_weak(obj, zig_atomicrmw_expected, zig_atomicrmw_desired, order, zig_memory_order_relaxed, Type, ReprType)); \ res = zig_atomicrmw_expected; \ } while (0) #define zig_atomicrmw_sub_float(res, obj, arg, order, Type, ReprType) do { \ zig_##Type zig_atomicrmw_expected; \ zig_##Type zig_atomicrmw_desired; \ - zig_atomic_load(zig_atomicrmw_expected, obj, memory_order_relaxed, Type, ReprType); \ + zig_atomic_load(zig_atomicrmw_expected, obj, zig_memory_order_relaxed, Type, ReprType); \ do { \ zig_atomicrmw_desired = zig_sub_##Type(zig_atomicrmw_expected, arg); \ - } while (!zig_cmpxchg_weak(obj, zig_atomicrmw_expected, zig_atomicrmw_desired, order, memory_order_relaxed, Type, ReprType)); \ + } while (!zig_cmpxchg_weak(obj, zig_atomicrmw_expected, zig_atomicrmw_desired, order, zig_memory_order_relaxed, Type, ReprType)); \ res = zig_atomicrmw_expected; \ } while (0) #define zig_atomicrmw_min_float(res, obj, arg, order, Type, ReprType) do { \ zig_##Type zig_atomicrmw_expected; \ zig_##Type zig_atomicrmw_desired; \ - zig_atomic_load(zig_atomicrmw_expected, obj, memory_order_relaxed, Type, ReprType); \ + zig_atomic_load(zig_atomicrmw_expected, obj, zig_memory_order_relaxed, Type, ReprType); \ do { \ - zig_atomicrmw_desired = zig_libc_name_##Type(fmin)(zig_atomicrmw_expected, arg); \ - } while (!zig_cmpxchg_weak(obj, zig_atomicrmw_expected, zig_atomicrmw_desired, order, memory_order_relaxed, Type, ReprType)); \ + zig_atomicrmw_desired = zig_float_fn_##Type##_fmin(zig_atomicrmw_expected, arg); \ + } while (!zig_cmpxchg_weak(obj, zig_atomicrmw_expected, zig_atomicrmw_desired, order, zig_memory_order_relaxed, Type, ReprType)); \ res = zig_atomicrmw_expected; \ } while (0) #define zig_atomicrmw_max_float(res, obj, arg, order, Type, ReprType) do { \ zig_##Type zig_atomicrmw_expected; \ zig_##Type zig_atomicrmw_desired; \ - zig_atomic_load(zig_atomicrmw_expected, obj, memory_order_relaxed, Type, ReprType); \ + zig_atomic_load(zig_atomicrmw_expected, obj, zig_memory_order_relaxed, Type, ReprType); \ do { \ - zig_atomicrmw_desired = zig_libc_name_##Type(fmax)(zig_atomicrmw_expected, arg); \ - } while (!zig_cmpxchg_weak(obj, zig_atomicrmw_expected, zig_atomicrmw_desired, order, memory_order_relaxed, Type, ReprType)); \ + zig_atomicrmw_desired = zig_float_fn_##Type##_fmax(zig_atomicrmw_expected, arg); \ + } while (!zig_cmpxchg_weak(obj, zig_atomicrmw_expected, zig_atomicrmw_desired, order, zig_memory_order_relaxed, Type, ReprType)); \ res = zig_atomicrmw_expected; \ } while (0) #define zig_atomicrmw_xchg_int128(res, obj, arg, order, Type, ReprType) do { \ zig_##Type zig_atomicrmw_expected; \ - zig_atomic_load(zig_atomicrmw_expected, obj, memory_order_relaxed, Type, ReprType); \ - while (!zig_cmpxchg_weak(obj, zig_atomicrmw_expected, arg, order, memory_order_relaxed, Type, ReprType)); \ + zig_atomic_load(zig_atomicrmw_expected, obj, zig_memory_order_relaxed, Type, ReprType); \ + while (!zig_cmpxchg_weak(obj, zig_atomicrmw_expected, arg, order, zig_memory_order_relaxed, Type, ReprType)); \ res = zig_atomicrmw_expected; \ } while (0) #define zig_atomicrmw_add_int128(res, obj, arg, order, Type, ReprType) do { \ zig_##Type zig_atomicrmw_expected; \ zig_##Type zig_atomicrmw_desired; \ - zig_atomic_load(zig_atomicrmw_expected, obj, memory_order_relaxed, Type, ReprType); \ + zig_atomic_load(zig_atomicrmw_expected, obj, zig_memory_order_relaxed, Type, ReprType); \ do { \ zig_atomicrmw_desired = zig_add_##Type(zig_atomicrmw_expected, arg); \ - } while (!zig_cmpxchg_weak(obj, zig_atomicrmw_expected, zig_atomicrmw_desired, order, memory_order_relaxed, Type, ReprType)); \ + } while (!zig_cmpxchg_weak(obj, zig_atomicrmw_expected, zig_atomicrmw_desired, order, zig_memory_order_relaxed, Type, ReprType)); \ res = zig_atomicrmw_expected; \ } while (0) #define zig_atomicrmw_sub_int128(res, obj, arg, order, Type, ReprType) do { \ zig_##Type zig_atomicrmw_expected; \ zig_##Type zig_atomicrmw_desired; \ - zig_atomic_load(zig_atomicrmw_expected, obj, memory_order_relaxed, Type, ReprType); \ + zig_atomic_load(zig_atomicrmw_expected, obj, zig_memory_order_relaxed, Type, ReprType); \ do { \ zig_atomicrmw_desired = zig_sub_##Type(zig_atomicrmw_expected, arg); \ - } while (!zig_cmpxchg_weak(obj, zig_atomicrmw_expected, zig_atomicrmw_desired, order, memory_order_relaxed, Type, ReprType)); \ + } while (!zig_cmpxchg_weak(obj, zig_atomicrmw_expected, zig_atomicrmw_desired, order, zig_memory_order_relaxed, Type, ReprType)); \ res = zig_atomicrmw_expected; \ } while (0) #define zig_atomicrmw_and_int128(res, obj, arg, order, Type, ReprType) do { \ zig_##Type zig_atomicrmw_expected; \ zig_##Type zig_atomicrmw_desired; \ - zig_atomic_load(zig_atomicrmw_expected, obj, memory_order_relaxed, Type, ReprType); \ + zig_atomic_load(zig_atomicrmw_expected, obj, zig_memory_order_relaxed, Type, ReprType); \ do { \ zig_atomicrmw_desired = zig_and_##Type(zig_atomicrmw_expected, arg); \ - } while (!zig_cmpxchg_weak(obj, zig_atomicrmw_expected, zig_atomicrmw_desired, order, memory_order_relaxed, Type, ReprType)); \ + } while (!zig_cmpxchg_weak(obj, zig_atomicrmw_expected, zig_atomicrmw_desired, order, zig_memory_order_relaxed, Type, ReprType)); \ res = zig_atomicrmw_expected; \ } while (0) #define zig_atomicrmw_nand_int128(res, obj, arg, order, Type, ReprType) do { \ zig_##Type zig_atomicrmw_expected; \ zig_##Type zig_atomicrmw_desired; \ - zig_atomic_load(zig_atomicrmw_expected, obj, memory_order_relaxed, Type, ReprType); \ + zig_atomic_load(zig_atomicrmw_expected, obj, zig_memory_order_relaxed, Type, ReprType); \ do { \ zig_atomicrmw_desired = zig_not_##Type(zig_and_##Type(zig_atomicrmw_expected, arg), 128); \ - } while (!zig_cmpxchg_weak(obj, zig_atomicrmw_expected, zig_atomicrmw_desired, order, memory_order_relaxed, Type, ReprType)); \ + } while (!zig_cmpxchg_weak(obj, zig_atomicrmw_expected, zig_atomicrmw_desired, order, zig_memory_order_relaxed, Type, ReprType)); \ res = zig_atomicrmw_expected; \ } while (0) #define zig_atomicrmw_or_int128(res, obj, arg, order, Type, ReprType) do { \ zig_##Type zig_atomicrmw_expected; \ zig_##Type zig_atomicrmw_desired; \ - zig_atomic_load(zig_atomicrmw_expected, obj, memory_order_relaxed, Type, ReprType); \ + zig_atomic_load(zig_atomicrmw_expected, obj, zig_memory_order_relaxed, Type, ReprType); \ do { \ zig_atomicrmw_desired = zig_or_##Type(zig_atomicrmw_expected, arg); \ - } while (!zig_cmpxchg_weak(obj, zig_atomicrmw_expected, zig_atomicrmw_desired, order, memory_order_relaxed, Type, ReprType)); \ + } while (!zig_cmpxchg_weak(obj, zig_atomicrmw_expected, zig_atomicrmw_desired, order, zig_memory_order_relaxed, Type, ReprType)); \ res = zig_atomicrmw_expected; \ } while (0) #define zig_atomicrmw_xor_int128(res, obj, arg, order, Type, ReprType) do { \ zig_##Type zig_atomicrmw_expected; \ zig_##Type zig_atomicrmw_desired; \ - zig_atomic_load(zig_atomicrmw_expected, obj, memory_order_relaxed, Type, ReprType); \ + zig_atomic_load(zig_atomicrmw_expected, obj, zig_memory_order_relaxed, Type, ReprType); \ do { \ zig_atomicrmw_desired = zig_xor_##Type(zig_atomicrmw_expected, arg); \ - } while (!zig_cmpxchg_weak(obj, zig_atomicrmw_expected, zig_atomicrmw_desired, order, memory_order_relaxed, Type, ReprType)); \ + } while (!zig_cmpxchg_weak(obj, zig_atomicrmw_expected, zig_atomicrmw_desired, order, zig_memory_order_relaxed, Type, ReprType)); \ res = zig_atomicrmw_expected; \ } while (0) #define zig_atomicrmw_min_int128(res, obj, arg, order, Type, ReprType) do { \ zig_##Type zig_atomicrmw_expected; \ zig_##Type zig_atomicrmw_desired; \ - zig_atomic_load(zig_atomicrmw_expected, obj, memory_order_relaxed, Type, ReprType); \ + zig_atomic_load(zig_atomicrmw_expected, obj, zig_memory_order_relaxed, Type, ReprType); \ do { \ zig_atomicrmw_desired = zig_min_##Type(zig_atomicrmw_expected, arg); \ - } while (!zig_cmpxchg_weak(obj, zig_atomicrmw_expected, zig_atomicrmw_desired, order, memory_order_relaxed, Type, ReprType)); \ + } while (!zig_cmpxchg_weak(obj, zig_atomicrmw_expected, zig_atomicrmw_desired, order, zig_memory_order_relaxed, Type, ReprType)); \ res = zig_atomicrmw_expected; \ } while (0) #define zig_atomicrmw_max_int128(res, obj, arg, order, Type, ReprType) do { \ zig_##Type zig_atomicrmw_expected; \ zig_##Type zig_atomicrmw_desired; \ - zig_atomic_load(zig_atomicrmw_expected, obj, memory_order_relaxed, Type, ReprType); \ + zig_atomic_load(zig_atomicrmw_expected, obj, zig_memory_order_relaxed, Type, ReprType); \ do { \ zig_atomicrmw_desired = zig_max_##Type(zig_atomicrmw_expected, arg); \ - } while (!zig_cmpxchg_weak(obj, zig_atomicrmw_expected, zig_atomicrmw_desired, order, memory_order_relaxed, Type, ReprType)); \ + } while (!zig_cmpxchg_weak(obj, zig_atomicrmw_expected, zig_atomicrmw_desired, order, zig_memory_order_relaxed, Type, ReprType)); \ res = zig_atomicrmw_expected; \ } while (0) #if __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_ATOMICS__) #include typedef enum memory_order zig_memory_order; +#define zig_memory_order_relaxed memory_order_relaxed +#define zig_memory_order_acquire memory_order_acquire +#define zig_memory_order_release memory_order_release +#define zig_memory_order_acq_rel memory_order_acq_rel +#define zig_memory_order_seq_cst memory_order_seq_cst #define zig_atomic(Type) _Atomic(Type) #define zig_cmpxchg_strong( obj, expected, desired, succ, fail, Type, ReprType) atomic_compare_exchange_strong_explicit(obj, &(expected), desired, succ, fail) #define zig_cmpxchg_weak( obj, expected, desired, succ, fail, Type, ReprType) atomic_compare_exchange_weak_explicit (obj, &(expected), desired, succ, fail) @@ -3583,12 +3628,11 @@ typedef enum memory_order zig_memory_order; #define zig_fence(order) atomic_thread_fence(order) #elif defined(__GNUC__) typedef int zig_memory_order; -#define memory_order_relaxed __ATOMIC_RELAXED -#define memory_order_consume __ATOMIC_CONSUME -#define memory_order_acquire __ATOMIC_ACQUIRE -#define memory_order_release __ATOMIC_RELEASE -#define memory_order_acq_rel __ATOMIC_ACQ_REL -#define memory_order_seq_cst __ATOMIC_SEQ_CST +#define zig_memory_order_relaxed __ATOMIC_RELAXED +#define zig_memory_order_acquire __ATOMIC_ACQUIRE +#define zig_memory_order_release __ATOMIC_RELEASE +#define zig_memory_order_acq_rel __ATOMIC_ACQ_REL +#define zig_memory_order_seq_cst __ATOMIC_SEQ_CST #define zig_atomic(Type) Type #define zig_cmpxchg_strong( obj, expected, desired, succ, fail, Type, ReprType) __atomic_compare_exchange(obj, &(expected), &(desired), false, succ, fail) #define zig_cmpxchg_weak( obj, expected, desired, succ, fail, Type, ReprType) __atomic_compare_exchange(obj, &(expected), &(desired), true, succ, fail) @@ -3607,12 +3651,11 @@ typedef int zig_memory_order; #define zig_atomicrmw_xchg_float zig_atomicrmw_xchg #define zig_fence(order) __atomic_thread_fence(order) #elif _MSC_VER && (_M_IX86 || _M_X64) -#define memory_order_relaxed 0 -#define memory_order_consume 1 -#define memory_order_acquire 2 -#define memory_order_release 3 -#define memory_order_acq_rel 4 -#define memory_order_seq_cst 5 +#define zig_memory_order_relaxed 0 +#define zig_memory_order_acquire 2 +#define zig_memory_order_release 3 +#define zig_memory_order_acq_rel 4 +#define zig_memory_order_seq_cst 5 #define zig_atomic(Type) Type #define zig_cmpxchg_strong( obj, expected, desired, succ, fail, Type, ReprType) zig_msvc_cmpxchg_##Type(obj, &(expected), desired) #define zig_cmpxchg_weak( obj, expected, desired, succ, fail, Type, ReprType) zig_cmpxchg_strong(obj, expected, desired, succ, fail, Type, ReprType) @@ -3634,12 +3677,11 @@ typedef int zig_memory_order; #endif /* TODO: _MSC_VER && (_M_ARM || _M_ARM64) */ #else -#define memory_order_relaxed 0 -#define memory_order_consume 1 -#define memory_order_acquire 2 -#define memory_order_release 3 -#define memory_order_acq_rel 4 -#define memory_order_seq_cst 5 +#define zig_memory_order_relaxed 0 +#define zig_memory_order_acquire 2 +#define zig_memory_order_release 3 +#define zig_memory_order_acq_rel 4 +#define zig_memory_order_seq_cst 5 #define zig_atomic(Type) Type #define zig_cmpxchg_strong( obj, expected, desired, succ, fail, Type, ReprType) zig_atomics_unavailable #define zig_cmpxchg_weak( obj, expected, desired, succ, fail, Type, ReprType) zig_atomics_unavailable @@ -3830,9 +3872,32 @@ static inline bool zig_msvc_cmpxchg_u128(zig_u128 volatile* obj, zig_u128* expec return _InterlockedCompareExchange128((__int64 volatile*)obj, (__int64)zig_hi_u128(desired), (__int64)zig_lo_u128(desired), (__int64*)expected); } +static inline zig_u128 zig_msvc_atomic_load_u128(zig_u128 volatile* obj) { + zig_u128 expected = zig_make_u128(UINT64_C(0), UINT64_C(0)); + (void)zig_cmpxchg_strong(obj, expected, expected, zig_memory_order_seq_cst, zig_memory_order_seq_cst, u128, zig_u128); + return expected; +} + +static inline void zig_msvc_atomic_store_u128(zig_u128 volatile* obj, zig_u128 arg) { + zig_u128 expected = zig_make_u128(UINT64_C(0), UINT64_C(0)); + while (!zig_cmpxchg_weak(obj, expected, arg, zig_memory_order_seq_cst, zig_memory_order_seq_cst, u128, zig_u128)); +} + static inline bool zig_msvc_cmpxchg_i128(zig_i128 volatile* obj, zig_i128* expected, zig_i128 desired) { return _InterlockedCompareExchange128((__int64 volatile*)obj, (__int64)zig_hi_i128(desired), (__int64)zig_lo_i128(desired), (__int64*)expected); } + +static inline zig_i128 zig_msvc_atomic_load_i128(zig_i128 volatile* obj) { + zig_i128 expected = zig_make_i128(INT64_C(0), UINT64_C(0)); + (void)zig_cmpxchg_strong(obj, expected, expected, zig_memory_order_seq_cst, zig_memory_order_seq_cst, i128, zig_i128); + return expected; +} + +static inline void zig_msvc_atomic_store_i128(zig_i128 volatile* obj, zig_i128 arg) { + zig_i128 expected = zig_make_i128(INT64_C(0), UINT64_C(0)); + while (!zig_cmpxchg_weak(obj, expected, arg, zig_memory_order_seq_cst, zig_memory_order_seq_cst, i128, zig_i128)); +} + #endif /* _M_IX86 */ #endif /* _MSC_VER && (_M_IX86 || _M_X64) */ diff --git a/stage1/zig1.wasm b/stage1/zig1.wasm index 4d692444efab1ee22476f35ee0ff29bc02a9ac0a..c249daa0674cd74250c9fc53b1a5acf6125e105e 100644 Binary files a/stage1/zig1.wasm and b/stage1/zig1.wasm differ diff --git a/test/behavior/align.zig b/test/behavior/align.zig index e00fbe67443f0bf13281cc15d06f69653beaead3..671e46501a130f565c9a6a362b9f85acdb1decb1 100644 --- a/test/behavior/align.zig +++ b/test/behavior/align.zig @@ -311,12 +311,6 @@ test "page aligned array on stack" { try expect(number2 == 43); } -fn derp() align(@sizeOf(usize) * 2) i32 { - return 1234; -} -fn noop1() align(1) void {} -fn noop4() align(4) void {} - test "function alignment" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; @@ -325,11 +319,25 @@ test "function alignment" { // function alignment is a compile error on wasm32/wasm64 if (native_arch == .wasm32 or native_arch == .wasm64) return error.SkipZigTest; - try expect(derp() == 1234); - try expect(@TypeOf(noop1) == fn () align(1) void); - try expect(@TypeOf(noop4) == fn () align(4) void); - noop1(); - noop4(); + const S = struct { + fn alignExpr() align(@sizeOf(usize) * 2) i32 { + return 1234; + } + fn align1() align(1) void {} + fn align4() align(4) void {} + }; + + try expect(S.alignExpr() == 1234); + try expect(@TypeOf(S.alignExpr) == fn () i32); + try expect(@TypeOf(&S.alignExpr) == *align(@sizeOf(usize) * 2) const fn () i32); + + S.align1(); + try expect(@TypeOf(S.align1) == fn () void); + try expect(@TypeOf(&S.align1) == *align(1) const fn () void); + + S.align4(); + try expect(@TypeOf(S.align4) == fn () void); + try expect(@TypeOf(&S.align4) == *align(4) const fn () void); } test "implicitly decreasing fn alignment" { @@ -345,7 +353,7 @@ test "implicitly decreasing fn alignment" { try testImplicitlyDecreaseFnAlign(alignedBig, 5678); } -fn testImplicitlyDecreaseFnAlign(ptr: *const fn () align(1) i32, answer: i32) !void { +fn testImplicitlyDecreaseFnAlign(ptr: *align(1) const fn () i32, answer: i32) !void { try expect(ptr() == answer); } @@ -368,10 +376,10 @@ test "@alignCast functions" { try expect(fnExpectsOnly1(simple4) == 0x19); } -fn fnExpectsOnly1(ptr: *const fn () align(1) i32) i32 { +fn fnExpectsOnly1(ptr: *align(1) const fn () i32) i32 { return fnExpects4(@alignCast(ptr)); } -fn fnExpects4(ptr: *const fn () align(4) i32) i32 { +fn fnExpects4(ptr: *align(4) const fn () i32) i32 { return ptr(); } fn simple4() align(4) i32 { diff --git a/test/behavior/type.zig b/test/behavior/type.zig index da42672be6d7473a9db8c3d707549cc14ef51c19..c9290ecc31ec877f00d9f9d3895d86dccbfb98b8 100644 --- a/test/behavior/type.zig +++ b/test/behavior/type.zig @@ -527,7 +527,6 @@ test "Type.Fn" { { const fn_info = std.builtin.Type{ .Fn = .{ .calling_convention = .C, - .alignment = 0, .is_generic = false, .is_var_args = false, .return_type = void, @@ -643,7 +642,6 @@ test "reified function type params initialized with field pointer" { const Bar = @Type(.{ .Fn = .{ .calling_convention = .Unspecified, - .alignment = 0, .is_generic = false, .is_var_args = false, .return_type = void, diff --git a/test/behavior/type_info.zig b/test/behavior/type_info.zig index d4e3eb3b833cedb0ed96d2477a25323dc933d97f..0d54b14c390c9aaabeb20d04a4876ad7ba282fd4 100644 --- a/test/behavior/type_info.zig +++ b/test/behavior/type_info.zig @@ -356,16 +356,38 @@ test "type info: function type info" { } fn testFunction() !void { - const fn_info = @typeInfo(@TypeOf(typeInfoFoo)); - try expect(fn_info == .Fn); - try expect(fn_info.Fn.alignment > 0); - try expect(fn_info.Fn.calling_convention == .C); - try expect(!fn_info.Fn.is_generic); - try expect(fn_info.Fn.params.len == 2); - try expect(fn_info.Fn.is_var_args); - try expect(fn_info.Fn.return_type.? == usize); - const fn_aligned_info = @typeInfo(@TypeOf(typeInfoFooAligned)); - try expect(fn_aligned_info.Fn.alignment == 4); + const foo_fn_type = @TypeOf(typeInfoFoo); + const foo_fn_info = @typeInfo(foo_fn_type); + try expect(foo_fn_info.Fn.calling_convention == .C); + try expect(!foo_fn_info.Fn.is_generic); + try expect(foo_fn_info.Fn.params.len == 2); + try expect(foo_fn_info.Fn.is_var_args); + try expect(foo_fn_info.Fn.return_type.? == usize); + const foo_ptr_fn_info = @typeInfo(@TypeOf(&typeInfoFoo)); + try expect(foo_ptr_fn_info.Pointer.size == .One); + try expect(foo_ptr_fn_info.Pointer.is_const); + try expect(!foo_ptr_fn_info.Pointer.is_volatile); + try expect(foo_ptr_fn_info.Pointer.address_space == .generic); + try expect(foo_ptr_fn_info.Pointer.child == foo_fn_type); + try expect(!foo_ptr_fn_info.Pointer.is_allowzero); + try expect(foo_ptr_fn_info.Pointer.sentinel == null); + + const aligned_foo_fn_type = @TypeOf(typeInfoFooAligned); + const aligned_foo_fn_info = @typeInfo(aligned_foo_fn_type); + try expect(aligned_foo_fn_info.Fn.calling_convention == .C); + try expect(!aligned_foo_fn_info.Fn.is_generic); + try expect(aligned_foo_fn_info.Fn.params.len == 2); + try expect(aligned_foo_fn_info.Fn.is_var_args); + try expect(aligned_foo_fn_info.Fn.return_type.? == usize); + const aligned_foo_ptr_fn_info = @typeInfo(@TypeOf(&typeInfoFooAligned)); + try expect(aligned_foo_ptr_fn_info.Pointer.size == .One); + try expect(aligned_foo_ptr_fn_info.Pointer.is_const); + try expect(!aligned_foo_ptr_fn_info.Pointer.is_volatile); + try expect(aligned_foo_ptr_fn_info.Pointer.alignment == 4); + try expect(aligned_foo_ptr_fn_info.Pointer.address_space == .generic); + try expect(aligned_foo_ptr_fn_info.Pointer.child == aligned_foo_fn_type); + try expect(!aligned_foo_ptr_fn_info.Pointer.is_allowzero); + try expect(aligned_foo_ptr_fn_info.Pointer.sentinel == null); } extern fn typeInfoFoo(a: usize, b: bool, ...) callconv(.C) usize; diff --git a/test/behavior/typename.zig b/test/behavior/typename.zig index 80c9a0619ea11087bad517150aac39f9e0beed75..c3eefc8de775064b5137fb85dcb4be017fa4ba71 100644 --- a/test/behavior/typename.zig +++ b/test/behavior/typename.zig @@ -78,11 +78,9 @@ test "basic" { try expectEqualStrings("fn (comptime u32) void", @typeName(fn (comptime u32) void)); try expectEqualStrings("fn (noalias []u8) void", @typeName(fn (noalias []u8) void)); - try expectEqualStrings("fn () align(32) void", @typeName(fn () align(32) void)); try expectEqualStrings("fn () callconv(.C) void", @typeName(fn () callconv(.C) void)); - try expectEqualStrings("fn () align(32) callconv(.C) void", @typeName(fn () align(32) callconv(.C) void)); - try expectEqualStrings("fn (...) align(32) callconv(.C) void", @typeName(fn (...) align(32) callconv(.C) void)); - try expectEqualStrings("fn (u32, ...) align(32) callconv(.C) void", @typeName(fn (u32, ...) align(32) callconv(.C) void)); + try expectEqualStrings("fn (...) callconv(.C) void", @typeName(fn (...) callconv(.C) void)); + try expectEqualStrings("fn (u32, ...) callconv(.C) void", @typeName(fn (u32, ...) callconv(.C) void)); } test "top level decl" { diff --git a/test/cases/compile_errors/function_ptr_alignment.zig b/test/cases/compile_errors/function_ptr_alignment.zig index 995ef8d9b1cbf8035e36a3745287146d5fe416f1..cf97e61f40147ef636bd7536d9a6cab0783a6b71 100644 --- a/test/cases/compile_errors/function_ptr_alignment.zig +++ b/test/cases/compile_errors/function_ptr_alignment.zig @@ -1,28 +1,16 @@ -comptime { - var a: *align(2) @TypeOf(foo) = undefined; - _ = &a; -} -fn foo() void {} +fn align1() align(1) void {} +fn align2() align(2) void {} comptime { - var a: *align(1) fn () void = undefined; - _ = &a; -} -comptime { - var a: *align(2) fn () align(2) void = undefined; - _ = &a; -} -comptime { - var a: *align(2) fn () void = undefined; - _ = &a; -} -comptime { - var a: *align(1) fn () align(2) void = undefined; - _ = &a; + _ = @as(*align(1) const fn () void, &align2); + _ = @as(*align(1) const fn () void, &align1); + _ = @as(*align(2) const fn () void, &align2); + _ = @as(*align(2) const fn () void, &align1); } // error // backend=stage2 // target=native // -// :20:19: error: function pointer alignment disagrees with function alignment +// :8:41: error: expected type '*align(2) const fn () void', found '*const fn () void' +// :8:41: note: pointer alignment '1' cannot cast into pointer alignment '2' diff --git a/test/cases/compile_errors/inferring_error_set_of_function_pointer.zig b/test/cases/compile_errors/inferring_error_set_of_function_pointer.zig deleted file mode 100644 index ce1b2763609a599e8597db7d91c5eb92250109f2..0000000000000000000000000000000000000000 --- a/test/cases/compile_errors/inferring_error_set_of_function_pointer.zig +++ /dev/null @@ -1,9 +0,0 @@ -comptime { - const z: ?fn () !void = null; -} - -// error -// backend=stage2 -// target=native -// -// :2:21: error: function prototype may not have inferred error set diff --git a/test/cases/compile_errors/invalid_function_types.zig b/test/cases/compile_errors/invalid_function_types.zig new file mode 100644 index 0000000000000000000000000000000000000000..e4553d22f12d2a14a5483d17605ca448dc0af863 --- /dev/null +++ b/test/cases/compile_errors/invalid_function_types.zig @@ -0,0 +1,25 @@ +comptime { + _ = fn name() void; +} +comptime { + _ = fn () align(128) void; +} +comptime { + _ = fn () addrspace(.generic) void; +} +comptime { + _ = fn () linksection("section") void; +} +comptime { + _ = fn () !void; +} + +// error +// backend=stage2 +// target=native +// +// :2:12: error: function type cannot have a name +// :5:21: error: function type cannot have an alignment +// :8:26: error: function type cannot have an addrspace +// :11:27: error: function type cannot have a linksection +// :14:15: error: function type cannot have an inferred error set diff --git a/test/cases/compile_errors/passing_an_under-aligned_function_pointer.zig b/test/cases/compile_errors/passing_an_under-aligned_function_pointer.zig index c6b06f7329a9a6f17cd909f054ffe4fc4c505908..e37441563b07b77cee89c438bbbb8fb3c1c05549 100644 --- a/test/cases/compile_errors/passing_an_under-aligned_function_pointer.zig +++ b/test/cases/compile_errors/passing_an_under-aligned_function_pointer.zig @@ -1,7 +1,7 @@ export fn entry() void { testImplicitlyDecreaseFnAlign(alignedSmall, 1234); } -fn testImplicitlyDecreaseFnAlign(ptr: *const fn () align(8) i32, answer: i32) void { +fn testImplicitlyDecreaseFnAlign(ptr: *align(8) const fn () i32, answer: i32) void { if (ptr() != answer) unreachable; } fn alignedSmall() align(4) i32 { @@ -12,5 +12,5 @@ fn alignedSmall() align(4) i32 { // backend=stage2 // target=x86_64-linux // -// :2:35: error: expected type '*const fn () align(8) i32', found '*const fn () align(4) i32' +// :2:35: error: expected type '*align(8) const fn () i32', found '*align(4) const fn () i32' // :2:35: note: pointer alignment '4' cannot cast into pointer alignment '8' diff --git a/test/cases/compile_errors/reify_type.Fn_with_is_generic_true.zig b/test/cases/compile_errors/reify_type.Fn_with_is_generic_true.zig index bb2c4bbe628278de38d5ebc2c4731930a9ee7fcb..4ac1312440bba2e8fa69c3c1de8ed09927025c20 100644 --- a/test/cases/compile_errors/reify_type.Fn_with_is_generic_true.zig +++ b/test/cases/compile_errors/reify_type.Fn_with_is_generic_true.zig @@ -1,7 +1,6 @@ const Foo = @Type(.{ .Fn = .{ .calling_convention = .Unspecified, - .alignment = 0, .is_generic = true, .is_var_args = false, .return_type = u0, diff --git a/test/cases/compile_errors/reify_type.Fn_with_is_var_args_true_and_non-C_callconv.zig b/test/cases/compile_errors/reify_type.Fn_with_is_var_args_true_and_non-C_callconv.zig index 678fbe3ed7be8a3110b5a8e73fb5a55b174cd7bc..23ea4f209521c2c69f38662e63d8cf9c0586e878 100644 --- a/test/cases/compile_errors/reify_type.Fn_with_is_var_args_true_and_non-C_callconv.zig +++ b/test/cases/compile_errors/reify_type.Fn_with_is_var_args_true_and_non-C_callconv.zig @@ -1,7 +1,6 @@ const Foo = @Type(.{ .Fn = .{ .calling_convention = .Unspecified, - .alignment = 0, .is_generic = false, .is_var_args = true, .return_type = u0, diff --git a/test/cases/compile_errors/reify_type.Fn_with_return_type_null.zig b/test/cases/compile_errors/reify_type.Fn_with_return_type_null.zig index d348a0c908a042ae3ba3cfb17ee70f49d0d2fd95..073cf0f4ccf7ed617b6514bae74e9850dd6c8301 100644 --- a/test/cases/compile_errors/reify_type.Fn_with_return_type_null.zig +++ b/test/cases/compile_errors/reify_type.Fn_with_return_type_null.zig @@ -1,7 +1,6 @@ const Foo = @Type(.{ .Fn = .{ .calling_convention = .Unspecified, - .alignment = 0, .is_generic = false, .is_var_args = false, .return_type = null,