diff --git a/.gitignore b/.gitignore index 1c2b629..5f2b938 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ zig-cache/ zig-out/ nat/ +*.o +*.out +*.obj diff --git a/bootstrap/Compilation.zig b/bootstrap/Compilation.zig index cbd74ae..8cc6cf2 100644 --- a/bootstrap/Compilation.zig +++ b/bootstrap/Compilation.zig @@ -6,6 +6,8 @@ const data_structures = @import("library.zig"); const assert = data_structures.assert; const byte_equal = data_structures.byte_equal; const byte_equal_terminated = data_structures.byte_equal_terminated; +const first_slice = data_structures.first_slice; +const starts_with_slice = data_structures.starts_with_slice; const UnpinnedArray = data_structures.UnpinnedArray; const BlockList = data_structures.BlockList; const MyAllocator = data_structures.MyAllocator; @@ -17,6 +19,7 @@ const lexer = @import("frontend/lexer.zig"); const parser = @import("frontend/parser.zig"); const Node = parser.Node; const llvm = @import("backend/llvm.zig"); +const linker = @import("linker/linker.zig"); const cache_dir_name = "cache"; const installation_dir_name = "installation"; @@ -59,7 +62,7 @@ pub fn createContext(allocator: Allocator, my_allocator: *MyAllocator) !*const C return context; } -pub fn compileBuildExecutable(context: *const Context, arguments: [][*:0]u8) !void { +pub fn compileBuildExecutable(context: *const Context, arguments: []const []const u8) !void { _ = arguments; // autofix const unit = try context.my_allocator.allocate_one(Unit); unit.* = .{ @@ -83,7 +86,9 @@ pub fn compileBuildExecutable(context: *const Context, arguments: [][*:0]u8) !vo }, .only_parse = false, .executable_path = "nat/build", + .object_path = "nat/build.o", .link_libc = @import("builtin").os.tag == .macos, + .link_libcpp = false, .generate_debug_information = true, .name = "build", .is_test = false, @@ -263,126 +268,608 @@ fn compileMusl(context: *const Context) MuslContext{ return musl; } -pub fn compileCSourceFile(context: *const Context, arguments: [][*:0]u8, kind: CSourceKind) !void { - var clang_args = UnpinnedArray([]const u8){}; - try clang_args.append(context.my_allocator, context.executable_absolute_path); - try clang_args.append(context.my_allocator, "clang"); - +pub fn compileCSourceFile(context: *const Context, arguments: []const []const u8, kind: CSourceKind) !void { + _ = kind; // autofix + var argument_index: usize = 0; + _ = &argument_index; const Mode = enum{ object, link, }; - const mode: Mode = for (arguments) |argz| { - const arg = span(argz); - if (byte_equal(arg, "-c")) break .object; - } else .link; - _ = mode; // autofix + var out_path: ?[]const u8 = null; + var out_mode: ?Mode = null; + const Extension = enum{ + c, + cpp, + assembly, + object, + static_library, + shared_library, + }; + const CSourceFile = struct{ + path: []const u8, + extension: Extension, + }; + const DebugInfo = enum{ + yes, + no, + }; + const LinkArch = enum{ + arm64, + }; + var debug_info: ?DebugInfo = null; + var stack_protector: ?bool = null; + var link_arch: ?LinkArch = null; - if (kind == .cpp) { - try clang_args.append(context.my_allocator, "-nostdinc++"); + var cc_argv = UnpinnedArray([]const u8){}; + var ld_argv = UnpinnedArray([]const u8){}; + var c_source_files = UnpinnedArray(CSourceFile){}; + var link_objects = UnpinnedArray(linker.Object){}; + var link_libraries = UnpinnedArray(linker.Library){}; - switch (@import("builtin").os.tag) { - .linux => { - switch (@import("builtin").abi) { - .gnu => { - try clang_args.append_slice(context.my_allocator, &.{ - "-isystem", "/usr/include/c++/13.2.1", - "-isystem", "/usr/include/c++/13.2.1/x86_64-pc-linux-gnu", + while (argument_index < arguments.len) { + const argument = arguments[argument_index]; + + if (argument[0] != '-') { + if (data_structures.last_byte(argument, '.')) |dot_index| { + const extension_string = argument[dot_index..]; + const extension: Extension = + if (byte_equal(extension_string, ".c")) .c + else if (byte_equal(extension_string, ".cpp") or byte_equal(extension_string, ".cxx") or byte_equal(extension_string, ".cc")) .cpp + else if (byte_equal(extension_string, ".S")) .assembly + else if (byte_equal(extension_string, ".o")) .object + else if (byte_equal(extension_string, ".a")) .static_library + else if (byte_equal(extension_string, ".so") or + byte_equal(extension_string, ".dll") or + byte_equal(extension_string, ".dylib") or + byte_equal(extension_string, ".tbd") + ) .shared_library + else { + try write(.panic, argument); + try write(.panic, "\n"); + @panic("Unable to recognize extension for the file above"); + }; + switch (extension) { + .c, .cpp, .assembly => { + try c_source_files.append(context.my_allocator, .{ + .path = argument, + .extension = extension, }); }, - .musl => { - try clang_args.append_slice(context.my_allocator, &.{ - "-isystem", try context.pathFromCompiler("lib/libcxx/include"), - "-isystem", try context.pathFromCompiler("lib/libcxxabi/include"), - "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS", - "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS", - "-D_LIBCPP_HAS_NO_VENDOR_AVAILABILITY_ANNOTATIONS", - "-D_LIBCPP_PSTL_CPU_BACKEND_SERIAL", - "-D_LIBCPP_ABI_VERSION=1", - "-D_LIBCPP_ABI_NAMESPACE=__1", + .object, .static_library, .shared_library => { + try link_objects.append(context.my_allocator, .{ + .path = argument, }); }, - else => unreachable, } - }, - .macos => { - try clang_args.append_slice(context.my_allocator, &.{ - "-isystem", try context.pathFromCompiler("lib/libcxx/include"), - "-isystem", try context.pathFromCompiler("lib/libcxxabi/include"), - "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS", - "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS", - "-D_LIBCPP_HAS_NO_VENDOR_AVAILABILITY_ANNOTATIONS", - "-D_LIBCPP_PSTL_CPU_BACKEND_SERIAL", - "-D_LIBCPP_ABI_VERSION=1", - "-D_LIBCPP_ABI_NAMESPACE=__1", - }); - }, - else => @compileError("Operating system not supported"), + } else { + try write(.panic, argument); + try write(.panic, "\n"); + @panic("Positional argument without extension"); + } + } else if (byte_equal(argument, "-c")) { + out_mode = .object; + } else if (byte_equal(argument, "-o")) { + argument_index += 1; + out_path = arguments[argument_index]; + } else if (byte_equal(argument, "-g")) { + debug_info = .yes; + } else if (byte_equal(argument, "-fno-stack-protector")) { + stack_protector = false; + } else if (byte_equal(argument, "-arch")) { + argument_index += 1; + const arch_argument = arguments[argument_index]; + if (byte_equal(arch_argument, "arm64")) { + link_arch = .arm64; + try cc_argv.append(context.my_allocator, "-arch"); + try cc_argv.append(context.my_allocator, "arm64"); + } else { + unreachable; + } + } else if (byte_equal(argument, "-bundle")) { + try ld_argv.append(context.my_allocator, argument); + } else if (byte_equal(argument, "-pthread")) { + try cc_argv.append(context.my_allocator, argument); + } else if (byte_equal(argument, "-fPIC")) { + try cc_argv.append(context.my_allocator, argument); + } else if (byte_equal(argument, "-MD")) { + try cc_argv.append(context.my_allocator, argument); + } else if (byte_equal(argument, "-MT")) { + try cc_argv.append(context.my_allocator, argument); + argument_index += 1; + const arg = arguments[argument_index]; + try cc_argv.append(context.my_allocator, arg); + } else if (byte_equal(argument, "-MF")) { + try cc_argv.append(context.my_allocator, argument); + argument_index += 1; + const arg = arguments[argument_index]; + try cc_argv.append(context.my_allocator, arg); + } else if (byte_equal(argument, "-isysroot")) { + try cc_argv.append(context.my_allocator, argument); + argument_index += 1; + const arg = arguments[argument_index]; + try cc_argv.append(context.my_allocator, arg); + } else if (byte_equal(argument, "-isystem")) { + try cc_argv.append(context.my_allocator, argument); + argument_index += 1; + const arg = arguments[argument_index]; + try cc_argv.append(context.my_allocator, arg); + } else if (byte_equal(argument, "-h")) { + try cc_argv.append(context.my_allocator, argument); + } else if (byte_equal(argument, "-framework")) { + try ld_argv.append(context.my_allocator, argument); + argument_index += 1; + const framework = arguments[argument_index]; + try ld_argv.append(context.my_allocator, framework); + } else if (byte_equal(argument, "--coverage")) { + try cc_argv.append(context.my_allocator, argument); + } else if (byte_equal(argument, "-pedantic")) { + try cc_argv.append(context.my_allocator, argument); + } else if (byte_equal(argument, "-pedantic-errors")) { + try cc_argv.append(context.my_allocator, argument); + } else if (byte_equal(argument, "-?")) { + try cc_argv.append(context.my_allocator, argument); + } else if (byte_equal(argument, "-v")) { + try cc_argv.append(context.my_allocator, argument); + } else if (byte_equal(argument, "-V")) { + try cc_argv.append(context.my_allocator, argument); + } else if (byte_equal(argument, "--version")) { + try cc_argv.append(context.my_allocator, argument); + } else if (byte_equal(argument, "-version")) { + try cc_argv.append(context.my_allocator, argument); + } else if (byte_equal(argument, "-qversion")) { + try cc_argv.append(context.my_allocator, argument); + } else if (byte_equal(argument, "-print-resource-dir")) { + try cc_argv.append(context.my_allocator, argument); + } else if (byte_equal(argument, "-shared")) { + try ld_argv.append(context.my_allocator, argument); + } else if (byte_equal(argument, "-compatibility_version")) { + try ld_argv.append(context.my_allocator, argument); + argument_index += 1; + const arg = arguments[argument_index]; + try ld_argv.append(context.my_allocator, arg); + } else if (byte_equal(argument, "-current_version")) { + try ld_argv.append(context.my_allocator, argument); + argument_index += 1; + const arg = arguments[argument_index]; + try ld_argv.append(context.my_allocator, arg); + } else if (byte_equal(argument, "-install_name")) { + try ld_argv.append(context.my_allocator, argument); + argument_index += 1; + const arg = arguments[argument_index]; + try ld_argv.append(context.my_allocator, arg); + } else if (starts_with_slice(argument, "-f")) { + try cc_argv.append(context.my_allocator, argument); + } else if (starts_with_slice(argument, "-wd")) { + try cc_argv.append(context.my_allocator, argument); + } else if (starts_with_slice(argument, "-D")) { + try cc_argv.append(context.my_allocator, argument); + } else if (starts_with_slice(argument, "-I")) { + try cc_argv.append(context.my_allocator, argument); + } else if (starts_with_slice(argument, "-W")) { + try cc_argv.append(context.my_allocator, argument); + } else if (starts_with_slice(argument, "-l")) { + try link_libraries.append(context.my_allocator, .{ + .path = argument[2..], + }); + } else if (starts_with_slice(argument, "-O")) { + try cc_argv.append(context.my_allocator, argument); + } else if (starts_with_slice(argument, "-std=")) { + try cc_argv.append(context.my_allocator, argument); + } else if (starts_with_slice(argument, "-rdynamic")) { + try ld_argv.append(context.my_allocator, "-export_dynamic"); + } else if (starts_with_slice(argument, "-dynamiclib")) { + try ld_argv.append(context.my_allocator, "-dylib"); + } else if (starts_with_slice(argument, "-Wl,")) { + const wl_arg = argument["-Wl,".len..]; + if (data_structures.first_byte(wl_arg, ',')) |comma_index| { + const key = wl_arg[0..comma_index]; + const value = wl_arg[comma_index + 1..]; + try ld_argv.append(context.my_allocator, key); + try ld_argv.append(context.my_allocator, value); + } else { + try ld_argv.append(context.my_allocator, wl_arg); + } + } else if (starts_with_slice(argument, "-m")) { + try cc_argv.append(context.my_allocator, argument); + } else { + const debug_args = true; + if (debug_args) { + const home_dir = std.posix.getenv("HOME") orelse unreachable; + var list = UnpinnedArray(u8){}; + for (arguments) |arg| { + try list.append_slice(context.my_allocator, arg); + try list.append(context.my_allocator, ' '); + } + try list.append(context.my_allocator, '\n'); + try list.append_slice(context.my_allocator, "Unhandled argument: "); + try list.append_slice(context.my_allocator, argument); + try list.append(context.my_allocator, '\n'); + + try std.fs.cwd().writeFile(try std.fmt.allocPrint(context.allocator, "{s}/dev/nativity/nat/unhandled_arg_{}", .{home_dir, std.time.milliTimestamp()}), list.slice()); + } + try write(.panic, "unhandled argument: '"); + try write(.panic, argument); + try write(.panic, "'\n"); + @panic("Unhandled argument"); } + + argument_index += 1; } - if (kind == .c or kind == .cpp) { - try clang_args.append(context.my_allocator, "-nostdinc"); + const link_libcpp = true; + const mode = out_mode orelse .link; - switch (@import("builtin").os.tag) { - .linux => { - switch (@import("builtin").abi) { - .gnu => { - try clang_args.append_slice(context.my_allocator, &.{ - "-isystem", "/usr/lib/clang/17/include", - "-isystem", "/usr/include", - "-isystem", "/usr/include/linux", - }); + if (c_source_files.length > 0) { + for (c_source_files.slice()) |c_source_file| { + var argv = UnpinnedArray([]const u8){}; + try argv.append(context.my_allocator, context.executable_absolute_path); + try argv.append(context.my_allocator, "clang"); + try argv.append(context.my_allocator, "--no-default-config"); + + try argv.append(context.my_allocator, c_source_file.path); + + if (c_source_file.extension == .cpp) { + try argv.append(context.my_allocator, "-nostdinc++"); + } + + const caret = true; + if (!caret) { + try argv.append(context.my_allocator, "-fno-caret-diagnostics"); + } + + const function_sections = false; + if (function_sections) { + try argv.append(context.my_allocator, "-ffunction-sections"); + } + + const data_sections = false; + if (data_sections) { + try argv.append(context.my_allocator, "-fdata-sections"); + } + + const builtin = true; + if (!builtin) { + try argv.append(context.my_allocator, "-fno-builtin"); + } + + + if (link_libcpp) { + // include paths + + } + + const link_libc = c_source_file.extension == .c; + if (link_libc) { + } + + const link_libunwind = false; + if (link_libunwind) { + unreachable; + } + + const target_triple = blk: { + // Emit target + var target_triple_buffer = UnpinnedArray(u8){}; + switch (@import("builtin").target.cpu.arch) { + .x86_64 => { + try target_triple_buffer.append_slice(context.my_allocator, "x86_64-"); }, - .musl => { - try clang_args.append_slice(context.my_allocator, &.{ - "-isystem", try context.pathFromCompiler("lib/include"), - "-isystem", try context.pathFromCompiler("lib/libc/include/x86_64-linux-gnu"), - "-isystem", try context.pathFromCompiler("lib/libc/include/generic-glibc"), - "-isystem", try context.pathFromCompiler("lib/libc/include/x86-linux-any"), - "-isystem", try context.pathFromCompiler("lib/libc/include/any-linux-any"), - }); + .aarch64 => { + try target_triple_buffer.append_slice(context.my_allocator, "aarch64-"); }, - else => @compileError("Abi not supported"), + else => @compileError("Architecture not supported"), } - }, - .macos => { - try clang_args.append_slice(context.my_allocator, &.{ - "-iframework", "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks", - "-isystem", try context.pathFromCompiler("lib/include"), - "-isystem", "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include", - }); - }, - else => @compileError("Operating system not supported"), - } - } - if (kind == .cpp) { - switch (@import("builtin").os.tag) { - .linux => { - switch (@import("builtin").abi) { - .gnu => { - try clang_args.append(context.my_allocator, "-lstdc++"); + if (@import("builtin").target.cpu.arch == .aarch64 and @import("builtin").target.os.tag == .macos) { + try target_triple_buffer.append_slice(context.my_allocator, "apple-"); + } else { + try target_triple_buffer.append_slice(context.my_allocator, "pc-"); + } + + switch (@import("builtin").target.os.tag) { + .linux => { + try target_triple_buffer.append_slice(context.my_allocator, "linux-"); }, + .macos => { + try target_triple_buffer.append_slice(context.my_allocator, "macos-"); + }, + else => @compileError("OS not supported"), + } + + switch (@import("builtin").target.abi) { .musl => { + try target_triple_buffer.append_slice(context.my_allocator, "musl"); + }, + .gnu => { + try target_triple_buffer.append_slice(context.my_allocator, "gnu"); + }, + .none => { + try target_triple_buffer.append_slice(context.my_allocator, "unknown"); + }, + else => @compileError("OS not supported"), + } + + break :blk target_triple_buffer.slice(); + }; + try argv.append_slice(context.my_allocator, &.{"-target", target_triple}); + + const object_path = switch (mode) { + .object => out_path.?, + .link => try std.mem.concat(context.allocator, u8, &.{if (out_path) |op| op else "a.o", ".o"}), + }; + + try link_objects.append(context.my_allocator, .{ + .path = object_path, + }); + + switch (c_source_file.extension) { + .c, .cpp => { + try argv.append(context.my_allocator, "-nostdinc"); + try argv.append(context.my_allocator, "-fno-spell-checking"); + + const lto = false; + if (lto) { + try argv.append(context.my_allocator, "-flto"); + } + + const mm = false; + if (mm) { + try argv.append(context.my_allocator, "-ObjC++"); + } + + const libc_framework_dirs: []const []const u8 = switch (@import("builtin").os.tag) { + .macos => &.{"/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks"}, + else => &.{}, + }; + for (libc_framework_dirs) |framework_dir| { + try argv.append_slice(context.my_allocator, &.{"-iframework", framework_dir}); + } + + const framework_dirs = &[_][]const u8{}; + for (framework_dirs) |framework_dir| { + try argv.append_slice(context.my_allocator, &.{"-F", framework_dir}); + } + + // TODO: c headers dir + + const libc_include_dirs: []const []const u8 = switch (@import("builtin").os.tag) { + .macos => &.{ + "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1", + try context.pathFromCompiler("lib/include"), + "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include", + }, + .linux => switch (@import("builtin").abi) { + .gnu => &.{ + "/usr/include/c++/13.2.1", + "/usr/include/c++/13.2.1/x86_64-pc-linux-gnu", + "/usr/lib/clang/17/include", + "/usr/include", + "/usr/include/linux", + }, + else => unreachable, //@compileError("ABI not supported"), + }, + else => @compileError("OS not supported"), + }; + + for (libc_include_dirs) |include_dir| { + try argv.append_slice(context.my_allocator, &.{"-isystem", include_dir}); + } + + // TODO: cpu model + // TODO: cpu features + // TODO: code model + // TODO: OS-specific flags + // TODO: sanitize flags + // const red_zone = true; + // if (red_zone) { + // try argv.append(context.my_allocator, "-mred-zone"); + // } else { + // unreachable; + // } + + const omit_frame_pointer = false; + if (omit_frame_pointer) { + try argv.append(context.my_allocator, "-fomit-frame-pointer"); + } else { + try argv.append(context.my_allocator, "-fno-omit-frame-pointer"); + } + + if (stack_protector orelse false) { + try argv.append(context.my_allocator, "-fstack-protector-strong"); + } else { + try argv.append(context.my_allocator, "-fno-stack-protector"); + } + + const is_debug = true; + if (is_debug) { + try argv.append(context.my_allocator, "-D_DEBUG"); + try argv.append(context.my_allocator, "-O0"); + } else { unreachable; - }, - else => @compileError("Abi not supported"), - } - }, - .macos => unreachable, - else => @compileError("OS not supported"), + } + + const pic = false; + if (pic) { + try argv.append(context.my_allocator, "-fPIC"); + } + + const unwind_tables = false; + if (unwind_tables) { + try argv.append(context.my_allocator, "-funwind-tables"); + } else { + try argv.append(context.my_allocator, "-fno-unwind-tables"); + } + }, + .assembly => { + // TODO: + }, + .object, .static_library, .shared_library => unreachable, + } + + const has_debug_info = true; + if (has_debug_info) { + try argv.append(context.my_allocator, "-g"); + } else { + unreachable; + } + + // TODO: machine ABI + const freestanding = false; + if (freestanding) { + try argv.append(context.my_allocator, "-ffrestanding"); + } + + // TODO: native system include paths + // TODO: global cc argv + + try argv.append_slice(context.my_allocator, cc_argv.slice()); + + // TODO: extra flags + // TODO: cache exempt flags + try argv.append_slice(context.my_allocator, &.{"-c", "-o", object_path}); + // TODO: emit ASM/LLVM IR + + const debug_clang_args = false; + if (debug_clang_args) { + std.debug.print("Argv: {s}\n", .{argv.slice()}); + } + const result = try clangMain(context.allocator, argv.slice()); + if (result != 0) { + unreachable; + } } + } else if (link_objects.length == 0) { + var argv = UnpinnedArray([]const u8){}; + try argv.append(context.my_allocator, context.executable_absolute_path); + try argv.append(context.my_allocator, "clang"); + try argv.append(context.my_allocator, "--no-default-config"); + try argv.append_slice(context.my_allocator, cc_argv.slice()); + const result = try clangMain(context.allocator, argv.slice()); + if (result != 0) { + unreachable; + } + return; } - for (arguments) |arg| { - try clang_args.append(context.my_allocator, span(arg)); + if (mode == .link) { + assert(link_objects.length > 0); + try linker.link(context, .{ + .backend = .lld, + .output_file_path = out_path orelse "a.out", + .objects = link_objects.slice(), + .libraries = link_libraries.slice(), + .extra_arguments = ld_argv.slice(), + .link_libc = true, + .link_libcpp = link_libcpp, + }); } - const result = try clangMain(context.allocator, clang_args.slice()); - if (result != 0) { - unreachable; - } + // if (kind == .cpp) { + // try clang_args.append(context.my_allocator, "-nostdinc++"); + // + // switch (@import("builtin").os.tag) { + // .linux => { + // switch (@import("builtin").abi) { + // .gnu => { + // try clang_args.append_slice(context.my_allocator, &.{ + // "-isystem", "/usr/include/c++/13.2.1", + // "-isystem", "/usr/include/c++/13.2.1/x86_64-pc-linux-gnu", + // }); + // }, + // .musl => { + // try clang_args.append_slice(context.my_allocator, &.{ + // "-isystem", try context.pathFromCompiler("lib/libcxx/include"), + // "-isystem", try context.pathFromCompiler("lib/libcxxabi/include"), + // "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS", + // "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS", + // "-D_LIBCPP_HAS_NO_VENDOR_AVAILABILITY_ANNOTATIONS", + // "-D_LIBCPP_PSTL_CPU_BACKEND_SERIAL", + // "-D_LIBCPP_ABI_VERSION=1", + // "-D_LIBCPP_ABI_NAMESPACE=__1", + // }); + // }, + // else => unreachable, + // } + // }, + // .macos => { + // try clang_args.append_slice(context.my_allocator, &.{ + // "-isystem", try context.pathFromCompiler("lib/libcxx/include"), + // "-isystem", try context.pathFromCompiler("lib/libcxxabi/include"), + // "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS", + // "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS", + // "-D_LIBCPP_HAS_NO_VENDOR_AVAILABILITY_ANNOTATIONS", + // "-D_LIBCPP_PSTL_CPU_BACKEND_SERIAL", + // "-D_LIBCPP_ABI_VERSION=1", + // "-D_LIBCPP_ABI_NAMESPACE=__1", + // }); + // }, + // else => @compileError("Operating system not supported"), + // } + // } + // + // if (kind == .c or kind == .cpp) { + // try clang_args.append(context.my_allocator, "-nostdinc"); + // + // switch (@import("builtin").os.tag) { + // .linux => { + // switch (@import("builtin").abi) { + // .gnu => { + // try clang_args.append_slice(context.my_allocator, &.{ + // "-isystem", "/usr/lib/clang/17/include", + // "-isystem", "/usr/include", + // "-isystem", "/usr/include/linux", + // }); + // }, + // .musl => { + // try clang_args.append_slice(context.my_allocator, &.{ + // "-isystem", try context.pathFromCompiler("lib/include"), + // "-isystem", try context.pathFromCompiler("lib/libc/include/x86_64-linux-gnu"), + // "-isystem", try context.pathFromCompiler("lib/libc/include/generic-glibc"), + // "-isystem", try context.pathFromCompiler("lib/libc/include/x86-linux-any"), + // "-isystem", try context.pathFromCompiler("lib/libc/include/any-linux-any"), + // }); + // }, + // else => @compileError("Abi not supported"), + // } + // }, + // .macos => { + // try clang_args.append_slice(context.my_allocator, &.{ + // "-iframework", "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks", + // "-isystem", try context.pathFromCompiler("lib/include"), + // "-isystem", "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include", + // }); + // }, + // else => @compileError("Operating system not supported"), + // } + // } + // + // if (kind == .cpp) { + // switch (@import("builtin").os.tag) { + // .linux => { + // switch (@import("builtin").abi) { + // .gnu => { + // try clang_args.append(context.my_allocator, "-lstdc++"); + // }, + // .musl => { + // unreachable; + // }, + // else => @compileError("Abi not supported"), + // } + // }, + // .macos => unreachable, + // else => @compileError("OS not supported"), + // } + // } + // + // for (arguments) |arg| { + // try clang_args.append(context.my_allocator, span(arg)); + // } + // + // const result = try clangMain(context.allocator, clang_args.slice()); + // if (result != 0) { + // unreachable; + // } // const output_object_file = "nat/main.o"; // const exit_code = try clangMain(context.allocator, &.{ context.executable_absolute_path, "--no-default-config", "-target", "x86_64-unknown-linux-musl", "-nostdinc", "-fno-spell-checking", "-isystem", "lib/include", "-isystem", "lib/libc/include/x86_64-linux-musl", "-isystem", "lib/libc/include/generic-musl", "-isystem", "lib/libc/include/x86-linux-any", "-isystem", "lib/libc/include/any-linux-any", "-c", argument, "-o", output_object_file }); @@ -2230,7 +2717,7 @@ const musl_arch_files = [_][]const u8{ musl_lib_dir_relative_path ++ "src/unistd/x32/lseek.c", }; -fn argsCopyZ(alloc: Allocator, args: []const []const u8) ![:null]?[*:0]u8 { +pub fn argsCopyZ(alloc: Allocator, args: []const []const u8) ![:null]?[*:0]u8 { var argv = try alloc.allocSentinel(?[*:0]u8, args.len, null); for (args, 0..) |arg, i| { argv[i] = try alloc.dupeZ(u8, arg); // TODO If there was an argsAllocZ we could avoid this allocation. @@ -2246,7 +2733,7 @@ fn arMain(allocator: Allocator, arguments: []const []const u8) !u8 { } extern "c" fn NativityClangMain(argc: c_int, argv: [*:null]?[*:0]u8) c_int; -fn clangMain(allocator: Allocator, arguments: []const []const u8) !u8 { +pub fn clangMain(allocator: Allocator, arguments: []const []const u8) !u8 { const argv = try argsCopyZ(allocator, arguments); const exit_code = NativityClangMain(@as(c_int, @intCast(arguments.len)), argv.ptr); return @as(u8, @bitCast(@as(i8, @truncate(exit_code)))); @@ -2272,7 +2759,7 @@ const Abi = enum { musl, }; -pub fn buildExecutable(context: *const Context, arguments: [][*:0]u8, options: ExecutableOptions) !void { +pub fn buildExecutable(context: *const Context, arguments: []const []const u8, options: ExecutableOptions) !void { var maybe_executable_path: ?[]const u8 = null; var maybe_main_package_path: ?[]const u8 = null; var arch: Arch = undefined; @@ -2296,17 +2783,17 @@ pub fn buildExecutable(context: *const Context, arguments: [][*:0]u8, options: E var maybe_only_parse: ?bool = null; var link_libc = false; var maybe_executable_name: ?[]const u8 = null; - var c_source_files = UnpinnedArray([*:0]u8){}; + var c_source_files = UnpinnedArray([]const u8){}; const generate_debug_information = true; if (arguments.len == 0) return error.InvalidInput; var i: usize = 0; while (i < arguments.len) : (i += 1) { - const current_argument = span(arguments[i]); + const current_argument = arguments[i]; if (byte_equal(current_argument, "-o")) { if (i + 1 != arguments.len) { - maybe_executable_path = span(arguments[i + 1]); + maybe_executable_path = arguments[i + 1]; assert(maybe_executable_path.?.len != 0); i += 1; } else { @@ -2364,7 +2851,7 @@ pub fn buildExecutable(context: *const Context, arguments: [][*:0]u8, options: E if (i + 1 != arguments.len) { i += 1; - const arg = span(arguments[i]); + const arg = arguments[i]; maybe_main_package_path = arg; maybe_only_parse = true; } else { @@ -2374,7 +2861,7 @@ pub fn buildExecutable(context: *const Context, arguments: [][*:0]u8, options: E if (i + 1 != arguments.len) { i += 1; - const arg = span(arguments[i]); + const arg = arguments[i]; if (byte_equal(arg, "true")) { link_libc = true; } else if (byte_equal(arg, "false")) { @@ -2389,7 +2876,7 @@ pub fn buildExecutable(context: *const Context, arguments: [][*:0]u8, options: E if (i + 1 != arguments.len) { i += 1; - const arg = span(arguments[i]); + const arg = arguments[i]; maybe_main_package_path = arg; } else { reportUnterminatedArgumentError(current_argument); @@ -2398,7 +2885,7 @@ pub fn buildExecutable(context: *const Context, arguments: [][*:0]u8, options: E if (i + 1 != arguments.len) { i += 1; - const arg = span(arguments[i]); + const arg = arguments[i]; maybe_executable_name = arg; } else { reportUnterminatedArgumentError(current_argument); @@ -2437,11 +2924,20 @@ pub fn buildExecutable(context: *const Context, arguments: [][*:0]u8, options: E break :blk result; }; + const object_file_path = blk: { + const slice = try context.allocator.alloc(u8, executable_path.len + 2); + @memcpy(slice[0..executable_path.len], executable_path); + slice[executable_path.len] = '.'; + slice[executable_path.len + 1] = 'o'; + break :blk slice; + }; + const unit = try context.allocator.create(Unit); unit.* = .{ .descriptor = .{ .main_package_path = main_package_path, .executable_path = executable_path, + .object_path = object_file_path, .only_parse = only_parse, .arch = arch, .os = os, @@ -2452,6 +2948,7 @@ pub fn buildExecutable(context: *const Context, arguments: [][*:0]u8, options: E // .windows => link_libc, // else => unreachable, }, + .link_libcpp = false, .generate_debug_information = generate_debug_information, .name = executable_name, .is_test = options.is_test, @@ -14427,7 +14924,7 @@ pub const Unit = struct { cc_type: Type.Index = .null, test_function_type: Type.Index = .null, descriptor: Descriptor, - object_files: UnpinnedArray([*:0]const u8) = .{}, + // object_files: []const linker.Object, discard_identifiers: usize = 0, anon_i: usize = 0, anon_arr: usize = 0, @@ -15157,30 +15654,40 @@ pub const Unit = struct { } if (!unit.descriptor.only_parse) { - try unit.object_files.ensure_capacity(context.my_allocator, @intCast(unit.descriptor.c_source_files.len)); + var object_files = try UnpinnedArray(linker.Object).initialize_with_capacity(context.my_allocator, @intCast(unit.descriptor.c_source_files.len + 1)); + object_files.append_with_capacity(.{ + .path = unit.descriptor.object_path, + }); + for (unit.descriptor.c_source_files) |c_source_file| { - const c_src = span(c_source_file); - const dot_index = data_structures.last(c_src, '.') orelse unreachable; - const path_without_extension = c_src[0..dot_index]; + const dot_index = data_structures.last_byte(c_source_file, '.') orelse unreachable; + const path_without_extension = c_source_file[0..dot_index]; const basename = std.fs.path.basename(path_without_extension); const o_file = try std.mem.concat(context.allocator, u8, &.{ basename, ".o" }); - const basename_z = try std.mem.joinZ(context.allocator, "/", &.{ - "nat", + const object_path = try std.mem.concat(context.allocator, u8, &.{ + "nat/", o_file, }); - var c_flag = "-c".*; - var o_flag = "-o".*; - var g_flag = "-g".*; - var stack_protector = "-fno-stack-protector".*; - var arguments = [_][*:0]u8{ &c_flag, c_source_file, &o_flag, basename_z, &g_flag, &stack_protector }; + var arguments = [_][]const u8{ "-c", c_source_file, "-o", object_path, "-g", "-fno-stack-protector"}; try compileCSourceFile(context, &arguments, .c); - unit.object_files.append_with_capacity(basename_z); + object_files.append_with_capacity(.{ + .path = object_path, + }); } try unit.analyze(context); try llvm.codegen(unit, context); + + try linker.link(context, .{ + .output_file_path = unit.descriptor.executable_path, + .objects = object_files.slice(), + .libraries = &.{}, + .link_libc = unit.descriptor.link_libc, + .link_libcpp = false, + .extra_arguments = &.{}, + }); } } @@ -15241,14 +15748,16 @@ pub const FixedKeyword = enum { pub const Descriptor = struct { main_package_path: []const u8, executable_path: []const u8, + object_path: []const u8, arch: Arch, os: Os, abi: Abi, only_parse: bool, link_libc: bool, + link_libcpp: bool, is_test: bool, generate_debug_information: bool, - c_source_files: []const [*:0]u8, + c_source_files: []const []const u8, name: []const u8, }; diff --git a/bootstrap/backend/llvm.zig b/bootstrap/backend/llvm.zig index 1380607..f9970a4 100644 --- a/bootstrap/backend/llvm.zig +++ b/bootstrap/backend/llvm.zig @@ -3173,118 +3173,121 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo const object_file_path = slice[0 .. slice.len - 1 :0]; break :blk object_file_path; }; - const destination_file_path = blk: { - const slice = try context.allocator.alloc(u8, file_path.len + 1); // try std.mem.concatWithSentinel(context.allocator, &.{file_path}); - @memcpy(slice[0..file_path.len], file_path); - slice[slice.len - 1] = 0; - const destination_file_path = slice[0..file_path.len :0]; - break :blk destination_file_path; - }; + // const destination_file_path = blk: { + // const slice = try context.allocator.alloc(u8, file_path.len + 1); // try std.mem.concatWithSentinel(context.allocator, &.{file_path}); + // @memcpy(slice[0..file_path.len], file_path); + // slice[slice.len - 1] = 0; + // const destination_file_path = slice[0..file_path.len :0]; + // break :blk destination_file_path; + // }; + // _ = destination_file_path; // autofix const disable_verify = false; const result = llvm.module.addPassesToEmitFile(target_machine, object_file_path.ptr, object_file_path.len, LLVM.CodeGenFileType.object, disable_verify); if (!result) { @panic("can't generate machine code"); } - const format: Format = switch (unit.descriptor.os) { - // .windows => .coff, - .macos => .macho, - .linux => .elf, - // else => unreachable, - }; + // const format: Format = switch (unit.descriptor.os) { + // // .windows => .coff, + // .macos => .macho, + // .linux => .elf, + // // else => unreachable, + // }; - const driver_program = switch (format) { - .coff => "lld-link", - .elf => "ld.lld", - .macho => "ld64.lld", - }; - var arguments = UnpinnedArray([*:0]const u8){}; - try arguments.append(context.my_allocator, driver_program); + // const driver_program = switch (format) { + // .coff => "lld-link", + // .elf => "ld.lld", + // .macho => "ld64.lld", + // }; + // _ = driver_program; // autofix - try arguments.append(context.my_allocator, "--error-limit=0"); - - try arguments.append(context.my_allocator, "-o"); - try arguments.append(context.my_allocator, destination_file_path.ptr); - - try arguments.append(context.my_allocator, object_file_path.ptr); - - try arguments.append_slice(context.my_allocator, unit.object_files.slice()); - // for (unit.object_files.slice()) |object_file| { - // _ = object_file; // autofix + // var arguments = UnpinnedArray([]const u8){}; + // try arguments.append(context.my_allocator, driver_program); + // + // try arguments.append(context.my_allocator, "--error-limit=0"); + // + // try arguments.append(context.my_allocator, "-o"); + // try arguments.append(context.my_allocator, destination_file_path.ptr); + // + // try arguments.append(context.my_allocator, object_file_path.ptr); + // + // try arguments.append_slice(context.my_allocator, unit.object_files.slice()); + // // for (unit.object_files.slice()) |object_file| { + // // _ = object_file; // autofix + // // } + // + // switch (unit.descriptor.os) { + // .macos => { + // try arguments.append(context.my_allocator, "-dynamic"); + // try arguments.append_slice(context.my_allocator, &.{ "-platform_version", "macos", "13.4.1", "13.3" }); + // try arguments.append(context.my_allocator, "-arch"); + // try arguments.append(context.my_allocator, switch (unit.descriptor.arch) { + // .aarch64 => "arm64", + // else => |t| @panic(@tagName(t)), + // }); + // try arguments.append_slice(context.my_allocator, &.{ "-syslibroot", "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk" }); + // try arguments.append_slice(context.my_allocator, &.{ "-e", "_main" }); + // try arguments.append(context.my_allocator, "-lSystem"); + // }, + // .linux => { + // try arguments.append_slice(context.my_allocator, &.{ "--entry", "_start" }); + // try arguments.append(context.my_allocator, "-m"); + // try arguments.append(context.my_allocator, switch (unit.descriptor.arch) { + // .x86_64 => "elf_x86_64", + // else => |t| @panic(@tagName(t)), + // }); + // + // if (unit.descriptor.link_libc) { + // try arguments.append(context.my_allocator, "/usr/lib/crt1.o"); + // try arguments.append(context.my_allocator, "/usr/lib/crti.o"); + // try arguments.append_slice(context.my_allocator, &.{ "-L", "/usr/lib" }); + // try arguments.append_slice(context.my_allocator, &.{ "-dynamic-linker", "/lib64/ld-linux-x86-64.so.2" }); + // try arguments.append(context.my_allocator, "--as-needed"); + // try arguments.append(context.my_allocator, "-lm"); + // try arguments.append(context.my_allocator, "-lpthread"); + // try arguments.append(context.my_allocator, "-lc"); + // try arguments.append(context.my_allocator, "-ldl"); + // try arguments.append(context.my_allocator, "-lrt"); + // try arguments.append(context.my_allocator, "-lutil"); + // try arguments.append(context.my_allocator, "/usr/lib/crtn.o"); + // } + // + // // if (unit.descriptor.link_libc) { + // // try arguments.append_slice(context.allocator, &.{ "-lc" }); + // // } + // }, + // // .windows => {}, + // // else => |t| @panic(@tagName(t)), + // } + // + // var stdout_ptr: [*]const u8 = undefined; + // var stdout_len: usize = 0; + // var stderr_ptr: [*]const u8 = undefined; + // var stderr_len: usize = 0; + // + // const linking_result = switch (format) { + // .elf => bindings.NativityLLDLinkELF(arguments.pointer, arguments.length, &stdout_ptr, &stdout_len, &stderr_ptr, &stderr_len), + // .coff => bindings.NativityLLDLinkCOFF(arguments.pointer, arguments.length, &stdout_ptr, &stdout_len, &stderr_ptr, &stderr_len), + // .macho => bindings.NativityLLDLinkMachO(arguments.pointer, arguments.length, &stdout_ptr, &stdout_len, &stderr_ptr, &stderr_len), + // }; + // + // if (stdout_len > 0) { + // // std.debug.print("{s}\n", .{stdout_ptr[0..stdout_len]}); + // } + // + // if (stderr_len > 0) { + // // std.debug.print("{s}\n", .{stderr_ptr[0..stderr_len]}); + // } + // + // if (!linking_result) { + // try write(.panic, "\n"); + // for (arguments.slice()) |argument| { + // const arg = data_structures.span(argument); + // try write(.panic, arg); + // try write(.panic, " "); + // } + // try write(.panic, "\n"); + // + // @panic(stderr_ptr[0..stderr_len]); // } - - switch (unit.descriptor.os) { - .macos => { - try arguments.append(context.my_allocator, "-dynamic"); - try arguments.append_slice(context.my_allocator, &.{ "-platform_version", "macos", "13.4.1", "13.3" }); - try arguments.append(context.my_allocator, "-arch"); - try arguments.append(context.my_allocator, switch (unit.descriptor.arch) { - .aarch64 => "arm64", - else => |t| @panic(@tagName(t)), - }); - try arguments.append_slice(context.my_allocator, &.{ "-syslibroot", "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk" }); - try arguments.append_slice(context.my_allocator, &.{ "-e", "_main" }); - try arguments.append(context.my_allocator, "-lSystem"); - }, - .linux => { - try arguments.append_slice(context.my_allocator, &.{ "--entry", "_start" }); - try arguments.append(context.my_allocator, "-m"); - try arguments.append(context.my_allocator, switch (unit.descriptor.arch) { - .x86_64 => "elf_x86_64", - else => |t| @panic(@tagName(t)), - }); - - if (unit.descriptor.link_libc) { - try arguments.append(context.my_allocator, "/usr/lib/crt1.o"); - try arguments.append(context.my_allocator, "/usr/lib/crti.o"); - try arguments.append_slice(context.my_allocator, &.{ "-L", "/usr/lib" }); - try arguments.append_slice(context.my_allocator, &.{ "-dynamic-linker", "/lib64/ld-linux-x86-64.so.2" }); - try arguments.append(context.my_allocator, "--as-needed"); - try arguments.append(context.my_allocator, "-lm"); - try arguments.append(context.my_allocator, "-lpthread"); - try arguments.append(context.my_allocator, "-lc"); - try arguments.append(context.my_allocator, "-ldl"); - try arguments.append(context.my_allocator, "-lrt"); - try arguments.append(context.my_allocator, "-lutil"); - try arguments.append(context.my_allocator, "/usr/lib/crtn.o"); - } - - // if (unit.descriptor.link_libc) { - // try arguments.append_slice(context.allocator, &.{ "-lc" }); - // } - }, - // .windows => {}, - // else => |t| @panic(@tagName(t)), - } - - var stdout_ptr: [*]const u8 = undefined; - var stdout_len: usize = 0; - var stderr_ptr: [*]const u8 = undefined; - var stderr_len: usize = 0; - - const linking_result = switch (format) { - .elf => bindings.NativityLLDLinkELF(arguments.pointer, arguments.length, &stdout_ptr, &stdout_len, &stderr_ptr, &stderr_len), - .coff => bindings.NativityLLDLinkCOFF(arguments.pointer, arguments.length, &stdout_ptr, &stdout_len, &stderr_ptr, &stderr_len), - .macho => bindings.NativityLLDLinkMachO(arguments.pointer, arguments.length, &stdout_ptr, &stdout_len, &stderr_ptr, &stderr_len), - }; - - if (stdout_len > 0) { - // std.debug.print("{s}\n", .{stdout_ptr[0..stdout_len]}); - } - - if (stderr_len > 0) { - // std.debug.print("{s}\n", .{stderr_ptr[0..stderr_len]}); - } - - if (!linking_result) { - try write(.panic, "\n"); - for (arguments.slice()) |argument| { - const arg = data_structures.span(argument); - try write(.panic, arg); - try write(.panic, " "); - } - try write(.panic, "\n"); - - @panic(stderr_ptr[0..stderr_len]); - } } diff --git a/bootstrap/backend/llvm_bindings.zig b/bootstrap/backend/llvm_bindings.zig index 17b590a..48b834c 100644 --- a/bootstrap/backend/llvm_bindings.zig +++ b/bootstrap/backend/llvm_bindings.zig @@ -152,7 +152,3 @@ pub extern fn NativityLLVMModuleSetTargetMachineDataLayout(module: *LLVM.Module, pub extern fn NativityLLVMModuleSetTargetTriple(module: *LLVM.Module, target_triple_ptr: [*]const u8, target_triple_len: usize) void; pub extern fn NativityLLVMModuleAddPassesToEmitFile(module: *LLVM.Module, target_machine: *LLVM.Target.Machine, object_file_path_ptr: [*]const u8, object_file_path_len: usize, codegen_file_type: LLVM.CodeGenFileType, disable_verify: bool) bool; pub extern fn NativityLLVMTypeAssertEqual(a: *LLVM.Type, b: *LLVM.Type) void; -pub extern fn NativityLLDLinkELF(argument_ptr: [*]const [*:0]const u8, argument_count: usize, stdout_ptr: *[*]const u8, stdout_len: *usize, stderr_ptr: *[*]const u8, stderr_len: *usize) bool; -pub extern fn NativityLLDLinkCOFF(argument_ptr: [*]const [*:0]const u8, argument_count: usize, stdout_ptr: *[*]const u8, stdout_len: *usize, stderr_ptr: *[*]const u8, stderr_len: *usize) bool; -pub extern fn NativityLLDLinkMachO(argument_ptr: [*]const [*:0]const u8, argument_count: usize, stdout_ptr: *[*]const u8, stdout_len: *usize, stderr_ptr: *[*]const u8, stderr_len: *usize) bool; -pub extern fn NativityLLDLinkWasm(argument_ptr: [*]const [*:0]const u8, argument_count: usize, stdout_ptr: *[*]const u8, stdout_len: *usize, stderr_ptr: *[*]const u8, stderr_len: *usize) bool; diff --git a/bootstrap/library.zig b/bootstrap/library.zig index 613ad9f..2341fc3 100644 --- a/bootstrap/library.zig +++ b/bootstrap/library.zig @@ -724,7 +724,53 @@ pub fn span(ptr: [*:0]const u8) [:0]const u8 { return ptr[0..len :0]; } -pub fn last(bytes: []const u8, byte: u8) ?usize { +pub fn starts_with_slice(bytes: []const u8, slice: []const u8) bool { + if (slice.len <= bytes.len) { + if (byte_equal(bytes[0..slice.len], slice)) { + return true; + } + } + + return false; +} + +pub fn ends_with_slice(bytes: []const u8, slice: []const u8) bool { + if (slice.len <= bytes.len) { + if (byte_equal(bytes[bytes.len - slice.len..], slice)) { + return true; + } + } + + return false; +} + +pub fn first_byte(bytes: []const u8, byte: u8) ?usize { + for (bytes, 0..) |b, i| { + if (b == byte) { + return i; + } + } + + return null; +} + +pub fn first_slice(bytes: []const u8, slice: []const u8) ?usize { + if (slice.len <= bytes.len) { + const top = bytes.len - slice.len; + var i: usize = 0; + + while (i < top) : (i += 1) { + const chunk = bytes[i..][0..slice.len]; + if (byte_equal(chunk, slice)) { + return i; + } + } + } + + return null; +} + +pub fn last_byte(bytes: []const u8, byte: u8) ?usize { var i = bytes.len; while (i > 0) { i -= 1; diff --git a/bootstrap/linker/linker.zig b/bootstrap/linker/linker.zig new file mode 100644 index 0000000..fd78ed1 --- /dev/null +++ b/bootstrap/linker/linker.zig @@ -0,0 +1,30 @@ +const Compilation = @import("../Compilation.zig"); +const Context = Compilation.Context; +const lld = @import("lld.zig"); +pub const Options = struct{ + backend: Backend = .lld, + output_file_path: []const u8, + objects: []const Object, + libraries: []const Library, + extra_arguments: []const []const u8, + link_libc: bool, + link_libcpp: bool, +}; + +const Backend = enum{ + lld, +}; + +pub const Object = struct{ + path: []const u8, +}; + +pub const Library = struct{ + path: []const u8, +}; + +pub fn link(context: *const Context, options: Options) !void { + switch (options.backend) { + .lld => try lld.link(context, options), + } +} diff --git a/bootstrap/linker/lld.zig b/bootstrap/linker/lld.zig new file mode 100644 index 0000000..188b476 --- /dev/null +++ b/bootstrap/linker/lld.zig @@ -0,0 +1,126 @@ +const std = @import("std"); +const assert = std.debug.assert; +const linker = @import("linker.zig"); + +const library = @import("../library.zig"); +const UnpinnedArray = library.UnpinnedArray; + +const Compilation = @import("../Compilation.zig"); +const write = Compilation.write; + +pub fn link(context: *const Compilation.Context, options: linker.Options) !void { + assert(options.backend == .lld); + var argv = UnpinnedArray([]const u8){}; + const driver_program = switch (@import("builtin").os.tag) { + .windows => "lld-link", + .linux => "ld.lld", + .macos => "ld64.lld", + else => @compileError("OS not supported"), + }; + try argv.append(context.my_allocator, driver_program); + try argv.append(context.my_allocator, "--error-limit=0"); + + // const output_path = out_path orelse "a.out"; + try argv.append(context.my_allocator, "-o"); + const is_dylib = library.ends_with_slice(options.output_file_path, ".dylib"); + try argv.append(context.my_allocator, options.output_file_path); + if (library.byte_equal(options.output_file_path, "lib/LLVMHello.dylib")) { + assert(is_dylib); + } + + try argv.append_slice(context.my_allocator, options.extra_arguments); + + for (options.objects) |object| { + try argv.append(context.my_allocator, object.path); + } + + switch (@import("builtin").os.tag) { + .macos => { + try argv.append(context.my_allocator, "-dynamic"); + try argv.append_slice(context.my_allocator, &.{ "-platform_version", "macos", "13.4.1", "13.3" }); + try argv.append(context.my_allocator, "-arch"); + try argv.append(context.my_allocator, switch (@import("builtin").cpu.arch) { + .aarch64 => "arm64", + else => |t| @panic(@tagName(t)), + }); + try argv.append_slice(context.my_allocator, &.{ "-syslibroot", "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk" }); + + if (!library.ends_with_slice(options.output_file_path, ".dylib")) { + try argv.append_slice(context.my_allocator, &.{ "-e", "_main" }); + } + + try argv.append(context.my_allocator, "-lSystem"); + + if (options.link_libcpp) { + try argv.append(context.my_allocator, "-L/Library/Developer/CommandLineTools/SDKs/MacOSX13.3.sdk/usr/lib"); + try argv.append(context.my_allocator, "-lc++"); + } + }, + .linux => { + if (options.link_libcpp) { + assert(options.link_libc); + try argv.append(context.my_allocator, "/usr/lib/libstdc++.so" ); + } + + if (options.link_libc) { + try argv.append(context.my_allocator, "/usr/lib/crt1.o"); + try argv.append(context.my_allocator, "/usr/lib/crti.o"); + try argv.append_slice(context.my_allocator, &.{ "-L", "/usr/lib" }); + try argv.append_slice(context.my_allocator, &.{ "-dynamic-linker", "/lib64/ld-linux-x86-64.so.2" }); + try argv.append(context.my_allocator, "--as-needed"); + try argv.append(context.my_allocator, "-lm"); + try argv.append(context.my_allocator, "-lpthread"); + try argv.append(context.my_allocator, "-lc"); + try argv.append(context.my_allocator, "-ldl"); + try argv.append(context.my_allocator, "-lrt"); + try argv.append(context.my_allocator, "-lutil"); + try argv.append(context.my_allocator, "/usr/lib/crtn.o"); + } + }, + else => @compileError("OS not supported"), + } + + + for (options.libraries) |lib| { + try argv.append(context.my_allocator, try std.mem.concat(context.allocator, u8, &.{"-l", lib.path})); + } + + const argv_zero_terminated = try Compilation.argsCopyZ(context.allocator, argv.slice()); + + var stdout_ptr: [*]const u8 = undefined; + var stdout_len: usize = 0; + var stderr_ptr: [*]const u8 = undefined; + var stderr_len: usize = 0; + const result = switch (@import("builtin").os.tag) { + .linux => NativityLLDLinkELF (argv_zero_terminated.ptr, argv_zero_terminated.len, &stdout_ptr, &stdout_len, &stderr_ptr, &stderr_len), + .macos => NativityLLDLinkMachO (argv_zero_terminated.ptr, argv_zero_terminated.len, &stdout_ptr, &stdout_len, &stderr_ptr, &stderr_len), + .windows => NativityLLDLinkCOFF(argv_zero_terminated.ptr, argv_zero_terminated.len, &stdout_ptr, &stdout_len, &stderr_ptr, &stderr_len), + else => @compileError("OS not supported"), + }; + + if (!result) { + const stdout = stdout_ptr[0..stdout_len]; + const stderr = stderr_ptr[0..stderr_len]; + for (argv.slice()) |arg| { + try write(.panic, arg); + try write(.panic, " "); + } + try write(.panic, "\n"); + if (stdout.len > 0) { + try write(.panic, stdout); + try write(.panic, "\n"); + } + + if (stderr.len > 0) { + try write(.panic, stderr); + try write(.panic, "\n"); + } + + @panic("Linking with LLD failed"); + } +} + +extern fn NativityLLDLinkELF(argument_ptr: [*:null]?[*:0]u8, argument_count: usize, stdout_ptr: *[*]const u8, stdout_len: *usize, stderr_ptr: *[*]const u8, stderr_len: *usize) bool; +extern fn NativityLLDLinkCOFF(argument_ptr: [*:null]?[*:0]u8, argument_count: usize, stdout_ptr: *[*]const u8, stdout_len: *usize, stderr_ptr: *[*]const u8, stderr_len: *usize) bool; +extern fn NativityLLDLinkMachO(argument_ptr: [*:null]?[*:0]u8, argument_count: usize, stdout_ptr: *[*]const u8, stdout_len: *usize, stderr_ptr: *[*]const u8, stderr_len: *usize) bool; +extern fn NativityLLDLinkWasm(argument_ptr: [*:null]?[*:0]u8, argument_count: usize, stdout_ptr: *[*]const u8, stdout_len: *usize, stderr_ptr: *[*]const u8, stderr_len: *usize) bool; diff --git a/bootstrap/main.zig b/bootstrap/main.zig index 394fc29..9a7e189 100644 --- a/bootstrap/main.zig +++ b/bootstrap/main.zig @@ -1,5 +1,6 @@ const std = @import("std"); const Allocator = std.mem.Allocator; +const assert = std.debug.assert; const Compilation = @import("Compilation.zig"); pub const panic = Compilation.panic; @@ -21,35 +22,53 @@ fn todo() noreturn { } var my_allocator = PageAllocator{}; -pub export fn main(c_argc: c_int, c_argv: [*][*:0]c_char, c_envp: [*:null]?[*:0]c_char) callconv(.C) c_int { - _ = c_envp; // autofix - const argument_count: usize = @intCast(c_argc); - const argument_values: [*][*:0]u8 = @ptrCast(c_argv); - const arguments = argument_values[0..argument_count]; - if (entry_point(arguments)) |_| { - return 0; - } else |err| { - const print_stack_trace = @import("configuration").print_stack_trace; - switch (print_stack_trace) { - true => if (@errorReturnTrace()) |trace| { - std.debug.dumpStackTrace(trace.*); - }, - false => { - const error_name: []const u8 = @errorName(err); - Compilation.write(.panic, "Error: ") catch {}; - Compilation.write(.panic, error_name) catch {}; - Compilation.write(.panic, "\n") catch {}; - }, - } +// pub export fn main(c_argc: c_int, c_argv: [*][*:0]c_char, c_envp: [*:null]?[*:0]c_char) callconv(.C) c_int { +// _ = c_envp; // autofix +// // const argument_count: usize = @intCast(c_argc); +// // const argument_values: [*][*:0]u8 = @ptrCast(c_argv); +// if (entry_point(arguments)) |_| { +// return 0; +// } else |err| { +// const print_stack_trace = @import("configuration").print_stack_trace; +// switch (print_stack_trace) { +// true => if (@errorReturnTrace()) |trace| { +// std.debug.dumpStackTrace(trace.*); +// }, +// false => { +// const error_name: []const u8 = @errorName(err); +// Compilation.write(.panic, "Error: ") catch {}; +// Compilation.write(.panic, error_name) catch {}; +// Compilation.write(.panic, "\n") catch {}; +// }, +// } +// +// return 1; +// } +// } - return 1; - } -} - -pub fn entry_point(arguments: [][*:0]u8) !void { +pub fn main() !void { var arena_allocator = std.heap.ArenaAllocator.init(std.heap.page_allocator); const allocator = arena_allocator.allocator(); - // const arguments = try std.process.argsAlloc(allocator); + var arg_it = std.process.ArgIterator.init(); + var args = library.UnpinnedArray([]const u8){}; + const context = try Compilation.createContext(allocator, &my_allocator.allocator); + while (arg_it.next()) |arg| { + try args.append(context.my_allocator, arg); + } + const arguments = args.slice(); + const debug_args = true; + if (debug_args) { + assert(arguments.len > 0); + const home_dir = std.posix.getenv("HOME") orelse unreachable; + const timestamp = std.time.milliTimestamp(); + var argument_list = std.ArrayList(u8).init(std.heap.page_allocator); + for (arguments) |arg| { + argument_list.appendSlice(arg) catch {}; + argument_list.append(' ') catch {}; + } + argument_list.append('\n') catch {}; + std.fs.cwd().writeFile(std.fmt.allocPrint(std.heap.page_allocator, "{s}/dev/nativity/nat/invocation_log_{}", .{home_dir, timestamp}) catch unreachable, argument_list.items) catch {}; + } if (arguments.len <= 1) { return error.InvalidInput; @@ -59,15 +78,14 @@ pub fn entry_point(arguments: [][*:0]u8) !void { todo(); } - const command = library.span(arguments[1]); + const command = arguments[1]; const command_arguments = arguments[2..]; - const context = try Compilation.createContext(allocator, &my_allocator.allocator); if (byte_equal(command, "build")) { try Compilation.compileBuildExecutable(context, command_arguments); } else if (byte_equal(command, "clang") or byte_equal(command, "-cc1") or byte_equal(command, "-cc1as")) { - // const exit_code = try clangMain(allocator, arguments); - // std.process.exit(exit_code); + const exit_code = try Compilation.clangMain(allocator, arguments); + std.process.exit(exit_code); } else if (byte_equal(command, "cc")) { try Compilation.compileCSourceFile(context, command_arguments, .c); } else if (byte_equal(command, "c++")) { @@ -88,3 +106,7 @@ pub fn entry_point(arguments: [][*:0]u8) !void { todo(); } } + +pub const std_options = std.Options{ + .enable_segfault_handler = false, +}; diff --git a/build/test_runner.zig b/build/test_runner.zig index 04875db..ad27617 100644 --- a/build/test_runner.zig +++ b/build/test_runner.zig @@ -353,10 +353,10 @@ fn runStdTests(allocator: Allocator) !void { if (errors) return error.fail; } -fn runCmakeTests(allocator: Allocator) !void { +fn runCmakeTests(allocator: Allocator, dir_path: []const u8) !void { var errors = false; const original_dir = try std.fs.cwd().realpathAlloc(allocator, "."); - const cc_dir = try std.fs.cwd().openDir("test/cc", .{ + const cc_dir = try std.fs.cwd().openDir(dir_path, .{ .iterate = true, }); @@ -379,8 +379,10 @@ fn runCmakeTests(allocator: Allocator) !void { .argv = &.{ "cmake", "..", - "-G", "Unix Makefiles", - "-DCMAKE_VERBOSE_MAKEFILE=On", + // "--debug-trycompile", + // "--debug-output", + // "-G", "Unix Makefiles", + // "-DCMAKE_VERBOSE_MAKEFILE=On", try std.mem.concat(allocator, u8, &.{ "-DCMAKE_C_COMPILER=", "nat;cc" }), try std.mem.concat(allocator, u8, &.{ "-DCMAKE_CXX_COMPILER=", "nat;c++" }), try std.mem.concat(allocator, u8, &.{ "-DCMAKE_ASM_COMPILER=", "nat;cc" }), @@ -399,13 +401,11 @@ fn runCmakeTests(allocator: Allocator) !void { break :b false; }; - if (!cmake_success) { - if (cmake.stdout.len > 0) { - std.debug.print("STDOUT:\n\n{s}\n\n", .{cmake.stdout}); - } - if (cmake.stderr.len > 0) { - std.debug.print("STDERR:\n\n{s}\n\n", .{cmake.stderr}); - } + if (cmake.stdout.len > 0) { + std.debug.print("STDOUT:\n\n{s}\n\n", .{cmake.stdout}); + } + if (cmake.stderr.len > 0) { + std.debug.print("STDERR:\n\n{s}\n\n", .{cmake.stderr}); } var success = cmake_success; @@ -414,10 +414,10 @@ fn runCmakeTests(allocator: Allocator) !void { .allocator = allocator, // TODO: delete -main_source_file? .argv = &.{ - "make" + "ninja" }, .max_output_bytes = std.math.maxInt(u64), - }); + }); const ninja_result: TestError!bool = switch (ninja.term) { .Exited => |exit_code| if (exit_code == 0) true else error.abnormal_exit_code, .Signal => error.signaled, @@ -429,6 +429,7 @@ fn runCmakeTests(allocator: Allocator) !void { errors = true; break :b false; }; + if (!ninja_success) { if (ninja.stdout.len > 0) { std.debug.print("STDOUT:\n\n{s}\n\n", .{ninja.stdout}); @@ -508,15 +509,25 @@ pub fn main() !void { }) catch { errors = true; }; - + // runStdTests(allocator) catch { errors = true; }; + runCmakeTests(allocator, "test/cc") catch { + errors = true; + }; + runCmakeTests(allocator, "test/c++") catch { + errors = true; + }; + switch (@import("builtin").os.tag) { .macos => {}, + // .macos => {}, .linux => switch (@import("builtin").abi) { - .gnu => try runCmakeTests(allocator), + .gnu => runCmakeTests(allocator, "test/cc_linux") catch { + errors = true; + }, .musl => {}, else => @compileError("ABI not supported"), }, diff --git a/test/cc/cpp_first/.gitignore b/test/c++/cpp_first/.gitignore similarity index 100% rename from test/cc/cpp_first/.gitignore rename to test/c++/cpp_first/.gitignore diff --git a/test/c++/cpp_first/CMakeLists.txt b/test/c++/cpp_first/CMakeLists.txt new file mode 100644 index 0000000..e764a54 --- /dev/null +++ b/test/c++/cpp_first/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 3.15) +project(cpp_first CXX) +include(CMakePrintHelpers) +cmake_print_variables(CMAKE_CXX_IMPLICIT_LINK_LIBRARIES) +cmake_print_variables(CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES) +add_executable(cpp_first main.cpp) diff --git a/test/cc/cpp_first/main.cpp b/test/c++/cpp_first/main.cpp similarity index 100% rename from test/cc/cpp_first/main.cpp rename to test/c++/cpp_first/main.cpp diff --git a/test/cc/c_first/CMakeLists.txt b/test/cc/c_first/CMakeLists.txt index 160c094..67c447b 100644 --- a/test/cc/c_first/CMakeLists.txt +++ b/test/cc/c_first/CMakeLists.txt @@ -1,3 +1,6 @@ cmake_minimum_required(VERSION 3.15) project(c_first C) +include(CMakePrintHelpers) +cmake_print_variables(CMAKE_C_IMPLICIT_LINK_LIBRARIES) +cmake_print_variables(CMAKE_C_IMPLICIT_LINK_DIRECTORIES) add_executable(c_first main.c) diff --git a/test/cc/cpp_first/CMakeLists.txt b/test/cc/cpp_first/CMakeLists.txt deleted file mode 100644 index 8e749af..0000000 --- a/test/cc/cpp_first/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -cmake_minimum_required(VERSION 3.15) -project(cpp_first CXX) -add_executable(cpp_first main.cpp) diff --git a/test/cc_linux/c_asm/.gitignore b/test/cc_linux/c_asm/.gitignore new file mode 100644 index 0000000..8326e08 --- /dev/null +++ b/test/cc_linux/c_asm/.gitignore @@ -0,0 +1,2 @@ +*.o +build/ diff --git a/test/cc_linux/c_asm/CMakeLists.txt b/test/cc_linux/c_asm/CMakeLists.txt new file mode 100644 index 0000000..5294827 --- /dev/null +++ b/test/cc_linux/c_asm/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.15) +project(c_asm C ASM) +add_executable(c_asm main.c assembly.S) diff --git a/test/cc_linux/c_asm/assembly.S b/test/cc_linux/c_asm/assembly.S new file mode 100644 index 0000000..c63b973 --- /dev/null +++ b/test/cc_linux/c_asm/assembly.S @@ -0,0 +1,4 @@ +.global foo +foo: + mov $42, %eax + ret diff --git a/test/cc_linux/c_asm/main.c b/test/cc_linux/c_asm/main.c new file mode 100644 index 0000000..cfa19e8 --- /dev/null +++ b/test/cc_linux/c_asm/main.c @@ -0,0 +1,7 @@ +extern int foo(); +#include +int main() +{ + assert(foo() == 42); + return 0; +}