Commit 751903ba8f

Andrew Kelley <andrew@ziglang.org>
2020-04-30 01:11:40
zir: add breakpoint() instruction and object file ability
1 parent f89dbe6
Changed files (4)
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).?;
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).?),
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);
 
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) {