Skip to main content

zydis_rs/encoder/
request.rs

1//! Encoder request builder
2//!
3//! This module provides the `EncoderRequest` struct for building
4//! instruction encoding requests.
5
6use super::evex::EvexZeroingMode;
7use super::operand::{EncoderOperand, MemoryOperand};
8use super::types::{
9    BranchType, BranchWidth, BroadcastMode, EncodableEncoding, EncoderHints, RoundingMode,
10};
11use crate::isa::{Mnemonic, Register};
12
13/// Maximum operands for an encoded instruction
14pub const MAX_ENCODER_OPERANDS: usize = 5;
15
16/// Request to encode an x86/x64 instruction
17///
18/// This structure contains all the information needed to encode an instruction,
19/// including the mnemonic, operands, and size overrides.
20#[derive(Debug, Clone)]
21pub struct EncoderRequest {
22    /// The instruction mnemonic
23    pub mnemonic: Mnemonic,
24    /// Operands for the instruction
25    pub operands: [EncoderOperand; MAX_ENCODER_OPERANDS],
26    /// Number of valid operands
27    pub operand_count: u8,
28    /// Effective operand size override (0 = default, 16/32/64 = override)
29    pub operand_size: u8,
30    /// Effective address size override (0 = default, 16/32/64 = override)
31    pub address_size: u8,
32    /// Mask register for AVX-512 (k0-k7), None for no masking
33    pub mask_reg: Option<Register>,
34    /// Zeroing mode for AVX-512 masked operations
35    pub zeroing_mode: EvexZeroingMode,
36    /// Preferred encoding type (None = auto-select)
37    pub preferred_encoding: Option<EncodableEncoding>,
38    /// Branch type (None = auto-select, Short/Near/Far = explicit)
39    pub branch_type: BranchType,
40    /// Branch width (None = auto-select based on offset)
41    pub branch_width: BranchWidth,
42    /// Encoder hints for advanced features
43    pub hints: EncoderHints,
44}
45
46impl EncoderRequest {
47    /// Create a new encoder request with the given mnemonic
48    #[must_use]
49    pub fn new(mnemonic: Mnemonic) -> Self {
50        Self {
51            mnemonic,
52            operands: [EncoderOperand::None; MAX_ENCODER_OPERANDS],
53            operand_count: 0,
54            operand_size: 0,
55            address_size: 0,
56            mask_reg: None,
57            zeroing_mode: EvexZeroingMode::Merging,
58            preferred_encoding: None,
59            branch_type: BranchType::None,
60            branch_width: BranchWidth::None,
61            hints: EncoderHints::default(),
62        }
63    }
64
65    /// Add an operand to the request
66    ///
67    /// Operands are added in order. Up to `MAX_ENCODER_OPERANDS` operands can be added.
68    #[must_use]
69    pub fn with_operand(mut self, operand: EncoderOperand) -> Self {
70        if (self.operand_count as usize) < MAX_ENCODER_OPERANDS {
71            self.operands[self.operand_count as usize] = operand;
72            self.operand_count += 1;
73        }
74        self
75    }
76
77    /// Add a register operand
78    #[must_use]
79    pub fn with_reg(self, register: Register) -> Self {
80        self.with_operand(EncoderOperand::reg(register))
81    }
82
83    /// Add a memory operand
84    #[must_use]
85    pub fn with_mem(self, memory: MemoryOperand) -> Self {
86        self.with_operand(EncoderOperand::mem(memory))
87    }
88
89    /// Add an immediate operand
90    #[must_use]
91    pub fn with_imm(self, value: u64) -> Self {
92        self.with_operand(EncoderOperand::imm(value))
93    }
94
95    /// Add a relative offset operand
96    #[must_use]
97    pub fn with_rel(self, offset: i32) -> Self {
98        self.with_operand(EncoderOperand::rel(offset))
99    }
100
101    /// Set the operand size override (16, 32, or 64 bits)
102    #[must_use]
103    pub fn with_operand_size(mut self, size: u16) -> Self {
104        self.operand_size = size as u8;
105        self
106    }
107
108    /// Set the address size override (16, 32, or 64 bits)
109    #[must_use]
110    pub fn with_address_size(mut self, size: u16) -> Self {
111        self.address_size = size as u8;
112        self
113    }
114
115    /// Set the mask register for AVX-512 operations (k0-k7)
116    #[must_use]
117    pub fn with_mask(mut self, mask: Register) -> Self {
118        self.mask_reg = Some(mask);
119        self
120    }
121
122    /// Set the zeroing mode for AVX-512 masked operations
123    #[must_use]
124    pub fn with_zeroing(mut self, zeroing: EvexZeroingMode) -> Self {
125        self.zeroing_mode = zeroing;
126        self
127    }
128
129    /// Enable zeroing mode (convenience method)
130    #[must_use]
131    pub fn with_zeroing_enabled(self) -> Self {
132        self.with_zeroing(EvexZeroingMode::Zeroing)
133    }
134
135    /// Set the preferred encoding type
136    #[must_use]
137    pub fn with_encoding(mut self, encoding: EncodableEncoding) -> Self {
138        self.preferred_encoding = Some(encoding);
139        self
140    }
141
142    /// Set the branch type (Short, Near, Far)
143    #[must_use]
144    pub fn with_branch_type(mut self, branch_type: BranchType) -> Self {
145        self.branch_type = branch_type;
146        self
147    }
148
149    /// Set the branch width
150    #[must_use]
151    pub fn with_branch_width(mut self, branch_width: BranchWidth) -> Self {
152        self.branch_width = branch_width;
153        self
154    }
155
156    /// Set broadcast mode for AVX-512
157    #[must_use]
158    pub fn with_broadcast(mut self, mode: BroadcastMode) -> Self {
159        self.hints.broadcast_mode = mode;
160        self
161    }
162
163    /// Set rounding mode for AVX-512
164    #[must_use]
165    pub fn with_rounding(mut self, mode: RoundingMode) -> Self {
166        self.hints.rounding_mode = Some(mode);
167        self
168    }
169
170    /// Enable SAE (Suppress All Exceptions) for AVX-512
171    #[must_use]
172    pub fn with_sae(mut self) -> Self {
173        self.hints.suppress_all_exceptions = true;
174        self
175    }
176
177    /// Set encoder hints
178    #[must_use]
179    pub fn with_hints(mut self, hints: EncoderHints) -> Self {
180        self.hints = hints;
181        self
182    }
183
184    /// Check if AVX-512 masking is enabled
185    #[must_use]
186    pub const fn has_mask(&self) -> bool {
187        self.mask_reg.is_some()
188    }
189
190    /// Check if zeroing mode is enabled
191    #[must_use]
192    pub const fn is_zeroing(&self) -> bool {
193        matches!(self.zeroing_mode, EvexZeroingMode::Zeroing)
194    }
195
196    /// Check if a specific encoding is requested
197    #[must_use]
198    pub const fn has_preferred_encoding(&self) -> bool {
199        self.preferred_encoding.is_some()
200    }
201
202    /// Check if broadcast mode is enabled
203    #[must_use]
204    pub const fn has_broadcast(&self) -> bool {
205        !matches!(self.hints.broadcast_mode, BroadcastMode::None)
206    }
207
208    /// Check if rounding mode is specified
209    #[must_use]
210    pub const fn has_rounding(&self) -> bool {
211        self.hints.rounding_mode.is_some()
212    }
213
214    /// Check if SAE is enabled
215    #[must_use]
216    pub const fn has_sae(&self) -> bool {
217        self.hints.suppress_all_exceptions
218    }
219
220    /// Check if a specific branch type is requested
221    #[must_use]
222    pub const fn has_branch_type(&self) -> bool {
223        !matches!(self.branch_type, BranchType::None)
224    }
225
226    /// Create from a decoded instruction
227    ///
228    /// This is useful for re-encoding a decoded instruction, possibly with modifications.
229    #[cfg(feature = "decoder")]
230    #[must_use]
231    pub fn from_decoded(instruction: &crate::decoder::DecodedInstruction) -> Self {
232        // TODO: implement full conversion
233        // For now, just create a basic request with the mnemonic
234        let mut request = Self::new(instruction.mnemonic)
235            .with_operand_size(instruction.operand_size)
236            .with_address_size(instruction.address_size);
237
238        // Convert each decoded operand to an encoder operand
239        for i in 0..instruction.operand_count as usize {
240            if let Some(operand) = instruction.operand(i) {
241                let encoder_op = Self::convert_operand(operand);
242                request = request.with_operand(encoder_op);
243            }
244        }
245
246        request
247    }
248
249    /// Convert a decoded operand to an encoder operand
250    #[cfg(feature = "decoder")]
251    fn convert_operand(operand: &crate::decoder::Operand) -> EncoderOperand {
252        use crate::isa::OperandKind;
253
254        match operand.kind {
255            OperandKind::Register => {
256                if let Some(reg) = operand.reg() {
257                    EncoderOperand::reg(reg)
258                } else {
259                    EncoderOperand::None
260                }
261            }
262            OperandKind::Memory => {
263                if let Some(mem_data) = operand.mem() {
264                    let mem = MemoryOperand {
265                        segment: mem_data.segment,
266                        base: mem_data.base,
267                        index: mem_data.index,
268                        scale: mem_data.scale,
269                        displacement: mem_data.displacement,
270                        disp_size: mem_data.disp_size,
271                    };
272                    EncoderOperand::mem(mem)
273                } else {
274                    EncoderOperand::None
275                }
276            }
277            OperandKind::Immediate | OperandKind::RelativeOffset => {
278                if let Some(value) = operand.imm() {
279                    EncoderOperand::imm(value)
280                } else {
281                    EncoderOperand::None
282                }
283            }
284            _ => EncoderOperand::None,
285        }
286    }
287
288    /// Get an operand by index
289    #[must_use]
290    pub fn operand(&self, index: usize) -> Option<&EncoderOperand> {
291        if index < self.operand_count as usize {
292            Some(&self.operands[index])
293        } else {
294            None
295        }
296    }
297
298    /// Iterate over valid operands
299    pub fn operands(&self) -> impl Iterator<Item = &EncoderOperand> {
300        self.operands[..self.operand_count as usize].iter()
301    }
302
303    /// Check if the request has any operands
304    #[must_use]
305    pub const fn has_operands(&self) -> bool {
306        self.operand_count > 0
307    }
308
309    /// Check if the request has an operand size override
310    #[must_use]
311    pub const fn has_operand_size_override(&self) -> bool {
312        self.operand_size != 0
313    }
314
315    /// Check if the request has an address size override
316    #[must_use]
317    pub const fn has_address_size_override(&self) -> bool {
318        self.address_size != 0
319    }
320
321    /// Check if any operand is a memory operand
322    #[must_use]
323    pub fn has_memory_operand(&self) -> bool {
324        self.operands().any(|op| op.is_memory())
325    }
326
327    /// Check if any operand is an immediate
328    #[must_use]
329    pub fn has_immediate_operand(&self) -> bool {
330        self.operands().any(|op| op.is_immediate())
331    }
332
333    /// Check if any operand is a relative offset
334    #[must_use]
335    pub fn has_relative_operand(&self) -> bool {
336        self.operands().any(|op| op.is_relative())
337    }
338
339    /// Check if any operand is a ZMM register (for AVX-512 detection)
340    #[must_use]
341    pub fn has_zmm_operand(&self) -> bool {
342        self.operands().any(|op| {
343            if let EncoderOperand::Reg(reg) = op {
344                reg.is_zmm()
345            } else {
346                false
347            }
348        })
349    }
350
351    /// Clear all operands
352    pub fn clear_operands(&mut self) {
353        self.operands = [EncoderOperand::None; MAX_ENCODER_OPERANDS];
354        self.operand_count = 0;
355    }
356}
357
358impl Default for EncoderRequest {
359    fn default() -> Self {
360        Self::new(Mnemonic::Invalid)
361    }
362}
363
364#[cfg(test)]
365mod tests {
366    use super::*;
367
368    #[test]
369    fn test_encoder_request_new() {
370        let request = EncoderRequest::new(Mnemonic::MOV);
371        assert_eq!(request.mnemonic, Mnemonic::MOV);
372        assert_eq!(request.operand_count, 0);
373        assert_eq!(request.operand_size, 0);
374        assert_eq!(request.address_size, 0);
375        assert!(request.preferred_encoding.is_none());
376        assert_eq!(request.branch_type, BranchType::None);
377        assert_eq!(request.branch_width, BranchWidth::None);
378    }
379
380    #[test]
381    fn test_encoder_request_with_reg() {
382        let request = EncoderRequest::new(Mnemonic::MOV)
383            .with_reg(Register::RAX)
384            .with_reg(Register::RBX);
385
386        assert_eq!(request.operand_count, 2);
387        assert!(request.operands[0].is_register());
388        assert!(request.operands[1].is_register());
389        assert_eq!(request.operands[0].as_register(), Some(Register::RAX));
390        assert_eq!(request.operands[1].as_register(), Some(Register::RBX));
391    }
392
393    #[test]
394    fn test_encoder_request_with_mem() {
395        let mem = MemoryOperand::base_disp(Register::RBP, -16);
396        let request = EncoderRequest::new(Mnemonic::MOV).with_mem(mem);
397
398        assert_eq!(request.operand_count, 1);
399        assert!(request.operands[0].is_memory());
400    }
401
402    #[test]
403    fn test_encoder_request_with_imm() {
404        let request = EncoderRequest::new(Mnemonic::MOV).with_imm(0x12345678);
405
406        assert_eq!(request.operand_count, 1);
407        assert!(request.operands[0].is_immediate());
408        assert_eq!(request.operands[0].as_immediate(), Some(0x12345678));
409    }
410
411    #[test]
412    fn test_encoder_request_with_rel() {
413        let request = EncoderRequest::new(Mnemonic::JMP).with_rel(-128);
414
415        assert_eq!(request.operand_count, 1);
416        assert!(request.operands[0].is_relative());
417        assert_eq!(request.operands[0].as_relative(), Some(-128));
418    }
419
420    #[test]
421    fn test_encoder_request_with_operand_size() {
422        let request = EncoderRequest::new(Mnemonic::MOV).with_operand_size(32);
423
424        assert_eq!(request.operand_size, 32);
425        assert!(request.has_operand_size_override());
426    }
427
428    #[test]
429    fn test_encoder_request_with_address_size() {
430        let request = EncoderRequest::new(Mnemonic::LEA).with_address_size(32);
431
432        assert_eq!(request.address_size, 32);
433        assert!(request.has_address_size_override());
434    }
435
436    #[test]
437    fn test_encoder_request_max_operands() {
438        let mut request = EncoderRequest::new(Mnemonic::MOV)
439            .with_reg(Register::RAX)
440            .with_reg(Register::RBX)
441            .with_reg(Register::RCX)
442            .with_reg(Register::RDX)
443            .with_reg(Register::RSI);
444
445        // Adding a 6th operand should be ignored
446        request = request.with_reg(Register::RDI);
447
448        assert_eq!(request.operand_count, 5);
449        assert!(request.operand(5).is_none());
450    }
451
452    #[test]
453    fn test_encoder_request_operands_iter() {
454        let request = EncoderRequest::new(Mnemonic::MOV)
455            .with_reg(Register::RAX)
456            .with_reg(Register::RBX);
457
458        let count = request.operands().count();
459        assert_eq!(count, 2);
460    }
461
462    #[test]
463    fn test_encoder_request_has_memory_operand() {
464        let mem = MemoryOperand::base(Register::RAX);
465        let request = EncoderRequest::new(Mnemonic::MOV).with_mem(mem);
466
467        assert!(request.has_memory_operand());
468        assert!(!request.has_immediate_operand());
469    }
470
471    #[test]
472    fn test_encoder_request_has_immediate_operand() {
473        let request = EncoderRequest::new(Mnemonic::MOV).with_imm(0x1234);
474
475        assert!(request.has_immediate_operand());
476        assert!(!request.has_memory_operand());
477    }
478
479    #[test]
480    fn test_encoder_request_clear_operands() {
481        let mut request = EncoderRequest::new(Mnemonic::MOV)
482            .with_reg(Register::RAX)
483            .with_reg(Register::RBX);
484
485        assert_eq!(request.operand_count, 2);
486
487        request.clear_operands();
488
489        assert_eq!(request.operand_count, 0);
490        assert!(!request.has_operands());
491    }
492
493    #[test]
494    fn test_encoder_request_default() {
495        let request = EncoderRequest::default();
496        assert_eq!(request.mnemonic, Mnemonic::Invalid);
497        assert_eq!(request.operand_count, 0);
498        assert!(request.mask_reg.is_none());
499        assert_eq!(request.zeroing_mode, EvexZeroingMode::Merging);
500    }
501
502    #[test]
503    fn test_encoder_request_with_mask() {
504        let request = EncoderRequest::new(Mnemonic::VADDPS).with_mask(Register::K1);
505
506        assert!(request.has_mask());
507        assert_eq!(request.mask_reg, Some(Register::K1));
508        assert!(!request.is_zeroing());
509    }
510
511    #[test]
512    fn test_encoder_request_with_zeroing() {
513        let request = EncoderRequest::new(Mnemonic::VADDPS)
514            .with_mask(Register::K1)
515            .with_zeroing_enabled();
516
517        assert!(request.has_mask());
518        assert!(request.is_zeroing());
519        assert_eq!(request.zeroing_mode, EvexZeroingMode::Zeroing);
520    }
521
522    #[test]
523    fn test_encoder_request_has_zmm_operand() {
524        let request = EncoderRequest::new(Mnemonic::VADDPS)
525            .with_reg(Register::ZMM1)
526            .with_reg(Register::ZMM2)
527            .with_reg(Register::ZMM3);
528
529        assert!(request.has_zmm_operand());
530
531        let request_xmm = EncoderRequest::new(Mnemonic::VADDPS)
532            .with_reg(Register::XMM1)
533            .with_reg(Register::XMM2)
534            .with_reg(Register::XMM3);
535
536        assert!(!request_xmm.has_zmm_operand());
537    }
538
539    #[test]
540    fn test_encoder_request_with_encoding() {
541        let request = EncoderRequest::new(Mnemonic::VADDPS).with_encoding(EncodableEncoding::Evex);
542
543        assert!(request.has_preferred_encoding());
544        assert_eq!(request.preferred_encoding, Some(EncodableEncoding::Evex));
545    }
546
547    #[test]
548    fn test_encoder_request_with_branch_type() {
549        let request = EncoderRequest::new(Mnemonic::JMP).with_branch_type(BranchType::Near);
550
551        assert!(request.has_branch_type());
552        assert_eq!(request.branch_type, BranchType::Near);
553    }
554
555    #[test]
556    fn test_encoder_request_with_broadcast() {
557        let request = EncoderRequest::new(Mnemonic::VADDPS).with_broadcast(BroadcastMode::To4);
558
559        assert!(request.has_broadcast());
560        assert_eq!(request.hints.broadcast_mode, BroadcastMode::To4);
561    }
562
563    #[test]
564    fn test_encoder_request_with_rounding() {
565        let request = EncoderRequest::new(Mnemonic::VADDPS).with_rounding(RoundingMode::RD);
566
567        assert!(request.has_rounding());
568        assert_eq!(request.hints.rounding_mode, Some(RoundingMode::RD));
569    }
570
571    #[test]
572    fn test_encoder_request_with_sae() {
573        let request = EncoderRequest::new(Mnemonic::VADDPS).with_sae();
574
575        assert!(request.has_sae());
576    }
577}