1use super::arch_traits::Arch;
10use super::globals::{InstOptions, MAX_OP_COUNT};
11use super::operand::Operand;
12use crate::AsmError;
13
14#[derive(Clone, Copy, PartialEq, Eq, Debug)]
21pub struct Inst {
22 arch: Arch,
23 pub(crate) id: u32,
25 pub(crate) options: InstOptions,
27 pub(crate) extra_reg: Operand,
29 op_count: u8,
31 operands: [Operand; MAX_OP_COUNT],
33}
34
35impl Inst {
36 #[cfg(test)]
38 #[allow(dead_code)]
39 pub(crate) const fn new(id: u32) -> Self {
40 Self::new_for(Arch::HOST, id)
41 }
42
43 pub(crate) const fn new_for(arch: Arch, id: u32) -> Self {
45 Self {
46 arch,
47 id,
48 options: InstOptions::NONE,
49 extra_reg: Operand::new(),
50 op_count: 0,
51 operands: [Operand::new(); MAX_OP_COUNT],
52 }
53 }
54
55 #[cfg(test)]
57 #[allow(dead_code)]
58 pub(crate) fn with_operands(id: u32, ops: &[Operand]) -> Self {
59 Self::with_arch_operands(Arch::HOST, id, ops)
60 .expect("instruction operands must fit the inline array")
61 }
62
63 pub fn for_arch(arch: Arch, id: u32) -> Result<Self, AsmError> {
65 Self::with_arch_operands(arch, id, &[])
66 }
67
68 pub fn with_arch_operands(arch: Arch, id: u32, ops: &[Operand]) -> Result<Self, AsmError> {
70 if !matches!(
71 arch,
72 Arch::X86 | Arch::X64 | Arch::AArch64 | Arch::RISCV32 | Arch::RISCV64
73 ) {
74 return Err(AsmError::InvalidArch);
75 }
76 let mut inst = Self::new_for(arch, id);
77 inst.set_operands(ops)?;
78 Ok(inst)
79 }
80
81 pub const fn arch(&self) -> Arch {
83 self.arch
84 }
85
86 pub const fn id(&self) -> u32 {
88 self.id
89 }
90
91 pub const fn options(&self) -> InstOptions {
93 self.options
94 }
95
96 pub const fn extra_reg(&self) -> Operand {
98 self.extra_reg
99 }
100
101 pub fn set_options(&mut self, options: InstOptions) {
103 self.options = options;
104 }
105
106 pub fn set_extra_reg(&mut self, extra_reg: Operand) {
108 self.extra_reg = extra_reg;
109 }
110
111 pub const fn op_count(&self) -> usize {
113 self.op_count as usize
114 }
115
116 pub fn operands(&self) -> &[Operand] {
118 self.operands.split_at(self.op_count as usize).0
119 }
120
121 pub fn operands_mut(&mut self) -> &mut [Operand] {
123 self.operands.split_at_mut(self.op_count as usize).0
124 }
125
126 pub fn operand(&self, index: usize) -> Option<&Operand> {
128 self.operands().get(index)
129 }
130
131 pub fn set_operand(&mut self, index: usize, op: Operand) -> Result<(), AsmError> {
133 let Some(slot) = self.operands_mut().get_mut(index) else {
134 return Err(AsmError::InvalidArgument);
135 };
136 *slot = op;
137 Ok(())
138 }
139
140 pub fn add_operand(&mut self, op: Operand) -> Result<(), AsmError> {
142 if self.op_count() >= MAX_OP_COUNT {
143 return Err(AsmError::InvalidState);
144 }
145 self.operands[self.op_count()] = op;
146 self.op_count += 1;
147 Ok(())
148 }
149
150 pub fn set_operands(&mut self, ops: &[Operand]) -> Result<(), AsmError> {
154 if ops.len() > MAX_OP_COUNT {
155 return Err(AsmError::InvalidState);
156 }
157 self.operands[..ops.len()].copy_from_slice(ops);
158 self.operands[ops.len()..].fill(Operand::new());
159 self.op_count = ops.len() as u8;
160 Ok(())
161 }
162}
163
164#[cfg(test)]
165mod tests {
166 use super::*;
167
168 #[test]
169 fn build_and_mutate() {
170 let mut inst = Inst::new_for(Arch::X64, 42);
171 assert_eq!(inst.id(), 42);
172 assert_eq!(inst.op_count(), 0);
173 assert!(inst.operands().is_empty());
174
175 inst.add_operand(Operand::new()).unwrap();
176 inst.add_operand(Operand::new()).unwrap();
177 assert_eq!(inst.op_count(), 2);
178
179 inst.set_operands(&[Operand::new(); 3]).unwrap();
180 assert_eq!(inst.op_count(), 3);
181
182 for _ in 0..3 {
183 inst.add_operand(Operand::new()).unwrap();
184 }
185 assert_eq!(inst.op_count(), MAX_OP_COUNT);
186 assert!(inst.add_operand(Operand::new()).is_err());
187
188 assert_eq!(
189 inst.set_operand(MAX_OP_COUNT, Operand::new()),
190 Err(AsmError::InvalidArgument)
191 );
192 assert_eq!(
193 Inst::with_arch_operands(Arch::Unknown, 42, &[]),
194 Err(AsmError::InvalidArch)
195 );
196
197 inst.set_options(InstOptions::X86_ZMASK);
198 inst.set_extra_reg(Operand::new());
199 assert_eq!(inst.options(), InstOptions::X86_ZMASK);
200 }
201}