From 6b0f7de247f3c12281f47f38738e93651d6bf51b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 28 Apr 2020 21:04:18 -0400 Subject: ZIR: add cmp and condbr instructions --- src-self-hosted/codegen.zig | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src-self-hosted/codegen.zig') diff --git a/src-self-hosted/codegen.zig b/src-self-hosted/codegen.zig index 3a8d0e1282..8e6c19e869 100644 --- a/src-self-hosted/codegen.zig +++ b/src-self-hosted/codegen.zig @@ -501,11 +501,19 @@ fn Reg(comptime arch: Target.Cpu.Arch) type { bh, ch, dh, + bph, + sph, + sih, + dih, al, bl, cl, dl, + bpl, + spl, + sil, + dil, r8b, r9b, r10b, -- cgit v1.2.3 From f89dbe6c4ebe3fa1ffe3eb455ed96fe615e2c903 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 29 Apr 2020 18:14:15 -0400 Subject: link: introduce the concept of output mode and link mode --- lib/std/target.zig | 1 + src-self-hosted/codegen.zig | 46 +++++++++++++++++++++++++++++------ src-self-hosted/ir.zig | 59 ++++++++++++++++++++++++--------------------- src-self-hosted/link.zig | 56 ++++++++++++++++++++++++++++++------------ 4 files changed, 110 insertions(+), 52 deletions(-) (limited to 'src-self-hosted/codegen.zig') diff --git a/lib/std/target.zig b/lib/std/target.zig index 6e383ba4c7..9df3e21e52 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -404,6 +404,7 @@ pub const Target = struct { }; pub const ObjectFormat = enum { + /// TODO Get rid of this one. unknown, coff, elf, 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, + } +} -- cgit v1.2.3 From 751903ba8fba467411942317c8da0e6bc22a0ff6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 29 Apr 2020 19:11:40 -0400 Subject: zir: add breakpoint() instruction and object file ability --- src-self-hosted/codegen.zig | 1 + src-self-hosted/ir.zig | 112 +++++++++++++++++++++++++++++--------------- src-self-hosted/ir/text.zig | 42 ++++++++++------- src-self-hosted/link.zig | 21 ++++++--- 4 files changed, 114 insertions(+), 62 deletions(-) (limited to 'src-self-hosted/codegen.zig') diff --git a/src-self-hosted/codegen.zig b/src-self-hosted/codegen.zig index 2e8bcd9624..675b8faad2 100644 --- a/src-self-hosted/codegen.zig +++ b/src-self-hosted/codegen.zig @@ -77,6 +77,7 @@ const Function = struct { fn genFuncInst(self: *Function, inst: *ir.Inst) !MCValue { switch (inst.tag) { + .breakpoint => return self.genBreakpoint(inst.src), .unreach => return MCValue{ .unreach = {} }, .constant => unreachable, // excluded from function bodies .assembly => return self.genAsm(inst.cast(ir.Inst.Assembly).?), diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig index d34726d23e..e78ccaf4f0 100644 --- a/src-self-hosted/ir.zig +++ b/src-self-hosted/ir.zig @@ -21,16 +21,17 @@ pub const Inst = struct { src: usize, pub const Tag = enum { - unreach, - ret, - constant, assembly, - ptrtoint, bitcast, + breakpoint, cmp, condbr, - isnull, + constant, isnonnull, + isnull, + ptrtoint, + ret, + unreach, }; pub fn cast(base: *Inst, comptime T: type) ?*T { @@ -53,25 +54,6 @@ pub const Inst = struct { return inst.val; } - pub const Unreach = struct { - pub const base_tag = Tag.unreach; - base: Inst, - args: void, - }; - - pub const Ret = struct { - pub const base_tag = Tag.ret; - base: Inst, - args: void, - }; - - pub const Constant = struct { - pub const base_tag = Tag.constant; - base: Inst, - - val: Value, - }; - pub const Assembly = struct { pub const base_tag = Tag.assembly; base: Inst, @@ -86,15 +68,6 @@ pub const Inst = struct { }, }; - pub const PtrToInt = struct { - pub const base_tag = Tag.ptrtoint; - - base: Inst, - args: struct { - ptr: *Inst, - }, - }; - pub const BitCast = struct { pub const base_tag = Tag.bitcast; @@ -104,6 +77,12 @@ pub const Inst = struct { }, }; + pub const Breakpoint = struct { + pub const base_tag = Tag.breakpoint; + base: Inst, + args: void, + }; + pub const Cmp = struct { pub const base_tag = Tag.cmp; @@ -126,6 +105,22 @@ pub const Inst = struct { }, }; + pub const Constant = struct { + pub const base_tag = Tag.constant; + base: Inst, + + val: Value, + }; + + pub const IsNonNull = struct { + pub const base_tag = Tag.isnonnull; + + base: Inst, + args: struct { + operand: *Inst, + }, + }; + pub const IsNull = struct { pub const base_tag = Tag.isnull; @@ -135,14 +130,26 @@ pub const Inst = struct { }, }; - pub const IsNonNull = struct { - pub const base_tag = Tag.isnonnull; + pub const PtrToInt = struct { + pub const base_tag = Tag.ptrtoint; base: Inst, args: struct { - operand: *Inst, + ptr: *Inst, }, }; + + pub const Ret = struct { + pub const base_tag = Tag.ret; + base: Inst, + args: void, + }; + + pub const Unreach = struct { + pub const base_tag = Tag.unreach; + base: Inst, + args: void, + }; }; pub const TypedValue = struct { @@ -159,6 +166,7 @@ pub const Module = struct { link_mode: std.builtin.LinkMode, output_mode: std.builtin.OutputMode, object_format: std.Target.ObjectFormat, + optimize_mode: std.builtin.Mode, pub const Export = struct { name: []const u8, @@ -198,6 +206,7 @@ pub const AnalyzeOptions = struct { output_mode: std.builtin.OutputMode, link_mode: std.builtin.LinkMode, object_format: ?std.Target.ObjectFormat = null, + optimize_mode: std.builtin.Mode, }; pub fn analyze(allocator: *Allocator, old_module: text.Module, options: AnalyzeOptions) !Module { @@ -210,6 +219,9 @@ pub fn analyze(allocator: *Allocator, old_module: text.Module, options: AnalyzeO .exports = std.ArrayList(Module.Export).init(allocator), .fns = std.ArrayList(Module.Fn).init(allocator), .target = options.target, + .optimize_mode = options.optimize_mode, + .link_mode = options.link_mode, + .output_mode = options.output_mode, }; defer ctx.errors.deinit(); defer ctx.decl_table.deinit(); @@ -229,9 +241,10 @@ pub fn analyze(allocator: *Allocator, old_module: text.Module, options: AnalyzeO .fns = ctx.fns.toOwnedSlice(), .arena = ctx.arena, .target = ctx.target, - .link_mode = options.link_mode, - .output_mode = options.output_mode, + .link_mode = ctx.link_mode, + .output_mode = ctx.output_mode, .object_format = options.object_format orelse ctx.target.getObjectFormat(), + .optimize_mode = ctx.optimize_mode, }; } @@ -244,6 +257,9 @@ const Analyze = struct { exports: std.ArrayList(Module.Export), fns: std.ArrayList(Module.Fn), target: Target, + link_mode: std.builtin.LinkMode, + optimize_mode: std.builtin.Mode, + output_mode: std.builtin.OutputMode, const NewDecl = struct { /// null means a semantic analysis error happened @@ -495,6 +511,7 @@ const Analyze = struct { fn analyzeInst(self: *Analyze, block: ?*Block, old_inst: *text.Inst) InnerError!*Inst { switch (old_inst.tag) { + .breakpoint => return self.analyzeInstBreakpoint(block, old_inst.cast(text.Inst.Breakpoint).?), .str => { // We can use this reference because Inst.Const's Value is arena-allocated. // The value would get copied to a MemoryCell before the `text.Inst.Str` lifetime ends. @@ -530,6 +547,11 @@ const Analyze = struct { } } + fn analyzeInstBreakpoint(self: *Analyze, block: ?*Block, inst: *text.Inst.Breakpoint) InnerError!*Inst { + const b = try self.requireRuntimeBlock(block, inst.base.src); + return self.addNewInstArgs(b, inst.base.src, Type.initTag(.void), Inst.Breakpoint, Inst.Args(Inst.Breakpoint){}); + } + fn analyzeInstFn(self: *Analyze, block: ?*Block, fn_inst: *text.Inst.Fn) InnerError!*Inst { const fn_type = try self.resolveType(block, fn_inst.positionals.fn_type); @@ -909,8 +931,21 @@ const Analyze = struct { }); } + fn wantSafety(self: *Analyze, block: ?*Block) bool { + return switch (self.optimize_mode) { + .Debug => true, + .ReleaseSafe => true, + .ReleaseFast => false, + .ReleaseSmall => false, + }; + } + fn analyzeInstUnreachable(self: *Analyze, block: ?*Block, unreach: *text.Inst.Unreachable) InnerError!*Inst { const b = try self.requireRuntimeBlock(block, unreach.base.src); + if (self.wantSafety(block)) { + // TODO Once we have a panic function to call, call it here instead of this. + _ = try self.addNewInstArgs(b, unreach.base.src, Type.initTag(.void), Inst.Breakpoint, {}); + } return self.addNewInstArgs(b, unreach.base.src, Type.initTag(.noreturn), Inst.Unreach, {}); } @@ -1258,6 +1293,7 @@ pub fn main() anyerror!void { .target = native_info.target, .output_mode = .Obj, .link_mode = .Static, + .optimize_mode = .Debug, }); defer analyzed_module.deinit(allocator); diff --git a/src-self-hosted/ir/text.zig b/src-self-hosted/ir/text.zig index 7d444d9310..536d50864d 100644 --- a/src-self-hosted/ir/text.zig +++ b/src-self-hosted/ir/text.zig @@ -18,6 +18,7 @@ pub const Inst = struct { /// These names are used directly as the instruction names in the text format. pub const Tag = enum { + breakpoint, str, int, ptrtoint, @@ -43,6 +44,7 @@ pub const Inst = struct { pub fn TagToType(tag: Tag) type { return switch (tag) { + .breakpoint => Breakpoint, .str => Str, .int => Int, .ptrtoint => PtrToInt, @@ -74,6 +76,14 @@ pub const Inst = struct { return @fieldParentPtr(T, "base", base); } + pub const Breakpoint = struct { + pub const base_tag = Tag.breakpoint; + base: Inst, + + positionals: struct {}, + kw_args: struct {}, + }; + pub const Str = struct { pub const base_tag = Tag.str; base: Inst, @@ -419,6 +429,7 @@ pub const Module = struct { ) @TypeOf(stream).Error!void { // TODO I tried implementing this with an inline for loop and hit a compiler bug switch (decl.tag) { + .breakpoint => return self.writeInstToStreamGeneric(stream, .breakpoint, decl, inst_table), .str => return self.writeInstToStreamGeneric(stream, .str, decl, inst_table), .int => return self.writeInstToStreamGeneric(stream, .int, decl, inst_table), .ptrtoint => return self.writeInstToStreamGeneric(stream, .ptrtoint, decl, inst_table), @@ -1030,6 +1041,16 @@ const EmitZIR = struct { } } + fn emitTrivial(self: *EmitZIR, src: usize, comptime T: type) Allocator.Error!*Inst { + const new_inst = try self.arena.allocator.create(T); + new_inst.* = .{ + .base = .{ .src = src, .tag = T.base_tag }, + .positionals = .{}, + .kw_args = .{}, + }; + return &new_inst.base; + } + fn emitBody( self: *EmitZIR, body: ir.Module.Body, @@ -1038,24 +1059,9 @@ const EmitZIR = struct { ) Allocator.Error!void { for (body.instructions) |inst| { const new_inst = switch (inst.tag) { - .unreach => blk: { - const unreach_inst = try self.arena.allocator.create(Inst.Unreachable); - unreach_inst.* = .{ - .base = .{ .src = inst.src, .tag = Inst.Unreachable.base_tag }, - .positionals = .{}, - .kw_args = .{}, - }; - break :blk &unreach_inst.base; - }, - .ret => blk: { - const ret_inst = try self.arena.allocator.create(Inst.Return); - ret_inst.* = .{ - .base = .{ .src = inst.src, .tag = Inst.Return.base_tag }, - .positionals = .{}, - .kw_args = .{}, - }; - break :blk &ret_inst.base; - }, + .breakpoint => try self.emitTrivial(inst.src, Inst.Breakpoint), + .unreach => try self.emitTrivial(inst.src, Inst.Unreachable), + .ret => try self.emitTrivial(inst.src, Inst.Return), .constant => unreachable, // excluded from function bodies .assembly => blk: { const old_inst = inst.cast(ir.Inst.Assembly).?; diff --git a/src-self-hosted/link.zig b/src-self-hosted/link.zig index 4fe31de768..504c374ca7 100644 --- a/src-self-hosted/link.zig +++ b/src-self-hosted/link.zig @@ -431,7 +431,7 @@ const Update = struct { }, } } - if (self.entry_addr == null) { + if (self.entry_addr == null and self.module.output_mode == .Exe) { const msg = try std.fmt.allocPrint(self.errors.allocator, "no entry point found", .{}); errdefer self.errors.allocator.free(msg); try self.errors.append(.{ @@ -480,7 +480,15 @@ const Update = struct { assert(index == 16); - mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(elf.ET.EXEC), endian); + const elf_type = switch (self.module.output_mode) { + .Exe => elf.ET.EXEC, + .Obj => elf.ET.REL, + .Lib => switch (self.module.link_mode) { + .Static => elf.ET.REL, + .Dynamic => elf.ET.DYN, + }, + }; + mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(elf_type), endian); index += 2; const machine = self.module.target.cpu.arch.toElfMachine(); @@ -491,10 +499,11 @@ const Update = struct { mem.writeInt(u32, hdr_buf[index..][0..4], 1, endian); index += 4; + const e_entry = if (elf_type == .REL) 0 else self.entry_addr.?; + switch (ptr_width) { .p32 => { - // e_entry - mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, self.entry_addr.?), endian); + mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, e_entry), endian); index += 4; // e_phoff @@ -507,7 +516,7 @@ const Update = struct { }, .p64 => { // e_entry - mem.writeInt(u64, hdr_buf[index..][0..8], self.entry_addr.?, endian); + mem.writeInt(u64, hdr_buf[index..][0..8], e_entry, endian); index += 8; // e_phoff @@ -748,7 +757,7 @@ const Update = struct { pub fn writeFile(allocator: *Allocator, module: ir.Module, file: fs.File) !Result { switch (module.output_mode) { .Exe => {}, - .Obj => return error.TODOImplementWritingObjectFiles, + .Obj => {}, .Lib => return error.TODOImplementWritingLibFiles, } switch (module.object_format) { -- cgit v1.2.3