| ... | ... | @@ -1,50 +1,387 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | const DW = std.dwarf; |
| 3 | 3 | |
| 4 | | pub 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. |
| 6 | pub const Instruction = union(enum) { |
| 7 | R: packed struct { |
| 8 | opcode: u7, |
| 17 | 9 | rd: u5, |
| 18 | | mode: u3, //: Mode |
| 10 | funct3: u3, |
| 19 | 11 | 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, |
| 24 | 17 | 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, |
| 33 | 19 | 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, |
| 41 | 35 | 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); |
| 44 | 381 | }; |
| 45 | 382 | |
| 46 | 383 | // zig fmt: off |
| 47 | | pub const RawRegister = enum(u8) { |
| 384 | pub const RawRegister = enum(u5) { |
| 48 | 385 | x0, x1, x2, x3, x4, x5, x6, x7, |
| 49 | 386 | x8, x9, x10, x11, x12, x13, x14, x15, |
| 50 | 387 | x16, x17, x18, x19, x20, x21, x22, x23, |
| ... | ... | @@ -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 | 396 | // 64 bit registers |
| 60 | 397 | zero, // zero |
| 61 | 398 | ra, // return address. caller saved |
| ... | ... | @@ -76,11 +413,6 @@ pub const Register = enum(u8) { |
| 76 | 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 | 416 | /// Returns the index into `callee_preserved_regs`. |
| 85 | 417 | pub fn allocIndex(self: Register) ?u4 { |
| 86 | 418 | inline for(callee_preserved_regs) |cpreg, i| { |
| ... | ... | @@ -90,7 +422,7 @@ pub const Register = enum(u8) { |
| 90 | 422 | } |
| 91 | 423 | |
| 92 | 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 | |