From 071417161d5d7ab91c32073693590ef161017dbe Mon Sep 17 00:00:00 2001 From: Timon Kruiper Date: Sat, 19 Dec 2020 11:22:49 +0100 Subject: stage2: add initial impl of LLVM backend in self-hosted compiler --- src/link/Elf.zig | 45 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) (limited to 'src/link/Elf.zig') diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 6232a64e4a..479dc415e5 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -24,6 +24,7 @@ const build_options = @import("build_options"); const target_util = @import("../target.zig"); const glibc = @import("../glibc.zig"); const Cache = @import("../Cache.zig"); +const llvm_backend = @import("../llvm_backend.zig"); const default_entry_addr = 0x8000000; @@ -33,6 +34,9 @@ base: File, ptr_width: PtrWidth, +/// If this is not null, an object file is created by LLVM and linked with LLD afterwards. +llvm_ir_module: ?*llvm_backend.LLVMIRModule = null, + /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write. /// Same order as in the file. sections: std.ArrayListUnmanaged(elf.Elf64_Shdr) = std.ArrayListUnmanaged(elf.Elf64_Shdr){}, @@ -224,7 +228,13 @@ pub const SrcFn = struct { pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Elf { assert(options.object_format == .elf); - if (options.use_llvm) return error.LLVMBackendUnimplementedForELF; // TODO + if (options.use_llvm) { + const self = try createEmpty(allocator, options); + errdefer self.base.destroy(); + + self.llvm_ir_module = try llvm_backend.LLVMIRModule.create(allocator, sub_path, options); + return self; + } const file = try options.emit.?.directory.handle.createFile(sub_path, .{ .truncate = false, @@ -288,6 +298,7 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Elf { } pub fn deinit(self: *Elf) void { + if (self.llvm_ir_module) |ir_module| ir_module.deinit(self.base.allocator); self.sections.deinit(self.base.allocator); self.program_headers.deinit(self.base.allocator); self.shstrtab.deinit(self.base.allocator); @@ -423,6 +434,8 @@ fn updateString(self: *Elf, old_str_off: u32, new_name: []const u8) !u32 { } pub fn populateMissingMetadata(self: *Elf) !void { + if (self.llvm_ir_module) |_| return; + const small_ptr = switch (self.ptr_width) { .p32 => true, .p64 => false, @@ -727,6 +740,11 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void { const tracy = trace(@src()); defer tracy.end(); + if (self.llvm_ir_module) |llvm_ir_module| { + try llvm_ir_module.flushModule(comp); + return; + } + // TODO This linker code currently assumes there is only 1 compilation unit and it corresponds to the // Zig source code. const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented; @@ -1261,6 +1279,9 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { const stack_size = self.base.options.stack_size_override orelse 16777216; const allow_shlib_undefined = self.base.options.allow_shlib_undefined orelse !self.base.options.is_native_os; const compiler_rt_path: ?[]const u8 = if (self.base.options.include_compiler_rt) blk: { + // TODO: remove when stage2 can build compiler_rt.zig + if (!build_options.is_stage1) break :blk null; + if (is_exe_or_dyn_lib) { break :blk comp.compiler_rt_static_lib.?.full_object_path; } else { @@ -1552,7 +1573,12 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { } // libc - if (is_exe_or_dyn_lib and !self.base.options.skip_linker_dependencies and !self.base.options.link_libc) { + // TODO: enable when stage2 can build c.zig + if (is_exe_or_dyn_lib and + !self.base.options.skip_linker_dependencies and + !self.base.options.link_libc and + build_options.is_stage1) + { try argv.append(comp.libc_static_lib.?.full_object_path); } @@ -2046,6 +2072,8 @@ fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, al } pub fn allocateDeclIndexes(self: *Elf, decl: *Module.Decl) !void { + if (self.llvm_ir_module) |_| return; + if (decl.link.elf.local_sym_index != 0) return; try self.local_symbols.ensureCapacity(self.base.allocator, self.local_symbols.items.len + 1); @@ -2082,6 +2110,8 @@ pub fn allocateDeclIndexes(self: *Elf, decl: *Module.Decl) !void { } pub fn freeDecl(self: *Elf, decl: *Module.Decl) void { + if (self.llvm_ir_module) |_| return; + // Appending to free lists is allowed to fail because the free lists are heuristics based anyway. self.freeTextBlock(&decl.link.elf); if (decl.link.elf.local_sym_index != 0) { @@ -2119,6 +2149,11 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void { const tracy = trace(@src()); defer tracy.end(); + if (self.llvm_ir_module) |llvm_ir_module| { + try llvm_ir_module.updateDecl(module, decl); + return; + } + var code_buffer = std.ArrayList(u8).init(self.base.allocator); defer code_buffer.deinit(); @@ -2594,6 +2629,8 @@ pub fn updateDeclExports( decl: *const Module.Decl, exports: []const *Module.Export, ) !void { + if (self.llvm_ir_module) |_| return; + const tracy = trace(@src()); defer tracy.end(); @@ -2667,6 +2704,8 @@ pub fn updateDeclLineNumber(self: *Elf, module: *Module, decl: *const Module.Dec const tracy = trace(@src()); defer tracy.end(); + if (self.llvm_ir_module) |_| return; + const container_scope = decl.scope.cast(Module.Scope.Container).?; const tree = container_scope.file_scope.contents.tree; const file_ast_decls = tree.root_node.decls(); @@ -2685,6 +2724,8 @@ pub fn updateDeclLineNumber(self: *Elf, module: *Module, decl: *const Module.Dec } pub fn deleteExport(self: *Elf, exp: Export) void { + if (self.llvm_ir_module) |_| return; + const sym_index = exp.sym_index orelse return; self.global_symbol_free_list.append(self.base.allocator, sym_index) catch {}; self.global_symbols.items[sym_index].st_info = 0; -- cgit v1.2.3 From 6b8d28312ccca56d652af32bad360e9a5394fb8e Mon Sep 17 00:00:00 2001 From: Timon Kruiper Date: Mon, 21 Dec 2020 10:21:30 +0100 Subject: stage2: fix building self-hosted without llvm-backend enabled. --- src/link/Coff.zig | 18 ++++++++---------- src/link/Elf.zig | 19 +++++++++---------- 2 files changed, 17 insertions(+), 20 deletions(-) (limited to 'src/link/Elf.zig') diff --git a/src/link/Coff.zig b/src/link/Coff.zig index 161a4092f4..8c60086010 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -125,7 +125,7 @@ pub const SrcFn = void; pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Coff { assert(options.object_format == .coff); - if (options.use_llvm) { + if (build_options.have_llvm and options.use_llvm) { const self = try createEmpty(allocator, options); errdefer self.base.destroy(); @@ -657,10 +657,8 @@ pub fn updateDecl(self: *Coff, module: *Module, decl: *Module.Decl) !void { const tracy = trace(@src()); defer tracy.end(); - if (self.llvm_ir_module) |llvm_ir_module| { - try llvm_ir_module.updateDecl(module, decl); - return; - } + if (build_options.have_llvm) + if (self.llvm_ir_module) |llvm_ir_module| return try llvm_ir_module.updateDecl(module, decl); var code_buffer = std.ArrayList(u8).init(self.base.allocator); defer code_buffer.deinit(); @@ -760,10 +758,8 @@ pub fn flushModule(self: *Coff, comp: *Compilation) !void { const tracy = trace(@src()); defer tracy.end(); - if (self.llvm_ir_module) |llvm_ir_module| { - try llvm_ir_module.flushModule(comp); - return; - } + if (build_options.have_llvm) + if (self.llvm_ir_module) |llvm_ir_module| return try llvm_ir_module.flushModule(comp); if (self.text_section_size_dirty) { // Write the new raw size in the .text header @@ -1257,7 +1253,9 @@ pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl: *Module.Decl) !v } pub fn deinit(self: *Coff) void { - if (self.llvm_ir_module) |ir_module| ir_module.deinit(self.base.allocator); + if (build_options.have_llvm) + if (self.llvm_ir_module) |ir_module| ir_module.deinit(self.base.allocator); + self.text_block_free_list.deinit(self.base.allocator); self.offset_table.deinit(self.base.allocator); self.offset_table_free_list.deinit(self.base.allocator); diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 479dc415e5..9472f8fca7 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -228,7 +228,7 @@ pub const SrcFn = struct { pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Elf { assert(options.object_format == .elf); - if (options.use_llvm) { + if (build_options.have_llvm and options.use_llvm) { const self = try createEmpty(allocator, options); errdefer self.base.destroy(); @@ -298,7 +298,10 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Elf { } pub fn deinit(self: *Elf) void { - if (self.llvm_ir_module) |ir_module| ir_module.deinit(self.base.allocator); + if (build_options.have_llvm) + if (self.llvm_ir_module) |ir_module| + ir_module.deinit(self.base.allocator); + self.sections.deinit(self.base.allocator); self.program_headers.deinit(self.base.allocator); self.shstrtab.deinit(self.base.allocator); @@ -740,10 +743,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void { const tracy = trace(@src()); defer tracy.end(); - if (self.llvm_ir_module) |llvm_ir_module| { - try llvm_ir_module.flushModule(comp); - return; - } + if (build_options.have_llvm) + if (self.llvm_ir_module) |llvm_ir_module| return try llvm_ir_module.flushModule(comp); // TODO This linker code currently assumes there is only 1 compilation unit and it corresponds to the // Zig source code. @@ -2149,10 +2150,8 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void { const tracy = trace(@src()); defer tracy.end(); - if (self.llvm_ir_module) |llvm_ir_module| { - try llvm_ir_module.updateDecl(module, decl); - return; - } + if (build_options.have_llvm) + if (self.llvm_ir_module) |llvm_ir_module| return try llvm_ir_module.updateDecl(module, decl); var code_buffer = std.ArrayList(u8).init(self.base.allocator); defer code_buffer.deinit(); -- cgit v1.2.3 From 09cf043efd5966e328e4454bc30bc756f439938b Mon Sep 17 00:00:00 2001 From: Timon Kruiper Date: Thu, 24 Dec 2020 11:40:54 +0100 Subject: stage2: add missing asserts and early returns for LLVM backend --- src/link/Coff.zig | 5 +++++ src/link/Elf.zig | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'src/link/Elf.zig') diff --git a/src/link/Coff.zig b/src/link/Coff.zig index 8c60086010..096fa2cd0b 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -413,6 +413,8 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Coff { } pub fn allocateDeclIndexes(self: *Coff, decl: *Module.Decl) !void { + if (self.llvm_ir_module) |_| return; + try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1); if (self.offset_table_free_list.popOrNull()) |i| { @@ -710,6 +712,8 @@ pub fn updateDecl(self: *Coff, module: *Module, decl: *Module.Decl) !void { } pub fn freeDecl(self: *Coff, decl: *Module.Decl) void { + if (self.llvm_ir_module) |_| return; + // Appending to free lists is allowed to fail because the free lists are heuristics based anyway. self.freeTextBlock(&decl.link.coff); self.offset_table_free_list.append(self.base.allocator, decl.link.coff.offset_table_index) catch {}; @@ -1245,6 +1249,7 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void { } pub fn getDeclVAddr(self: *Coff, decl: *const Module.Decl) u64 { + assert(self.llvm_ir_module == null); return self.text_section_virtual_address + decl.link.coff.text_offset; } diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 9472f8fca7..25b883f8c6 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -318,6 +318,7 @@ pub fn deinit(self: *Elf) void { } pub fn getDeclVAddr(self: *Elf, decl: *const Module.Decl) u64 { + assert(self.llvm_ir_module == null); assert(decl.link.elf.local_sym_index != 0); return self.local_symbols.items[decl.link.elf.local_sym_index].st_value; } @@ -437,7 +438,7 @@ fn updateString(self: *Elf, old_str_off: u32, new_name: []const u8) !u32 { } pub fn populateMissingMetadata(self: *Elf) !void { - if (self.llvm_ir_module) |_| return; + assert(self.llvm_ir_module == null); const small_ptr = switch (self.ptr_width) { .p32 => true, -- cgit v1.2.3