authorgravatar for koachan@protonmail.comKoakuma <koachan@protonmail.com> 2022-04-10 14:29:06+07:00
committergravatar for koachan@protonmail.comKoakuma <koachan@protonmail.com> 2022-04-14 22:18:06+07:00
log7051970ad7e277d93c8bffee7221e3e8c45c8eab
tree9a991d03359ef1d4bab8404bbba5bc825afe4d4d
parent5e2045cbe549c9020ebbbecc763699fc4a68c818

stage2: sparcv9: implement basic instruction lowering


4 files changed, 231 insertions(+), 57 deletions(-)

src/arch/sparcv9/CodeGen.zig+2-2
...@@ -1270,12 +1270,12 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {...@@ -1270,12 +1270,12 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
12701270
1271 if (typed_value.val.castTag(.decl_ref)) |payload| {1271 if (typed_value.val.castTag(.decl_ref)) |payload| {
1272 _ = payload;1272 _ = payload;
1273 return self.fail("TODO implement lowerDeclRef", .{});1273 return self.fail("TODO implement lowerDeclRef non-mut", .{});
1274 // return self.lowerDeclRef(typed_value, payload.data);1274 // return self.lowerDeclRef(typed_value, payload.data);
1275 }1275 }
1276 if (typed_value.val.castTag(.decl_ref_mut)) |payload| {1276 if (typed_value.val.castTag(.decl_ref_mut)) |payload| {
1277 _ = payload;1277 _ = payload;
1278 return self.fail("TODO implement lowerDeclRef", .{});1278 return self.fail("TODO implement lowerDeclRef mut", .{});
1279 // return self.lowerDeclRef(typed_value, payload.data.decl);1279 // return self.lowerDeclRef(typed_value, payload.data.decl);
1280 }1280 }
1281 const target = self.target.*;1281 const target = self.target.*;
src/arch/sparcv9/Emit.zig+143-51
...@@ -2,6 +2,7 @@...@@ -2,6 +2,7 @@
2//! machine code2//! machine code
33
4const std = @import("std");4const std = @import("std");
5const Endian = std.builtin.Endian;
5const assert = std.debug.assert;6const assert = std.debug.assert;
6const link = @import("../../link.zig");7const link = @import("../../link.zig");
7const Module = @import("../../Module.zig");8const Module = @import("../../Module.zig");
...@@ -14,6 +15,8 @@ const leb128 = std.leb;...@@ -14,6 +15,8 @@ const leb128 = std.leb;
14const Emit = @This();15const Emit = @This();
15const Mir = @import("Mir.zig");16const Mir = @import("Mir.zig");
16const bits = @import("bits.zig");17const bits = @import("bits.zig");
18const Instruction = bits.Instruction;
19const Register = bits.Register;
1720
18mir: Mir,21mir: Mir,
19bin_file: *link.File,22bin_file: *link.File,
...@@ -47,7 +50,7 @@ pub fn emitMir(...@@ -47,7 +50,7 @@ pub fn emitMir(
47 .dbg_prologue_end => try emit.mirDebugPrologueEnd(),50 .dbg_prologue_end => try emit.mirDebugPrologueEnd(),
48 .dbg_epilogue_begin => try emit.mirDebugEpilogueBegin(),51 .dbg_epilogue_begin => try emit.mirDebugEpilogueBegin(),
4952
50 .add => @panic("TODO implement sparcv9 add"),53 .add => try emit.mirArithmetic3Op(inst),
5154
52 .bpcc => @panic("TODO implement sparcv9 bpcc"),55 .bpcc => @panic("TODO implement sparcv9 bpcc"),
5356
...@@ -56,22 +59,22 @@ pub fn emitMir(...@@ -56,22 +59,22 @@ pub fn emitMir(
56 .jmpl => @panic("TODO implement sparcv9 jmpl"),59 .jmpl => @panic("TODO implement sparcv9 jmpl"),
57 .jmpl_i => @panic("TODO implement sparcv9 jmpl to reg"),60 .jmpl_i => @panic("TODO implement sparcv9 jmpl to reg"),
5861
59 .@"or" => @panic("TODO implement sparcv9 or"),62 .@"or" => try emit.mirArithmetic3Op(inst),
6063
61 .nop => @panic("TODO implement sparcv9 nop"),64 .nop => try emit.mirNop(),
6265
63 .@"return" => @panic("TODO implement sparcv9 return"),66 .@"return" => try emit.mirArithmetic2Op(inst),
6467
65 .save => @panic("TODO implement sparcv9 save"),68 .save => try emit.mirArithmetic3Op(inst),
66 .restore => @panic("TODO implement sparcv9 restore"),69 .restore => try emit.mirArithmetic3Op(inst),
6770
68 .sethi => @panic("TODO implement sparcv9 sethi"),71 .sethi => @panic("TODO implement sparcv9 sethi"),
6972
70 .sllx => @panic("TODO implement sparcv9 sllx"),73 .sllx => @panic("TODO implement sparcv9 sllx"),
7174
72 .sub => @panic("TODO implement sparcv9 sub"),75 .sub => try emit.mirArithmetic3Op(inst),
7376
74 .tcc => @panic("TODO implement sparcv9 tcc"),77 .tcc => try emit.mirTrap(inst),
75 }78 }
76 }79 }
77}80}
...@@ -80,6 +83,129 @@ pub fn deinit(emit: *Emit) void {...@@ -80,6 +83,129 @@ pub fn deinit(emit: *Emit) void {
80 emit.* = undefined;83 emit.* = undefined;
81}84}
8285
86fn mirDbgArg(emit: *Emit, inst: Mir.Inst.Index) !void {
87 const tag = emit.mir.instructions.items(.tag)[inst];
88 const dbg_arg_info = emit.mir.instructions.items(.data)[inst].dbg_arg_info;
89 _ = dbg_arg_info;
90
91 switch (tag) {
92 .dbg_arg => {}, // TODO try emit.genArgDbgInfo(dbg_arg_info.air_inst, dbg_arg_info.arg_index),
93 else => unreachable,
94 }
95}
96
97fn mirDbgLine(emit: *Emit, inst: Mir.Inst.Index) !void {
98 const tag = emit.mir.instructions.items(.tag)[inst];
99 const dbg_line_column = emit.mir.instructions.items(.data)[inst].dbg_line_column;
100
101 switch (tag) {
102 .dbg_line => try emit.dbgAdvancePCAndLine(dbg_line_column.line, dbg_line_column.column),
103 else => unreachable,
104 }
105}
106
107fn mirDebugPrologueEnd(self: *Emit) !void {
108 switch (self.debug_output) {
109 .dwarf => |dbg_out| {
110 try dbg_out.dbg_line.append(DW.LNS.set_prologue_end);
111 try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column);
112 },
113 .plan9 => {},
114 .none => {},
115 }
116}
117
118fn mirDebugEpilogueBegin(self: *Emit) !void {
119 switch (self.debug_output) {
120 .dwarf => |dbg_out| {
121 try dbg_out.dbg_line.append(DW.LNS.set_epilogue_begin);
122 try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column);
123 },
124 .plan9 => {},
125 .none => {},
126 }
127}
128
129fn mirArithmetic2Op(emit: *Emit, inst: Mir.Inst.Index) !void {
130 const tag = emit.mir.instructions.items(.tag)[inst];
131 const data = emit.mir.instructions.items(.data)[inst].arithmetic_2op;
132
133 const rs1 = data.rs1;
134
135 if (data.is_imm) {
136 const imm = data.rs2_or_imm.imm;
137 switch (tag) {
138 .@"return" => try emit.writeInstruction(Instruction.@"return"(i13, rs1, imm)),
139 else => unreachable,
140 }
141 } else {
142 const rs2 = data.rs2_or_imm.rs2;
143 switch (tag) {
144 .@"return" => try emit.writeInstruction(Instruction.@"return"(Register, rs1, rs2)),
145 else => unreachable,
146 }
147 }
148}
149
150fn mirArithmetic3Op(emit: *Emit, inst: Mir.Inst.Index) !void {
151 const tag = emit.mir.instructions.items(.tag)[inst];
152 const data = emit.mir.instructions.items(.data)[inst].arithmetic_3op;
153
154 const rd = data.rd;
155 const rs1 = data.rs1;
156
157 if (data.is_imm) {
158 const imm = data.rs2_or_imm.imm;
159 switch (tag) {
160 .add => try emit.writeInstruction(Instruction.add(i13, rs1, imm, rd)),
161 .@"or" => try emit.writeInstruction(Instruction.@"or"(i13, rs1, imm, rd)),
162 .save => try emit.writeInstruction(Instruction.save(i13, rs1, imm, rd)),
163 .restore => try emit.writeInstruction(Instruction.restore(i13, rs1, imm, rd)),
164 .sub => try emit.writeInstruction(Instruction.sub(i13, rs1, imm, rd)),
165 else => unreachable,
166 }
167 } else {
168 const rs2 = data.rs2_or_imm.rs2;
169 switch (tag) {
170 .add => try emit.writeInstruction(Instruction.add(Register, rs1, rs2, rd)),
171 .@"or" => try emit.writeInstruction(Instruction.@"or"(Register, rs1, rs2, rd)),
172 .save => try emit.writeInstruction(Instruction.save(Register, rs1, rs2, rd)),
173 .restore => try emit.writeInstruction(Instruction.restore(Register, rs1, rs2, rd)),
174 .sub => try emit.writeInstruction(Instruction.sub(Register, rs1, rs2, rd)),
175 else => unreachable,
176 }
177 }
178}
179
180fn mirNop(emit: *Emit) !void {
181 try emit.writeInstruction(Instruction.nop());
182}
183
184fn mirTrap(emit: *Emit, inst: Mir.Inst.Index) !void {
185 const tag = emit.mir.instructions.items(.tag)[inst];
186 const data = emit.mir.instructions.items(.data)[inst].trap;
187
188 const cond = data.cond;
189 const ccr = data.ccr;
190 const rs1 = data.rs1;
191
192 if (data.is_imm) {
193 const imm = data.rs2_or_imm.imm;
194 switch (tag) {
195 .tcc => try emit.writeInstruction(Instruction.trap(u7, cond, ccr, rs1, imm)),
196 else => unreachable,
197 }
198 } else {
199 const rs2 = data.rs2_or_imm.rs2;
200 switch (tag) {
201 .tcc => try emit.writeInstruction(Instruction.trap(Register, cond, ccr, rs1, rs2)),
202 else => unreachable,
203 }
204 }
205}
206
207// Common helper functions
208
83fn dbgAdvancePCAndLine(self: *Emit, line: u32, column: u32) !void {209fn dbgAdvancePCAndLine(self: *Emit, line: u32, column: u32) !void {
84 const delta_line = @intCast(i32, line) - @intCast(i32, self.prev_di_line);210 const delta_line = @intCast(i32, line) - @intCast(i32, self.prev_di_line);
85 const delta_pc: usize = self.code.items.len - self.prev_di_pc;211 const delta_pc: usize = self.code.items.len - self.prev_di_pc;
...@@ -131,52 +257,18 @@ fn dbgAdvancePCAndLine(self: *Emit, line: u32, column: u32) !void {...@@ -131,52 +257,18 @@ fn dbgAdvancePCAndLine(self: *Emit, line: u32, column: u32) !void {
131 }257 }
132}258}
133259
134fn mirDbgArg(emit: *Emit, inst: Mir.Inst.Index) !void {
135 const tag = emit.mir.instructions.items(.tag)[inst];
136 const dbg_arg_info = emit.mir.instructions.items(.data)[inst].dbg_arg_info;
137 _ = dbg_arg_info;
138
139 switch (tag) {
140 .dbg_arg => {}, // TODO try emit.genArgDbgInfo(dbg_arg_info.air_inst, dbg_arg_info.arg_index),
141 else => unreachable,
142 }
143}
144
145fn mirDbgLine(emit: *Emit, inst: Mir.Inst.Index) !void {
146 const tag = emit.mir.instructions.items(.tag)[inst];
147 const dbg_line_column = emit.mir.instructions.items(.data)[inst].dbg_line_column;
148
149 switch (tag) {
150 .dbg_line => try emit.dbgAdvancePCAndLine(dbg_line_column.line, dbg_line_column.column),
151 else => unreachable,
152 }
153}
154
155fn mirDebugPrologueEnd(self: *Emit) !void {
156 switch (self.debug_output) {
157 .dwarf => |dbg_out| {
158 try dbg_out.dbg_line.append(DW.LNS.set_prologue_end);
159 try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column);
160 },
161 .plan9 => {},
162 .none => {},
163 }
164}
165
166fn mirDebugEpilogueBegin(self: *Emit) !void {
167 switch (self.debug_output) {
168 .dwarf => |dbg_out| {
169 try dbg_out.dbg_line.append(DW.LNS.set_epilogue_begin);
170 try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column);
171 },
172 .plan9 => {},
173 .none => {},
174 }
175}
176
177fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError {260fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError {
178 @setCold(true);261 @setCold(true);
179 assert(emit.err_msg == null);262 assert(emit.err_msg == null);
180 emit.err_msg = try ErrorMsg.create(emit.bin_file.allocator, emit.src_loc, format, args);263 emit.err_msg = try ErrorMsg.create(emit.bin_file.allocator, emit.src_loc, format, args);
181 return error.EmitFail;264 return error.EmitFail;
182}265}
266
267fn writeInstruction(emit: *Emit, instruction: Instruction) !void {
268 // SPARCv9 instructions are always arranged in BE regardless of the
269 // endianness mode the CPU is running in.
270 // This is to ease porting in case someone wants to do a LE SPARCv9 backend.
271 const endian = Endian.Big;
272
273 std.mem.writeInt(u32, try emit.code.addManyAsArray(4), instruction.toU32(), endian);
274}
src/arch/sparcv9/Mir.zig+13-2
...@@ -167,7 +167,7 @@ pub const Inst = struct {...@@ -167,7 +167,7 @@ pub const Inst = struct {
167 /// Branch with prediction.167 /// Branch with prediction.
168 /// Used by e.g. bpcc168 /// Used by e.g. bpcc
169 branch_predict: struct {169 branch_predict: struct {
170 annul: bool,170 annul: bool = false,
171 pt: bool = true,171 pt: bool = true,
172 ccr: Instruction.CCR,172 ccr: Instruction.CCR,
173 cond: Instruction.Condition,173 cond: Instruction.Condition,
...@@ -215,10 +215,21 @@ pub const Inst = struct {...@@ -215,10 +215,21 @@ pub const Inst = struct {
215 rs1: Register = .g0,215 rs1: Register = .g0,
216 rs2_or_imm: union {216 rs2_or_imm: union {
217 rs2: Register,217 rs2: Register,
218 imm: u8,218 imm: u7,
219 },219 },
220 },220 },
221 };221 };
222
223 // Make sure we don't accidentally make instructions bigger than expected.
224 // Note that in Debug builds, Zig is allowed to insert a secret field for safety checks.
225 comptime {
226 if (builtin.mode != .Debug) {
227 // TODO clean up the definition of Data before enabling this.
228 // I'll do that after the PoC backend can produce usable binaries.
229
230 // assert(@sizeOf(Data) == 8);
231 }
232 }
222};233};
223234
224pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void {235pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void {
src/arch/sparcv9/bits.zig+73-2
...@@ -546,6 +546,9 @@ pub const Instruction = union(enum) {...@@ -546,6 +546,9 @@ pub const Instruction = union(enum) {
546 };546 };
547 }547 }
548548
549 // SPARCv9 Instruction formats.
550 // See section 6.2 of the SPARCv9 ISA manual.
551
549 fn format1(disp: i32) Instruction {552 fn format1(disp: i32) Instruction {
550 const udisp = @bitCast(u32, disp);553 const udisp = @bitCast(u32, disp);
551554
...@@ -561,7 +564,7 @@ pub const Instruction = union(enum) {...@@ -561,7 +564,7 @@ pub const Instruction = union(enum) {
561 };564 };
562 }565 }
563566
564 fn format2a(op2: u3, rd: Register, imm: u22) Instruction {567 fn format2a(op2: u3, imm: u22, rd: Register) Instruction {
565 return Instruction{568 return Instruction{
566 .format_2a = .{569 .format_2a = .{
567 .rd = rd.enc(),570 .rd = rd.enc(),
...@@ -956,6 +959,74 @@ pub const Instruction = union(enum) {...@@ -956,6 +959,74 @@ pub const Instruction = union(enum) {
956 },959 },
957 };960 };
958 }961 }
962
963 // SPARCv9 Instruction definition.
964 // See appendix A of the SPARCv9 ISA manual.
965
966 pub fn add(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
967 return switch(s2) {
968 Register => format3a(0b10, 0b00_0000, rs1, rs2, rd),
969 i13 => format3b(0b10, 0b00_0000, rs1, rs2, rd),
970 else => unreachable,
971 };
972 }
973
974 pub fn @"or"(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
975 return switch(s2) {
976 Register => format3a(0b10, 0b00_0010, rs1, rs2, rd),
977 i13 => format3b(0b10, 0b00_0010, rs1, rs2, rd),
978 else => unreachable,
979 };
980 }
981
982 pub fn nop() Instruction {
983 return sethi(0, .g0);
984 }
985
986 pub fn @"return"(comptime s2: type, rs1: Register, rs2: s2) Instruction {
987 return switch(s2) {
988 Register => format3c(0b10, 0b11_1001, rs1, rs2),
989 i13 => format3d(0b10, 0b11_1001, rs1, rs2),
990 else => unreachable,
991 };
992 }
993
994 pub fn save(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
995 return switch(s2) {
996 Register => format3a(0b10, 0b11_1100, rs1, rs2, rd),
997 i13 => format3b(0b10, 0b11_1100, rs1, rs2, rd),
998 else => unreachable,
999 };
1000 }
1001
1002 pub fn restore(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
1003 return switch(s2) {
1004 Register => format3a(0b10, 0b11_1101, rs1, rs2, rd),
1005 i13 => format3b(0b10, 0b11_1101, rs1, rs2, rd),
1006 else => unreachable,
1007 };
1008 }
1009
1010 pub fn sethi(imm: u22, rd: Register) Instruction {
1011 return format2a(0b100, imm, rd);
1012 }
1013
1014 pub fn sub(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
1015 return switch(s2) {
1016 Register => format3a(0b10, 0b00_0100, rs1, rs2, rd),
1017 i13 => format3b(0b10, 0b00_0100, rs1, rs2, rd),
1018 else => unreachable,
1019 };
1020 }
1021
1022 pub fn trap(comptime s2: type, cond: Condition, ccr: CCR, rs1: Register, rs2: s2) Instruction {
1023 // Tcc instructions abuse the rd field to store the conditionals.
1024 return switch(s2) {
1025 Register => format4a(0b11_1010, ccr, rs1, rs2, @intToEnum(Register, cond)),
1026 u7 => format4e(0b00_0100, ccr, rs1, @intToEnum(Register, cond), rs2),
1027 else => unreachable,
1028 };
1029 }
959};1030};
9601031
961test "Serialize formats" {1032test "Serialize formats" {
...@@ -973,7 +1044,7 @@ test "Serialize formats" {...@@ -973,7 +1044,7 @@ test "Serialize formats" {
973 .expected = 0b01_000000000000000000000000000001,1044 .expected = 0b01_000000000000000000000000000001,
974 },1045 },
975 .{1046 .{
976 .inst = Instruction.format2a(4, .g0, 0),1047 .inst = Instruction.format2a(4, 0, .g0),
977 .expected = 0b00_00000_100_0000000000000000000000,1048 .expected = 0b00_00000_100_0000000000000000000000,
978 },1049 },
979 .{1050 .{