use ckb_vm_definitions::instructions::{self as insts};
use ckb_vm_definitions::registers::SP;
use super::i::nop;
use super::register::Register;
use super::utils::{rd, x, xs};
use super::{blank_instruction, set_instruction_length_2, Instruction, Itype, Rtype, Stype, Utype};
#[inline(always)]
fn c_rs2(instruction_bits: u32) -> usize {
x(instruction_bits, 2, 5, 0) as usize
}
#[inline(always)]
fn compact_register_number(instruction_bits: u32, least_bit: usize) -> usize {
x(instruction_bits, least_bit, 3, 0) as usize + 8
}
fn immediate(instruction_bits: u32) -> i32 {
(x(instruction_bits, 2, 5, 0) | xs(instruction_bits, 12, 1, 5)) as i32
}
fn uimmediate(instruction_bits: u32) -> u32 {
x(instruction_bits, 2, 5, 0) | x(instruction_bits, 12, 1, 5)
}
fn j_immediate(instruction_bits: u32) -> i32 {
(x(instruction_bits, 3, 3, 1)
| x(instruction_bits, 11, 1, 4)
| x(instruction_bits, 2, 1, 5)
| x(instruction_bits, 7, 1, 6)
| x(instruction_bits, 6, 1, 7)
| x(instruction_bits, 9, 2, 8)
| x(instruction_bits, 8, 1, 10)
| xs(instruction_bits, 12, 1, 11)) as i32
}
fn fld_uimmediate(instruction_bits: u32) -> u32 {
x(instruction_bits, 10, 3, 3) | x(instruction_bits, 5, 2, 6)
}
fn sw_uimmediate(instruction_bits: u32) -> u32 {
x(instruction_bits, 6, 1, 2) | x(instruction_bits, 10, 3, 3) | x(instruction_bits, 5, 1, 6)
}
fn lwsp_uimmediate(instruction_bits: u32) -> u32 {
x(instruction_bits, 4, 3, 2) | x(instruction_bits, 12, 1, 5) | x(instruction_bits, 2, 2, 6)
}
fn fldsp_uimmediate(instruction_bits: u32) -> u32 {
x(instruction_bits, 5, 2, 3) | x(instruction_bits, 12, 1, 5) | x(instruction_bits, 2, 3, 6)
}
fn fsdsp_uimmediate(instruction_bits: u32) -> u32 {
x(instruction_bits, 10, 3, 3) | x(instruction_bits, 7, 3, 6)
}
fn swsp_uimmediate(instruction_bits: u32) -> u32 {
x(instruction_bits, 9, 4, 2) | x(instruction_bits, 7, 2, 6)
}
fn b_immediate(instruction_bits: u32) -> i32 {
(x(instruction_bits, 3, 2, 1)
| x(instruction_bits, 10, 2, 3)
| x(instruction_bits, 2, 1, 5)
| x(instruction_bits, 5, 2, 6)
| xs(instruction_bits, 12, 1, 8)) as i32
}
#[allow(clippy::cognitive_complexity)]
pub fn factory<R: Register>(instruction_bits: u32, version: u32) -> Option<Instruction> {
let bit_length = R::BITS;
if bit_length != 32 && bit_length != 64 {
return None;
}
let rv32 = bit_length == 32;
let rv64 = bit_length == 64;
match instruction_bits & 0b_111_00000000000_11 {
0b_000_00000000000_00 => {
let nzuimm = x(instruction_bits, 6, 1, 2)
| x(instruction_bits, 5, 1, 3)
| x(instruction_bits, 11, 2, 4)
| x(instruction_bits, 7, 4, 6);
if nzuimm != 0 {
Some(
Itype::new_u(
insts::OP_ADDI,
compact_register_number(instruction_bits, 2),
SP,
nzuimm,
)
.0,
)
} else {
None
}
}
0b_010_00000000000_00 => Some(
Itype::new_u(
insts::OP_LW,
compact_register_number(instruction_bits, 2),
compact_register_number(instruction_bits, 7),
sw_uimmediate(instruction_bits),
)
.0,
),
0b_011_00000000000_00 => {
if rv32 {
None
} else {
Some(
Itype::new_u(
insts::OP_LD,
compact_register_number(instruction_bits, 2),
compact_register_number(instruction_bits, 7),
fld_uimmediate(instruction_bits),
)
.0,
)
}
}
0b_100_00000000000_00 => None,
0b_110_00000000000_00 => Some(
Stype::new_u(
insts::OP_SW,
sw_uimmediate(instruction_bits),
compact_register_number(instruction_bits, 7),
compact_register_number(instruction_bits, 2),
)
.0,
),
0b_111_00000000000_00 => {
if rv32 {
None
} else {
Some(
Stype::new_u(
insts::OP_SD,
fld_uimmediate(instruction_bits),
compact_register_number(instruction_bits, 7),
compact_register_number(instruction_bits, 2),
)
.0,
)
}
}
0b_000_00000000000_01 => {
let nzimm = immediate(instruction_bits);
let rd = rd(instruction_bits);
if rd != 0 {
if nzimm != 0 {
Some(Itype::new_s(insts::OP_ADDI, rd, rd, nzimm).0)
} else if version >= 1 {
Some(nop())
} else {
None
}
} else {
#[allow(clippy::if_same_then_else)]
if nzimm == 0 {
Some(nop())
} else if version >= 1 {
Some(nop())
} else {
None
}
}
}
0b_001_00000000000_01 => {
if rv32 {
Some(Utype::new_s(insts::OP_JAL, 1, j_immediate(instruction_bits)).0)
} else {
let rd = rd(instruction_bits);
if rd != 0 {
Some(Itype::new_s(insts::OP_ADDIW, rd, rd, immediate(instruction_bits)).0)
} else {
None
}
}
}
0b_010_00000000000_01 => {
let rd = rd(instruction_bits);
if rd != 0 {
Some(Itype::new_s(insts::OP_ADDI, rd, 0, immediate(instruction_bits)).0)
} else if version >= 1 {
Some(nop())
} else {
None
}
}
0b_011_00000000000_01 => {
let imm = immediate(instruction_bits) << 12;
if imm != 0 {
let rd = rd(instruction_bits);
if rd == SP {
Some(
Itype::new_s(
insts::OP_ADDI,
SP,
SP,
(x(instruction_bits, 6, 1, 4)
| x(instruction_bits, 2, 1, 5)
| x(instruction_bits, 5, 1, 6)
| x(instruction_bits, 3, 2, 7)
| xs(instruction_bits, 12, 1, 9))
as i32,
)
.0,
)
} else {
if rd != 0 {
Some(Utype::new_s(insts::OP_LUI, rd, imm).0)
} else if version >= 1 {
Some(nop())
} else {
None
}
}
} else {
None
}
}
0b_100_00000000000_01 => {
let rd = compact_register_number(instruction_bits, 7);
match instruction_bits & 0b_1_11_000_11000_00 {
0b_0_00_000_00000_00 if instruction_bits & 0b_111_00 == 0 => Some(nop()),
0b_0_01_000_00000_00 if instruction_bits & 0b_111_00 == 0 => Some(nop()),
0b_0_11_000_00000_00 => Some(
Rtype::new(
insts::OP_SUB,
rd,
rd,
compact_register_number(instruction_bits, 2),
)
.0,
),
0b_0_11_000_01000_00 => Some(
Rtype::new(
insts::OP_XOR,
rd,
rd,
compact_register_number(instruction_bits, 2),
)
.0,
),
0b_0_11_000_10000_00 => Some(
Rtype::new(
insts::OP_OR,
rd,
rd,
compact_register_number(instruction_bits, 2),
)
.0,
),
0b_0_11_000_11000_00 => Some(
Rtype::new(
insts::OP_AND,
rd,
rd,
compact_register_number(instruction_bits, 2),
)
.0,
),
0b_1_11_000_00000_00 if rv64 => Some(
Rtype::new(
insts::OP_SUBW,
rd,
rd,
compact_register_number(instruction_bits, 2),
)
.0,
),
0b_1_11_000_01000_00 if rv64 => Some(
Rtype::new(
insts::OP_ADDW,
rd,
rd,
compact_register_number(instruction_bits, 2),
)
.0,
),
0b_1_11_000_10000_00 => None,
0b_1_11_000_11000_00 => None,
_ => {
let uimm = uimmediate(instruction_bits);
match (instruction_bits & 0b_11_000_00000_00, uimm) {
(0b_00_000_00000_00, 0) => None,
(0b_00_000_00000_00, uimm) => Some(
Itype::new_u(insts::OP_SRLI, rd, rd, uimm & u32::from(R::SHIFT_MASK)).0,
),
(0b_01_000_00000_00, 0) => None,
(0b_01_000_00000_00, uimm) => Some(
Itype::new_u(insts::OP_SRAI, rd, rd, uimm & u32::from(R::SHIFT_MASK)).0,
),
(0b_10_000_00000_00, _) => Some(
Itype::new_s(insts::OP_ANDI, rd, rd, immediate(instruction_bits)).0,
),
_ => None,
}
}
}
}
0b_101_00000000000_01 => {
Some(Utype::new_s(insts::OP_JAL, 0, j_immediate(instruction_bits)).0)
}
0b_110_00000000000_01 => Some(
Stype::new_s(
insts::OP_BEQ,
b_immediate(instruction_bits),
compact_register_number(instruction_bits, 7),
0,
)
.0,
),
0b_111_00000000000_01 => Some(
Stype::new_s(
insts::OP_BNE,
b_immediate(instruction_bits),
compact_register_number(instruction_bits, 7),
0,
)
.0,
),
0b_000_00000000000_10 => {
let uimm = uimmediate(instruction_bits);
let rd = rd(instruction_bits);
if rd != 0 && uimm != 0 {
Some(Itype::new_u(insts::OP_SLLI, rd, rd, uimm & u32::from(R::SHIFT_MASK)).0)
} else if version >= 1 {
Some(nop())
} else {
None
}
}
0b_010_00000000000_10 => {
let rd = rd(instruction_bits);
if rd != 0 {
Some(Itype::new_u(insts::OP_LW, rd, SP, lwsp_uimmediate(instruction_bits)).0)
} else {
None
}
}
0b_011_00000000000_10 => {
if rv32 {
None
} else {
let rd = rd(instruction_bits);
if rd != 0 {
Some(Itype::new_u(insts::OP_LD, rd, SP, fldsp_uimmediate(instruction_bits)).0)
} else {
None
}
}
}
0b_100_00000000000_10 => {
match instruction_bits & 0b_1_00000_00000_00 {
0b_0_00000_00000_00 => {
let rd = rd(instruction_bits);
let rs2 = c_rs2(instruction_bits);
if rs2 == 0 {
if rd != 0 {
Some(Itype::new_s(insts::OP_JALR, 0, rd, 0).0)
} else {
None
}
} else {
if rd != 0 {
Some(Rtype::new(insts::OP_ADD, rd, 0, rs2).0)
} else if version >= 1 {
Some(nop())
} else {
None
}
}
}
0b_1_00000_00000_00 => {
let rd = rd(instruction_bits);
let rs2 = c_rs2(instruction_bits);
match (rd, rs2) {
(0, 0) => Some(blank_instruction(insts::OP_EBREAK)),
(rs1, 0) => Some(Itype::new_s(insts::OP_JALR, 1, rs1, 0).0),
(rd, rs2) => {
if rd != 0 {
Some(Rtype::new(insts::OP_ADD, rd, rd, rs2).0)
} else if version >= 1 {
Some(nop())
} else {
None
}
}
}
}
_ => unreachable!(),
}
}
0b_110_00000000000_10 => Some(
Stype::new_u(
insts::OP_SW,
swsp_uimmediate(instruction_bits),
SP,
c_rs2(instruction_bits),
)
.0,
),
0b_111_00000000000_10 => {
if rv32 {
None
} else {
Some(
Stype::new_u(
insts::OP_SD,
fsdsp_uimmediate(instruction_bits),
2,
c_rs2(instruction_bits),
)
.0,
)
}
}
_ => None,
}
.map(set_instruction_length_2)
}