authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-04-29 19:11:40-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-05-01 06:47:20-04:00
log751903ba8fba467411942317c8da0e6bc22a0ff6
tree44074425b691cf0d03635d714db1dfaed5fdf7a7
parentf89dbe6c4ebe3fa1ffe3eb455ed96fe615e2c903

zir: add breakpoint() instruction and object file ability


4 files changed, 114 insertions(+), 62 deletions(-)

src-self-hosted/codegen.zig+1
...@@ -77,6 +77,7 @@ const Function = struct {...@@ -77,6 +77,7 @@ const Function = struct {
7777
78 fn genFuncInst(self: *Function, inst: *ir.Inst) !MCValue {78 fn genFuncInst(self: *Function, inst: *ir.Inst) !MCValue {
79 switch (inst.tag) {79 switch (inst.tag) {
80 .breakpoint => return self.genBreakpoint(inst.src),
80 .unreach => return MCValue{ .unreach = {} },81 .unreach => return MCValue{ .unreach = {} },
81 .constant => unreachable, // excluded from function bodies82 .constant => unreachable, // excluded from function bodies
82 .assembly => return self.genAsm(inst.cast(ir.Inst.Assembly).?),83 .assembly => return self.genAsm(inst.cast(ir.Inst.Assembly).?),
src-self-hosted/ir.zig+74-38
...@@ -21,16 +21,17 @@ pub const Inst = struct {...@@ -21,16 +21,17 @@ pub const Inst = struct {
21 src: usize,21 src: usize,
2222
23 pub const Tag = enum {23 pub const Tag = enum {
24 unreach,
25 ret,
26 constant,
27 assembly,24 assembly,
28 ptrtoint,
29 bitcast,25 bitcast,
26 breakpoint,
30 cmp,27 cmp,
31 condbr,28 condbr,
32 isnull,29 constant,
33 isnonnull,30 isnonnull,
31 isnull,
32 ptrtoint,
33 ret,
34 unreach,
34 };35 };
3536
36 pub fn cast(base: *Inst, comptime T: type) ?*T {37 pub fn cast(base: *Inst, comptime T: type) ?*T {
...@@ -53,25 +54,6 @@ pub const Inst = struct {...@@ -53,25 +54,6 @@ pub const Inst = struct {
53 return inst.val;54 return inst.val;
54 }55 }
5556
56 pub const Unreach = struct {
57 pub const base_tag = Tag.unreach;
58 base: Inst,
59 args: void,
60 };
61
62 pub const Ret = struct {
63 pub const base_tag = Tag.ret;
64 base: Inst,
65 args: void,
66 };
67
68 pub const Constant = struct {
69 pub const base_tag = Tag.constant;
70 base: Inst,
71
72 val: Value,
73 };
74
75 pub const Assembly = struct {57 pub const Assembly = struct {
76 pub const base_tag = Tag.assembly;58 pub const base_tag = Tag.assembly;
77 base: Inst,59 base: Inst,
...@@ -86,15 +68,6 @@ pub const Inst = struct {...@@ -86,15 +68,6 @@ pub const Inst = struct {
86 },68 },
87 };69 };
8870
89 pub const PtrToInt = struct {
90 pub const base_tag = Tag.ptrtoint;
91
92 base: Inst,
93 args: struct {
94 ptr: *Inst,
95 },
96 };
97
98 pub const BitCast = struct {71 pub const BitCast = struct {
99 pub const base_tag = Tag.bitcast;72 pub const base_tag = Tag.bitcast;
10073
...@@ -104,6 +77,12 @@ pub const Inst = struct {...@@ -104,6 +77,12 @@ pub const Inst = struct {
104 },77 },
105 };78 };
10679
80 pub const Breakpoint = struct {
81 pub const base_tag = Tag.breakpoint;
82 base: Inst,
83 args: void,
84 };
85
107 pub const Cmp = struct {86 pub const Cmp = struct {
108 pub const base_tag = Tag.cmp;87 pub const base_tag = Tag.cmp;
10988
...@@ -126,6 +105,22 @@ pub const Inst = struct {...@@ -126,6 +105,22 @@ pub const Inst = struct {
126 },105 },
127 };106 };
128107
108 pub const Constant = struct {
109 pub const base_tag = Tag.constant;
110 base: Inst,
111
112 val: Value,
113 };
114
115 pub const IsNonNull = struct {
116 pub const base_tag = Tag.isnonnull;
117
118 base: Inst,
119 args: struct {
120 operand: *Inst,
121 },
122 };
123
129 pub const IsNull = struct {124 pub const IsNull = struct {
130 pub const base_tag = Tag.isnull;125 pub const base_tag = Tag.isnull;
131126
...@@ -135,14 +130,26 @@ pub const Inst = struct {...@@ -135,14 +130,26 @@ pub const Inst = struct {
135 },130 },
136 };131 };
137132
138 pub const IsNonNull = struct {133 pub const PtrToInt = struct {
139 pub const base_tag = Tag.isnonnull;134 pub const base_tag = Tag.ptrtoint;
140135
141 base: Inst,136 base: Inst,
142 args: struct {137 args: struct {
143 operand: *Inst,138 ptr: *Inst,
144 },139 },
145 };140 };
141
142 pub const Ret = struct {
143 pub const base_tag = Tag.ret;
144 base: Inst,
145 args: void,
146 };
147
148 pub const Unreach = struct {
149 pub const base_tag = Tag.unreach;
150 base: Inst,
151 args: void,
152 };
146};153};
147154
148pub const TypedValue = struct {155pub const TypedValue = struct {
...@@ -159,6 +166,7 @@ pub const Module = struct {...@@ -159,6 +166,7 @@ pub const Module = struct {
159 link_mode: std.builtin.LinkMode,166 link_mode: std.builtin.LinkMode,
160 output_mode: std.builtin.OutputMode,167 output_mode: std.builtin.OutputMode,
161 object_format: std.Target.ObjectFormat,168 object_format: std.Target.ObjectFormat,
169 optimize_mode: std.builtin.Mode,
162170
163 pub const Export = struct {171 pub const Export = struct {
164 name: []const u8,172 name: []const u8,
...@@ -198,6 +206,7 @@ pub const AnalyzeOptions = struct {...@@ -198,6 +206,7 @@ pub const AnalyzeOptions = struct {
198 output_mode: std.builtin.OutputMode,206 output_mode: std.builtin.OutputMode,
199 link_mode: std.builtin.LinkMode,207 link_mode: std.builtin.LinkMode,
200 object_format: ?std.Target.ObjectFormat = null,208 object_format: ?std.Target.ObjectFormat = null,
209 optimize_mode: std.builtin.Mode,
201};210};
202211
203pub fn analyze(allocator: *Allocator, old_module: text.Module, options: AnalyzeOptions) !Module {212pub 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...@@ -210,6 +219,9 @@ pub fn analyze(allocator: *Allocator, old_module: text.Module, options: AnalyzeO
210 .exports = std.ArrayList(Module.Export).init(allocator),219 .exports = std.ArrayList(Module.Export).init(allocator),
211 .fns = std.ArrayList(Module.Fn).init(allocator),220 .fns = std.ArrayList(Module.Fn).init(allocator),
212 .target = options.target,221 .target = options.target,
222 .optimize_mode = options.optimize_mode,
223 .link_mode = options.link_mode,
224 .output_mode = options.output_mode,
213 };225 };
214 defer ctx.errors.deinit();226 defer ctx.errors.deinit();
215 defer ctx.decl_table.deinit();227 defer ctx.decl_table.deinit();
...@@ -229,9 +241,10 @@ pub fn analyze(allocator: *Allocator, old_module: text.Module, options: AnalyzeO...@@ -229,9 +241,10 @@ pub fn analyze(allocator: *Allocator, old_module: text.Module, options: AnalyzeO
229 .fns = ctx.fns.toOwnedSlice(),241 .fns = ctx.fns.toOwnedSlice(),
230 .arena = ctx.arena,242 .arena = ctx.arena,
231 .target = ctx.target,243 .target = ctx.target,
232 .link_mode = options.link_mode,244 .link_mode = ctx.link_mode,
233 .output_mode = options.output_mode,245 .output_mode = ctx.output_mode,
234 .object_format = options.object_format orelse ctx.target.getObjectFormat(),246 .object_format = options.object_format orelse ctx.target.getObjectFormat(),
247 .optimize_mode = ctx.optimize_mode,
235 };248 };
236}249}
237250
...@@ -244,6 +257,9 @@ const Analyze = struct {...@@ -244,6 +257,9 @@ const Analyze = struct {
244 exports: std.ArrayList(Module.Export),257 exports: std.ArrayList(Module.Export),
245 fns: std.ArrayList(Module.Fn),258 fns: std.ArrayList(Module.Fn),
246 target: Target,259 target: Target,
260 link_mode: std.builtin.LinkMode,
261 optimize_mode: std.builtin.Mode,
262 output_mode: std.builtin.OutputMode,
247263
248 const NewDecl = struct {264 const NewDecl = struct {
249 /// null means a semantic analysis error happened265 /// null means a semantic analysis error happened
...@@ -495,6 +511,7 @@ const Analyze = struct {...@@ -495,6 +511,7 @@ const Analyze = struct {
495511
496 fn analyzeInst(self: *Analyze, block: ?*Block, old_inst: *text.Inst) InnerError!*Inst {512 fn analyzeInst(self: *Analyze, block: ?*Block, old_inst: *text.Inst) InnerError!*Inst {
497 switch (old_inst.tag) {513 switch (old_inst.tag) {
514 .breakpoint => return self.analyzeInstBreakpoint(block, old_inst.cast(text.Inst.Breakpoint).?),
498 .str => {515 .str => {
499 // We can use this reference because Inst.Const's Value is arena-allocated.516 // We can use this reference because Inst.Const's Value is arena-allocated.
500 // The value would get copied to a MemoryCell before the `text.Inst.Str` lifetime ends.517 // The value would get copied to a MemoryCell before the `text.Inst.Str` lifetime ends.
...@@ -530,6 +547,11 @@ const Analyze = struct {...@@ -530,6 +547,11 @@ const Analyze = struct {
530 }547 }
531 }548 }
532549
550 fn analyzeInstBreakpoint(self: *Analyze, block: ?*Block, inst: *text.Inst.Breakpoint) InnerError!*Inst {
551 const b = try self.requireRuntimeBlock(block, inst.base.src);
552 return self.addNewInstArgs(b, inst.base.src, Type.initTag(.void), Inst.Breakpoint, Inst.Args(Inst.Breakpoint){});
553 }
554
533 fn analyzeInstFn(self: *Analyze, block: ?*Block, fn_inst: *text.Inst.Fn) InnerError!*Inst {555 fn analyzeInstFn(self: *Analyze, block: ?*Block, fn_inst: *text.Inst.Fn) InnerError!*Inst {
534 const fn_type = try self.resolveType(block, fn_inst.positionals.fn_type);556 const fn_type = try self.resolveType(block, fn_inst.positionals.fn_type);
535557
...@@ -909,8 +931,21 @@ const Analyze = struct {...@@ -909,8 +931,21 @@ const Analyze = struct {
909 });931 });
910 }932 }
911933
934 fn wantSafety(self: *Analyze, block: ?*Block) bool {
935 return switch (self.optimize_mode) {
936 .Debug => true,
937 .ReleaseSafe => true,
938 .ReleaseFast => false,
939 .ReleaseSmall => false,
940 };
941 }
942
912 fn analyzeInstUnreachable(self: *Analyze, block: ?*Block, unreach: *text.Inst.Unreachable) InnerError!*Inst {943 fn analyzeInstUnreachable(self: *Analyze, block: ?*Block, unreach: *text.Inst.Unreachable) InnerError!*Inst {
913 const b = try self.requireRuntimeBlock(block, unreach.base.src);944 const b = try self.requireRuntimeBlock(block, unreach.base.src);
945 if (self.wantSafety(block)) {
946 // TODO Once we have a panic function to call, call it here instead of this.
947 _ = try self.addNewInstArgs(b, unreach.base.src, Type.initTag(.void), Inst.Breakpoint, {});
948 }
914 return self.addNewInstArgs(b, unreach.base.src, Type.initTag(.noreturn), Inst.Unreach, {});949 return self.addNewInstArgs(b, unreach.base.src, Type.initTag(.noreturn), Inst.Unreach, {});
915 }950 }
916951
...@@ -1258,6 +1293,7 @@ pub fn main() anyerror!void {...@@ -1258,6 +1293,7 @@ pub fn main() anyerror!void {
1258 .target = native_info.target,1293 .target = native_info.target,
1259 .output_mode = .Obj,1294 .output_mode = .Obj,
1260 .link_mode = .Static,1295 .link_mode = .Static,
1296 .optimize_mode = .Debug,
1261 });1297 });
1262 defer analyzed_module.deinit(allocator);1298 defer analyzed_module.deinit(allocator);
12631299
src-self-hosted/ir/text.zig+24-18
...@@ -18,6 +18,7 @@ pub const Inst = struct {...@@ -18,6 +18,7 @@ pub const Inst = struct {
1818
19 /// These names are used directly as the instruction names in the text format.19 /// These names are used directly as the instruction names in the text format.
20 pub const Tag = enum {20 pub const Tag = enum {
21 breakpoint,
21 str,22 str,
22 int,23 int,
23 ptrtoint,24 ptrtoint,
...@@ -43,6 +44,7 @@ pub const Inst = struct {...@@ -43,6 +44,7 @@ pub const Inst = struct {
4344
44 pub fn TagToType(tag: Tag) type {45 pub fn TagToType(tag: Tag) type {
45 return switch (tag) {46 return switch (tag) {
47 .breakpoint => Breakpoint,
46 .str => Str,48 .str => Str,
47 .int => Int,49 .int => Int,
48 .ptrtoint => PtrToInt,50 .ptrtoint => PtrToInt,
...@@ -74,6 +76,14 @@ pub const Inst = struct {...@@ -74,6 +76,14 @@ pub const Inst = struct {
74 return @fieldParentPtr(T, "base", base);76 return @fieldParentPtr(T, "base", base);
75 }77 }
7678
79 pub const Breakpoint = struct {
80 pub const base_tag = Tag.breakpoint;
81 base: Inst,
82
83 positionals: struct {},
84 kw_args: struct {},
85 };
86
77 pub const Str = struct {87 pub const Str = struct {
78 pub const base_tag = Tag.str;88 pub const base_tag = Tag.str;
79 base: Inst,89 base: Inst,
...@@ -419,6 +429,7 @@ pub const Module = struct {...@@ -419,6 +429,7 @@ pub const Module = struct {
419 ) @TypeOf(stream).Error!void {429 ) @TypeOf(stream).Error!void {
420 // TODO I tried implementing this with an inline for loop and hit a compiler bug430 // TODO I tried implementing this with an inline for loop and hit a compiler bug
421 switch (decl.tag) {431 switch (decl.tag) {
432 .breakpoint => return self.writeInstToStreamGeneric(stream, .breakpoint, decl, inst_table),
422 .str => return self.writeInstToStreamGeneric(stream, .str, decl, inst_table),433 .str => return self.writeInstToStreamGeneric(stream, .str, decl, inst_table),
423 .int => return self.writeInstToStreamGeneric(stream, .int, decl, inst_table),434 .int => return self.writeInstToStreamGeneric(stream, .int, decl, inst_table),
424 .ptrtoint => return self.writeInstToStreamGeneric(stream, .ptrtoint, decl, inst_table),435 .ptrtoint => return self.writeInstToStreamGeneric(stream, .ptrtoint, decl, inst_table),
...@@ -1030,6 +1041,16 @@ const EmitZIR = struct {...@@ -1030,6 +1041,16 @@ const EmitZIR = struct {
1030 }1041 }
1031 }1042 }
10321043
1044 fn emitTrivial(self: *EmitZIR, src: usize, comptime T: type) Allocator.Error!*Inst {
1045 const new_inst = try self.arena.allocator.create(T);
1046 new_inst.* = .{
1047 .base = .{ .src = src, .tag = T.base_tag },
1048 .positionals = .{},
1049 .kw_args = .{},
1050 };
1051 return &new_inst.base;
1052 }
1053
1033 fn emitBody(1054 fn emitBody(
1034 self: *EmitZIR,1055 self: *EmitZIR,
1035 body: ir.Module.Body,1056 body: ir.Module.Body,
...@@ -1038,24 +1059,9 @@ const EmitZIR = struct {...@@ -1038,24 +1059,9 @@ const EmitZIR = struct {
1038 ) Allocator.Error!void {1059 ) Allocator.Error!void {
1039 for (body.instructions) |inst| {1060 for (body.instructions) |inst| {
1040 const new_inst = switch (inst.tag) {1061 const new_inst = switch (inst.tag) {
1041 .unreach => blk: {1062 .breakpoint => try self.emitTrivial(inst.src, Inst.Breakpoint),
1042 const unreach_inst = try self.arena.allocator.create(Inst.Unreachable);1063 .unreach => try self.emitTrivial(inst.src, Inst.Unreachable),
1043 unreach_inst.* = .{1064 .ret => try self.emitTrivial(inst.src, Inst.Return),
1044 .base = .{ .src = inst.src, .tag = Inst.Unreachable.base_tag },
1045 .positionals = .{},
1046 .kw_args = .{},
1047 };
1048 break :blk &unreach_inst.base;
1049 },
1050 .ret => blk: {
1051 const ret_inst = try self.arena.allocator.create(Inst.Return);
1052 ret_inst.* = .{
1053 .base = .{ .src = inst.src, .tag = Inst.Return.base_tag },
1054 .positionals = .{},
1055 .kw_args = .{},
1056 };
1057 break :blk &ret_inst.base;
1058 },
1059 .constant => unreachable, // excluded from function bodies1065 .constant => unreachable, // excluded from function bodies
1060 .assembly => blk: {1066 .assembly => blk: {
1061 const old_inst = inst.cast(ir.Inst.Assembly).?;1067 const old_inst = inst.cast(ir.Inst.Assembly).?;
src-self-hosted/link.zig+15-6
...@@ -431,7 +431,7 @@ const Update = struct {...@@ -431,7 +431,7 @@ const Update = struct {
431 },431 },
432 }432 }
433 }433 }
434 if (self.entry_addr == null) {434 if (self.entry_addr == null and self.module.output_mode == .Exe) {
435 const msg = try std.fmt.allocPrint(self.errors.allocator, "no entry point found", .{});435 const msg = try std.fmt.allocPrint(self.errors.allocator, "no entry point found", .{});
436 errdefer self.errors.allocator.free(msg);436 errdefer self.errors.allocator.free(msg);
437 try self.errors.append(.{437 try self.errors.append(.{
...@@ -480,7 +480,15 @@ const Update = struct {...@@ -480,7 +480,15 @@ const Update = struct {
480480
481 assert(index == 16);481 assert(index == 16);
482482
483 mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(elf.ET.EXEC), endian);483 const elf_type = switch (self.module.output_mode) {
484 .Exe => elf.ET.EXEC,
485 .Obj => elf.ET.REL,
486 .Lib => switch (self.module.link_mode) {
487 .Static => elf.ET.REL,
488 .Dynamic => elf.ET.DYN,
489 },
490 };
491 mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(elf_type), endian);
484 index += 2;492 index += 2;
485493
486 const machine = self.module.target.cpu.arch.toElfMachine();494 const machine = self.module.target.cpu.arch.toElfMachine();
...@@ -491,10 +499,11 @@ const Update = struct {...@@ -491,10 +499,11 @@ const Update = struct {
491 mem.writeInt(u32, hdr_buf[index..][0..4], 1, endian);499 mem.writeInt(u32, hdr_buf[index..][0..4], 1, endian);
492 index += 4;500 index += 4;
493501
502 const e_entry = if (elf_type == .REL) 0 else self.entry_addr.?;
503
494 switch (ptr_width) {504 switch (ptr_width) {
495 .p32 => {505 .p32 => {
496 // e_entry506 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, e_entry), endian);
497 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, self.entry_addr.?), endian);
498 index += 4;507 index += 4;
499508
500 // e_phoff509 // e_phoff
...@@ -507,7 +516,7 @@ const Update = struct {...@@ -507,7 +516,7 @@ const Update = struct {
507 },516 },
508 .p64 => {517 .p64 => {
509 // e_entry518 // e_entry
510 mem.writeInt(u64, hdr_buf[index..][0..8], self.entry_addr.?, endian);519 mem.writeInt(u64, hdr_buf[index..][0..8], e_entry, endian);
511 index += 8;520 index += 8;
512521
513 // e_phoff522 // e_phoff
...@@ -748,7 +757,7 @@ const Update = struct {...@@ -748,7 +757,7 @@ const Update = struct {
748pub fn writeFile(allocator: *Allocator, module: ir.Module, file: fs.File) !Result {757pub fn writeFile(allocator: *Allocator, module: ir.Module, file: fs.File) !Result {
749 switch (module.output_mode) {758 switch (module.output_mode) {
750 .Exe => {},759 .Exe => {},
751 .Obj => return error.TODOImplementWritingObjectFiles,760 .Obj => {},
752 .Lib => return error.TODOImplementWritingLibFiles,761 .Lib => return error.TODOImplementWritingLibFiles,
753 }762 }
754 switch (module.object_format) {763 switch (module.object_format) {