diff options
author | iximeow <me@iximeow.net> | 2025-04-12 13:29:20 -0700 |
---|---|---|
committer | iximeow <me@iximeow.net> | 2025-04-12 13:29:20 -0700 |
commit | 7d077f45b7e4a9776ac516da36381861e0d7e7bd (patch) | |
tree | d79c4edb32e505c036a809e5a231f531a5079940 /src | |
parent | b5b1947a9c37d60f5e9d6bd9e082fd81fd0a43da (diff) |
initial instruction extender support, more system instructions
Diffstat (limited to 'src')
-rw-r--r-- | src/display.rs | 6 | ||||
-rw-r--r-- | src/lib.rs | 91 |
2 files changed, 74 insertions, 23 deletions
diff --git a/src/display.rs b/src/display.rs index eb19107..a9e5e49 100644 --- a/src/display.rs +++ b/src/display.rs @@ -714,6 +714,8 @@ impl fmt::Display for Opcode { Opcode::DcInvA => { f.write_str("dcinva") }, Opcode::DcCleanInvA => { f.write_str("dccleaninva") }, Opcode::DcZeroA => { f.write_str("dczeroa") }, + Opcode::DcKill => { f.write_str("dckill") }, + Opcode::IcKill => { f.write_str("ickill") }, Opcode::L2Fetch => { f.write_str("l2fetch") }, Opcode::DmSyncHt => { f.write_str("dmsyncht") }, Opcode::SyncHt => { f.write_str("syncht") }, @@ -792,6 +794,7 @@ impl fmt::Display for Opcode { Opcode::Iassignr => { f.write_str("iassignr") }, Opcode::Icdatar => { f.write_str("icdatar") }, Opcode::Ictagr => { f.write_str("ictagr") }, + Opcode::Ictagw => { f.write_str("ictagw") }, Opcode::Icinvidx => { f.write_str("icinvidx") }, Opcode::SubAsl => { f.write_str("subasl") }, @@ -1051,6 +1054,9 @@ impl fmt::Display for Operand { Operand::ImmU32 { imm } => { write!(f, "#{:}", imm) } + Operand::Immext { imm } => { + write!(f, "##{:#x}", imm) + } Operand::RegShiftOffset { base, shift, offset } => { write!(f, "r{}<<{} + {:#x}", base, shift, offset) } @@ -646,6 +646,8 @@ pub enum Opcode { DcInvA, DcCleanInvA, DcZeroA, + DcKill, + IcKill, L2Fetch, DmSyncHt, SyncHt, @@ -844,6 +846,7 @@ pub enum Opcode { Iassignr, Icdatar, Ictagr, + Ictagw, Icinvidx, AndAnd = 0x8000, @@ -1148,6 +1151,8 @@ pub enum Operand { ImmU32 { imm: u32 }, + Immext { imm: u32 }, + // TODO: offset should be signed, check if test cases exercise this.. RegOffsetCirc { base: u8, offset: u32, mu: u8 }, @@ -1249,6 +1254,17 @@ impl Operand { fn imm_u32(num: u32) -> Self { Self::ImmU32 { imm: num } } + + fn immext( + i: u32, extender: &mut Option<u32>, unextended: impl FnOnce(u32) -> Self + ) -> Result<Self, yaxpeax_arch::StandardDecodeError> { + if let Some(extender) = extender.take() { + operand_check!(i & !0x3f == 0); + Ok(Self::Immext { imm: i | (extender << 6) }) + } else { + Ok(unextended(i)) + } + } } #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -1705,7 +1721,17 @@ fn decode_packet< extender = Some((inst & 0x3fff) | ((inst >> 2) & 0xfff)); } else { handler.start_instruction(); - decode_instruction(decoder, handler, inst, extender)?; + decode_instruction(decoder, handler, inst, &mut extender)?; + // V73 section 10.9: + // > The constant extender serves as a prefix for an instruction: it does not execute + // > in a slot, nor does it consume slot resources. + // > ... + // > If a constant extender is encoded in a packet for an instruction that does not + // > accept a constant extender, the execution result is undefined. The assembler + // > normally ensures that only valid constant extenders are generated. + // + // the extender must extend the instruction word that follows it. + opcode_check!(extender.is_none()); handler.end_instruction(); } @@ -1724,22 +1750,11 @@ fn can_be_extended(iclass: u8, regclass: u8) -> bool { fn decode_instruction< T: Reader<<Hexagon as Arch>::Address, <Hexagon as Arch>::Word>, H: DecodeHandler<T>, ->(_decoder: &<Hexagon as Arch>::Decoder, handler: &mut H, inst: u32, extender: Option<u32>) -> Result<(), <Hexagon as Arch>::DecodeError> { +>(_decoder: &<Hexagon as Arch>::Decoder, handler: &mut H, inst: u32, extender: &mut Option<u32>) -> Result<(), <Hexagon as Arch>::DecodeError> { let iclass = (inst >> 28) & 0b1111; use Opcode::*; - // V73 Section 10.9 - // > A constant extender must be positioned in a packet immediately before the - // > instruction that it extends - // > ... - // > If a constant extender is encoded in a packet for an instruction that does not - // > accept a constant extender, the execution result is undefined. The assembler - // > normally ensures that only valid constant extenders are generated. - if extender.is_some() { - eprintln!("TODO: error; unconsumed extender"); - } - // this is *called* "RegType" in the manual but it seem to more often describe // opcodes? let reg_type = (inst >> 24) & 0b1111; @@ -2335,13 +2350,20 @@ fn decode_instruction< handler.on_source_decoded(Operand::gpr(xxxxx))?; handler.on_source_decoded(Operand::imm_u8(u_8 as u8))?; } else if minbits == 0b110 { + opcode_check!(inst & (1 << 21) != 0); handler.on_opcode_decoded(Opcode::Icdatar)?; handler.on_dest_decoded(Operand::gpr(reg_b0(inst)))?; handler.on_source_decoded(Operand::gpr(reg_b16(inst)))?; } else if minbits == 0b111 { - handler.on_opcode_decoded(Opcode::Ictagr)?; - handler.on_dest_decoded(Operand::gpr(reg_b0(inst)))?; - handler.on_source_decoded(Operand::gpr(reg_b16(inst)))?; + if inst & (1 << 21) == 0 { + handler.on_opcode_decoded(Opcode::Ictagw)?; + handler.on_source_decoded(Operand::gpr(reg_b16(inst)))?; + handler.on_source_decoded(Operand::gpr(reg_b8(inst)))?; + } else { + handler.on_opcode_decoded(Opcode::Ictagr)?; + handler.on_dest_decoded(Operand::gpr(reg_b0(inst)))?; + handler.on_source_decoded(Operand::gpr(reg_b16(inst)))?; + } } else { opcode_check!(false); } @@ -2351,9 +2373,15 @@ fn decode_instruction< let minbits = (inst >> 21) & 0b1111; if minbits == 0b0110 { - handler.on_opcode_decoded(Opcode::Icinva)?; - handler.on_source_decoded(Operand::gpr(reg_b16(inst)))?; - opcode_check!(inst & 0x3800 == 0x0000); + let op = (inst >> 11) & 0b111; + if op == 0b000 { + handler.on_opcode_decoded(Opcode::Icinva)?; + handler.on_source_decoded(Operand::gpr(reg_b16(inst)))?; + } else if op == 0b010 { + handler.on_opcode_decoded(Opcode::IcKill)?; + } else { + opcode_check!(false); + } } else if minbits == 0b0111 { handler.on_opcode_decoded(Opcode::Icinvidx)?; handler.on_source_decoded(Operand::gpr(reg_b16(inst)))?; @@ -2373,6 +2401,7 @@ fn decode_instruction< handler.on_opcode_decoded(Opcode::Jump)?; let imm = ((inst >> 1) & 0x1fff) | ((inst >> 3) & 0xffe000); let imm = ((imm as i32) << 10) >> 10; + // TODO: extend handler.on_dest_decoded(Operand::PCRel32 { rel: imm << 2 })?; }, 0b101 => { @@ -2404,10 +2433,12 @@ fn decode_instruction< let is_call = (inst >> 24) & 1 == 1; if is_call { + // TODO: extend handler.on_opcode_decoded(Opcode::Call)?; handler.inst_predicated(uu as u8, negated, false)?; opcode_check!(!dotnew); } else { + // ... not shown as extended in the manual? handler.on_opcode_decoded(Opcode::Jump)?; handler.inst_predicated(uu as u8, negated, dotnew)?; handler.branch_hint(hint_taken)?; @@ -3084,11 +3115,21 @@ fn decode_instruction< } if min_low == 0b01 { - handler.on_source_decoded(Operand::imm_i8(i))?; + handler.on_source_decoded( + Operand::immext( + i as u32, extender, + |i: u32| Operand::imm_i8(i as i8) + )? + )?; handler.on_source_decoded(Operand::gpr(sssss))?; } else { handler.on_source_decoded(Operand::gpr(sssss))?; - handler.on_source_decoded(Operand::imm_i8(i))?; + handler.on_source_decoded( + Operand::immext( + i as u32, extender, + |i: u32| Operand::imm_i8(i as i8) + )? + )?; } } else { let sssss = reg_b16(inst); @@ -4391,9 +4432,13 @@ fn decode_instruction< } }, } + } else if opc_upper == 0b01 { + // 1010|0010 + // in V65, not in V73, in LLVM definitions: dckill + handler.on_opcode_decoded(Opcode::DcKill)?; } else { - // if there are any encodings like 1010|001 or 1010|010, they are not in - // the V73 manual... + // if there are any encodings like 1010|010, they are not in the V73 + // manual... opcode_check!(opc_upper == 0b11); let opc_lower = (inst >> 21) & 0b111; // similar.. |