stage2 sparcv9: Add register definitions & instruction formats

This adds the GPR/FPR register definitions and instruction formats
for SPARCv9.
I need to implement a separate enc() function because the register
values for the FPRs have to be encoded to a special format that's
separate from the normal register ID.
This commit is contained in:
Koakuma 2022-03-13 00:40:19 +07:00
parent 8a43d67c3b
commit a212d5931d

447
src/arch/sparcv9/bits.zig Normal file
View File

@ -0,0 +1,447 @@
const std = @import("std");
const DW = std.dwarf;
const testing = std.testing;
/// General purpose registers in the SPARCv9 instruction set
pub const Register = enum(u6) {
// zig fmt: off
g0, g1, g2, g3, g4, g5, g6, g7,
o0, o1, o2, o3, o4, o5, o6, o7,
l0, l1, l2, l3, l4, l5, l6, l7,
@"i0", @"i1", @"i2", @"i3", @"i4", @"i5", @"i6", @"i7",
sp = 46, // stack pointer (o6)
fp = 62, // frame pointer (i6)
// zig fmt: on
pub fn id(self: Register) u5 {
return @truncate(u5, @enumToInt(self));
}
pub fn enc(self: Register) u5 {
// For integer registers, enc() == id().
return self.id();
}
pub fn dwarfLocOp(reg: Register) u8 {
return @as(u8, reg.id()) + DW.OP.reg0;
}
};
test "Register.id" {
// SP
try testing.expectEqual(@as(u5, 14), Register.o6.id());
try testing.expectEqual(Register.o6.id(), Register.sp.id());
// FP
try testing.expectEqual(@as(u5, 30), Register.@"i6".id());
try testing.expectEqual(Register.@"i6".id(), Register.fp.id());
// x0
try testing.expectEqual(@as(u5, 0), Register.g0.id());
try testing.expectEqual(@as(u5, 8), Register.o0.id());
try testing.expectEqual(@as(u5, 16), Register.l0.id());
try testing.expectEqual(@as(u5, 24), Register.@"i0".id());
}
test "Register.enc" {
// x0
try testing.expectEqual(@as(u5, 0), Register.g0.enc());
try testing.expectEqual(@as(u5, 8), Register.o0.enc());
try testing.expectEqual(@as(u5, 16), Register.l0.enc());
try testing.expectEqual(@as(u5, 24), Register.@"i0".enc());
// For integer registers, enc() == id().
try testing.expectEqual(Register.g0.enc(), Register.g0.id());
try testing.expectEqual(Register.o0.enc(), Register.o0.id());
try testing.expectEqual(Register.l0.enc(), Register.l0.id());
try testing.expectEqual(Register.@"i0".enc(), Register.@"i0".id());
}
/// Scalar floating point registers in the SPARCv9 instruction set
pub const FloatingPointRegister = enum(u7) {
// SPARCv9 has 64 f32 registers, 32 f64 registers, and 16 f128 registers,
// which are aliased in this way:
//
// | %d0 | %d2 |
// %q0 | %f0 | %f1 | %f2 | %f3 |
// | %d4 | %d6 |
// %q4 | %f4 | %f5 | %f6 | %f7 |
// ...
// | %d60 | %d62 |
// %q60 | %f60 | %f61 | %f62 | %f63 |
//
// Though, since the instructions uses five-bit addressing, only %f0-%f31
// is usable with f32 instructions.
// zig fmt: off
// 32-bit registers
@"f0", @"f1", @"f2", @"f3", @"f4", @"f5", @"f6", @"f7",
@"f8", @"f9", @"f10", @"f11", @"f12", @"f13", @"f14", @"f15",
@"f16", @"f17", @"f18", @"f19", @"f20", @"f21", @"f22", @"f23",
@"f24", @"f25", @"f26", @"f27", @"f28", @"f29", @"f30", @"f31",
// 64-bit registers
d0, d2, d4, d6, d8, d10, d12, d14,
d16, d18, d20, d22, d24, d26, d28, d30,
d32, d34, d36, d38, d40, d42, d44, d46,
d48, d50, d52, d54, d56, d58, d60, d62,
// 128-bit registers
q0, q4, q8, q12, q16, q20, q24, q28,
q32, q36, q40, q44, q48, q52, q56, q60,
// zig fmt: on
pub fn id(self: FloatingPointRegister) u6 {
return switch (self.size()) {
32 => @truncate(u6, @enumToInt(self)),
64 => @truncate(u6, (@enumToInt(self) - 32) * 2),
128 => @truncate(u6, (@enumToInt(self) - 64) * 4),
else => unreachable,
};
}
pub fn enc(self: FloatingPointRegister) u5 {
// Floating point registers use an encoding scheme to map from the 6-bit
// ID to 5-bit encoded value.
// (See section 5.1.4.1 of SPARCv9 ISA specification)
const reg_id = self.id();
return @truncate(u5, reg_id | (reg_id >> 5));
}
/// Returns the bit-width of the register.
pub fn size(self: FloatingPointRegister) u8 {
return switch (@enumToInt(self)) {
0...31 => 32,
32...63 => 64,
64...79 => 128,
else => unreachable,
};
}
};
test "FloatingPointRegister.id" {
// Low region
try testing.expectEqual(@as(u6, 0), FloatingPointRegister.q0.id());
try testing.expectEqual(FloatingPointRegister.q0.id(), FloatingPointRegister.d0.id());
try testing.expectEqual(FloatingPointRegister.d0.id(), FloatingPointRegister.@"f0".id());
try testing.expectEqual(@as(u6, 28), FloatingPointRegister.q28.id());
try testing.expectEqual(FloatingPointRegister.q28.id(), FloatingPointRegister.d28.id());
try testing.expectEqual(FloatingPointRegister.d28.id(), FloatingPointRegister.@"f28".id());
// High region
try testing.expectEqual(@as(u6, 32), FloatingPointRegister.q32.id());
try testing.expectEqual(FloatingPointRegister.q32.id(), FloatingPointRegister.d32.id());
try testing.expectEqual(@as(u6, 60), FloatingPointRegister.q60.id());
try testing.expectEqual(FloatingPointRegister.q60.id(), FloatingPointRegister.d60.id());
}
test "FloatingPointRegister.enc" {
// f registers
try testing.expectEqual(@as(u5, 0), FloatingPointRegister.@"f0".enc());
try testing.expectEqual(@as(u5, 1), FloatingPointRegister.@"f1".enc());
try testing.expectEqual(@as(u5, 31), FloatingPointRegister.@"f31".enc());
// d registers
try testing.expectEqual(@as(u5, 0), FloatingPointRegister.d0.enc());
try testing.expectEqual(@as(u5, 1), FloatingPointRegister.d32.enc());
try testing.expectEqual(@as(u5, 31), FloatingPointRegister.d62.enc());
// q registers
try testing.expectEqual(@as(u5, 0), FloatingPointRegister.q0.enc());
try testing.expectEqual(@as(u5, 1), FloatingPointRegister.q32.enc());
try testing.expectEqual(@as(u5, 29), FloatingPointRegister.q60.enc());
}
/// Represents an instruction in the SPARCv9 instruction set
pub const Instruction = union(enum) {
// Some of the instruction formats have several minor formats, here I
// name them with letters since there's no official naming scheme.
// TODO: need to rename the minor formats to a more descriptive name.
// Format 1 (op = 1): CALL
format_1: packed struct {
op: u2 = 0b01,
disp30: u30,
},
// Format 2 (op = 0): SETHI & Branches (Bicc, BPcc, BPr, FBfcc, FBPfcc)
format_2a: packed struct {
op: u2 = 0b00,
rd: u5,
op2: u3,
imm22: u22,
},
format_2b: packed struct {
op: u2 = 0b00,
a: u1,
cond: u4,
op2: u3,
disp22: u22,
},
format_2c: packed struct {
op: u2 = 0b00,
a: u1,
cond: u4,
op2: u3,
cc1: u1,
cc0: u1,
p: u1,
disp19: u19,
},
format_2d: packed struct {
op: u2 = 0b00,
a: u1,
fixed: u1 = 0b0,
rcond: u3,
op2: u3,
d16hi: u2,
p: u1,
rs1: u5,
d16lo: u14,
},
// Format 3 (op = 2 or 3): Arithmetic, Logical, MOVr, MEMBAR, Load, and Store
format_3a: packed struct {
op: u2,
rd: u5,
op3: u6,
rs1: u5,
i: u1 = 0b0,
reserved: u8 = 0b00000000,
rs2: u5,
},
format_3b: packed struct {
op: u2,
rd: u5,
op3: u6,
rs1: u5,
i: u1 = 0b1,
simm13: u13,
},
format_3c: packed struct {
op: u2,
reserved1: u5 = 0b00000,
op3: u6,
rs1: u5,
i: u1 = 0b0,
reserved2: u8 = 0b00000000,
rs2: u5,
},
format_3d: packed struct {
op: u2,
reserved: u5 = 0b00000,
op3: u6,
rs1: u5,
i: u1 = 0b1,
simm13: u13,
},
format_3e: packed struct {
op: u2,
rd: u5,
op3: u6,
rs1: u5,
i: u1 = 0b0,
rcond: u3,
reserved: u5 = 0b00000,
rs2: u5,
},
format_3f: packed struct {
op: u2,
rd: u5,
op3: u6,
rs1: u5,
i: u1 = 0b1,
rcond: u3,
simm10: u10,
},
format_3g: packed struct {
op: u2,
rd: u5,
op3: u6,
rs1: u5,
i: u1 = 0b1,
reserved: u8 = 0b00000000,
rs2: u5,
},
format_3h: packed struct {
op: u2,
rd: u5,
op3: u6,
rs1: u5,
i: u1 = 0b1,
reserved: u6,
cmask: u3,
mmask: u4,
},
format_3i: packed struct {
op: u2,
rd: u5,
op3: u6,
rs1: u5,
i: u1 = 0b0,
imm_asi: u8,
rs2: u5,
},
format_3j: packed struct {
op: u2,
impl_dep1: u5,
op3: u6,
impl_dep2: u19,
},
format_3k: packed struct {
op: u2,
rd: u5,
op3: u6,
rs1: u5,
i: u1 = 0b0,
x: u1,
reserved: u7 = 0b0000000,
rs2: u5,
},
format_3l: packed struct {
op: u2,
rd: u5,
op3: u6,
rs1: u5,
i: u1 = 0b1,
x: u1 = 0b0,
reserved: u7 = 0b0000000,
shcnt32: u5,
},
format_3m: packed struct {
op: u2,
rd: u5,
op3: u6,
rs1: u5,
i: u1 = 0b1,
x: u1 = 0b1,
reserved: u6 = 0b000000,
shcnt64: u6,
},
format_3n: packed struct {
op: u2,
rd: u5,
op3: u6,
reserved: u5 = 0b00000,
opf: u9,
rs2: u5,
},
format_3o: packed struct {
op: u2,
fixed: u3 = 0b000,
cc1: u1,
cc0: i1,
op3: u6,
opf: u9,
rs2: u5,
},
format_3p: packed struct {
op: u2,
rd: u5,
op3: u6,
rs1: u5,
opf: u9,
rs2: u5,
},
format_3q: packed struct {
op: u2,
rd: u5,
op3: u6,
rs1: u5,
reserved: u14 = 0b00000000000000,
},
format_3r: packed struct {
op: u2,
fcn: u5,
op3: u6,
reserved: u19 = 0b0000000000000000000,
},
format_3s: packed struct {
op: u2,
rd: u5,
op3: u6,
reserved: u19 = 0b0000000000000000000,
},
//Format 4 (op = 2): MOVcc, FMOVr, FMOVcc, and Tcc
format_4a: packed struct {
op: u2 = 0b10,
rd: u5,
op3: u6,
rs1: u5,
i: u1 = 0b0,
cc1: u1,
cc0: u1,
reserved: u6 = 0b000000,
rs2: u5,
},
format_4b: packed struct {
op: u2 = 0b10,
rd: u5,
op3: u6,
rs1: u5,
i: u1 = 0b1,
cc1: u1,
cc0: u1,
simm11: u11,
},
format_4c: packed struct {
op: u2 = 0b10,
rd: u5,
op3: u6,
cc2: u1,
cond: u4,
i: u1 = 0b0,
cc1: u1,
cc0: u1,
reserved: u6 = 0b000000,
rs2: u5,
},
format_4d: packed struct {
op: u2 = 0b10,
rd: u5,
op3: u6,
cc2: u1,
cond: u4,
i: u1 = 0b1,
cc1: u1,
cc0: u1,
simm11: u11,
},
format_4e: packed struct {
op: u2 = 0b10,
rd: u5,
op3: u6,
rs1: u5,
i: u1 = 0b1,
cc1: u1,
cc0: u1,
reserved: u4 = 0b0000,
sw_trap: u7,
},
format_4f: packed struct {
op: u2 = 0b10,
rd: u5,
op3: u6,
rs1: u5,
fixed: u1 = 0b0,
rcond: u3,
opf_low: u5,
rs2: u5,
},
format_4g: packed struct {
op: u2 = 0b10,
rd: u5,
op3: u6,
fixed: u1 = 0b0,
cond: u4,
opf_cc: u3,
opf_low: u6,
rs2: u5,
},
};