Files
zig/src/stage1.zig
Andrew Kelley 2a990d6966 stage1: rework tokenizer to match stage2
* 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
2021-05-28 12:58:40 -07:00

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;
}