diff options
Diffstat (limited to 'src-self-hosted')
| -rw-r--r-- | src-self-hosted/codegen.zig | 46 | ||||
| -rw-r--r-- | src-self-hosted/ir.zig | 59 | ||||
| -rw-r--r-- | src-self-hosted/link.zig | 56 |
3 files changed, 109 insertions, 52 deletions
diff --git a/src-self-hosted/codegen.zig b/src-self-hosted/codegen.zig index 8e6c19e869..2e8bcd9624 100644 --- a/src-self-hosted/codegen.zig +++ b/src-self-hosted/codegen.zig @@ -39,7 +39,7 @@ pub fn generateSymbol(typed_value: ir.TypedValue, module: ir.Module, code: *std. defer function.inst_table.deinit(); defer function.errors.deinit(); - for (module_fn.body) |inst| { + for (module_fn.body.instructions) |inst| { const new_inst = function.genFuncInst(inst) catch |err| switch (err) { error.CodegenFail => { assert(function.errors.items.len != 0); @@ -77,32 +77,62 @@ const Function = struct { fn genFuncInst(self: *Function, inst: *ir.Inst) !MCValue { switch (inst.tag) { - .unreach => return self.genPanic(inst.src), + .unreach => return MCValue{ .unreach = {} }, .constant => unreachable, // excluded from function bodies .assembly => return self.genAsm(inst.cast(ir.Inst.Assembly).?), .ptrtoint => return self.genPtrToInt(inst.cast(ir.Inst.PtrToInt).?), .bitcast => return self.genBitCast(inst.cast(ir.Inst.BitCast).?), + .ret => return self.genRet(inst.cast(ir.Inst.Ret).?), + .cmp => return self.genCmp(inst.cast(ir.Inst.Cmp).?), + .condbr => return self.genCondBr(inst.cast(ir.Inst.CondBr).?), + .isnull => return self.genIsNull(inst.cast(ir.Inst.IsNull).?), + .isnonnull => return self.genIsNonNull(inst.cast(ir.Inst.IsNonNull).?), } } - fn genPanic(self: *Function, src: usize) !MCValue { - // TODO change this to call the panic function + fn genBreakpoint(self: *Function, src: usize) !MCValue { switch (self.module.target.cpu.arch) { .i386, .x86_64 => { try self.code.append(0xcc); // int3 }, - else => return self.fail(src, "TODO implement panic for {}", .{self.module.target.cpu.arch}), + else => return self.fail(src, "TODO implement @breakpoint() for {}", .{self.module.target.cpu.arch}), } return .unreach; } - fn genRet(self: *Function, src: usize) !void { - // TODO change this to call the panic function + fn genRet(self: *Function, inst: *ir.Inst.Ret) !MCValue { switch (self.module.target.cpu.arch) { .i386, .x86_64 => { try self.code.append(0xc3); // ret }, - else => return self.fail(src, "TODO implement ret for {}", .{self.module.target.cpu.arch}), + else => return self.fail(inst.base.src, "TODO implement return for {}", .{self.module.target.cpu.arch}), + } + return .unreach; + } + + fn genCmp(self: *Function, inst: *ir.Inst.Cmp) !MCValue { + switch (self.module.target.cpu.arch) { + else => return self.fail(inst.base.src, "TODO implement cmp for {}", .{self.module.target.cpu.arch}), + } + } + + fn genCondBr(self: *Function, inst: *ir.Inst.CondBr) !MCValue { + switch (self.module.target.cpu.arch) { + else => return self.fail(inst.base.src, "TODO implement condbr for {}", .{self.module.target.cpu.arch}), + } + } + + fn genIsNull(self: *Function, inst: *ir.Inst.IsNull) !MCValue { + switch (self.module.target.cpu.arch) { + else => return self.fail(inst.base.src, "TODO implement isnull for {}", .{self.module.target.cpu.arch}), + } + } + + fn genIsNonNull(self: *Function, inst: *ir.Inst.IsNonNull) !MCValue { + // Here you can specialize this instruction if it makes sense to, otherwise the default + // will call genIsNull and invert the result. + switch (self.module.target.cpu.arch) { + else => return self.fail(inst.base.src, "TODO call genIsNull and invert the result ", .{}), } } diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig index c4f3bdf579..d34726d23e 100644 --- a/src-self-hosted/ir.zig +++ b/src-self-hosted/ir.zig @@ -156,6 +156,9 @@ pub const Module = struct { arena: std.heap.ArenaAllocator, fns: []Fn, target: Target, + link_mode: std.builtin.LinkMode, + output_mode: std.builtin.OutputMode, + object_format: std.Target.ObjectFormat, pub const Export = struct { name: []const u8, @@ -190,7 +193,14 @@ pub const ErrorMsg = struct { msg: []const u8, }; -pub fn analyze(allocator: *Allocator, old_module: text.Module, target: Target) !Module { +pub const AnalyzeOptions = struct { + target: Target, + output_mode: std.builtin.OutputMode, + link_mode: std.builtin.LinkMode, + object_format: ?std.Target.ObjectFormat = null, +}; + +pub fn analyze(allocator: *Allocator, old_module: text.Module, options: AnalyzeOptions) !Module { var ctx = Analyze{ .allocator = allocator, .arena = std.heap.ArenaAllocator.init(allocator), @@ -199,7 +209,7 @@ pub fn analyze(allocator: *Allocator, old_module: text.Module, target: Target) ! .decl_table = std.AutoHashMap(*text.Inst, Analyze.NewDecl).init(allocator), .exports = std.ArrayList(Module.Export).init(allocator), .fns = std.ArrayList(Module.Fn).init(allocator), - .target = target, + .target = options.target, }; defer ctx.errors.deinit(); defer ctx.decl_table.deinit(); @@ -218,7 +228,10 @@ pub fn analyze(allocator: *Allocator, old_module: text.Module, target: Target) ! .errors = ctx.errors.toOwnedSlice(), .fns = ctx.fns.toOwnedSlice(), .arena = ctx.arena, - .target = target, + .target = ctx.target, + .link_mode = options.link_mode, + .output_mode = options.output_mode, + .object_format = options.object_format orelse ctx.target.getObjectFormat(), }; } @@ -1241,7 +1254,11 @@ pub fn main() anyerror!void { const native_info = try std.zig.system.NativeTargetInfo.detect(allocator, .{}); - var analyzed_module = try analyze(allocator, zir_module, native_info.target); + var analyzed_module = try analyze(allocator, zir_module, .{ + .target = native_info.target, + .output_mode = .Obj, + .link_mode = .Static, + }); defer analyzed_module.deinit(allocator); if (analyzed_module.errors.len != 0) { @@ -1263,31 +1280,17 @@ pub fn main() anyerror!void { try bos.flush(); } - // executable - //const link = @import("link.zig"); - //var result = try link.updateExecutableFilePath(allocator, analyzed_module, std.fs.cwd(), "a.out"); - //defer result.deinit(allocator); - //if (result.errors.len != 0) { - // for (result.errors) |err_msg| { - // const loc = std.zig.findLineColumn(source, err_msg.byte_offset); - // std.debug.warn("{}:{}:{}: error: {}\n", .{ src_path, loc.line + 1, loc.column + 1, err_msg.msg }); - // } - // if (debug_error_trace) return error.LinkFailure; - // std.process.exit(1); - //} - - // object file const link = @import("link.zig"); - //var result = try link.updateExecutableFilePath(allocator, analyzed_module, std.fs.cwd(), "a.out"); - //defer result.deinit(allocator); - //if (result.errors.len != 0) { - // for (result.errors) |err_msg| { - // const loc = std.zig.findLineColumn(source, err_msg.byte_offset); - // std.debug.warn("{}:{}:{}: error: {}\n", .{ src_path, loc.line + 1, loc.column + 1, err_msg.msg }); - // } - // if (debug_error_trace) return error.LinkFailure; - // std.process.exit(1); - //} + var result = try link.updateFilePath(allocator, analyzed_module, std.fs.cwd(), "zir.o"); + defer result.deinit(allocator); + if (result.errors.len != 0) { + for (result.errors) |err_msg| { + const loc = std.zig.findLineColumn(source, err_msg.byte_offset); + std.debug.warn("{}:{}:{}: error: {}\n", .{ src_path, loc.line + 1, loc.column + 1, err_msg.msg }); + } + if (debug_error_trace) return error.LinkFailure; + std.process.exit(1); + } } // Performance optimization ideas: diff --git a/src-self-hosted/link.zig b/src-self-hosted/link.zig index 9f01088359..4fe31de768 100644 --- a/src-self-hosted/link.zig +++ b/src-self-hosted/link.zig @@ -7,11 +7,6 @@ const fs = std.fs; const elf = std.elf; const codegen = @import("codegen.zig"); -/// On common systems with a 0o022 umask, 0o777 will still result in a file created -/// with 0o755 permissions, but it works appropriately if the system is configured -/// more leniently. As another data point, C's fopen seems to open files with the -/// 666 mode. -const executable_mode = if (std.Target.current.os.tag == .windows) 0 else 0o777; const default_entry_addr = 0x8000000; pub const ErrorMsg = struct { @@ -35,29 +30,29 @@ pub const Result = struct { /// If incremental linking fails, falls back to truncating the file and rewriting it. /// A malicious file is detected as incremental link failure and does not cause Illegal Behavior. /// This operation is not atomic. -pub fn updateExecutableFilePath( +pub fn updateFilePath( allocator: *Allocator, module: ir.Module, dir: fs.Dir, sub_path: []const u8, ) !Result { - const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = executable_mode }); + const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = determineMode(module) }); defer file.close(); - return updateExecutableFile(allocator, module, file); + return updateFile(allocator, module, file); } /// Atomically overwrites the old file, if present. -pub fn writeExecutableFilePath( +pub fn writeFilePath( allocator: *Allocator, module: ir.Module, dir: fs.Dir, sub_path: []const u8, ) !Result { - const af = try dir.atomicFile(sub_path, .{ .mode = executable_mode }); + const af = try dir.atomicFile(sub_path, .{ .mode = determineMode(module) }); defer af.deinit(); - const result = try writeExecutableFile(allocator, module, af.file); + const result = try writeFile(allocator, module, af.file); try af.finish(); return result; } @@ -67,10 +62,10 @@ pub fn writeExecutableFilePath( /// Returns an error if `file` is not already open with +read +write +seek abilities. /// A malicious file is detected as incremental link failure and does not cause Illegal Behavior. /// This operation is not atomic. -pub fn updateExecutableFile(allocator: *Allocator, module: ir.Module, file: fs.File) !Result { - return updateExecutableFileInner(allocator, module, file) catch |err| switch (err) { +pub fn updateFile(allocator: *Allocator, module: ir.Module, file: fs.File) !Result { + return updateFileInner(allocator, module, file) catch |err| switch (err) { error.IncrFailed => { - return writeExecutableFile(allocator, module, file); + return writeFile(allocator, module, file); }, else => |e| return e, }; @@ -750,7 +745,20 @@ const Update = struct { /// Truncates the existing file contents and overwrites the contents. /// Returns an error if `file` is not already open with +read +write +seek abilities. -pub fn writeExecutableFile(allocator: *Allocator, module: ir.Module, file: fs.File) !Result { +pub fn writeFile(allocator: *Allocator, module: ir.Module, file: fs.File) !Result { + switch (module.output_mode) { + .Exe => {}, + .Obj => return error.TODOImplementWritingObjectFiles, + .Lib => return error.TODOImplementWritingLibFiles, + } + switch (module.object_format) { + .unknown => unreachable, // TODO remove this tag from the enum + .coff => return error.TODOImplementWritingCOFF, + .elf => {}, + .macho => return error.TODOImplementWritingMachO, + .wasm => return error.TODOImplementWritingWasmObjects, + } + var update = Update{ .file = file, .module = &module, @@ -778,7 +786,7 @@ pub fn writeExecutableFile(allocator: *Allocator, module: ir.Module, file: fs.Fi } /// Returns error.IncrFailed if incremental update could not be performed. -fn updateExecutableFileInner(allocator: *Allocator, module: ir.Module, file: fs.File) !Result { +fn updateFileInner(allocator: *Allocator, module: ir.Module, file: fs.File) !Result { //var ehdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined; // TODO implement incremental linking @@ -822,3 +830,19 @@ fn sectHeaderTo32(shdr: elf.Elf64_Shdr) elf.Elf32_Shdr { .sh_entsize = @intCast(u32, shdr.sh_entsize), }; } + +fn determineMode(module: ir.Module) fs.File.Mode { + // On common systems with a 0o022 umask, 0o777 will still result in a file created + // with 0o755 permissions, but it works appropriately if the system is configured + // more leniently. As another data point, C's fopen seems to open files with the + // 666 mode. + const executable_mode = if (std.Target.current.os.tag == .windows) 0 else 0o777; + switch (module.output_mode) { + .Lib => return switch (module.link_mode) { + .Dynamic => executable_mode, + .Static => fs.File.default_mode, + }, + .Exe => return executable_mode, + .Obj => return fs.File.default_mode, + } +} |
