Improve clang integration

LLVM can now be compiled with nat
This commit is contained in:
David Gonzalez Martin 2024-03-25 14:22:55 -06:00
parent f245b28ed8
commit dfcda18a70
18 changed files with 1044 additions and 276 deletions

3
.gitignore vendored
View File

@ -1,3 +1,6 @@
zig-cache/
zig-out/
nat/
*.o
*.out
*.obj

View File

@ -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",
});
},
.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"),
}
}
while (argument_index < arguments.len) {
const argument = arguments[argument_index];
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",
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/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"),
.object, .static_library, .shared_library => {
try link_objects.append(context.my_allocator, .{
.path = argument,
});
},
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"),
} else {
try write(.panic, argument);
try write(.panic, "\n");
@panic("Positional argument without extension");
}
}
if (kind == .cpp) {
switch (@import("builtin").os.tag) {
.linux => {
switch (@import("builtin").abi) {
.gnu => {
try clang_args.append(context.my_allocator, "-lstdc++");
},
.musl => {
} 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 => @compileError("Abi not supported"),
}
} 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;
}
const link_libcpp = true;
const mode = out_mode orelse .link;
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-");
},
.aarch64 => {
try target_triple_buffer.append_slice(context.my_allocator, "aarch64-");
},
else => @compileError("Architecture not supported"),
}
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-");
},
.macos => unreachable,
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"),
}
for (arguments) |arg| {
try clang_args.append(context.my_allocator, span(arg));
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 result = try clangMain(context.allocator, clang_args.slice());
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;
}
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;
}
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,
});
}
// 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,
};

View File

@ -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
// }
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 => {},
// 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)),
}
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]);
}
// });
// 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]);
// }
}

View File

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

View File

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

View File

@ -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),
}
}

126
bootstrap/linker/lld.zig Normal file
View File

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

View File

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

View File

@ -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,14 +401,12 @@ 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});
}
}
var success = cmake_success;
if (success) {
@ -414,7 +414,7 @@ fn runCmakeTests(allocator: Allocator) !void {
.allocator = allocator,
// TODO: delete -main_source_file?
.argv = &.{
"make"
"ninja"
},
.max_output_bytes = std.math.maxInt(u64),
});
@ -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"),
},

View File

@ -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)

View File

@ -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)

View File

@ -1,3 +0,0 @@
cmake_minimum_required(VERSION 3.15)
project(cpp_first CXX)
add_executable(cpp_first main.cpp)

2
test/cc_linux/c_asm/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*.o
build/

View File

@ -0,0 +1,3 @@
cmake_minimum_required(VERSION 3.15)
project(c_asm C ASM)
add_executable(c_asm main.c assembly.S)

View File

@ -0,0 +1,4 @@
.global foo
foo:
mov $42, %eax
ret

View File

@ -0,0 +1,7 @@
extern int foo();
#include <assert.h>
int main()
{
assert(foo() == 42);
return 0;
}