zig/src/stage1.zig
Andrew Kelley 8ce880ca75 avoid calling into stage1 backend when AstGen fails
The motivation for this commit is that there exists source files which
produce ast-check errors, but crash stage1 or otherwise trigger stage1
bugs. Previously to this commit, Zig would run AstGen, collect the
compile errors, run stage1, report stage1 compile errors and exit if
any, and then report AstGen compile errors.

The main change in this commit is to report AstGen errors prior to
invoking stage1, and in fact if any AstGen errors occur, do not invoke
stage1 at all.

This caused most of the compile error tests to fail due to things such
as unused local variables and mismatched stage1/stage2 error messages.
It was taking a long time to update the test cases one-by-one, so I
took this opportunity to unify the stage1 and stage2 testing harness,
specifically with regards to compile errors. In this way we can start
keeping track of which tests pass for 1, 2, or both.
`zig build test-compile-errors` no longer works; it is now integrated
into `zig build test-stage2`.

This is one step closer to executing compile error tests in parallel; in
fact the ThreadPool object is already in scope.

There are some cases where the stage1 compile errors were actually
better; those are left failing in this commit, to be addressed in a
follow-up commit.

Other changes in this commit:

 * build.zig: improve support for -Dstage1 used with the test step.
 * AstGen: minor cosmetic changes to error messages.
 * stage2: add -fstage1 and -fno-stage1 flags. This now allows one to
   download a binary of the zig compiler and use the llvm backend of
   self-hosted. This was also needed for hooking up the test harness.
   However, I realized that stage1 calls exit() and also has memory
   leaks, so had to complicate the test harness by not using this flag
   after all and instead invoking as a child process.
   - These CLI flags will disappear once we start shipping the
     self-hosted compiler as the main compiler. Until then, they can be
     used to try out the work-in-progress stage2.
 * stage2: select the LLVM backend by default for release modes, as long
   as the target architecture is supported by LLVM.
 * test harness: support setting the optimize mode
2021-07-02 13:27:28 -07:00

463 lines
12 KiB
Zig

//! This is the main entry point for the Zig/C++ hybrid compiler (stage1).
//! It has the functions exported from Zig, called in C++, and bindings for
//! the functions exported from C++, called from Zig.
const std = @import("std");
const assert = std.debug.assert;
const mem = std.mem;
const CrossTarget = std.zig.CrossTarget;
const Target = std.Target;
const builtin = @import("builtin");
const build_options = @import("build_options");
const stage2 = @import("main.zig");
const fatal = stage2.fatal;
const Compilation = @import("Compilation.zig");
const translate_c = @import("translate_c.zig");
const target_util = @import("target.zig");
comptime {
assert(builtin.link_libc);
assert(build_options.is_stage1);
assert(build_options.have_llvm);
if (!builtin.is_test) {
_ = @import("compiler_rt");
@export(main, .{ .name = "main" });
}
}
pub const log = stage2.log;
pub const log_level = stage2.log_level;
pub fn main(argc: c_int, argv: [*][*:0]u8) callconv(.C) c_int {
std.os.argv = argv[0..@intCast(usize, argc)];
std.debug.maybeEnableSegfaultHandler();
zig_stage1_os_init();
const gpa = std.heap.c_allocator;
var arena_instance = std.heap.ArenaAllocator.init(gpa);
defer arena_instance.deinit();
const arena = &arena_instance.allocator;
const args = arena.alloc([]const u8, @intCast(usize, argc)) catch fatal("{s}", .{"OutOfMemory"});
for (args) |*arg, i| {
arg.* = mem.spanZ(argv[i]);
}
if (builtin.mode == .Debug) {
stage2.mainArgs(gpa, arena, args) catch unreachable;
} else {
stage2.mainArgs(gpa, arena, args) catch |err| fatal("{s}", .{@errorName(err)});
}
return 0;
}
/// Matches stage2.Color;
pub const ErrColor = c_int;
/// Matches std.builtin.CodeModel
pub const CodeModel = c_int;
/// Matches std.Target.Os.Tag
pub const OS = c_int;
/// Matches std.builtin.BuildMode
pub const BuildMode = c_int;
pub const TargetSubsystem = enum(c_int) {
Console,
Windows,
Posix,
Native,
EfiApplication,
EfiBootServiceDriver,
EfiRom,
EfiRuntimeDriver,
Auto,
};
pub const Pkg = extern struct {
name_ptr: [*]const u8,
name_len: usize,
path_ptr: [*]const u8,
path_len: usize,
children_ptr: [*]*Pkg,
children_len: usize,
parent: ?*Pkg,
};
pub const Module = extern struct {
root_name_ptr: [*]const u8,
root_name_len: usize,
emit_o_ptr: [*]const u8,
emit_o_len: usize,
emit_h_ptr: [*]const u8,
emit_h_len: usize,
emit_asm_ptr: [*]const u8,
emit_asm_len: usize,
emit_llvm_ir_ptr: [*]const u8,
emit_llvm_ir_len: usize,
emit_analysis_json_ptr: [*]const u8,
emit_analysis_json_len: usize,
emit_docs_ptr: [*]const u8,
emit_docs_len: usize,
builtin_zig_path_ptr: [*]const u8,
builtin_zig_path_len: usize,
test_filter_ptr: [*]const u8,
test_filter_len: usize,
test_name_prefix_ptr: [*]const u8,
test_name_prefix_len: usize,
userdata: usize,
root_pkg: *Pkg,
main_progress_node: ?*std.Progress.Node,
code_model: CodeModel,
subsystem: TargetSubsystem,
err_color: ErrColor,
pic: bool,
pie: bool,
lto: bool,
unwind_tables: bool,
link_libc: bool,
link_libcpp: bool,
strip: bool,
is_single_threaded: bool,
dll_export_fns: bool,
link_mode_dynamic: bool,
valgrind_enabled: bool,
tsan_enabled: bool,
function_sections: bool,
enable_stack_probing: bool,
red_zone: bool,
enable_time_report: bool,
enable_stack_report: bool,
test_is_evented: bool,
verbose_ir: bool,
verbose_llvm_ir: bool,
verbose_cimport: bool,
verbose_llvm_cpu_features: bool,
// Set by stage1
have_c_main: bool,
have_winmain: bool,
have_wwinmain: bool,
have_winmain_crt_startup: bool,
have_wwinmain_crt_startup: bool,
have_dllmain_crt_startup: bool,
pub fn build_object(mod: *Module) void {
zig_stage1_build_object(mod);
}
pub fn destroy(mod: *Module) void {
zig_stage1_destroy(mod);
}
};
pub const os_init = zig_stage1_os_init;
extern fn zig_stage1_os_init() void;
pub const create = zig_stage1_create;
extern fn zig_stage1_create(
optimize_mode: BuildMode,
main_pkg_path_ptr: [*]const u8,
main_pkg_path_len: usize,
root_src_path_ptr: [*]const u8,
root_src_path_len: usize,
zig_lib_dir_ptr: [*c]const u8,
zig_lib_dir_len: usize,
target: [*c]const Stage2Target,
is_test_build: bool,
) ?*Module;
extern fn zig_stage1_build_object(*Module) void;
extern fn zig_stage1_destroy(*Module) void;
// ABI warning
export fn stage2_panic(ptr: [*]const u8, len: usize) void {
@panic(ptr[0..len]);
}
// ABI warning
const Error = enum(c_int) {
None,
OutOfMemory,
InvalidFormat,
SemanticAnalyzeFail,
AccessDenied,
Interrupted,
SystemResources,
FileNotFound,
FileSystem,
FileTooBig,
DivByZero,
Overflow,
PathAlreadyExists,
Unexpected,
ExactDivRemainder,
NegativeDenominator,
ShiftedOutOneBits,
CCompileErrors,
EndOfFile,
IsDir,
NotDir,
UnsupportedOperatingSystem,
SharingViolation,
PipeBusy,
PrimitiveTypeNotFound,
CacheUnavailable,
PathTooLong,
CCompilerCannotFindFile,
NoCCompilerInstalled,
ReadingDepFile,
InvalidDepFile,
MissingArchitecture,
MissingOperatingSystem,
UnknownArchitecture,
UnknownOperatingSystem,
UnknownABI,
InvalidFilename,
DiskQuota,
DiskSpace,
UnexpectedWriteFailure,
UnexpectedSeekFailure,
UnexpectedFileTruncationFailure,
Unimplemented,
OperationAborted,
BrokenPipe,
NoSpaceLeft,
NotLazy,
IsAsync,
ImportOutsidePkgPath,
UnknownCpuModel,
UnknownCpuFeature,
InvalidCpuFeatures,
InvalidLlvmCpuFeaturesFormat,
UnknownApplicationBinaryInterface,
ASTUnitFailure,
BadPathName,
SymLinkLoop,
ProcessFdQuotaExceeded,
SystemFdQuotaExceeded,
NoDevice,
DeviceBusy,
UnableToSpawnCCompiler,
CCompilerExitCode,
CCompilerCrashed,
CCompilerCannotFindHeaders,
LibCRuntimeNotFound,
LibCStdLibHeaderNotFound,
LibCKernel32LibNotFound,
UnsupportedArchitecture,
WindowsSdkNotFound,
UnknownDynamicLinkerPath,
TargetHasNoDynamicLinker,
InvalidAbiVersion,
InvalidOperatingSystemVersion,
UnknownClangOption,
NestedResponseFile,
ZigIsTheCCompiler,
FileBusy,
Locked,
InvalidCharacter,
UnicodePointTooLarge,
};
// ABI warning
export fn stage2_version_string() [*:0]const u8 {
return build_options.version;
}
// ABI warning
export fn stage2_version() Stage2SemVer {
return .{
.major = build_options.semver.major,
.minor = build_options.semver.minor,
.patch = build_options.semver.patch,
};
}
// ABI warning
export fn stage2_attach_segfault_handler() void {
if (std.debug.runtime_safety and std.debug.have_segfault_handling_support) {
std.debug.attachSegfaultHandler();
}
}
// ABI warning
export fn stage2_progress_create() *std.Progress {
const ptr = std.heap.c_allocator.create(std.Progress) catch @panic("out of memory");
// If the terminal is dumb, we dont want to show the user all the
// output.
ptr.* = std.Progress{ .dont_print_on_dumb = true };
return ptr;
}
// ABI warning
export fn stage2_progress_destroy(progress: *std.Progress) void {
std.heap.c_allocator.destroy(progress);
}
// ABI warning
export fn stage2_progress_start_root(
progress: *std.Progress,
name_ptr: [*]const u8,
name_len: usize,
estimated_total_items: usize,
) *std.Progress.Node {
return progress.start(
name_ptr[0..name_len],
estimated_total_items,
) catch @panic("timer unsupported");
}
// ABI warning
export fn stage2_progress_disable_tty(progress: *std.Progress) void {
progress.terminal = null;
}
// ABI warning
export fn stage2_progress_start(
node: *std.Progress.Node,
name_ptr: [*]const u8,
name_len: usize,
estimated_total_items: usize,
) *std.Progress.Node {
const child_node = std.heap.c_allocator.create(std.Progress.Node) catch @panic("out of memory");
child_node.* = node.start(
name_ptr[0..name_len],
estimated_total_items,
);
child_node.activate();
return child_node;
}
// ABI warning
export fn stage2_progress_end(node: *std.Progress.Node) void {
node.end();
if (&node.context.root != node) {
std.heap.c_allocator.destroy(node);
}
}
// ABI warning
export fn stage2_progress_complete_one(node: *std.Progress.Node) void {
node.completeOne();
}
// ABI warning
export fn stage2_progress_update_node(node: *std.Progress.Node, done_count: usize, total_count: usize) void {
node.setCompletedItems(done_count);
node.setEstimatedTotalItems(total_count);
node.activate();
node.context.maybeRefresh();
}
// ABI warning
pub const Stage2Target = extern struct {
arch: c_int,
os: OS,
abi: c_int,
is_native_os: bool,
is_native_cpu: bool,
llvm_cpu_name: ?[*:0]const u8,
llvm_cpu_features: ?[*:0]const u8,
};
// ABI warning
const Stage2SemVer = extern struct {
major: u32,
minor: u32,
patch: u32,
};
// ABI warning
export fn stage2_cimport(
stage1: *Module,
c_src_ptr: [*]const u8,
c_src_len: usize,
out_zig_path_ptr: *[*]const u8,
out_zig_path_len: *usize,
out_errors_ptr: *[*]translate_c.ClangErrMsg,
out_errors_len: *usize,
) Error {
const comp = @intToPtr(*Compilation, stage1.userdata);
const c_src = c_src_ptr[0..c_src_len];
const result = comp.cImport(c_src) catch |err| switch (err) {
error.SystemResources => return .SystemResources,
error.OperationAborted => return .OperationAborted,
error.BrokenPipe => return .BrokenPipe,
error.DiskQuota => return .DiskQuota,
error.FileTooBig => return .FileTooBig,
error.NoSpaceLeft => return .NoSpaceLeft,
error.AccessDenied => return .AccessDenied,
error.OutOfMemory => return .OutOfMemory,
error.Unexpected => return .Unexpected,
error.InputOutput => return .FileSystem,
error.ASTUnitFailure => return .ASTUnitFailure,
error.CacheUnavailable => return .CacheUnavailable,
else => return .Unexpected,
};
out_zig_path_ptr.* = result.out_zig_path.ptr;
out_zig_path_len.* = result.out_zig_path.len;
out_errors_ptr.* = result.errors.ptr;
out_errors_len.* = result.errors.len;
if (result.errors.len != 0) return .CCompileErrors;
return Error.None;
}
export fn stage2_add_link_lib(
stage1: *Module,
lib_name_ptr: [*c]const u8,
lib_name_len: usize,
symbol_name_ptr: [*c]const u8,
symbol_name_len: usize,
) ?[*:0]const u8 {
_ = symbol_name_len;
_ = symbol_name_ptr;
const comp = @intToPtr(*Compilation, stage1.userdata);
const lib_name = std.ascii.allocLowerString(comp.gpa, lib_name_ptr[0..lib_name_len]) catch return "out of memory";
const target = comp.getTarget();
const is_libc = target_util.is_libc_lib_name(target, lib_name);
if (is_libc) {
if (!comp.bin_file.options.link_libc) {
return "dependency on libc must be explicitly specified in the build command";
}
return null;
}
if (target_util.is_libcpp_lib_name(target, lib_name)) {
if (!comp.bin_file.options.link_libcpp) {
return "dependency on libc++ must be explicitly specified in the build command";
}
return null;
}
if (!target.isWasm() and !comp.bin_file.options.pic) {
return std.fmt.allocPrint0(
comp.gpa,
"dependency on dynamic library '{s}' requires enabling Position Independent Code. Fixed by `-l{s}` or `-fPIC`.",
.{ lib_name, lib_name },
) catch "out of memory";
}
comp.stage1AddLinkLib(lib_name) catch |err| {
return std.fmt.allocPrint0(comp.gpa, "unable to add link lib '{s}': {s}", .{
lib_name, @errorName(err),
}) catch "out of memory";
};
return null;
}
export fn stage2_fetch_file(
stage1: *Module,
path_ptr: [*]const u8,
path_len: usize,
result_len: *usize,
) ?[*]const u8 {
const comp = @intToPtr(*Compilation, stage1.userdata);
const file_path = path_ptr[0..path_len];
const max_file_size = std.math.maxInt(u32);
const contents = comp.stage1_cache_manifest.addFilePostFetch(file_path, max_file_size) catch return null;
result_len.* = contents.len;
// TODO https://github.com/ziglang/zig/issues/3328#issuecomment-716749475
if (contents.len == 0) return @intToPtr(?[*]const u8, 0x1);
return contents.ptr;
}