| ... | @@ -1,50 +1,387 @@ | ... | @@ -1,50 +1,387 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const DW = std.dwarf; | 2 | const DW = std.dwarf; |
| 3 | | 3 | |
| 4 | pub const instructions = struct { | 4 | // TODO: this is only tagged to facilitate the monstrosity. |
| 5 | pub const CallBreak = packed struct { | 5 | // Once packed structs work make it packed. |
| 6 | pub const Mode = packed enum(u12) { ecall, ebreak }; | 6 | pub const Instruction = union(enum) { |
| 7 | opcode: u7 = 0b1110011, | 7 | R: packed struct { |
| 8 | unused1: u5 = 0, | 8 | opcode: u7, |
| 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, | | |
| 17 | rd: u5, | 9 | rd: u5, |
| 18 | mode: u3, //: Mode | 10 | funct3: u3, |
| 19 | rs1: u5, | 11 | rs1: u5, |
| 20 | imm: i12, | 12 | rs2: u5, |
| 21 | }; | 13 | funct7: u7, |
| 22 | pub const Lui = packed struct { | 14 | }, |
| 23 | opcode: u7 = 0b0110111, | 15 | I: packed struct { |
| | 16 | opcode: u7, |
| 24 | rd: u5, | 17 | rd: u5, |
| 25 | imm: i20, | 18 | funct3: u3, |
| 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 | | |
| 33 | rs1: u5, | 19 | rs1: u5, |
| 34 | offset: i12, | 20 | imm0_11: u12, |
| 35 | }; | 21 | }, |
| 36 | // I-type | 22 | S: packed struct { |
| 37 | pub const Jalr = packed struct { | 23 | opcode: u7, |
| 38 | opcode: u7 = 0b1100111, | 24 | imm0_4: u5, |
| 39 | rd: u5, | 25 | funct3: u3, |
| 40 | mode: u3 = 0, | 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, |
| 41 | rs1: u5, | 35 | rs1: u5, |
| 42 | offset: i12, | 36 | rs2: u5, |
| 43 | }; | 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); |
| 44 | }; | 381 | }; |
| 45 | | 382 | |
| 46 | // zig fmt: off | 383 | // zig fmt: off |
| 47 | pub const RawRegister = enum(u8) { | 384 | pub const RawRegister = enum(u5) { |
| 48 | x0, x1, x2, x3, x4, x5, x6, x7, | 385 | x0, x1, x2, x3, x4, x5, x6, x7, |
| 49 | x8, x9, x10, x11, x12, x13, x14, x15, | 386 | x8, x9, x10, x11, x12, x13, x14, x15, |
| 50 | x16, x17, x18, x19, x20, x21, x22, x23, | 387 | x16, x17, x18, x19, x20, x21, x22, x23, |
| ... | @@ -55,7 +392,7 @@ pub const RawRegister = enum(u8) { | ... | @@ -55,7 +392,7 @@ pub const RawRegister = enum(u8) { |
| 55 | } | 392 | } |
| 56 | }; | 393 | }; |
| 57 | | 394 | |
| 58 | pub const Register = enum(u8) { | 395 | pub const Register = enum(u5) { |
| 59 | // 64 bit registers | 396 | // 64 bit registers |
| 60 | zero, // zero | 397 | zero, // zero |
| 61 | ra, // return address. caller saved | 398 | ra, // return address. caller saved |
| ... | @@ -76,11 +413,6 @@ pub const Register = enum(u8) { | ... | @@ -76,11 +413,6 @@ pub const Register = enum(u8) { |
| 76 | return null; | 413 | return null; |
| 77 | } | 414 | } |
| 78 | | 415 | |
| 79 | /// Returns the register's id. | | |
| 80 | pub fn id(self: @This()) u5 { | | |
| 81 | return @truncate(u5, @enumToInt(self)); | | |
| 82 | } | | |
| 83 | | | |
| 84 | /// Returns the index into `callee_preserved_regs`. | 416 | /// Returns the index into `callee_preserved_regs`. |
| 85 | pub fn allocIndex(self: Register) ?u4 { | 417 | pub fn allocIndex(self: Register) ?u4 { |
| 86 | inline for(callee_preserved_regs) |cpreg, i| { | 418 | inline for(callee_preserved_regs) |cpreg, i| { |
| ... | @@ -90,7 +422,7 @@ pub const Register = enum(u8) { | ... | @@ -90,7 +422,7 @@ pub const Register = enum(u8) { |
| 90 | } | 422 | } |
| 91 | | 423 | |
| 92 | pub fn dwarfLocOp(reg: Register) u8 { | 424 | pub fn dwarfLocOp(reg: Register) u8 { |
| 93 | return @enumToInt(reg) + DW.OP_reg0; | 425 | return @as(u8, @enumToInt(reg)) + DW.OP_reg0; |
| 94 | } | 426 | } |
| 95 | }; | 427 | }; |
| 96 | | 428 | |