1 //! A DSL for describing x64 instruction formats--the shape of the operands. 2 //! 3 //! Every instruction has a format that corresponds to its encoding's expected 4 //! operands. The format is what allows us to generate code that accepts 5 //! operands of the right type and check that the operands are used in the right 6 //! way. 7 //! 8 //! The entry point for this module is [`fmt`]. 9 //! 10 //! ``` 11 //! # use cranelift_assembler_x64_meta::dsl::{fmt, rw, r, Location::*}; 12 //! let f = fmt("rm", [rw(r32), r(rm32)]); 13 //! assert_eq!(f.to_string(), "rm(r32[rw], rm32)") 14 //! ``` 15 16 /// An abbreviated constructor for an instruction "format." 17 /// 18 /// These model what the reference manual calls "instruction operand encodings," 19 /// usually defined in a table after an instruction's opcodes. 20 pub fn fmt(name: impl Into<String>, operands: impl IntoIterator<Item = Operand>) -> Format { 21 Format { 22 name: name.into(), 23 operands: operands.into_iter().collect(), 24 eflags: Eflags::default(), 25 } 26 } 27 28 /// An abbreviated constructor for a "read-write" operand. 29 /// 30 /// # Panics 31 /// 32 /// This function panics if the location is an immediate (i.e., an immediate 33 /// cannot be written to). 34 #[must_use] 35 pub fn rw(op: impl Into<Operand>) -> Operand { 36 let op = op.into(); 37 assert!(!matches!(op.location.kind(), OperandKind::Imm(_))); 38 Operand { 39 mutability: Mutability::ReadWrite, 40 ..op 41 } 42 } 43 44 /// An abbreviated constructor for a "read" operand. 45 #[must_use] 46 pub fn r(op: impl Into<Operand>) -> Operand { 47 let op = op.into(); 48 assert!(op.mutability.is_read()); 49 op 50 } 51 52 /// An abbreviated constructor for a "write" operand. 53 #[must_use] 54 pub fn w(op: impl Into<Operand>) -> Operand { 55 let op = op.into(); 56 Operand { 57 mutability: Mutability::Write, 58 ..op 59 } 60 } 61 62 /// An abbreviated constructor for a memory operand that requires alignment. 63 pub fn align(location: Location) -> Operand { 64 assert!(location.uses_memory()); 65 Operand { 66 align: true, 67 ..Operand::from(location) 68 } 69 } 70 71 /// An abbreviated constructor for an operand that is used by the instruction 72 /// but not visible in its disassembly. 73 pub fn implicit(location: Location) -> Operand { 74 assert!(matches!(location.kind(), OperandKind::FixedReg(_))); 75 Operand { 76 implicit: true, 77 ..Operand::from(location) 78 } 79 } 80 81 /// An abbreviated constructor for a "read" operand that is sign-extended to 64 82 /// bits (quadword). 83 /// 84 /// # Panics 85 /// 86 /// This function panics if the location size is too large to extend. 87 #[must_use] 88 pub fn sxq(location: Location) -> Operand { 89 assert!(location.bits() <= 64); 90 Operand { 91 extension: Extension::SignExtendQuad, 92 ..Operand::from(location) 93 } 94 } 95 96 /// An abbreviated constructor for a "read" operand that is sign-extended to 32 97 /// bits (longword). 98 /// 99 /// # Panics 100 /// 101 /// This function panics if the location size is too large to extend. 102 #[must_use] 103 pub fn sxl(location: Location) -> Operand { 104 assert!(location.bits() <= 32); 105 Operand { 106 extension: Extension::SignExtendLong, 107 ..Operand::from(location) 108 } 109 } 110 111 /// An abbreviated constructor for a "read" operand that is sign-extended to 16 112 /// bits (word). 113 /// 114 /// # Panics 115 /// 116 /// This function panics if the location size is too large to extend. 117 #[must_use] 118 pub fn sxw(location: Location) -> Operand { 119 assert!(location.bits() <= 16); 120 Operand { 121 extension: Extension::SignExtendWord, 122 ..Operand::from(location) 123 } 124 } 125 126 /// A format describes the operands for an instruction. 127 #[derive(Clone)] 128 pub struct Format { 129 /// This name, when combined with the instruction mnemonic, uniquely 130 /// identifies an instruction. The reference manual uses this name in the 131 /// "Instruction Operand Encoding" table. 132 pub name: String, 133 /// These operands should match the "Instruction" column in the reference 134 /// manual. 135 pub operands: Vec<Operand>, 136 /// This should match eflags description of an instruction. 137 pub eflags: Eflags, 138 } 139 140 impl Format { 141 /// Iterate over the operand locations. 142 pub fn locations(&self) -> impl Iterator<Item = &Location> + '_ { 143 self.operands.iter().map(|o| &o.location) 144 } 145 146 /// Return the location of the operand that uses memory, if any; return 147 /// `None` otherwise. 148 pub fn uses_memory(&self) -> Option<Location> { 149 debug_assert!( 150 self.locations() 151 .copied() 152 .filter(Location::uses_memory) 153 .count() 154 <= 1 155 ); 156 self.locations().copied().find(Location::uses_memory) 157 } 158 159 /// Return `true` if any of the operands accepts a register (i.e., not an 160 /// immediate); return `false` otherwise. 161 #[must_use] 162 pub fn uses_register(&self) -> bool { 163 self.locations().any(Location::uses_register) 164 } 165 166 /// Collect into operand kinds. 167 pub fn operands_by_kind(&self) -> Vec<OperandKind> { 168 self.locations().map(Location::kind).collect() 169 } 170 171 /// Set the EFLAGS mutability for this instruction. 172 pub fn flags(mut self, eflags: Eflags) -> Self { 173 self.eflags = eflags; 174 self 175 } 176 177 /// Return true if an instruction uses EFLAGS. 178 pub fn uses_eflags(&self) -> bool { 179 self.eflags != Eflags::None 180 } 181 } 182 183 impl core::fmt::Display for Format { 184 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { 185 let Format { 186 name, 187 operands, 188 eflags, 189 } = self; 190 let operands = operands 191 .iter() 192 .map(|operand| format!("{operand}")) 193 .collect::<Vec<_>>() 194 .join(", "); 195 write!(f, "{name}({operands})")?; 196 197 if *eflags != Eflags::None { 198 write!(f, "[flags:{eflags}]")?; 199 } 200 201 Ok(()) 202 } 203 } 204 205 /// An x64 operand. 206 /// 207 /// This is designed to look and feel like the operands as expressed in Intel's 208 /// _Instruction Set Reference_. 209 /// 210 /// ``` 211 /// # use cranelift_assembler_x64_meta::dsl::{align, r, rw, sxq, Location::*}; 212 /// assert_eq!(r(r8).to_string(), "r8"); 213 /// assert_eq!(rw(rm16).to_string(), "rm16[rw]"); 214 /// assert_eq!(sxq(imm32).to_string(), "imm32[sxq]"); 215 /// assert_eq!(align(xmm_m128).to_string(), "xmm_m128[align]"); 216 /// ``` 217 #[derive(Clone, Copy, Debug, PartialEq)] 218 pub struct Operand { 219 /// The location of the data: memory, register, immediate. 220 pub location: Location, 221 /// An operand can be read-only or read-write. 222 pub mutability: Mutability, 223 /// Some operands are sign- or zero-extended. 224 pub extension: Extension, 225 /// Some memory operands require alignment; `true` indicates that the memory 226 /// address used in the operand must align to the size of the operand (e.g., 227 /// `m128` must be 16-byte aligned). 228 pub align: bool, 229 /// Some register operands are implicit: that is, they do not appear in the 230 /// disassembled output even though they are used in the instruction. 231 pub implicit: bool, 232 } 233 234 impl core::fmt::Display for Operand { 235 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { 236 let Self { 237 location, 238 mutability, 239 extension, 240 align, 241 implicit, 242 } = self; 243 write!(f, "{location}")?; 244 let mut flags = vec![]; 245 if !matches!(mutability, Mutability::Read) { 246 flags.push(format!("{mutability}")); 247 } 248 if !matches!(extension, Extension::None) { 249 flags.push(format!("{extension}")); 250 } 251 if *align != false { 252 flags.push("align".to_owned()); 253 } 254 if *implicit { 255 flags.push("implicit".to_owned()); 256 } 257 if !flags.is_empty() { 258 write!(f, "[{}]", flags.join(","))?; 259 } 260 Ok(()) 261 } 262 } 263 264 impl From<Location> for Operand { 265 fn from(location: Location) -> Self { 266 let mutability = Mutability::default(); 267 let extension = Extension::default(); 268 let align = false; 269 let implicit = false; 270 Self { 271 location, 272 mutability, 273 extension, 274 align, 275 implicit, 276 } 277 } 278 } 279 280 /// The kind of register used in a [`Location`]. 281 pub enum RegClass { 282 Gpr, 283 Xmm, 284 } 285 286 impl core::fmt::Display for RegClass { 287 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { 288 match self { 289 RegClass::Gpr => write!(f, "Gpr"), 290 RegClass::Xmm => write!(f, "Xmm"), 291 } 292 } 293 } 294 295 /// An operand location, as expressed in Intel's _Instruction Set Reference_. 296 #[derive(Clone, Copy, Debug, PartialEq)] 297 #[allow(non_camel_case_types, reason = "makes DSL definitions easier to read")] 298 pub enum Location { 299 // Fixed registers. 300 al, 301 ax, 302 eax, 303 rax, 304 rbx, 305 dx, 306 edx, 307 rdx, 308 cl, 309 rcx, 310 xmm0, 311 312 // Immediate values. 313 imm8, 314 imm16, 315 imm32, 316 imm64, 317 318 // General-purpose registers, and their memory forms. 319 r8, 320 r16, 321 r32, 322 r32a, 323 r32b, 324 r64, 325 r64a, 326 r64b, 327 rm8, 328 rm16, 329 rm32, 330 rm64, 331 332 // XMM registers, and their memory forms. 333 xmm1, 334 xmm2, 335 xmm3, 336 xmm_m8, 337 xmm_m16, 338 xmm_m32, 339 xmm_m64, 340 xmm_m128, 341 342 // Memory-only locations. 343 m8, 344 m16, 345 m32, 346 m64, 347 m128, 348 } 349 350 impl Location { 351 /// Return the number of bits accessed. 352 #[must_use] 353 pub fn bits(&self) -> u16 { 354 use Location::*; 355 match self { 356 al | cl | imm8 | r8 | rm8 | m8 | xmm_m8 => 8, 357 ax | dx | imm16 | r16 | rm16 | m16 | xmm_m16 => 16, 358 eax | edx | imm32 | r32 | r32a | r32b | rm32 | m32 | xmm_m32 => 32, 359 rax | rbx | rcx | rdx | imm64 | r64 | r64a | r64b | rm64 | m64 | xmm_m64 => 64, 360 xmm1 | xmm2 | xmm3 | xmm_m128 | xmm0 | m128 => 128, 361 } 362 } 363 364 /// Return the number of bytes accessed, for convenience. 365 #[must_use] 366 pub fn bytes(&self) -> u16 { 367 self.bits() / 8 368 } 369 370 /// Return `true` if the location accesses memory; `false` otherwise. 371 #[must_use] 372 pub fn uses_memory(&self) -> bool { 373 use OperandKind::*; 374 match self.kind() { 375 FixedReg(_) | Imm(_) | Reg(_) => false, 376 RegMem(_) | Mem(_) => true, 377 } 378 } 379 380 /// Return `true` if any of the operands accepts a register (i.e., not an 381 /// immediate); return `false` otherwise. 382 #[must_use] 383 pub fn uses_register(&self) -> bool { 384 use OperandKind::*; 385 match self.kind() { 386 Imm(_) => false, 387 FixedReg(_) | Reg(_) | RegMem(_) | Mem(_) => true, 388 } 389 } 390 391 /// Convert the location to an [`OperandKind`]. 392 #[must_use] 393 pub fn kind(&self) -> OperandKind { 394 use Location::*; 395 match self { 396 al | ax | eax | rax | rbx | cl | rcx | dx | edx | rdx | xmm0 => { 397 OperandKind::FixedReg(*self) 398 } 399 imm8 | imm16 | imm32 | imm64 => OperandKind::Imm(*self), 400 r8 | r16 | r32 | r32a | r32b | r64 | r64a | r64b | xmm1 | xmm2 | xmm3 => { 401 OperandKind::Reg(*self) 402 } 403 rm8 | rm16 | rm32 | rm64 | xmm_m8 | xmm_m16 | xmm_m32 | xmm_m64 | xmm_m128 => { 404 OperandKind::RegMem(*self) 405 } 406 m8 | m16 | m32 | m64 | m128 => OperandKind::Mem(*self), 407 } 408 } 409 410 /// If a location directly uses data from a register, return the register 411 /// class; otherwise, return `None`. Memory-only locations, though their 412 /// address is stored in a register, use data from memory and thus also 413 /// return `None`. 414 #[must_use] 415 pub fn reg_class(&self) -> Option<RegClass> { 416 use Location::*; 417 match self { 418 imm8 | imm16 | imm32 | imm64 | m8 | m16 | m32 | m64 | m128 => None, 419 al | ax | eax | rax | rbx | cl | rcx | dx | edx | rdx | r8 | r16 | r32 | r32a 420 | r32b | r64 | r64a | r64b | rm8 | rm16 | rm32 | rm64 => Some(RegClass::Gpr), 421 xmm1 | xmm2 | xmm3 | xmm_m8 | xmm_m16 | xmm_m32 | xmm_m64 | xmm_m128 | xmm0 => { 422 Some(RegClass::Xmm) 423 } 424 } 425 } 426 } 427 428 impl core::fmt::Display for Location { 429 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { 430 use Location::*; 431 match self { 432 imm8 => write!(f, "imm8"), 433 imm16 => write!(f, "imm16"), 434 imm32 => write!(f, "imm32"), 435 imm64 => write!(f, "imm64"), 436 437 al => write!(f, "al"), 438 ax => write!(f, "ax"), 439 eax => write!(f, "eax"), 440 rax => write!(f, "rax"), 441 rbx => write!(f, "rbx"), 442 cl => write!(f, "cl"), 443 rcx => write!(f, "rcx"), 444 dx => write!(f, "dx"), 445 edx => write!(f, "edx"), 446 rdx => write!(f, "rdx"), 447 xmm0 => write!(f, "xmm0"), 448 449 r8 => write!(f, "r8"), 450 r16 => write!(f, "r16"), 451 r32 => write!(f, "r32"), 452 r32a => write!(f, "r32a"), 453 r32b => write!(f, "r32b"), 454 r64 => write!(f, "r64"), 455 r64a => write!(f, "r64a"), 456 r64b => write!(f, "r64b"), 457 rm8 => write!(f, "rm8"), 458 rm16 => write!(f, "rm16"), 459 rm32 => write!(f, "rm32"), 460 rm64 => write!(f, "rm64"), 461 462 xmm1 => write!(f, "xmm1"), 463 xmm2 => write!(f, "xmm2"), 464 xmm3 => write!(f, "xmm3"), 465 xmm_m8 => write!(f, "xmm_m8"), 466 xmm_m16 => write!(f, "xmm_m16"), 467 xmm_m32 => write!(f, "xmm_m32"), 468 xmm_m64 => write!(f, "xmm_m64"), 469 xmm_m128 => write!(f, "xmm_m128"), 470 471 m8 => write!(f, "m8"), 472 m16 => write!(f, "m16"), 473 m32 => write!(f, "m32"), 474 m64 => write!(f, "m64"), 475 m128 => write!(f, "m128"), 476 } 477 } 478 } 479 480 /// Organize the operand locations by kind. 481 /// 482 /// ``` 483 /// # use cranelift_assembler_x64_meta::dsl::{OperandKind, Location}; 484 /// let k: OperandKind = Location::imm32.kind(); 485 /// ``` 486 #[derive(Clone, Copy, Debug)] 487 pub enum OperandKind { 488 FixedReg(Location), 489 Imm(Location), 490 Reg(Location), 491 RegMem(Location), 492 Mem(Location), 493 } 494 495 /// x64 operands can be mutable or not. 496 /// 497 /// ``` 498 /// # use cranelift_assembler_x64_meta::dsl::{r, rw, Location::r8, Mutability}; 499 /// assert_eq!(r(r8).mutability, Mutability::Read); 500 /// assert_eq!(rw(r8).mutability, Mutability::ReadWrite); 501 /// ``` 502 #[derive(Clone, Copy, Debug, PartialEq)] 503 pub enum Mutability { 504 Read, 505 ReadWrite, 506 Write, 507 } 508 509 impl Mutability { 510 /// Returns whether this represents a read of the operand in question. 511 /// 512 /// Note that for read/write operands this returns `true`. 513 pub fn is_read(&self) -> bool { 514 match self { 515 Mutability::Read | Mutability::ReadWrite => true, 516 Mutability::Write => false, 517 } 518 } 519 520 /// Returns whether this represents a write of the operand in question. 521 /// 522 /// Note that for read/write operands this returns `true`. 523 pub fn is_write(&self) -> bool { 524 match self { 525 Mutability::Read => false, 526 Mutability::ReadWrite | Mutability::Write => true, 527 } 528 } 529 } 530 531 impl Default for Mutability { 532 fn default() -> Self { 533 Self::Read 534 } 535 } 536 537 impl core::fmt::Display for Mutability { 538 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 539 match self { 540 Self::Read => write!(f, "r"), 541 Self::ReadWrite => write!(f, "rw"), 542 Self::Write => write!(f, "w"), 543 } 544 } 545 } 546 547 /// x64 operands may be sign- or zero-extended. 548 /// 549 /// ``` 550 /// # use cranelift_assembler_x64_meta::dsl::{Location::r8, sxw, Extension}; 551 /// assert_eq!(sxw(r8).extension, Extension::SignExtendWord); 552 /// ``` 553 #[derive(Clone, Copy, Debug, PartialEq)] 554 pub enum Extension { 555 None, 556 SignExtendQuad, 557 SignExtendLong, 558 SignExtendWord, 559 } 560 561 impl Extension { 562 /// Check if the extension is sign-extended. 563 #[must_use] 564 pub fn is_sign_extended(&self) -> bool { 565 matches!( 566 self, 567 Self::SignExtendQuad | Self::SignExtendLong | Self::SignExtendWord 568 ) 569 } 570 } 571 572 impl Default for Extension { 573 fn default() -> Self { 574 Self::None 575 } 576 } 577 578 impl core::fmt::Display for Extension { 579 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 580 match self { 581 Extension::None => write!(f, ""), 582 Extension::SignExtendQuad => write!(f, "sxq"), 583 Extension::SignExtendLong => write!(f, "sxl"), 584 Extension::SignExtendWord => write!(f, "sxw"), 585 } 586 } 587 } 588 589 /// Describes if an instruction uses EFLAGS, and whether it reads, writes, or 590 /// reads/writes the EFLAGS register. 591 /// In the future, we might want to model specific EFLAGS bits instead of the 592 /// entire EFLAGS register. 593 /// Some related discussion in this GitHub issue 594 /// https://github.com/bytecodealliance/wasmtime/issues/10298 595 #[derive(Clone, Copy, Debug, PartialEq)] 596 pub enum Eflags { 597 None, 598 R, 599 W, 600 RW, 601 } 602 603 impl Eflags { 604 /// Returns whether this represents a read of any bit in the EFLAGS 605 /// register. 606 pub fn is_read(&self) -> bool { 607 match self { 608 Eflags::None | Eflags::W => false, 609 Eflags::R | Eflags::RW => true, 610 } 611 } 612 613 /// Returns whether this represents a writes to any bit in the EFLAGS 614 /// register. 615 pub fn is_write(&self) -> bool { 616 match self { 617 Eflags::None | Eflags::R => false, 618 Eflags::W | Eflags::RW => true, 619 } 620 } 621 } 622 623 impl Default for Eflags { 624 fn default() -> Self { 625 Self::None 626 } 627 } 628 629 impl core::fmt::Display for Eflags { 630 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 631 match self { 632 Self::None => write!(f, ""), 633 Self::R => write!(f, "r"), 634 Self::W => write!(f, "w"), 635 Self::RW => write!(f, "rw"), 636 } 637 } 638 } 639