Skip to content

Initial support for vex encoding for the new assembler. #10754

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions cranelift/assembler-x64/meta/src/dsl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ mod features;
pub mod format;

pub use encoding::{
Encoding, Group1Prefix, Group2Prefix, Group3Prefix, Group4Prefix, Opcodes, Prefixes, Rex,
Encoding, Group1Prefix, Group2Prefix, Group3Prefix, Group4Prefix, Opcodes, Prefixes, Rex, Vex,
VexLength, VexMMMMM, VexPP, rex, vex,
};
pub use encoding::{rex, vex};
pub use features::{ALL_FEATURES, Feature, Features};
pub use format::{Extension, Format, Location, Mutability, Operand, OperandKind, RegClass};
pub use format::{align, fmt, implicit, r, rw, sxl, sxq, sxw, w};
Expand Down
126 changes: 118 additions & 8 deletions cranelift/assembler-x64/meta/src/dsl/encoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,15 @@ pub fn rex(opcode: impl Into<Opcodes>) -> Rex {

/// An abbreviated constructor for VEX-encoded instructions.
#[must_use]
pub fn vex() -> Vex {
Vex {}
pub fn vex(opcode: impl Into<Opcodes>) -> Vex {
Vex {
opcodes: opcode.into(),
w: false,
length: VexLength::_128,
mmmmm: VexMMMMM::None,
pp: VexPP::None,
imm: None,
}
}

/// Enumerate the ways x64 encodes instructions.
Expand All @@ -48,7 +55,7 @@ impl Encoding {
pub fn validate(&self, operands: &[Operand]) {
match self {
Encoding::Rex(rex) => rex.validate(operands),
Encoding::Vex(vex) => vex.validate(),
Encoding::Vex(vex) => vex.validate(operands),
}
}
}
Expand All @@ -57,7 +64,7 @@ impl fmt::Display for Encoding {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Encoding::Rex(rex) => write!(f, "{rex}"),
Encoding::Vex(_vex) => todo!(),
Encoding::Vex(vex) => write!(f, "{vex}"),
}
}
}
Expand Down Expand Up @@ -563,7 +570,7 @@ pub enum Imm {
}

impl Imm {
fn bits(&self) -> u8 {
fn bits(&self) -> u16 {
match self {
Imm::None => 0,
Imm::ib => 8,
Expand All @@ -586,10 +593,113 @@ impl fmt::Display for Imm {
}
}

pub struct Vex {}
pub struct Vex {
pub opcodes: Opcodes,
pub w: bool,
pub length: VexLength,
pub mmmmm: VexMMMMM,
pub pp: VexPP,
pub imm: Option<u8>,
}

#[derive(PartialEq)]
pub enum VexPP {
None,
/// Operand size override -- here, denoting "16-bit operation".
_66,
/// REPNE, but no specific meaning here -- is just an opcode extension.
_F2,
/// REP/REPE, but no specific meaning here -- is just an opcode extension.
_F3,
}

impl fmt::Display for VexPP {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
VexPP::None => write!(f, "None"),
VexPP::_66 => write!(f, "_66"),
VexPP::_F3 => write!(f, "_F3"),
VexPP::_F2 => write!(f, "_F2"),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For printing VexPP and VexMMMMM, if we print using . instead of _ then we can match the manual a bit more closely. E.g., VEX.128.0F.WIG 58 /r.

Copy link
Contributor Author

@jlb6740 jlb6740 May 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the underscore is needed because this naming is not just used for printing, but it also used as part of syntax for an associated item:

vex.vvvv = Some(self.xmm2.enc()); // cranelift/assembler-x64/meta/src/generate/format.rs:135
vex.prefix = LegacyPrefix::.66; // cranelift/assembler-x64/meta/src/generate/format.rs:136
vex.map = OpcodeMap::_0F; // cranelift/assembler-x64/meta/src/generate/format.rs:137

where here the LegacyPrefix::.66 is not valid syntax.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I can get around this. Will see.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes .. could not remove but any suggestions to overcome the "expected identifier" error welcomed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you write!(f, "66") here, then we can add whatever prefix we want later, fmtln!(f, "_{}", vex.pp) or fmtln!(f, ".{}", vex.pp).

}
}
}

pub enum VexLength {
_128,
}

impl fmt::Display for VexLength {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
VexLength::_128 => write!(f, "_128"),
}
}
}

#[derive(PartialEq)]
pub enum VexMMMMM {
None,
_OF,
/// Operand size override -- here, denoting "16-bit operation".
_OF3A,
/// The lock prefix.
_OF38,
}

impl fmt::Display for VexMMMMM {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
VexMMMMM::None => write!(f, "None"),
VexMMMMM::_OF => write!(f, "_0F"),
VexMMMMM::_OF3A => write!(f, "_OF3A"),
VexMMMMM::_OF38 => write!(f, "_OF38"),
}
}
}

/// Describe the register index to use. This wrapper is a type-safe way to pass
/// around the registers defined in `inst/regs.rs`.
#[derive(Debug, Copy, Clone, Default)]
pub struct Register(u8);
impl From<u8> for Register {
fn from(reg: u8) -> Self {
debug_assert!(reg < 16);
Self(reg)
}
}
impl Into<u8> for Register {
fn into(self) -> u8 {
self.0
}
}
Comment on lines +660 to +674
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need this, right?


impl Vex {
fn validate(&self) {
todo!()
pub fn length(self, length: VexLength) -> Self {
Self { length, ..self }
}
pub fn pp(self, pp: VexPP) -> Self {
Self { pp, ..self }
}
pub fn mmmmm(self, mmmmm: VexMMMMM) -> Self {
Self { mmmmm, ..self }
}

fn validate(&self, _operands: &[Operand]) {}
}

impl From<Vex> for Encoding {
fn from(vex: Vex) -> Encoding {
Encoding::Vex(vex)
}
}

impl fmt::Display for Vex {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "VEX")?;
match self.length {
VexLength::_128 => write!(f, ".128")?,
}
write!(f, " {:#04x}", self.opcodes.primary)?;
Ok(())
}
}
26 changes: 16 additions & 10 deletions cranelift/assembler-x64/meta/src/dsl/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,6 @@ pub enum Location {
rm64,

// XMM registers, and their memory forms.
xmm,
xmm_m32,
xmm_m64,
xmm_m128,
Expand All @@ -310,25 +309,28 @@ pub enum Location {
m16,
m32,
m64,
xmm1,
xmm2,
xmm3,
}

impl Location {
/// Return the number of bits accessed.
#[must_use]
pub fn bits(&self) -> u8 {
pub fn bits(&self) -> u16 {
use Location::*;
match self {
al | cl | imm8 | r8 | rm8 | m8 => 8,
ax | dx | imm16 | r16 | rm16 | m16 => 16,
eax | edx | imm32 | r32 | rm32 | m32 | xmm_m32 => 32,
rax | rdx | r64 | rm64 | m64 | xmm_m64 => 64,
xmm | xmm_m128 => 128,
xmm1 | xmm2 | xmm3 | xmm_m128 => 128,
}
}

/// Return the number of bytes accessed, for convenience.
#[must_use]
pub fn bytes(&self) -> u8 {
pub fn bytes(&self) -> u16 {
self.bits() / 8
}

Expand All @@ -338,8 +340,8 @@ impl Location {
use Location::*;
match self {
al | ax | eax | rax | cl | dx | edx | rdx | imm8 | imm16 | imm32 | r8 | r16 | r32
| r64 | xmm => false,
rm8 | rm16 | rm32 | rm64 | xmm_m32 | xmm_m64 | xmm_m128 | m8 | m16 | m32 | m64 => true,
| r64 | xmm1 | xmm2 | xmm3 => false,
rm8 | rm16 | rm32 | rm64 | m8 | m16 | m32 | m64 | xmm_m32 | xmm_m64 | xmm_m128 => true,
}
}

Expand All @@ -351,7 +353,8 @@ impl Location {
match self {
imm8 | imm16 | imm32 => false,
al | ax | eax | rax | cl | dx | edx | rdx | r8 | r16 | r32 | r64 | rm8 | rm16
| rm32 | rm64 | xmm | xmm_m32 | xmm_m64 | xmm_m128 | m8 | m16 | m32 | m64 => true,
| rm32 | rm64 | m8 | m16 | m32 | m64 | xmm1 | xmm2 | xmm3 | xmm_m32 | xmm_m64
| xmm_m128 => true,
}
}

Expand All @@ -362,7 +365,7 @@ impl Location {
match self {
al | ax | eax | rax | cl | dx | edx | rdx => OperandKind::FixedReg(*self),
imm8 | imm16 | imm32 => OperandKind::Imm(*self),
r8 | r16 | r32 | r64 | xmm => OperandKind::Reg(*self),
r8 | r16 | r32 | r64 | xmm1 | xmm2 | xmm3 => OperandKind::Reg(*self),
rm8 | rm16 | rm32 | rm64 | xmm_m32 | xmm_m64 | xmm_m128 => OperandKind::RegMem(*self),
m8 | m16 | m32 | m64 => OperandKind::Mem(*self),
}
Expand All @@ -379,7 +382,7 @@ impl Location {
imm8 | imm16 | imm32 | m8 | m16 | m32 | m64 => None,
al | ax | eax | rax | cl | dx | edx | rdx | r8 | r16 | r32 | r64 | rm8 | rm16
| rm32 | rm64 => Some(RegClass::Gpr),
xmm | xmm_m32 | xmm_m64 | xmm_m128 => Some(RegClass::Xmm),
xmm1 | xmm2 | xmm3 | xmm_m32 | xmm_m64 | xmm_m128 => Some(RegClass::Xmm),
}
}
}
Expand Down Expand Up @@ -410,7 +413,6 @@ impl core::fmt::Display for Location {
rm32 => write!(f, "rm32"),
rm64 => write!(f, "rm64"),

xmm => write!(f, "xmm"),
xmm_m32 => write!(f, "xmm_m32"),
xmm_m64 => write!(f, "xmm_m64"),
xmm_m128 => write!(f, "xmm_m128"),
Expand All @@ -419,6 +421,10 @@ impl core::fmt::Display for Location {
m16 => write!(f, "m16"),
m32 => write!(f, "m32"),
m64 => write!(f, "m64"),

xmm1 => write!(f, "xmm1"),
xmm2 => write!(f, "xmm2"),
xmm3 => write!(f, "xmm3"),
}
}
}
Expand Down
41 changes: 36 additions & 5 deletions cranelift/assembler-x64/meta/src/generate/format.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
//! Generate format-related Rust code; this also includes generation of encoding
//! Rust code.

use super::{Formatter, fmtln};
use crate::dsl;

Expand Down Expand Up @@ -48,6 +47,38 @@ impl dsl::Format {
self.generate_immediate(f);
}

pub fn generate_vex_encoding(&self, f: &mut Formatter, vex: &dsl::Vex) {
use dsl::OperandKind::{Reg, RegMem};
f.empty_line();
f.comment("Emit New VEX prefix.");

match self.operands_by_kind().as_slice() {
[Reg(xmm1), Reg(xmm2), RegMem(xmm_m128)] => {
fmtln!(
f,
"vex_instruction::<R>(
0x{:0x},
VexVectorLength::{},
VexPP::{},
OpcodeMap::{},
self.{}.enc(),
Some(self.{}.enc()),
Some(self.{}),
{}).encode(buf, off);",
vex.opcodes.primary,
vex.length.to_string(),
vex.pp.to_string(),
vex.mmmmm.to_string(),
xmm1,
xmm2,
xmm_m128,
"None"
);
}
_ => unimplemented!(),
}
}

/// `buf.put1(...);`
fn generate_prefixes(&self, f: &mut Formatter, rex: &dsl::Rex) {
if !rex.opcodes.prefixes.is_empty() {
Expand Down Expand Up @@ -135,9 +166,9 @@ impl dsl::Format {
fmtln!(f, "let rex = self.{dst}.as_rex_prefix(src, {bits});");
}

[Reg(dst), Reg(xmm), Imm(_)] | [Reg(dst), Reg(xmm)] => {
[Reg(dst), Reg(xmm2), Imm(_)] | [Reg(dst), Reg(xmm2)] => {
fmtln!(f, "let reg = self.{dst}.enc();");
fmtln!(f, "let rm = self.xmm.enc();");
fmtln!(f, "let rm = self.xmm2.enc();");
fmtln!(f, "let rex = RexPrefix::two_op(reg, rm, {bits});");
}

Expand Down Expand Up @@ -197,8 +228,8 @@ impl dsl::Format {
);
}

[Reg(dst), Reg(xmm), Imm(_)] | [Reg(dst), Reg(xmm)] => {
fmtln!(f, "self.xmm.encode_modrm(buf, self.{dst}.enc());");
[Reg(dst), Reg(xmm2), Imm(_)] | [Reg(dst), Reg(xmm2)] => {
fmtln!(f, "self.xmm2.encode_modrm(buf, self.{dst}.enc());");
}

unknown => unimplemented!("unknown pattern: {unknown:?}"),
Expand Down
2 changes: 1 addition & 1 deletion cranelift/assembler-x64/meta/src/generate/inst.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ impl dsl::Inst {

match &self.encoding {
dsl::Encoding::Rex(rex) => self.format.generate_rex_encoding(f, rex),
dsl::Encoding::Vex(_) => todo!(),
dsl::Encoding::Vex(vex) => self.format.generate_vex_encoding(f, vex),
}
},
);
Expand Down
8 changes: 5 additions & 3 deletions cranelift/assembler-x64/meta/src/generate/operand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ impl dsl::Operand {
}
r8 | r16 | r32 | r64 => format!("Gpr<R::{mut_}Gpr>"),
rm8 | rm16 | rm32 | rm64 => format!("GprMem<R::{mut_}Gpr, R::ReadGpr>"),
xmm => format!("Xmm<R::{mut_}Xmm>"),
xmm1 | xmm2 | xmm3 => {
format!("Xmm<R::{mut_}Xmm>")
}
xmm_m32 | xmm_m64 | xmm_m128 => {
format!("XmmMem<R::{mut_}Xmm, R::ReadGpr>")
}
Expand Down Expand Up @@ -53,7 +55,7 @@ impl dsl::Location {
Some(size) => format!("self.{self}.to_string({size})"),
None => unreachable!(),
},
xmm | xmm_m32 | xmm_m64 | xmm_m128 | m8 | m16 | m32 | m64 => {
xmm_m32 | xmm_m64 | xmm1 | xmm2 | xmm3 | xmm_m128 | m8 | m16 | m32 | m64 => {
format!("self.{self}.to_string()")
}
}
Expand All @@ -72,7 +74,7 @@ impl dsl::Location {
m8 | m16 | m32 | m64 => {
panic!("no need to generate a size for memory-only access")
}
xmm | xmm_m32 | xmm_m64 | xmm_m128 => {
xmm_m32 | xmm_m64 | xmm1 | xmm2 | xmm3 | xmm_m128 => {
panic!("no need to generate a size for XMM-sized access")
}
}
Expand Down
Loading
Loading