authorgravatar for eleanor@eleanor-nb.comEleanor Bartle <eleanor@eleanor-nb.com> 2020-08-18 14:30:00+10:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-08-18 00:30:00-04:00
logfa8935426b425110a89c6a2008013c500b7a3a79
treec36c70a2a595b33e2b393e523fdbe4aae7a80b22
parent3cc1f8b62477c37c938863cba0ec0409e4c9c0be
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Cleaned up RISC-V instruction creation, added 32-bit immediates (#6077)

* Implemented all R-type arithmetic/logical instructions * Implemented all I-type arithmetic/logical instructions * Implemented all load and store instructions * Implemented all of RV64I except FENCE

2 files changed, 387 insertions(+), 99 deletions(-)

src-self-hosted/codegen.zig+12-56
......@@ -1113,11 +1113,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
11131113 try self.code.append(0xcc); // int3
11141114 },
11151115 .riscv64 => {
1116 const full = @bitCast(u32, instructions.CallBreak{
1117 .mode = @enumToInt(instructions.CallBreak.Mode.ebreak),
1118 });
1119
1120 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), full);
1116 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ebreak.toU32());
11211117 },
11221118 else => return self.fail(src, "TODO implement @breakpoint() for {}", .{self.target.cpu.arch}),
11231119 }
......@@ -1193,12 +1189,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
11931189 const got_addr = @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes);
11941190
11951191 try self.genSetReg(inst.base.src, .ra, .{ .memory = got_addr });
1196 const jalr = instructions.Jalr{
1197 .rd = Register.ra.id(),
1198 .rs1 = Register.ra.id(),
1199 .offset = 0,
1200 };
1201 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), @bitCast(u32, jalr));
1192 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.jalr(.ra, 0, .ra).toU32());
12021193 } else {
12031194 return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});
12041195 }
......@@ -1255,12 +1246,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
12551246 try self.exitlude_jump_relocs.append(self.gpa, self.code.items.len - 4);
12561247 },
12571248 .riscv64 => {
1258 const jalr = instructions.Jalr{
1259 .rd = Register.zero.id(),
1260 .rs1 = Register.ra.id(),
1261 .offset = 0,
1262 };
1263 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), @bitCast(u32, jalr));
1249 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.jalr(.zero, 0, .ra).toU32());
12641250 },
12651251 else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}),
12661252 }
......@@ -1512,11 +1498,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
15121498 }
15131499
15141500 if (mem.eql(u8, inst.asm_source, "ecall")) {
1515 const full = @bitCast(u32, instructions.CallBreak{
1516 .mode = @enumToInt(instructions.CallBreak.Mode.ecall),
1517 });
1518
1519 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), full);
1501 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ecall.toU32());
15201502 } else {
15211503 return self.fail(inst.base.src, "TODO implement support for more riscv64 assembly instructions", .{});
15221504 }
......@@ -1723,36 +1705,17 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
17231705 .immediate => |unsigned_x| {
17241706 const x = @bitCast(i64, unsigned_x);
17251707 if (math.minInt(i12) <= x and x <= math.maxInt(i12)) {
1726 const instruction = @bitCast(u32, instructions.Addi{
1727 .mode = @enumToInt(instructions.Addi.Mode.addi),
1728 .imm = @truncate(i12, x),
1729 .rs1 = Register.zero.id(),
1730 .rd = reg.id(),
1731 });
1732
1733 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), instruction);
1708 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.addi(reg, .zero, @truncate(i12, x)).toU32());
17341709 return;
17351710 }
17361711 if (math.minInt(i32) <= x and x <= math.maxInt(i32)) {
1737 const split = @bitCast(packed struct {
1738 low12: i12,
1739 up20: i20,
1740 }, @truncate(i32, x));
1741 if (split.low12 < 0) return self.fail(src, "TODO support riscv64 genSetReg i32 immediates with 12th bit set to 1", .{});
1742
1743 const lui = @bitCast(u32, instructions.Lui{
1744 .imm = split.up20,
1745 .rd = reg.id(),
1746 });
1747 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), lui);
1712 const lo12 = @truncate(i12, x);
1713 const carry: i32 = if (lo12 < 0) 1 else 0;
1714 const hi20 = @truncate(i20, (x >> 12) +% carry);
17481715
1749 const addi = @bitCast(u32, instructions.Addi{
1750 .mode = @enumToInt(instructions.Addi.Mode.addi),
1751 .imm = @truncate(i12, split.low12),
1752 .rs1 = reg.id(),
1753 .rd = reg.id(),
1754 });
1755 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), addi);
1716 // TODO: add test case for 32-bit immediate
1717 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.lui(reg, hi20).toU32());
1718 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.addi(reg, reg, lo12).toU32());
17561719 return;
17571720 }
17581721 // li rd, immediate
......@@ -1764,14 +1727,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
17641727 // If the type is a pointer, it means the pointer address is at this memory location.
17651728 try self.genSetReg(src, reg, .{ .immediate = addr });
17661729
1767 const ld = @bitCast(u32, instructions.Load{
1768 .mode = @enumToInt(instructions.Load.Mode.ld),
1769 .rs1 = reg.id(),
1770 .rd = reg.id(),
1771 .offset = 0,
1772 });
1773
1774 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), ld);
1730 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ld(reg, 0, reg).toU32());
17751731 // LOAD imm=[i12 offset = 0], rs1 =
17761732
17771733 // return self.fail("TODO implement genSetReg memory for riscv64");
src-self-hosted/codegen/riscv64.zig+375-43
......@@ -1,50 +1,387 @@
11const std = @import("std");
22const DW = std.dwarf;
33
4pub const instructions = struct {
5 pub const CallBreak = packed struct {
6 pub const Mode = packed enum(u12) { ecall, ebreak };
7 opcode: u7 = 0b1110011,
8 unused1: u5 = 0,
9 unused2: u3 = 0,
10 unused3: u5 = 0,
11 mode: u12, //: Mode
12 };
13 // I-type
14 pub const Addi = packed struct {
15 pub const Mode = packed enum(u3) { addi = 0b000, slti = 0b010, sltiu = 0b011, xori = 0b100, ori = 0b110, andi = 0b111 };
16 opcode: u7 = 0b0010011,
4// TODO: this is only tagged to facilitate the monstrosity.
5// Once packed structs work make it packed.
6pub const Instruction = union(enum) {
7 R: packed struct {
8 opcode: u7,
179 rd: u5,
18 mode: u3, //: Mode
10 funct3: u3,
1911 rs1: u5,
20 imm: i12,
21 };
22 pub const Lui = packed struct {
23 opcode: u7 = 0b0110111,
12 rs2: u5,
13 funct7: u7,
14 },
15 I: packed struct {
16 opcode: u7,
2417 rd: u5,
25 imm: i20,
26 };
27 // I_type
28 pub const Load = packed struct {
29 pub const Mode = packed enum(u3) { ld = 0b011, lwu = 0b110 };
30 opcode: u7 = 0b0000011,
31 rd: u5,
32 mode: u3, //: Mode
18 funct3: u3,
3319 rs1: u5,
34 offset: i12,
35 };
36 // I-type
37 pub const Jalr = packed struct {
38 opcode: u7 = 0b1100111,
39 rd: u5,
40 mode: u3 = 0,
20 imm0_11: u12,
21 },
22 S: packed struct {
23 opcode: u7,
24 imm0_4: u5,
25 funct3: u3,
26 rs1: u5,
27 rs2: u5,
28 imm5_11: u7,
29 },
30 B: packed struct {
31 opcode: u7,
32 imm11: u1,
33 imm1_4: u4,
34 funct3: u3,
4135 rs1: u5,
42 offset: i12,
43 };
36 rs2: u5,
37 imm5_10: u6,
38 imm12: u1,
39 },
40 U: packed struct {
41 opcode: u7,
42 rd: u5,
43 imm12_31: u20,
44 },
45 J: packed struct {
46 opcode: u7,
47 rd: u5,
48 imm12_19: u8,
49 imm11: u1,
50 imm1_10: u10,
51 imm20: u1,
52 },
53
54 // TODO: once packed structs work we can remove this monstrosity.
55 pub fn toU32(self: Instruction) u32 {
56 return switch (self) {
57 .R => |v| @bitCast(u32, v),
58 .I => |v| @bitCast(u32, v),
59 .S => |v| @bitCast(u32, v),
60 .B => |v| @intCast(u32, v.opcode) + (@intCast(u32, v.imm11) << 7) + (@intCast(u32, v.imm1_4) << 8) + (@intCast(u32, v.funct3) << 12) + (@intCast(u32, v.rs1) << 15) + (@intCast(u32, v.rs2) << 20) + (@intCast(u32, v.imm5_10) << 25) + (@intCast(u32, v.imm12) << 31),
61 .U => |v| @bitCast(u32, v),
62 .J => |v| @bitCast(u32, v),
63 };
64 }
65
66 fn rType(op: u7, fn3: u3, fn7: u7, rd: Register, r1: Register, r2: Register) Instruction {
67 return Instruction{
68 .R = .{
69 .opcode = op,
70 .funct3 = fn3,
71 .funct7 = fn7,
72 .rd = @enumToInt(rd),
73 .rs1 = @enumToInt(r1),
74 .rs2 = @enumToInt(r2),
75 },
76 };
77 }
78
79 // RISC-V is all signed all the time -- convert immediates to unsigned for processing
80 fn iType(op: u7, fn3: u3, rd: Register, r1: Register, imm: i12) Instruction {
81 const umm = @bitCast(u12, imm);
82
83 return Instruction{
84 .I = .{
85 .opcode = op,
86 .funct3 = fn3,
87 .rd = @enumToInt(rd),
88 .rs1 = @enumToInt(r1),
89 .imm0_11 = umm,
90 },
91 };
92 }
93
94 fn sType(op: u7, fn3: u3, r1: Register, r2: Register, imm: i12) Instruction {
95 const umm = @bitCast(u12, imm);
96
97 return Instruction{
98 .S = .{
99 .opcode = op,
100 .funct3 = fn3,
101 .rs1 = @enumToInt(r1),
102 .rs2 = @enumToInt(r2),
103 .imm0_4 = @truncate(u5, umm),
104 .imm5_11 = @truncate(u7, umm >> 5),
105 },
106 };
107 }
108
109 // Use significance value rather than bit value, same for J-type
110 // -- less burden on callsite, bonus semantic checking
111 fn bType(op: u7, fn3: u3, r1: Register, r2: Register, imm: i13) Instruction {
112 const umm = @bitCast(u13, imm);
113 if (umm % 2 != 0) @panic("Internal error: misaligned branch target");
114
115 return Instruction{
116 .B = .{
117 .opcode = op,
118 .funct3 = fn3,
119 .rs1 = @enumToInt(r1),
120 .rs2 = @enumToInt(r2),
121 .imm1_4 = @truncate(u4, umm >> 1),
122 .imm5_10 = @truncate(u6, umm >> 5),
123 .imm11 = @truncate(u1, umm >> 11),
124 .imm12 = @truncate(u1, umm >> 12),
125 },
126 };
127 }
128
129 // We have to extract the 20 bits anyway -- let's not make it more painful
130 fn uType(op: u7, rd: Register, imm: i20) Instruction {
131 const umm = @bitCast(u20, imm);
132
133 return Instruction{
134 .U = .{
135 .opcode = op,
136 .rd = @enumToInt(rd),
137 .imm12_31 = umm,
138 },
139 };
140 }
141
142 fn jType(op: u7, rd: Register, imm: i21) Instruction {
143 const umm = @bitcast(u21, imm);
144 if (umm % 2 != 0) @panic("Internal error: misaligned jump target");
145
146 return Instruction{
147 .J = .{
148 .opcode = op,
149 .rd = @enumToInt(rd),
150 .imm1_10 = @truncate(u10, umm >> 1),
151 .imm11 = @truncate(u1, umm >> 1),
152 .imm12_19 = @truncate(u8, umm >> 12),
153 .imm20 = @truncate(u1, umm >> 20),
154 },
155 };
156 }
157
158 // The meat and potatoes. Arguments are in the order in which they would appear in assembly code.
159
160 // Arithmetic/Logical, Register-Register
161
162 pub fn add(rd: Register, r1: Register, r2: Register) Instruction {
163 return rType(0b0110011, 0b000, 0b0000000, rd, r1, r2);
164 }
165
166 pub fn sub(rd: Register, r1: Register, r2: Register) Instruction {
167 return rType(0b0110011, 0b000, 0b0100000, rd, r1, r2);
168 }
169
170 pub fn @"and"(rd: Register, r1: Register, r2: Register) Instruction {
171 return rType(0b0110011, 0b111, 0b0000000, rd, r1, r2);
172 }
173
174 pub fn @"or"(rd: Register, r1: Register, r2: Register) Instruction {
175 return rType(0b0110011, 0b110, 0b0000000, rd, r1, r2);
176 }
177
178 pub fn xor(rd: Register, r1: Register, r2: Register) Instruction {
179 return rType(0b0110011, 0b100, 0b0000000, rd, r1, r2);
180 }
181
182 pub fn sll(rd: Register, r1: Register, r2: Register) Instruction {
183 return rType(0b0110011, 0b001, 0b0000000, rd, r1, r2);
184 }
185
186 pub fn srl(rd: Register, r1: Register, r2: Register) Instruction {
187 return rType(0b0110011, 0b101, 0b0000000, rd, r1, r2);
188 }
189
190 pub fn sra(rd: Register, r1: Register, r2: Register) Instruction {
191 return rType(0b0110011, 0b101, 0b0100000, rd, r1, r2);
192 }
193
194 pub fn slt(rd: Register, r1: Register, r2: Register) Instruction {
195 return rType(0b0110011, 0b010, 0b0000000, rd, r1, r2);
196 }
197
198 pub fn sltu(rd: Register, r1: Register, r2: Register) Instruction {
199 return rType(0b0110011, 0b011, 0b0000000, rd, r1, r2);
200 }
201
202 // Arithmetic/Logical, Register-Register (32-bit)
203
204 pub fn addw(rd: Register, r1: Register, r2: Register) Instruction {
205 return rType(0b0111011, 0b000, rd, r1, r2);
206 }
207
208 pub fn subw(rd: Register, r1: Register, r2: Register) Instruction {
209 return rType(0b0111011, 0b000, 0b0100000, rd, r1, r2);
210 }
211
212 pub fn sllw(rd: Register, r1: Register, r2: Register) Instruction {
213 return rType(0b0111011, 0b001, 0b0000000, rd, r1, r2);
214 }
215
216 pub fn srlw(rd: Register, r1: Register, r2: Register) Instruction {
217 return rType(0b0111011, 0b101, 0b0000000, rd, r1, r2);
218 }
219
220 pub fn sraw(rd: Register, r1: Register, r2: Register) Instruction {
221 return rType(0b0111011, 0b101, 0b0100000, rd, r1, r2);
222 }
223
224 // Arithmetic/Logical, Register-Immediate
225
226 pub fn addi(rd: Register, r1: Register, imm: i12) Instruction {
227 return iType(0b0010011, 0b000, rd, r1, imm);
228 }
229
230 pub fn andi(rd: Register, r1: Register, imm: i12) Instruction {
231 return iType(0b0010011, 0b111, rd, r1, imm);
232 }
233
234 pub fn ori(rd: Register, r1: Register, imm: i12) Instruction {
235 return iType(0b0010011, 0b110, rd, r1, imm);
236 }
237
238 pub fn xori(rd: Register, r1: Register, imm: i12) Instruction {
239 return iType(0b0010011, 0b100, rd, r1, imm);
240 }
241
242 pub fn slli(rd: Register, r1: Register, shamt: u6) Instruction {
243 return iType(0b0010011, 0b001, rd, r1, shamt);
244 }
245
246 pub fn srli(rd: Register, r1: Register, shamt: u6) Instruction {
247 return iType(0b0010011, 0b101, rd, r1, shamt);
248 }
249
250 pub fn srai(rd: Register, r1: Register, shamt: u6) Instruction {
251 return iType(0b0010011, 0b101, rd, r1, (1 << 10) + shamt);
252 }
253
254 pub fn slti(rd: Register, r1: Register, imm: i12) Instruction {
255 return iType(0b0010011, 0b010, rd, r1, imm);
256 }
257
258 pub fn sltiu(rd: Register, r1: Register, imm: u12) Instruction {
259 return iType(0b0010011, 0b011, rd, r1, @bitCast(i12, imm));
260 }
261
262 // Arithmetic/Logical, Register-Immediate (32-bit)
263
264 pub fn addiw(rd: Register, r1: Register, imm: i12) Instruction {
265 return iType(0b0011011, 0b000, rd, r1, imm);
266 }
267
268 pub fn slliw(rd: Register, r1: Register, shamt: u5) Instruction {
269 return iType(0b0011011, 0b001, rd, r1, shamt);
270 }
271
272 pub fn srliw(rd: Register, r1: Register, shamt: u5) Instruction {
273 return iType(0b0011011, 0b101, rd, r1, shamt);
274 }
275
276 pub fn sraiw(rd: Register, r1: Register, shamt: u5) Instruction {
277 return iType(0b0011011, 0b101, rd, r1, (1 << 10) + shamt);
278 }
279
280 // Upper Immediate
281
282 pub fn lui(rd: Register, imm: i20) Instruction {
283 return uType(0b0110111, rd, imm);
284 }
285
286 pub fn auipc(rd: Register, imm: i20) Instruction {
287 return uType(0b0010111, rd, imm);
288 }
289
290 // Load
291
292 pub fn ld(rd: Register, offset: i12, base: Register) Instruction {
293 return iType(0b0000011, 0b011, rd, base, offset);
294 }
295
296 pub fn lw(rd: Register, offset: i12, base: Register) Instruction {
297 return iType(0b0000011, 0b010, rd, base, offset);
298 }
299
300 pub fn lwu(rd: Register, offset: i12, base: Register) Instruction {
301 return iType(0b0000011, 0b110, rd, base, offset);
302 }
303
304 pub fn lh(rd: Register, offset: i12, base: Register) Instruction {
305 return iType(0b0000011, 0b001, rd, base, offset);
306 }
307
308 pub fn lhu(rd: Register, offset: i12, base: Register) Instruction {
309 return iType(0b0000011, 0b101, rd, base, offset);
310 }
311
312 pub fn lb(rd: Register, offset: i12, base: Register) Instruction {
313 return iType(0b0000011, 0b000, rd, base, offset);
314 }
315
316 pub fn lbu(rd: Register, offset: i12, base: Register) Instruction {
317 return iType(0b0000011, 0b100, rd, base, offset);
318 }
319
320 // Store
321
322 pub fn sd(rs: Register, offset: i12, base: Register) Instruction {
323 return sType(0b0100011, 0b011, base, rs, offset);
324 }
325
326 pub fn sw(rs: Register, offset: i12, base: Register) Instruction {
327 return sType(0b0100011, 0b010, base, rs, offset);
328 }
329
330 pub fn sh(rs: Register, offset: i12, base: Register) Instruction {
331 return sType(0b0100011, 0b001, base, rs, offset);
332 }
333
334 pub fn sb(rs: Register, offset: i12, base: Register) Instruction {
335 return sType(0b0100011, 0b000, base, rs, offset);
336 }
337
338 // Fence
339 // TODO: implement fence
340
341 // Branch
342
343 pub fn beq(r1: Register, r2: Register, offset: u13) Instruction {
344 return bType(0b1100011, 0b000, r1, r2, offset);
345 }
346
347 pub fn bne(r1: Register, r2: Register, offset: u13) Instruction {
348 return bType(0b1100011, 0b001, r1, r2, offset);
349 }
350
351 pub fn blt(r1: Register, r2: Register, offset: u13) Instruction {
352 return bType(0b1100011, 0b100, r1, r2, offset);
353 }
354
355 pub fn bge(r1: Register, r2: Register, offset: u13) Instruction {
356 return bType(0b1100011, 0b101, r1, r2, offset);
357 }
358
359 pub fn bltu(r1: Register, r2: Register, offset: u13) Instruction {
360 return bType(0b1100011, 0b110, r1, r2, offset);
361 }
362
363 pub fn bgeu(r1: Register, r2: Register, offset: u13) Instruction {
364 return bType(0b1100011, 0b111, r1, r2, offset);
365 }
366
367 // Jump
368
369 pub fn jal(link: Register, offset: i21) Instruction {
370 return jType(0b1101111, link, offset);
371 }
372
373 pub fn jalr(link: Register, offset: i12, base: Register) Instruction {
374 return iType(0b1100111, 0b000, link, base, offset);
375 }
376
377 // System
378
379 pub const ecall = iType(0b1110011, 0b000, .zero, .zero, 0x000);
380 pub const ebreak = iType(0b1110011, 0b000, .zero, .zero, 0x001);
44381};
45382
46383// zig fmt: off
47pub const RawRegister = enum(u8) {
384pub const RawRegister = enum(u5) {
48385 x0, x1, x2, x3, x4, x5, x6, x7,
49386 x8, x9, x10, x11, x12, x13, x14, x15,
50387 x16, x17, x18, x19, x20, x21, x22, x23,
......@@ -55,7 +392,7 @@ pub const RawRegister = enum(u8) {
55392 }
56393};
57394
58pub const Register = enum(u8) {
395pub const Register = enum(u5) {
59396 // 64 bit registers
60397 zero, // zero
61398 ra, // return address. caller saved
......@@ -76,11 +413,6 @@ pub const Register = enum(u8) {
76413 return null;
77414 }
78415
79 /// Returns the register's id.
80 pub fn id(self: @This()) u5 {
81 return @truncate(u5, @enumToInt(self));
82 }
83
84416 /// Returns the index into `callee_preserved_regs`.
85417 pub fn allocIndex(self: Register) ?u4 {
86418 inline for(callee_preserved_regs) |cpreg, i| {
......@@ -90,7 +422,7 @@ pub const Register = enum(u8) {
90422 }
91423
92424 pub fn dwarfLocOp(reg: Register) u8 {
93 return @enumToInt(reg) + DW.OP_reg0;
425 return @as(u8, @enumToInt(reg)) + DW.OP_reg0;
94426 }
95427};
96428