commit 7699b5b997c7a024a6d9558df0ec72d71ef402fe (tree)
parent 81f1f72197113a45e827d5c984e219a28aa28083
Author: Andrew Kelley <andrew@ziglang.org>
Date: Thu, 12 Dec 2019 18:56:39 -0500
Merge branch 'Xe-expose-callMain'
closes #3891
Diffstat:
16 files changed, 437 insertions(+), 453 deletions(-)
diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig
@@ -414,6 +414,13 @@ pub const CallOptions = struct {
/// This function type is used by the Zig language code generation and
/// therefore must be kept in sync with the compiler implementation.
+pub const TestFn = struct {
+ name: []const u8,
+ func: fn()anyerror!void,
+};
+
+/// This function type is used by the Zig language code generation and
+/// therefore must be kept in sync with the compiler implementation.
pub const PanicFn = fn ([]const u8, ?*StackTrace) noreturn;
/// This function is used by the Zig language code generation and
@@ -424,6 +431,10 @@ pub const panic: PanicFn = if (@hasDecl(root, "panic")) root.panic else default_
/// therefore must be kept in sync with the compiler implementation.
pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace) noreturn {
@setCold(true);
+ if (@hasDecl(root, "os") and @hasDecl(root.os, "panic")) {
+ root.os.panic(msg, error_return_trace);
+ unreachable;
+ }
switch (os) {
.freestanding => {
while (true) {
diff --git a/lib/std/os.zig b/lib/std/os.zig
@@ -187,19 +187,23 @@ pub fn abort() noreturn {
}
windows.kernel32.ExitProcess(3);
}
- if (builtin.link_libc) {
- system.abort();
+ if (!builtin.link_libc and builtin.os == .linux) {
+ raise(SIGABRT) catch {};
+
+ // TODO the rest of the implementation of abort() from musl libc here
+
+ raise(SIGKILL) catch {};
+ exit(127);
}
if (builtin.os == .uefi) {
exit(0); // TODO choose appropriate exit code
}
+ if (builtin.os == .wasi) {
+ @breakpoint();
+ exit(1);
+ }
- raise(SIGABRT) catch {};
-
- // TODO the rest of the implementation of abort() from musl libc here
-
- raise(SIGKILL) catch {};
- exit(127);
+ system.abort();
}
pub const RaiseError = UnexpectedError;
diff --git a/lib/std/special/c.zig b/lib/std/special/c.zig
@@ -83,7 +83,7 @@ pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn
@setCold(true);
std.debug.panic("{}", msg);
}
- if (builtin.os != .freestanding) {
+ if (builtin.os != .freestanding and builtin.os != .other) {
std.os.abort();
}
while (true) {}
diff --git a/lib/std/special/start.zig b/lib/std/special/start.zig
@@ -1,286 +0,0 @@
-// This file is included in the compilation unit when exporting an executable.
-
-const root = @import("root");
-const std = @import("std");
-const builtin = @import("builtin");
-const assert = std.debug.assert;
-const uefi = std.os.uefi;
-
-var starting_stack_ptr: [*]usize = undefined;
-
-const is_wasm = switch (builtin.arch) {
- .wasm32, .wasm64 => true,
- else => false,
-};
-
-const is_mips = switch (builtin.arch) {
- .mips, .mipsel, .mips64, .mips64el => true,
- else => false,
-};
-
-comptime {
- if (builtin.output_mode == .Lib and builtin.link_mode == .Dynamic) {
- if (builtin.os == .windows and !@hasDecl(root, "_DllMainCRTStartup")) {
- @export("_DllMainCRTStartup", _DllMainCRTStartup, .Strong);
- }
- } else if (builtin.output_mode == .Exe or @hasDecl(root, "main")) {
- if (builtin.link_libc and @hasDecl(root, "main")) {
- if (@typeInfo(@TypeOf(root.main)).Fn.calling_convention != .C) {
- @export("main", main, .Weak);
- }
- } else if (builtin.os == .windows) {
- if (!@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup")) {
- @export("WinMainCRTStartup", WinMainCRTStartup, .Strong);
- }
- } else if (builtin.os == .uefi) {
- if (!@hasDecl(root, "EfiMain")) @export("EfiMain", EfiMain, .Strong);
- } else if (builtin.os != .freestanding) {
- if (is_mips) {
- if (!@hasDecl(root, "__start")) @export("__start", _start, .Strong);
- } else {
- if (!@hasDecl(root, "_start")) @export("_start", _start, .Strong);
- }
- } else if (is_wasm) {
- if (!@hasDecl(root, "_start")) @export("_start", wasm_freestanding_start, .Strong);
- }
- }
-}
-
-stdcallcc fn _DllMainCRTStartup(
- hinstDLL: std.os.windows.HINSTANCE,
- fdwReason: std.os.windows.DWORD,
- lpReserved: std.os.windows.LPVOID,
-) std.os.windows.BOOL {
- if (@hasDecl(root, "DllMain")) {
- return root.DllMain(hinstDLL, fdwReason, lpReserved);
- }
-
- return std.os.windows.TRUE;
-}
-
-extern fn wasm_freestanding_start() void {
- // This is marked inline because for some reason LLVM in release mode fails to inline it,
- // and we want fewer call frames in stack traces.
- _ = @call(.{ .modifier = .always_inline }, callMain, .{});
-}
-
-extern fn EfiMain(handle: uefi.Handle, system_table: *uefi.tables.SystemTable) usize {
- const bad_efi_main_ret = "expected return type of main to be 'void', 'noreturn', or 'usize'";
- uefi.handle = handle;
- uefi.system_table = system_table;
-
- switch (@typeInfo(@TypeOf(root.main).ReturnType)) {
- .NoReturn => {
- root.main();
- },
- .Void => {
- root.main();
- return 0;
- },
- .Int => |info| {
- if (info.bits != @typeInfo(usize).Int.bits) {
- @compileError(bad_efi_main_ret);
- }
- return root.main();
- },
- else => @compileError(bad_efi_main_ret),
- }
-}
-
-nakedcc fn _start() noreturn {
- if (builtin.os == builtin.Os.wasi) {
- // This is marked inline because for some reason LLVM in release mode fails to inline it,
- // and we want fewer call frames in stack traces.
- std.os.wasi.proc_exit(@call(.{ .modifier = .always_inline }, callMain, .{}));
- }
-
- switch (builtin.arch) {
- .x86_64 => {
- starting_stack_ptr = asm (""
- : [argc] "={rsp}" (-> [*]usize)
- );
- },
- .i386 => {
- starting_stack_ptr = asm (""
- : [argc] "={esp}" (-> [*]usize)
- );
- },
- .aarch64, .aarch64_be, .arm => {
- starting_stack_ptr = asm ("mov %[argc], sp"
- : [argc] "=r" (-> [*]usize)
- );
- },
- .riscv64 => {
- starting_stack_ptr = asm ("mv %[argc], sp"
- : [argc] "=r" (-> [*]usize)
- );
- },
- .mipsel => {
- // Need noat here because LLVM is free to pick any register
- starting_stack_ptr = asm (
- \\ .set noat
- \\ move %[argc], $sp
- : [argc] "=r" (-> [*]usize)
- );
- },
- else => @compileError("unsupported arch"),
- }
- // If LLVM inlines stack variables into _start, they will overwrite
- // the command line argument data.
- @call(.{ .modifier = .never_inline }, posixCallMainAndExit, .{});
-}
-
-stdcallcc fn WinMainCRTStartup() noreturn {
- @setAlignStack(16);
- if (!builtin.single_threaded) {
- _ = @import("start_windows_tls.zig");
- }
-
- std.debug.maybeEnableSegfaultHandler();
-
- std.os.windows.kernel32.ExitProcess(initEventLoopAndCallMain());
-}
-
-// TODO https://github.com/ziglang/zig/issues/265
-fn posixCallMainAndExit() noreturn {
- if (builtin.os == builtin.Os.freebsd) {
- @setAlignStack(16);
- }
- const argc = starting_stack_ptr[0];
- const argv = @ptrCast([*][*:0]u8, starting_stack_ptr + 1);
-
- const envp_optional = @ptrCast([*:null]?[*:0]u8, argv + argc + 1);
- var envp_count: usize = 0;
- while (envp_optional[envp_count]) |_| : (envp_count += 1) {}
- const envp = @ptrCast([*][*:0]u8, envp_optional)[0..envp_count];
-
- if (builtin.os == .linux) {
- // Find the beginning of the auxiliary vector
- const auxv = @ptrCast([*]std.elf.Auxv, envp.ptr + envp_count + 1);
- std.os.linux.elf_aux_maybe = auxv;
- // Initialize the TLS area
- const gnu_stack_phdr = std.os.linux.tls.initTLS() orelse @panic("ELF missing stack size");
-
- if (std.os.linux.tls.tls_image) |tls_img| {
- const tls_addr = std.os.linux.tls.allocateTLS(tls_img.alloc_size);
- const tp = std.os.linux.tls.copyTLS(tls_addr);
- std.os.linux.tls.setThreadPointer(tp);
- }
-
- // TODO This is disabled because what should we do when linking libc and this code
- // does not execute? And also it's causing a test failure in stack traces in release modes.
-
- //// Linux ignores the stack size from the ELF file, and instead always does 8 MiB. A further
- //// problem is that it uses PROT_GROWSDOWN which prevents stores to addresses too far down
- //// the stack and requires "probing". So here we allocate our own stack.
- //const wanted_stack_size = gnu_stack_phdr.p_memsz;
- //assert(wanted_stack_size % std.mem.page_size == 0);
- //// Allocate an extra page as the guard page.
- //const total_size = wanted_stack_size + std.mem.page_size;
- //const new_stack = std.os.mmap(
- // null,
- // total_size,
- // std.os.PROT_READ | std.os.PROT_WRITE,
- // std.os.MAP_PRIVATE | std.os.MAP_ANONYMOUS,
- // -1,
- // 0,
- //) catch @panic("out of memory");
- //std.os.mprotect(new_stack[0..std.mem.page_size], std.os.PROT_NONE) catch {};
- //std.os.exit(@call(.{.stack = new_stack}, callMainWithArgs, .{argc, argv, envp}));
- }
-
- std.os.exit(@call(.{ .modifier = .always_inline }, callMainWithArgs, .{ argc, argv, envp }));
-}
-
-fn callMainWithArgs(argc: usize, argv: [*][*:0]u8, envp: [][*:0]u8) u8 {
- std.os.argv = argv[0..argc];
- std.os.environ = envp;
-
- std.debug.maybeEnableSegfaultHandler();
-
- return initEventLoopAndCallMain();
-}
-
-extern fn main(c_argc: i32, c_argv: [*][*:0]u8, c_envp: [*:null]?[*:0]u8) i32 {
- var env_count: usize = 0;
- while (c_envp[env_count] != null) : (env_count += 1) {}
- const envp = @ptrCast([*][*:0]u8, c_envp)[0..env_count];
- return @call(.{ .modifier = .always_inline }, callMainWithArgs, .{ @intCast(usize, c_argc), c_argv, envp });
-}
-
-// General error message for a malformed return type
-const bad_main_ret = "expected return type of main to be 'void', '!void', 'noreturn', 'u8', or '!u8'";
-
-// This is marked inline because for some reason LLVM in release mode fails to inline it,
-// and we want fewer call frames in stack traces.
-inline fn initEventLoopAndCallMain() u8 {
- if (std.event.Loop.instance) |loop| {
- if (!@hasDecl(root, "event_loop")) {
- loop.init() catch |err| {
- std.debug.warn("error: {}\n", .{@errorName(err)});
- if (@errorReturnTrace()) |trace| {
- std.debug.dumpStackTrace(trace.*);
- }
- return 1;
- };
- defer loop.deinit();
-
- var result: u8 = undefined;
- var frame: @Frame(callMainAsync) = undefined;
- _ = @asyncCall(&frame, &result, callMainAsync, loop);
- loop.run();
- return result;
- }
- }
-
- // This is marked inline because for some reason LLVM in release mode fails to inline it,
- // and we want fewer call frames in stack traces.
- return @call(.{ .modifier = .always_inline }, callMain, .{});
-}
-
-async fn callMainAsync(loop: *std.event.Loop) u8 {
- // This prevents the event loop from terminating at least until main() has returned.
- loop.beginOneEvent();
- defer loop.finishOneEvent();
- return callMain();
-}
-
-// This is not marked inline because it is called with @asyncCall when
-// there is an event loop.
-fn callMain() u8 {
- switch (@typeInfo(@TypeOf(root.main).ReturnType)) {
- .NoReturn => {
- root.main();
- },
- .Void => {
- root.main();
- return 0;
- },
- .Int => |info| {
- if (info.bits != 8) {
- @compileError(bad_main_ret);
- }
- return root.main();
- },
- .ErrorUnion => {
- const result = root.main() catch |err| {
- std.debug.warn("error: {}\n", .{@errorName(err)});
- if (@errorReturnTrace()) |trace| {
- std.debug.dumpStackTrace(trace.*);
- }
- return 1;
- };
- switch (@typeInfo(@TypeOf(result))) {
- .Void => return 0,
- .Int => |info| {
- if (info.bits != 8) {
- @compileError(bad_main_ret);
- }
- return result;
- },
- else => @compileError(bad_main_ret),
- }
- },
- else => @compileError(bad_main_ret),
- }
-}
diff --git a/lib/std/special/start_windows_tls.zig b/lib/std/special/start_windows_tls.zig
@@ -1,48 +0,0 @@
-const std = @import("std");
-const builtin = @import("builtin");
-
-export var _tls_index: u32 = std.os.windows.TLS_OUT_OF_INDEXES;
-export var _tls_start: u8 linksection(".tls") = 0;
-export var _tls_end: u8 linksection(".tls$ZZZ") = 0;
-export var __xl_a: std.os.windows.PIMAGE_TLS_CALLBACK linksection(".CRT$XLA") = null;
-export var __xl_z: std.os.windows.PIMAGE_TLS_CALLBACK linksection(".CRT$XLZ") = null;
-
-comptime {
- if (builtin.arch == .i386) {
- // The __tls_array is the offset of the ThreadLocalStoragePointer field
- // in the TEB block whose base address held in the %fs segment.
- asm (
- \\ .global __tls_array
- \\ __tls_array = 0x2C
- );
- }
-}
-
-// TODO this is how I would like it to be expressed
-// TODO also note, ReactOS has a +1 on StartAddressOfRawData and AddressOfCallBacks. Investigate
-// why they do that.
-//export const _tls_used linksection(".rdata$T") = std.os.windows.IMAGE_TLS_DIRECTORY {
-// .StartAddressOfRawData = @ptrToInt(&_tls_start),
-// .EndAddressOfRawData = @ptrToInt(&_tls_end),
-// .AddressOfIndex = @ptrToInt(&_tls_index),
-// .AddressOfCallBacks = @ptrToInt(__xl_a),
-// .SizeOfZeroFill = 0,
-// .Characteristics = 0,
-//};
-// This is the workaround because we can't do @ptrToInt at comptime like that.
-pub const IMAGE_TLS_DIRECTORY = extern struct {
- StartAddressOfRawData: *c_void,
- EndAddressOfRawData: *c_void,
- AddressOfIndex: *c_void,
- AddressOfCallBacks: *c_void,
- SizeOfZeroFill: u32,
- Characteristics: u32,
-};
-export const _tls_used linksection(".rdata$T") = IMAGE_TLS_DIRECTORY{
- .StartAddressOfRawData = &_tls_start,
- .EndAddressOfRawData = &_tls_end,
- .AddressOfIndex = &_tls_index,
- .AddressOfCallBacks = &__xl_a,
- .SizeOfZeroFill = 0,
- .Characteristics = 0,
-};
diff --git a/lib/std/special/test_runner.zig b/lib/std/special/test_runner.zig
@@ -1,9 +1,9 @@
const std = @import("std");
const io = std.io;
const builtin = @import("builtin");
-const test_fn_list = builtin.test_functions;
pub fn main() anyerror!void {
+ const test_fn_list = builtin.test_functions;
var ok_count: usize = 0;
var skip_count: usize = 0;
var progress = std.Progress{};
@@ -16,7 +16,9 @@ pub fn main() anyerror!void {
var test_node = root_node.start(test_fn.name, null);
test_node.activate();
progress.refresh();
- if (progress.terminal == null) std.debug.warn("{}/{} {}...", .{ i + 1, test_fn_list.len, test_fn.name });
+ if (progress.terminal == null) {
+ std.debug.warn("{}/{} {}...", .{ i + 1, test_fn_list.len, test_fn.name });
+ }
if (test_fn.func()) |_| {
ok_count += 1;
test_node.end();
diff --git a/lib/std/start.zig b/lib/std/start.zig
@@ -0,0 +1,283 @@
+// This file is included in the compilation unit when exporting an executable.
+
+const root = @import("root");
+const std = @import("std.zig");
+const builtin = std.builtin;
+const assert = std.debug.assert;
+const uefi = std.os.uefi;
+
+var starting_stack_ptr: [*]usize = undefined;
+
+const is_wasm = switch (builtin.arch) {
+ .wasm32, .wasm64 => true,
+ else => false,
+};
+
+const is_mips = switch (builtin.arch) {
+ .mips, .mipsel, .mips64, .mips64el => true,
+ else => false,
+};
+const start_sym_name = if (is_mips) "__start" else "_start";
+
+comptime {
+ if (builtin.output_mode == .Lib and builtin.link_mode == .Dynamic) {
+ if (builtin.os == .windows and !@hasDecl(root, "_DllMainCRTStartup")) {
+ @export("_DllMainCRTStartup", _DllMainCRTStartup, .Strong);
+ }
+ } else if (builtin.output_mode == .Exe or @hasDecl(root, "main")) {
+ if (builtin.link_libc and @hasDecl(root, "main")) {
+ if (@typeInfo(@TypeOf(root.main)).Fn.calling_convention != .C) {
+ @export("main", main, .Weak);
+ }
+ } else if (builtin.os == .windows) {
+ if (!@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup")) {
+ @export("WinMainCRTStartup", WinMainCRTStartup, .Strong);
+ }
+ } else if (builtin.os == .uefi) {
+ if (!@hasDecl(root, "EfiMain")) @export("EfiMain", EfiMain, .Strong);
+ } else if (is_wasm and builtin.os == .freestanding) {
+ if (!@hasDecl(root, start_sym_name)) @export(start_sym_name, wasm_freestanding_start, .Strong);
+ } else if (builtin.os != .other and builtin.os != .freestanding) {
+ if (!@hasDecl(root, start_sym_name)) @export(start_sym_name, _start, .Strong);
+ }
+ }
+}
+
+stdcallcc fn _DllMainCRTStartup(
+ hinstDLL: std.os.windows.HINSTANCE,
+ fdwReason: std.os.windows.DWORD,
+ lpReserved: std.os.windows.LPVOID,
+) std.os.windows.BOOL {
+ if (@hasDecl(root, "DllMain")) {
+ return root.DllMain(hinstDLL, fdwReason, lpReserved);
+ }
+
+ return std.os.windows.TRUE;
+}
+
+extern fn wasm_freestanding_start() void {
+ // This is marked inline because for some reason LLVM in release mode fails to inline it,
+ // and we want fewer call frames in stack traces.
+ _ = @call(.{ .modifier = .always_inline }, callMain, .{});
+}
+
+extern fn EfiMain(handle: uefi.Handle, system_table: *uefi.tables.SystemTable) usize {
+ const bad_efi_main_ret = "expected return type of main to be 'void', 'noreturn', or 'usize'";
+ uefi.handle = handle;
+ uefi.system_table = system_table;
+
+ switch (@typeInfo(@TypeOf(root.main).ReturnType)) {
+ .NoReturn => {
+ root.main();
+ },
+ .Void => {
+ root.main();
+ return 0;
+ },
+ .Int => |info| {
+ if (info.bits != @typeInfo(usize).Int.bits) {
+ @compileError(bad_efi_main_ret);
+ }
+ return root.main();
+ },
+ else => @compileError(bad_efi_main_ret),
+ }
+}
+
+nakedcc fn _start() noreturn {
+ if (builtin.os == builtin.Os.wasi) {
+ // This is marked inline because for some reason LLVM in release mode fails to inline it,
+ // and we want fewer call frames in stack traces.
+ std.os.wasi.proc_exit(@call(.{ .modifier = .always_inline }, callMain, .{}));
+ }
+
+ switch (builtin.arch) {
+ .x86_64 => {
+ starting_stack_ptr = asm (""
+ : [argc] "={rsp}" (-> [*]usize)
+ );
+ },
+ .i386 => {
+ starting_stack_ptr = asm (""
+ : [argc] "={esp}" (-> [*]usize)
+ );
+ },
+ .aarch64, .aarch64_be, .arm => {
+ starting_stack_ptr = asm ("mov %[argc], sp"
+ : [argc] "=r" (-> [*]usize)
+ );
+ },
+ .riscv64 => {
+ starting_stack_ptr = asm ("mv %[argc], sp"
+ : [argc] "=r" (-> [*]usize)
+ );
+ },
+ .mipsel => {
+ // Need noat here because LLVM is free to pick any register
+ starting_stack_ptr = asm (
+ \\ .set noat
+ \\ move %[argc], $sp
+ : [argc] "=r" (-> [*]usize)
+ );
+ },
+ else => @compileError("unsupported arch"),
+ }
+ // If LLVM inlines stack variables into _start, they will overwrite
+ // the command line argument data.
+ @call(.{ .modifier = .never_inline }, posixCallMainAndExit, .{});
+}
+
+stdcallcc fn WinMainCRTStartup() noreturn {
+ @setAlignStack(16);
+ if (!builtin.single_threaded) {
+ _ = @import("start_windows_tls.zig");
+ }
+
+ std.debug.maybeEnableSegfaultHandler();
+
+ std.os.windows.kernel32.ExitProcess(initEventLoopAndCallMain());
+}
+
+// TODO https://github.com/ziglang/zig/issues/265
+fn posixCallMainAndExit() noreturn {
+ if (builtin.os == builtin.Os.freebsd) {
+ @setAlignStack(16);
+ }
+ const argc = starting_stack_ptr[0];
+ const argv = @ptrCast([*][*:0]u8, starting_stack_ptr + 1);
+
+ const envp_optional = @ptrCast([*:null]?[*:0]u8, argv + argc + 1);
+ var envp_count: usize = 0;
+ while (envp_optional[envp_count]) |_| : (envp_count += 1) {}
+ const envp = @ptrCast([*][*:0]u8, envp_optional)[0..envp_count];
+
+ if (builtin.os == .linux) {
+ // Find the beginning of the auxiliary vector
+ const auxv = @ptrCast([*]std.elf.Auxv, envp.ptr + envp_count + 1);
+ std.os.linux.elf_aux_maybe = auxv;
+ // Initialize the TLS area
+ const gnu_stack_phdr = std.os.linux.tls.initTLS() orelse @panic("ELF missing stack size");
+
+ if (std.os.linux.tls.tls_image) |tls_img| {
+ const tls_addr = std.os.linux.tls.allocateTLS(tls_img.alloc_size);
+ const tp = std.os.linux.tls.copyTLS(tls_addr);
+ std.os.linux.tls.setThreadPointer(tp);
+ }
+
+ // TODO This is disabled because what should we do when linking libc and this code
+ // does not execute? And also it's causing a test failure in stack traces in release modes.
+
+ //// Linux ignores the stack size from the ELF file, and instead always does 8 MiB. A further
+ //// problem is that it uses PROT_GROWSDOWN which prevents stores to addresses too far down
+ //// the stack and requires "probing". So here we allocate our own stack.
+ //const wanted_stack_size = gnu_stack_phdr.p_memsz;
+ //assert(wanted_stack_size % std.mem.page_size == 0);
+ //// Allocate an extra page as the guard page.
+ //const total_size = wanted_stack_size + std.mem.page_size;
+ //const new_stack = std.os.mmap(
+ // null,
+ // total_size,
+ // std.os.PROT_READ | std.os.PROT_WRITE,
+ // std.os.MAP_PRIVATE | std.os.MAP_ANONYMOUS,
+ // -1,
+ // 0,
+ //) catch @panic("out of memory");
+ //std.os.mprotect(new_stack[0..std.mem.page_size], std.os.PROT_NONE) catch {};
+ //std.os.exit(@call(.{.stack = new_stack}, callMainWithArgs, .{argc, argv, envp}));
+ }
+
+ std.os.exit(@call(.{ .modifier = .always_inline }, callMainWithArgs, .{ argc, argv, envp }));
+}
+
+fn callMainWithArgs(argc: usize, argv: [*][*:0]u8, envp: [][*:0]u8) u8 {
+ std.os.argv = argv[0..argc];
+ std.os.environ = envp;
+
+ std.debug.maybeEnableSegfaultHandler();
+
+ return initEventLoopAndCallMain();
+}
+
+extern fn main(c_argc: i32, c_argv: [*][*:0]u8, c_envp: [*:null]?[*:0]u8) i32 {
+ var env_count: usize = 0;
+ while (c_envp[env_count] != null) : (env_count += 1) {}
+ const envp = @ptrCast([*][*:0]u8, c_envp)[0..env_count];
+ return @call(.{ .modifier = .always_inline }, callMainWithArgs, .{ @intCast(usize, c_argc), c_argv, envp });
+}
+
+// General error message for a malformed return type
+const bad_main_ret = "expected return type of main to be 'void', '!void', 'noreturn', 'u8', or '!u8'";
+
+// This is marked inline because for some reason LLVM in release mode fails to inline it,
+// and we want fewer call frames in stack traces.
+inline fn initEventLoopAndCallMain() u8 {
+ if (std.event.Loop.instance) |loop| {
+ if (!@hasDecl(root, "event_loop")) {
+ loop.init() catch |err| {
+ std.debug.warn("error: {}\n", .{@errorName(err)});
+ if (@errorReturnTrace()) |trace| {
+ std.debug.dumpStackTrace(trace.*);
+ }
+ return 1;
+ };
+ defer loop.deinit();
+
+ var result: u8 = undefined;
+ var frame: @Frame(callMainAsync) = undefined;
+ _ = @asyncCall(&frame, &result, callMainAsync, loop);
+ loop.run();
+ return result;
+ }
+ }
+
+ // This is marked inline because for some reason LLVM in release mode fails to inline it,
+ // and we want fewer call frames in stack traces.
+ return @call(.{ .modifier = .always_inline }, callMain, .{});
+}
+
+async fn callMainAsync(loop: *std.event.Loop) u8 {
+ // This prevents the event loop from terminating at least until main() has returned.
+ loop.beginOneEvent();
+ defer loop.finishOneEvent();
+ return callMain();
+}
+
+// This is not marked inline because it is called with @asyncCall when
+// there is an event loop.
+pub fn callMain() u8 {
+ switch (@typeInfo(@TypeOf(root.main).ReturnType)) {
+ .NoReturn => {
+ root.main();
+ },
+ .Void => {
+ root.main();
+ return 0;
+ },
+ .Int => |info| {
+ if (info.bits != 8) {
+ @compileError(bad_main_ret);
+ }
+ return root.main();
+ },
+ .ErrorUnion => {
+ const result = root.main() catch |err| {
+ std.debug.warn("error: {}\n", .{@errorName(err)});
+ if (@errorReturnTrace()) |trace| {
+ std.debug.dumpStackTrace(trace.*);
+ }
+ return 1;
+ };
+ switch (@typeInfo(@TypeOf(result))) {
+ .Void => return 0,
+ .Int => |info| {
+ if (info.bits != 8) {
+ @compileError(bad_main_ret);
+ }
+ return result;
+ },
+ else => @compileError(bad_main_ret),
+ }
+ },
+ else => @compileError(bad_main_ret),
+ }
+}
diff --git a/lib/std/start_windows_tls.zig b/lib/std/start_windows_tls.zig
@@ -0,0 +1,48 @@
+const std = @import("std");
+const builtin = std.builtin;
+
+export var _tls_index: u32 = std.os.windows.TLS_OUT_OF_INDEXES;
+export var _tls_start: u8 linksection(".tls") = 0;
+export var _tls_end: u8 linksection(".tls$ZZZ") = 0;
+export var __xl_a: std.os.windows.PIMAGE_TLS_CALLBACK linksection(".CRT$XLA") = null;
+export var __xl_z: std.os.windows.PIMAGE_TLS_CALLBACK linksection(".CRT$XLZ") = null;
+
+comptime {
+ if (builtin.arch == .i386) {
+ // The __tls_array is the offset of the ThreadLocalStoragePointer field
+ // in the TEB block whose base address held in the %fs segment.
+ asm (
+ \\ .global __tls_array
+ \\ __tls_array = 0x2C
+ );
+ }
+}
+
+// TODO this is how I would like it to be expressed
+// TODO also note, ReactOS has a +1 on StartAddressOfRawData and AddressOfCallBacks. Investigate
+// why they do that.
+//export const _tls_used linksection(".rdata$T") = std.os.windows.IMAGE_TLS_DIRECTORY {
+// .StartAddressOfRawData = @ptrToInt(&_tls_start),
+// .EndAddressOfRawData = @ptrToInt(&_tls_end),
+// .AddressOfIndex = @ptrToInt(&_tls_index),
+// .AddressOfCallBacks = @ptrToInt(__xl_a),
+// .SizeOfZeroFill = 0,
+// .Characteristics = 0,
+//};
+// This is the workaround because we can't do @ptrToInt at comptime like that.
+pub const IMAGE_TLS_DIRECTORY = extern struct {
+ StartAddressOfRawData: *c_void,
+ EndAddressOfRawData: *c_void,
+ AddressOfIndex: *c_void,
+ AddressOfCallBacks: *c_void,
+ SizeOfZeroFill: u32,
+ Characteristics: u32,
+};
+export const _tls_used linksection(".rdata$T") = IMAGE_TLS_DIRECTORY{
+ .StartAddressOfRawData = &_tls_start,
+ .EndAddressOfRawData = &_tls_end,
+ .AddressOfIndex = &_tls_index,
+ .AddressOfCallBacks = &__xl_a,
+ .SizeOfZeroFill = 0,
+ .Characteristics = 0,
+};
diff --git a/lib/std/std.zig b/lib/std/std.zig
@@ -65,6 +65,13 @@ pub const time = @import("time.zig");
pub const unicode = @import("unicode.zig");
pub const valgrind = @import("valgrind.zig");
pub const zig = @import("zig.zig");
+pub const start = @import("start.zig");
+
+// This forces the start.zig file to be imported, and the comptime logic inside that
+// file decides whether to export any appropriate start symbols.
+comptime {
+ _ = start;
+}
test "" {
meta.refAllDecls(@This());
diff --git a/src/all_types.hpp b/src/all_types.hpp
@@ -2003,10 +2003,11 @@ struct CodeGen {
ZigPackage *std_package;
ZigPackage *test_runner_package;
ZigPackage *compile_var_package;
+ ZigPackage *root_pkg; // @import("root")
+ ZigPackage *main_pkg; // usually same as root_pkg, except for `zig test`
ZigType *compile_var_import;
ZigType *root_import;
ZigType *start_import;
- ZigType *test_runner_import;
struct {
ZigType *entry_bool;
@@ -2179,7 +2180,6 @@ struct CodeGen {
Buf *root_out_name;
Buf *test_filter;
Buf *test_name_prefix;
- ZigPackage *root_package;
Buf *zig_lib_dir;
Buf *zig_std_dir;
Buf *dynamic_linker_path;
diff --git a/src/analyze.cpp b/src/analyze.cpp
@@ -3536,7 +3536,7 @@ static void preview_test_decl(CodeGen *g, AstNode *node, ScopeDecls *decls_scope
return;
ZigType *import = get_scope_import(&decls_scope->base);
- if (import->data.structure.root_struct->package != g->root_package)
+ if (import->data.structure.root_struct->package != g->main_pkg)
return;
Buf *decl_name_buf = node->data.test_decl.name;
@@ -3577,7 +3577,7 @@ void update_compile_var(CodeGen *g, Buf *name, ZigValue *value) {
resolve_top_level_decl(g, tld, tld->source_node, false);
assert(tld->id == TldIdVar);
TldVar *tld_var = (TldVar *)tld;
- tld_var->var->const_value = value;
+ copy_const_val(tld_var->var->const_value, value);
tld_var->var->var_type = value->type;
tld_var->var->align_bytes = get_abi_alignment(g, value->type);
}
@@ -9178,3 +9178,42 @@ bool is_anon_container(ZigType *ty) {
ty->data.structure.special == StructSpecialInferredTuple ||
ty->data.structure.special == StructSpecialInferredStruct);
}
+
+bool is_opt_err_set(ZigType *ty) {
+ return ty->id == ZigTypeIdErrorSet ||
+ (ty->id == ZigTypeIdOptional && ty->data.maybe.child_type->id == ZigTypeIdErrorSet);
+}
+
+// Returns whether the x_optional field of ZigValue is active.
+bool type_has_optional_repr(ZigType *ty) {
+ if (ty->id != ZigTypeIdOptional) {
+ return false;
+ } else if (get_codegen_ptr_type(ty) != nullptr) {
+ return false;
+ } else if (is_opt_err_set(ty)) {
+ return false;
+ } else {
+ return true;
+ }
+}
+
+void copy_const_val(ZigValue *dest, ZigValue *src) {
+ memcpy(dest, src, sizeof(ZigValue));
+ if (src->special != ConstValSpecialStatic)
+ return;
+ dest->parent.id = ConstParentIdNone;
+ if (dest->type->id == ZigTypeIdStruct) {
+ dest->data.x_struct.fields = alloc_const_vals_ptrs(dest->type->data.structure.src_field_count);
+ for (size_t i = 0; i < dest->type->data.structure.src_field_count; i += 1) {
+ copy_const_val(dest->data.x_struct.fields[i], src->data.x_struct.fields[i]);
+ dest->data.x_struct.fields[i]->parent.id = ConstParentIdStruct;
+ dest->data.x_struct.fields[i]->parent.data.p_struct.struct_val = dest;
+ dest->data.x_struct.fields[i]->parent.data.p_struct.field_index = i;
+ }
+ } else if (type_has_optional_repr(dest->type) && dest->data.x_optional != nullptr) {
+ dest->data.x_optional = create_const_vals(1);
+ copy_const_val(dest->data.x_optional, src->data.x_optional);
+ dest->data.x_optional->parent.id = ConstParentIdOptionalPayload;
+ dest->data.x_optional->parent.data.p_optional_payload.optional_val = dest;
+ }
+}
diff --git a/src/analyze.hpp b/src/analyze.hpp
@@ -276,4 +276,7 @@ Error analyze_import(CodeGen *codegen, ZigType *source_import, Buf *import_targe
ZigType **out_import, Buf **out_import_target_path, Buf *out_full_path);
ZigValue *get_the_one_possible_value(CodeGen *g, ZigType *type_entry);
bool is_anon_container(ZigType *ty);
+void copy_const_val(ZigValue *dest, ZigValue *src);
+bool type_has_optional_repr(ZigType *ty);
+bool is_opt_err_set(ZigType *ty);
#endif
diff --git a/src/codegen.cpp b/src/codegen.cpp
@@ -8440,11 +8440,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) {
if (g->is_test_build) {
buf_appendf(contents,
- "const TestFn = struct {\n"
- "name: []const u8,\n"
- "func: fn()anyerror!void,\n"
- "};\n"
- "pub const test_functions = {}; // overwritten later\n"
+ "pub var test_functions: []TestFn = undefined; // overwritten later\n"
);
}
@@ -8535,23 +8531,23 @@ static Error define_builtin_compile_vars(CodeGen *g) {
}
}
- assert(g->root_package);
+ assert(g->main_pkg);
assert(g->std_package);
g->compile_var_package = new_package(buf_ptr(this_dir), builtin_zig_basename, "builtin");
- g->compile_var_package->package_table.put(buf_create_from_str("std"), g->std_package);
- g->root_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package);
- g->std_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package);
- g->std_package->package_table.put(buf_create_from_str("std"), g->std_package);
- ZigPackage *root_pkg;
if (g->is_test_build) {
if (g->test_runner_package == nullptr) {
g->test_runner_package = create_test_runner_pkg(g);
}
- root_pkg = g->test_runner_package;
+ g->root_pkg = g->test_runner_package;
} else {
- root_pkg = g->root_package;
+ g->root_pkg = g->main_pkg;
}
- g->std_package->package_table.put(buf_create_from_str("root"), root_pkg);
+ g->compile_var_package->package_table.put(buf_create_from_str("std"), g->std_package);
+ g->main_pkg->package_table.put(buf_create_from_str("builtin"), g->compile_var_package);
+ g->main_pkg->package_table.put(buf_create_from_str("root"), g->root_pkg);
+ g->std_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package);
+ g->std_package->package_table.put(buf_create_from_str("std"), g->std_package);
+ g->std_package->package_table.put(buf_create_from_str("root"), g->root_pkg);
g->compile_var_import = add_source_file(g, g->compile_var_package, builtin_zig_path, contents,
SourceKindPkgMain);
@@ -8670,7 +8666,7 @@ static void init(CodeGen *g) {
// no longer reference DW_AT_comp_dir, for the purpose of being able to support the
// common practice of stripping all but the line number sections from an executable.
const char *compile_unit_dir = target_os_is_darwin(g->zig_target->os) ? "." :
- buf_ptr(&g->root_package->root_src_dir);
+ buf_ptr(&g->main_pkg->root_src_dir);
ZigLLVMDIFile *compile_unit_file = ZigLLVMCreateFile(g->dbuilder, buf_ptr(g->root_out_name),
compile_unit_dir);
@@ -9083,30 +9079,7 @@ void codegen_translate_c(CodeGen *g, Buf *full_path, FILE *out_file, bool use_us
}
}
-static ZigType *add_special_code(CodeGen *g, ZigPackage *package, const char *basename) {
- Buf *code_basename = buf_create_from_str(basename);
- Buf path_to_code_src = BUF_INIT;
- os_path_join(g->zig_std_special_dir, code_basename, &path_to_code_src);
-
- Buf *resolve_paths[] = {&path_to_code_src};
- Buf *resolved_path = buf_alloc();
- *resolved_path = os_path_resolve(resolve_paths, 1);
- Buf *import_code = buf_alloc();
- Error err;
- if ((err = file_fetch(g, resolved_path, import_code))) {
- zig_panic("unable to open '%s': %s\n", buf_ptr(&path_to_code_src), err_str(err));
- }
-
- return add_source_file(g, package, resolved_path, import_code, SourceKindPkgMain);
-}
-
-static ZigPackage *create_start_pkg(CodeGen *g, ZigPackage *pkg_with_main) {
- ZigPackage *package = codegen_create_package(g, buf_ptr(g->zig_std_special_dir), "start.zig", "std.special");
- package->package_table.put(buf_create_from_str("root"), pkg_with_main);
- return package;
-}
-
-static void create_test_compile_var_and_add_test_runner(CodeGen *g) {
+static void update_test_functions_builtin_decl(CodeGen *g) {
Error err;
assert(g->is_test_build);
@@ -9166,16 +9139,15 @@ static void create_test_compile_var_and_add_test_runner(CodeGen *g) {
update_compile_var(g, buf_create_from_str("test_functions"), test_fn_slice);
assert(g->test_runner_package != nullptr);
- g->test_runner_import = add_special_code(g, g->test_runner_package, "test_runner.zig");
}
static Buf *get_resolved_root_src_path(CodeGen *g) {
// TODO memoize
- if (buf_len(&g->root_package->root_src_path) == 0)
+ if (buf_len(&g->main_pkg->root_src_path) == 0)
return nullptr;
Buf rel_full_path = BUF_INIT;
- os_path_join(&g->root_package->root_src_dir, &g->root_package->root_src_path, &rel_full_path);
+ os_path_join(&g->main_pkg->root_src_dir, &g->main_pkg->root_src_path, &rel_full_path);
Buf *resolved_path = buf_alloc();
Buf *resolve_paths[] = {&rel_full_path};
@@ -9198,7 +9170,7 @@ static void gen_root_source(CodeGen *g) {
exit(1);
}
- ZigType *root_import_alias = add_source_file(g, g->root_package, resolved_path, source_code, SourceKindRoot);
+ ZigType *root_import_alias = add_source_file(g, g->main_pkg, resolved_path, source_code, SourceKindRoot);
assert(root_import_alias == g->root_import);
assert(g->root_out_name);
@@ -9250,16 +9222,8 @@ static void gen_root_source(CodeGen *g) {
}
report_errors_and_maybe_exit(g);
- if (!g->is_test_build) {
- g->start_import = add_special_code(g, create_start_pkg(g, g->root_package), "start.zig");
- }
- if (!g->error_during_imports) {
- semantic_analyze(g);
- }
if (g->is_test_build) {
- create_test_compile_var_and_add_test_runner(g);
- g->start_import = add_special_code(g, create_start_pkg(g, g->test_runner_package), "start.zig");
-
+ update_test_functions_builtin_decl(g);
if (!g->error_during_imports) {
semantic_analyze(g);
}
@@ -10058,7 +10022,7 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) {
CacheHash *ch = &g->cache_hash;
cache_init(ch, manifest_dir);
- add_cache_pkg(g, ch, g->root_package);
+ add_cache_pkg(g, ch, g->main_pkg);
if (g->linker_script != nullptr) {
cache_file(ch, buf_create_from_str(g->linker_script));
}
@@ -10141,7 +10105,7 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) {
}
static bool need_llvm_module(CodeGen *g) {
- return buf_len(&g->root_package->root_src_path) != 0;
+ return buf_len(&g->main_pkg->root_src_path) != 0;
}
static void resolve_out_paths(CodeGen *g) {
@@ -10388,8 +10352,7 @@ ZigPackage *codegen_create_package(CodeGen *g, const char *root_src_dir, const c
assert(g->compile_var_package != nullptr);
pkg->package_table.put(buf_create_from_str("std"), g->std_package);
- ZigPackage *main_pkg = g->is_test_build ? g->test_runner_package : g->root_package;
- pkg->package_table.put(buf_create_from_str("root"), main_pkg);
+ pkg->package_table.put(buf_create_from_str("root"), g->root_pkg);
pkg->package_table.put(buf_create_from_str("builtin"), g->compile_var_package);
}
@@ -10516,15 +10479,13 @@ CodeGen *codegen_create(Buf *main_pkg_path, Buf *root_src_path, const ZigTarget
buf_len(&resolved_root_src_path) - buf_len(&resolved_main_pkg_path) - 1);
}
- g->root_package = new_package(buf_ptr(root_pkg_path), buf_ptr(rel_root_src_path), "");
+ g->main_pkg = new_package(buf_ptr(root_pkg_path), buf_ptr(rel_root_src_path), "");
g->std_package = new_package(buf_ptr(g->zig_std_dir), "std.zig", "std");
- g->root_package->package_table.put(buf_create_from_str("std"), g->std_package);
+ g->main_pkg->package_table.put(buf_create_from_str("std"), g->std_package);
} else {
- g->root_package = new_package(".", "", "");
+ g->main_pkg = new_package(".", "", "");
}
- g->root_package->package_table.put(buf_create_from_str("root"), g->root_package);
-
g->zig_std_special_dir = buf_alloc();
os_path_join(g->zig_std_dir, buf_sprintf("special"), g->zig_std_special_dir);
diff --git a/src/dump_analysis.cpp b/src/dump_analysis.cpp
@@ -1216,7 +1216,7 @@ void zig_print_analysis_dump(CodeGen *g, FILE *f, const char *one_indent, const
jw_end_object(jw);
jw_object_field(jw, "rootPkg");
- anal_dump_pkg_ref(&ctx, g->root_package);
+ anal_dump_pkg_ref(&ctx, g->main_pkg);
// Poke the functions
for (size_t i = 0; i < g->fn_defs.length; i += 1) {
diff --git a/src/ir.cpp b/src/ir.cpp
@@ -232,7 +232,6 @@ static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source
static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *ptr,
ZigType *dest_type, IrInstruction *dest_type_src, bool safety_check_on);
static ZigValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value, UndefAllowed undef_allowed);
-static void copy_const_val(ZigValue *dest, ZigValue *src);
static Error resolve_ptr_align(IrAnalyze *ira, ZigType *ty, uint32_t *result_align);
static IrInstruction *ir_analyze_int_to_ptr(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target,
ZigType *ptr_type);
@@ -718,11 +717,6 @@ static ZigValue *const_ptr_pointee_unchecked(CodeGen *g, ZigValue *const_val) {
return result;
}
-static bool is_opt_err_set(ZigType *ty) {
- return ty->id == ZigTypeIdErrorSet ||
- (ty->id == ZigTypeIdOptional && ty->data.maybe.child_type->id == ZigTypeIdErrorSet);
-}
-
static bool is_tuple(ZigType *type) {
return type->id == ZigTypeIdStruct && type->data.structure.special == StructSpecialInferredTuple;
}
@@ -11451,40 +11445,6 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT
}
}
-// Returns whether the x_optional field of ZigValue is active.
-static bool type_has_optional_repr(ZigType *ty) {
- if (ty->id != ZigTypeIdOptional) {
- return false;
- } else if (get_codegen_ptr_type(ty) != nullptr) {
- return false;
- } else if (is_opt_err_set(ty)) {
- return false;
- } else {
- return true;
- }
-}
-
-static void copy_const_val(ZigValue *dest, ZigValue *src) {
- memcpy(dest, src, sizeof(ZigValue));
- if (src->special != ConstValSpecialStatic)
- return;
- dest->parent.id = ConstParentIdNone;
- if (dest->type->id == ZigTypeIdStruct) {
- dest->data.x_struct.fields = alloc_const_vals_ptrs(dest->type->data.structure.src_field_count);
- for (size_t i = 0; i < dest->type->data.structure.src_field_count; i += 1) {
- copy_const_val(dest->data.x_struct.fields[i], src->data.x_struct.fields[i]);
- dest->data.x_struct.fields[i]->parent.id = ConstParentIdStruct;
- dest->data.x_struct.fields[i]->parent.data.p_struct.struct_val = dest;
- dest->data.x_struct.fields[i]->parent.data.p_struct.field_index = i;
- }
- } else if (type_has_optional_repr(dest->type) && dest->data.x_optional != nullptr) {
- dest->data.x_optional = create_const_vals(1);
- copy_const_val(dest->data.x_optional, src->data.x_optional);
- dest->data.x_optional->parent.id = ConstParentIdOptionalPayload;
- dest->data.x_optional->parent.data.p_optional_payload.optional_val = dest;
- }
-}
-
static bool eval_const_expr_implicit_cast(IrAnalyze *ira, IrInstruction *source_instr,
CastOp cast_op,
ZigValue *other_val, ZigType *other_type,
diff --git a/src/main.cpp b/src/main.cpp
@@ -623,7 +623,7 @@ int main(int argc, char **argv) {
ZigPackage *build_pkg = codegen_create_package(g, buf_ptr(&build_file_dirname),
buf_ptr(&build_file_basename), "std.special");
- g->root_package->package_table.put(buf_create_from_str("@build"), build_pkg);
+ g->main_pkg->package_table.put(buf_create_from_str("@build"), build_pkg);
g->enable_cache = get_cache_opt(enable_cache, true);
codegen_build_and_link(g);
if (root_progress_node != nullptr) {
@@ -1269,7 +1269,7 @@ int main(int argc, char **argv) {
codegen_set_test_name_prefix(g, buf_create_from_str(test_name_prefix));
}
- add_package(g, cur_pkg, g->root_package);
+ add_package(g, cur_pkg, g->main_pkg);
if (cmd == CmdBuild || cmd == CmdRun || cmd == CmdTest) {
g->c_source_files = c_source_files;