* Extracts AstGen logic from ir.cpp into astgen.cpp. Reduces the
largest file of stage1 from 33,551 lines to 25,510.
* tokenizer: rework it completely to match the stage2 tokenizer logic.
They can now be maintained together; when one is changed, the other
can be changed in the same way.
- Each token now takes up 13 bytes instead of 64 bytes. The tokenizer
does not parse char literals, string literals, integer literals,
etc into meaningful data. Instead, that happens during parsing or
astgen.
- no longer store line offsets. Error messages scan source
files to find the line/column as needed (same as stage2).
- main loop: instead of checking the loop, handle a null byte
explicitly in the switch statements. This is a nice improvement
that we may want to backport to stage2.
- delete some dead tokens, artifacts of past syntax that no longer
exists.
* Parser: fix a TODO by parsing builtin functions as tokens rather than
`@` as a separate token. This is how stage2 does it.
* Remove some debugging infrastructure. These will need to be redone,
if at all, as the code migrates to match stage2.
- remove the ast_render code.
- remove the IR debugging stuff
- remove teh token printing code
457 lines
12 KiB
Zig
457 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 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(std.builtin.link_libc);
|
|
assert(build_options.is_stage1);
|
|
assert(build_options.have_llvm);
|
|
_ = @import("compiler_rt");
|
|
}
|
|
|
|
pub const log = stage2.log;
|
|
pub const log_level = stage2.log_level;
|
|
|
|
pub export fn main(argc: c_int, argv: [*][*:0]u8) 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 (std.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 = extern 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,
|
|
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_tokenize: bool,
|
|
verbose_ast: 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);
|
|
}
|
|
};
|
|
|
|
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 = extern enum {
|
|
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 {
|
|
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;
|
|
}
|