aboutsummaryrefslogtreecommitdiff
path: root/src/codegen/riscv64.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2020-09-21 18:38:55 -0700
committerAndrew Kelley <andrew@ziglang.org>2020-09-21 18:38:55 -0700
commit528832bd3a2e7b686ee84aef5887df740a6114db (patch)
tree90ccff9faa2ba2604c8538aeec0a147a4b01148c /src/codegen/riscv64.zig
parentb9f61d401502f5d221e72c0d0e3bf448b11dcd68 (diff)
downloadzig-528832bd3a2e7b686ee84aef5887df740a6114db.tar.gz
zig-528832bd3a2e7b686ee84aef5887df740a6114db.zip
rename src-self-hosted/ to src/
Diffstat (limited to 'src/codegen/riscv64.zig')
-rw-r--r--src/codegen/riscv64.zig433
1 files changed, 433 insertions, 0 deletions
diff --git a/src/codegen/riscv64.zig b/src/codegen/riscv64.zig
new file mode 100644
index 0000000000..96b9c58f9c
--- /dev/null
+++ b/src/codegen/riscv64.zig
@@ -0,0 +1,433 @@
+const std = @import("std");
+const DW = std.dwarf;
+
+// TODO: this is only tagged to facilitate the monstrosity.
+// Once packed structs work make it packed.
+pub const Instruction = union(enum) {
+ R: packed struct {
+ opcode: u7,
+ rd: u5,
+ funct3: u3,
+ rs1: u5,
+ rs2: u5,
+ funct7: u7,
+ },
+ I: packed struct {
+ opcode: u7,
+ rd: u5,
+ funct3: u3,
+ rs1: u5,
+ imm0_11: u12,
+ },
+ S: packed struct {
+ opcode: u7,
+ imm0_4: u5,
+ funct3: u3,
+ rs1: u5,
+ rs2: u5,
+ imm5_11: u7,
+ },
+ B: packed struct {
+ opcode: u7,
+ imm11: u1,
+ imm1_4: u4,
+ funct3: u3,
+ rs1: u5,
+ rs2: u5,
+ imm5_10: u6,
+ imm12: u1,
+ },
+ U: packed struct {
+ opcode: u7,
+ rd: u5,
+ imm12_31: u20,
+ },
+ J: packed struct {
+ opcode: u7,
+ rd: u5,
+ imm12_19: u8,
+ imm11: u1,
+ imm1_10: u10,
+ imm20: u1,
+ },
+
+ // TODO: once packed structs work we can remove this monstrosity.
+ pub fn toU32(self: Instruction) u32 {
+ return switch (self) {
+ .R => |v| @bitCast(u32, v),
+ .I => |v| @bitCast(u32, v),
+ .S => |v| @bitCast(u32, v),
+ .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),
+ .U => |v| @bitCast(u32, v),
+ .J => |v| @bitCast(u32, v),
+ };
+ }
+
+ fn rType(op: u7, fn3: u3, fn7: u7, rd: Register, r1: Register, r2: Register) Instruction {
+ return Instruction{
+ .R = .{
+ .opcode = op,
+ .funct3 = fn3,
+ .funct7 = fn7,
+ .rd = @enumToInt(rd),
+ .rs1 = @enumToInt(r1),
+ .rs2 = @enumToInt(r2),
+ },
+ };
+ }
+
+ // RISC-V is all signed all the time -- convert immediates to unsigned for processing
+ fn iType(op: u7, fn3: u3, rd: Register, r1: Register, imm: i12) Instruction {
+ const umm = @bitCast(u12, imm);
+
+ return Instruction{
+ .I = .{
+ .opcode = op,
+ .funct3 = fn3,
+ .rd = @enumToInt(rd),
+ .rs1 = @enumToInt(r1),
+ .imm0_11 = umm,
+ },
+ };
+ }
+
+ fn sType(op: u7, fn3: u3, r1: Register, r2: Register, imm: i12) Instruction {
+ const umm = @bitCast(u12, imm);
+
+ return Instruction{
+ .S = .{
+ .opcode = op,
+ .funct3 = fn3,
+ .rs1 = @enumToInt(r1),
+ .rs2 = @enumToInt(r2),
+ .imm0_4 = @truncate(u5, umm),
+ .imm5_11 = @truncate(u7, umm >> 5),
+ },
+ };
+ }
+
+ // Use significance value rather than bit value, same for J-type
+ // -- less burden on callsite, bonus semantic checking
+ fn bType(op: u7, fn3: u3, r1: Register, r2: Register, imm: i13) Instruction {
+ const umm = @bitCast(u13, imm);
+ if (umm % 2 != 0) @panic("Internal error: misaligned branch target");
+
+ return Instruction{
+ .B = .{
+ .opcode = op,
+ .funct3 = fn3,
+ .rs1 = @enumToInt(r1),
+ .rs2 = @enumToInt(r2),
+ .imm1_4 = @truncate(u4, umm >> 1),
+ .imm5_10 = @truncate(u6, umm >> 5),
+ .imm11 = @truncate(u1, umm >> 11),
+ .imm12 = @truncate(u1, umm >> 12),
+ },
+ };
+ }
+
+ // We have to extract the 20 bits anyway -- let's not make it more painful
+ fn uType(op: u7, rd: Register, imm: i20) Instruction {
+ const umm = @bitCast(u20, imm);
+
+ return Instruction{
+ .U = .{
+ .opcode = op,
+ .rd = @enumToInt(rd),
+ .imm12_31 = umm,
+ },
+ };
+ }
+
+ fn jType(op: u7, rd: Register, imm: i21) Instruction {
+ const umm = @bitcast(u21, imm);
+ if (umm % 2 != 0) @panic("Internal error: misaligned jump target");
+
+ return Instruction{
+ .J = .{
+ .opcode = op,
+ .rd = @enumToInt(rd),
+ .imm1_10 = @truncate(u10, umm >> 1),
+ .imm11 = @truncate(u1, umm >> 1),
+ .imm12_19 = @truncate(u8, umm >> 12),
+ .imm20 = @truncate(u1, umm >> 20),
+ },
+ };
+ }
+
+ // The meat and potatoes. Arguments are in the order in which they would appear in assembly code.
+
+ // Arithmetic/Logical, Register-Register
+
+ pub fn add(rd: Register, r1: Register, r2: Register) Instruction {
+ return rType(0b0110011, 0b000, 0b0000000, rd, r1, r2);
+ }
+
+ pub fn sub(rd: Register, r1: Register, r2: Register) Instruction {
+ return rType(0b0110011, 0b000, 0b0100000, rd, r1, r2);
+ }
+
+ pub fn @"and"(rd: Register, r1: Register, r2: Register) Instruction {
+ return rType(0b0110011, 0b111, 0b0000000, rd, r1, r2);
+ }
+
+ pub fn @"or"(rd: Register, r1: Register, r2: Register) Instruction {
+ return rType(0b0110011, 0b110, 0b0000000, rd, r1, r2);
+ }
+
+ pub fn xor(rd: Register, r1: Register, r2: Register) Instruction {
+ return rType(0b0110011, 0b100, 0b0000000, rd, r1, r2);
+ }
+
+ pub fn sll(rd: Register, r1: Register, r2: Register) Instruction {
+ return rType(0b0110011, 0b001, 0b0000000, rd, r1, r2);
+ }
+
+ pub fn srl(rd: Register, r1: Register, r2: Register) Instruction {
+ return rType(0b0110011, 0b101, 0b0000000, rd, r1, r2);
+ }
+
+ pub fn sra(rd: Register, r1: Register, r2: Register) Instruction {
+ return rType(0b0110011, 0b101, 0b0100000, rd, r1, r2);
+ }
+
+ pub fn slt(rd: Register, r1: Register, r2: Register) Instruction {
+ return rType(0b0110011, 0b010, 0b0000000, rd, r1, r2);
+ }
+
+ pub fn sltu(rd: Register, r1: Register, r2: Register) Instruction {
+ return rType(0b0110011, 0b011, 0b0000000, rd, r1, r2);
+ }
+
+ // Arithmetic/Logical, Register-Register (32-bit)
+
+ pub fn addw(rd: Register, r1: Register, r2: Register) Instruction {
+ return rType(0b0111011, 0b000, rd, r1, r2);
+ }
+
+ pub fn subw(rd: Register, r1: Register, r2: Register) Instruction {
+ return rType(0b0111011, 0b000, 0b0100000, rd, r1, r2);
+ }
+
+ pub fn sllw(rd: Register, r1: Register, r2: Register) Instruction {
+ return rType(0b0111011, 0b001, 0b0000000, rd, r1, r2);
+ }
+
+ pub fn srlw(rd: Register, r1: Register, r2: Register) Instruction {
+ return rType(0b0111011, 0b101, 0b0000000, rd, r1, r2);
+ }
+
+ pub fn sraw(rd: Register, r1: Register, r2: Register) Instruction {
+ return rType(0b0111011, 0b101, 0b0100000, rd, r1, r2);
+ }
+
+ // Arithmetic/Logical, Register-Immediate
+
+ pub fn addi(rd: Register, r1: Register, imm: i12) Instruction {
+ return iType(0b0010011, 0b000, rd, r1, imm);
+ }
+
+ pub fn andi(rd: Register, r1: Register, imm: i12) Instruction {
+ return iType(0b0010011, 0b111, rd, r1, imm);
+ }
+
+ pub fn ori(rd: Register, r1: Register, imm: i12) Instruction {
+ return iType(0b0010011, 0b110, rd, r1, imm);
+ }
+
+ pub fn xori(rd: Register, r1: Register, imm: i12) Instruction {
+ return iType(0b0010011, 0b100, rd, r1, imm);
+ }
+
+ pub fn slli(rd: Register, r1: Register, shamt: u6) Instruction {
+ return iType(0b0010011, 0b001, rd, r1, shamt);
+ }
+
+ pub fn srli(rd: Register, r1: Register, shamt: u6) Instruction {
+ return iType(0b0010011, 0b101, rd, r1, shamt);
+ }
+
+ pub fn srai(rd: Register, r1: Register, shamt: u6) Instruction {
+ return iType(0b0010011, 0b101, rd, r1, (1 << 10) + shamt);
+ }
+
+ pub fn slti(rd: Register, r1: Register, imm: i12) Instruction {
+ return iType(0b0010011, 0b010, rd, r1, imm);
+ }
+
+ pub fn sltiu(rd: Register, r1: Register, imm: u12) Instruction {
+ return iType(0b0010011, 0b011, rd, r1, @bitCast(i12, imm));
+ }
+
+ // Arithmetic/Logical, Register-Immediate (32-bit)
+
+ pub fn addiw(rd: Register, r1: Register, imm: i12) Instruction {
+ return iType(0b0011011, 0b000, rd, r1, imm);
+ }
+
+ pub fn slliw(rd: Register, r1: Register, shamt: u5) Instruction {
+ return iType(0b0011011, 0b001, rd, r1, shamt);
+ }
+
+ pub fn srliw(rd: Register, r1: Register, shamt: u5) Instruction {
+ return iType(0b0011011, 0b101, rd, r1, shamt);
+ }
+
+ pub fn sraiw(rd: Register, r1: Register, shamt: u5) Instruction {
+ return iType(0b0011011, 0b101, rd, r1, (1 << 10) + shamt);
+ }
+
+ // Upper Immediate
+
+ pub fn lui(rd: Register, imm: i20) Instruction {
+ return uType(0b0110111, rd, imm);
+ }
+
+ pub fn auipc(rd: Register, imm: i20) Instruction {
+ return uType(0b0010111, rd, imm);
+ }
+
+ // Load
+
+ pub fn ld(rd: Register, offset: i12, base: Register) Instruction {
+ return iType(0b0000011, 0b011, rd, base, offset);
+ }
+
+ pub fn lw(rd: Register, offset: i12, base: Register) Instruction {
+ return iType(0b0000011, 0b010, rd, base, offset);
+ }
+
+ pub fn lwu(rd: Register, offset: i12, base: Register) Instruction {
+ return iType(0b0000011, 0b110, rd, base, offset);
+ }
+
+ pub fn lh(rd: Register, offset: i12, base: Register) Instruction {
+ return iType(0b0000011, 0b001, rd, base, offset);
+ }
+
+ pub fn lhu(rd: Register, offset: i12, base: Register) Instruction {
+ return iType(0b0000011, 0b101, rd, base, offset);
+ }
+
+ pub fn lb(rd: Register, offset: i12, base: Register) Instruction {
+ return iType(0b0000011, 0b000, rd, base, offset);
+ }
+
+ pub fn lbu(rd: Register, offset: i12, base: Register) Instruction {
+ return iType(0b0000011, 0b100, rd, base, offset);
+ }
+
+ // Store
+
+ pub fn sd(rs: Register, offset: i12, base: Register) Instruction {
+ return sType(0b0100011, 0b011, base, rs, offset);
+ }
+
+ pub fn sw(rs: Register, offset: i12, base: Register) Instruction {
+ return sType(0b0100011, 0b010, base, rs, offset);
+ }
+
+ pub fn sh(rs: Register, offset: i12, base: Register) Instruction {
+ return sType(0b0100011, 0b001, base, rs, offset);
+ }
+
+ pub fn sb(rs: Register, offset: i12, base: Register) Instruction {
+ return sType(0b0100011, 0b000, base, rs, offset);
+ }
+
+ // Fence
+ // TODO: implement fence
+
+ // Branch
+
+ pub fn beq(r1: Register, r2: Register, offset: u13) Instruction {
+ return bType(0b1100011, 0b000, r1, r2, offset);
+ }
+
+ pub fn bne(r1: Register, r2: Register, offset: u13) Instruction {
+ return bType(0b1100011, 0b001, r1, r2, offset);
+ }
+
+ pub fn blt(r1: Register, r2: Register, offset: u13) Instruction {
+ return bType(0b1100011, 0b100, r1, r2, offset);
+ }
+
+ pub fn bge(r1: Register, r2: Register, offset: u13) Instruction {
+ return bType(0b1100011, 0b101, r1, r2, offset);
+ }
+
+ pub fn bltu(r1: Register, r2: Register, offset: u13) Instruction {
+ return bType(0b1100011, 0b110, r1, r2, offset);
+ }
+
+ pub fn bgeu(r1: Register, r2: Register, offset: u13) Instruction {
+ return bType(0b1100011, 0b111, r1, r2, offset);
+ }
+
+ // Jump
+
+ pub fn jal(link: Register, offset: i21) Instruction {
+ return jType(0b1101111, link, offset);
+ }
+
+ pub fn jalr(link: Register, offset: i12, base: Register) Instruction {
+ return iType(0b1100111, 0b000, link, base, offset);
+ }
+
+ // System
+
+ pub const ecall = iType(0b1110011, 0b000, .zero, .zero, 0x000);
+ pub const ebreak = iType(0b1110011, 0b000, .zero, .zero, 0x001);
+};
+
+// zig fmt: off
+pub const RawRegister = enum(u5) {
+ x0, x1, x2, x3, x4, x5, x6, x7,
+ x8, x9, x10, x11, x12, x13, x14, x15,
+ x16, x17, x18, x19, x20, x21, x22, x23,
+ x24, x25, x26, x27, x28, x29, x30, x31,
+
+ pub fn dwarfLocOp(reg: RawRegister) u8 {
+ return @enumToInt(reg) + DW.OP_reg0;
+ }
+};
+
+pub const Register = enum(u5) {
+ // 64 bit registers
+ zero, // zero
+ ra, // return address. caller saved
+ sp, // stack pointer. callee saved.
+ gp, // global pointer
+ tp, // thread pointer
+ t0, t1, t2, // temporaries. caller saved.
+ s0, // s0/fp, callee saved.
+ s1, // callee saved.
+ a0, a1, // fn args/return values. caller saved.
+ a2, a3, a4, a5, a6, a7, // fn args. caller saved.
+ s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, // saved registers. callee saved.
+ t3, t4, t5, t6, // caller saved
+
+ pub fn parseRegName(name: []const u8) ?Register {
+ if(std.meta.stringToEnum(Register, name)) |reg| return reg;
+ if(std.meta.stringToEnum(RawRegister, name)) |rawreg| return @intToEnum(Register, @enumToInt(rawreg));
+ return null;
+ }
+
+ /// Returns the index into `callee_preserved_regs`.
+ pub fn allocIndex(self: Register) ?u4 {
+ inline for(callee_preserved_regs) |cpreg, i| {
+ if(self == cpreg) return i;
+ }
+ return null;
+ }
+
+ pub fn dwarfLocOp(reg: Register) u8 {
+ return @as(u8, @enumToInt(reg)) + DW.OP_reg0;
+ }
+};
+
+// zig fmt: on
+
+pub const callee_preserved_regs = [_]Register{
+ .s0, .s1, .s2, .s3, .s4, .s5, .s6, .s7, .s8, .s9, .s10, .s11,
+};