Skip to content
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

asm: add more ALU instructions, fix sign-extended immediates #10214

Closed
wants to merge 5 commits into from
Closed
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
1 change: 1 addition & 0 deletions cranelift/assembler-x64/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ pedantic = "warn"
module_name_repetitions = { level = "allow", priority = 1 }
similar_names = { level = "allow", priority = 1 }
wildcard_imports = { level = "allow", priority = 1 }
too_many_lines = { level = "allow", priority = 1 }

[features]
fuzz = ['dep:arbitrary', 'dep:capstone']
15 changes: 7 additions & 8 deletions cranelift/assembler-x64/meta/src/dsl/encoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ pub fn rex(opcode: u8) -> Rex {
opcode,
w: false,
r: false,
digit: 0,
digit: None,
imm: Imm::None,
}
}
Expand Down Expand Up @@ -93,7 +93,7 @@ pub struct Rex {
/// ModR/M byte of the instruction uses only the r/m (register or memory)
/// operand. The reg field contains the digit that provides an extension to
/// the instruction's opcode."
pub digit: u8,
pub digit: Option<u8>,
/// The number of bits used as an immediate operand to the instruction.
///
/// From the reference manual: "a 1-byte (ib), 2-byte (iw), 4-byte (id) or
Expand Down Expand Up @@ -132,8 +132,8 @@ impl Rex {
/// Panics if `digit` is too large.
#[must_use]
pub fn digit(self, digit: u8) -> Self {
assert!(digit < 8);
Self { digit, ..self }
assert!(digit <= 0b111, "must fit in 3 bits");
Self { digit: Some(digit), ..self }
}

/// Append a byte-sized immediate operand (8-bit); equivalent to `ib` in the
Expand Down Expand Up @@ -188,8 +188,7 @@ impl Rex {
/// _Instruction Format_, of the Intel® 64 and IA-32 Architectures Software
/// Developer’s Manual, Volume 2A.
fn validate(&self, operands: &[Operand]) {
assert!(self.digit < 8);
assert!(!(self.r && self.digit > 0));
assert!(!(self.r && self.digit.is_some()));
assert!(!(self.r && self.imm != Imm::None));
assert!(
!(self.w && (self.prefix.contains_66())),
Expand Down Expand Up @@ -241,8 +240,8 @@ impl fmt::Display for Rex {
if self.r {
write!(f, " /r")?;
}
if self.digit > 0 {
write!(f, " /{}", self.digit)?;
if let Some(digit) = self.digit {
write!(f, " /{digit}")?;
}
if self.imm != Imm::None {
write!(f, " {}", self.imm)?;
Expand Down
10 changes: 8 additions & 2 deletions cranelift/assembler-x64/meta/src/dsl/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,14 @@ pub enum Extension {
SignExtendQuad,
SignExtendLong,
SignExtendWord,
ZeroExtend,
}

impl Extension {
/// Check if the extension is sign-extended.
#[must_use]
pub fn is_sign_extended(&self) -> bool {
matches!(self, Self::SignExtendQuad | Self::SignExtendLong | Self::SignExtendWord)
}
}

impl Default for Extension {
Expand All @@ -365,7 +372,6 @@ impl core::fmt::Display for Extension {
Extension::SignExtendQuad => write!(f, "sxq"),
Extension::SignExtendLong => write!(f, "sxl"),
Extension::SignExtendWord => write!(f, "sxw"),
Extension::ZeroExtend => write!(f, "zx"),
}
}
}
3 changes: 3 additions & 0 deletions cranelift/assembler-x64/meta/src/generate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,11 @@ pub fn isle_macro(f: &mut Formatter, insts: &[dsl::Inst]) {
/// above.
pub fn isle_definitions(f: &mut Formatter, insts: &[dsl::Inst]) {
f.line("(type AssemblerImm8 extern (enum))", None);
f.line("(type AssemblerSimm8 extern (enum))", None);
f.line("(type AssemblerImm16 extern (enum))", None);
f.line("(type AssemblerSimm16 extern (enum))", None);
f.line("(type AssemblerImm32 extern (enum))", None);
f.line("(type AssemblerSimm32 extern (enum))", None);
f.line("(type AssemblerReadGpr extern (enum))", None);
f.line("(type AssemblerReadWriteGpr extern (enum))", None);
f.line("(type AssemblerReadGprMem extern (enum))", None);
Expand Down
37 changes: 16 additions & 21 deletions cranelift/assembler-x64/meta/src/generate/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,21 +86,20 @@ impl dsl::Format {
[FixedReg(dst), Imm(_)] => {
// TODO: don't emit REX byte here.
fmtln!(f, "let {dst} = {};", dst.generate_fixed_reg().unwrap());
fmtln!(f, "let digit = 0x{:x};", rex.digit);
fmtln!(f, "let digit = 0;"); // No digit for this pattern.
fmtln!(f, "rex.emit_two_op(buf, digit, {dst}.enc());");
}
[RegMem(dst), Imm(_)] => {
if rex.digit > 0 {
fmtln!(f, "let digit = 0x{:x};", rex.digit);
fmtln!(f, "match &self.{dst} {{");
f.indent(|f| {
fmtln!(f, "GprMem::Gpr({dst}) => rex.emit_two_op(buf, digit, {dst}.enc()),");
fmtln!(f, "GprMem::Mem({dst}) => {dst}.emit_rex_prefix(rex, digit, buf),");
});
fmtln!(f, "}}");
} else {
unimplemented!();
}
let digit = rex
.digit
.expect("REX digit must be set for operands: [RegMem, Imm]");
fmtln!(f, "let digit = 0x{digit:x};");
fmtln!(f, "match &self.{dst} {{");
f.indent(|f| {
fmtln!(f, "GprMem::Gpr({dst}) => rex.emit_two_op(buf, digit, {dst}.enc()),");
fmtln!(f, "GprMem::Mem({dst}) => {dst}.emit_rex_prefix(rex, digit, buf),");
});
fmtln!(f, "}}");
}
[Reg(dst), RegMem(src)] => {
fmtln!(f, "let {dst} = self.{dst}.enc();");
Expand Down Expand Up @@ -140,8 +139,10 @@ impl dsl::Format {
// No need to emit a ModRM byte: we know the register used.
}
[RegMem(dst), Imm(_)] => {
debug_assert!(rex.digit > 0);
fmtln!(f, "let digit = 0x{:x};", rex.digit);
let digit = rex
.digit
.expect("REX digit must be set for operands: [RegMem, Imm]");
fmtln!(f, "let digit = 0x{digit:x};");
fmtln!(f, "match &self.{dst} {{");
f.indent(|f| {
fmtln!(f, "GprMem::Gpr({dst}) => emit_modrm(buf, digit, {dst}.enc()),");
Expand Down Expand Up @@ -178,13 +179,7 @@ impl dsl::Format {
[_, Imm(imm)] => {
f.empty_line();
f.comment("Emit immediate.");
fmtln!(f, "let bytes = {};", imm.bytes());
if imm.bits() == 32 {
fmtln!(f, "let value = self.{imm}.value();");
} else {
fmtln!(f, "let value = u32::from(self.{imm}.value());");
};
fmtln!(f, "emit_simm(buf, bytes, value);");
fmtln!(f, "self.{imm}.encode(buf);");
}
unknown => {
// Do nothing: no immediates expected.
Expand Down
9 changes: 8 additions & 1 deletion cranelift/assembler-x64/meta/src/generate/inst.rs
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,14 @@ impl dsl::Inst {
.iter()
.filter_map(|o| match o.location.kind() {
FixedReg(_) => None,
Imm(loc) => Some(format!("AssemblerImm{}", loc.bits())),
Imm(loc) => {
let bits = loc.bits();
if o.extension.is_sign_extended() {
Some(format!("AssemblerSimm{bits}"))
} else {
Some(format!("AssemblerImm{bits}"))
}
}
Reg(_) => Some(format!("Assembler{}Gpr", o.mutability.generate_type())),
RegMem(_) => Some(format!("Assembler{}GprMem", o.mutability.generate_type())),
})
Expand Down
27 changes: 22 additions & 5 deletions cranelift/assembler-x64/meta/src/generate/operand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,14 @@ impl dsl::Operand {
use dsl::OperandKind::*;
match self.location.kind() {
FixedReg(_) => None,
Imm(loc) => Some(format!("Imm{}", loc.bits())),
Imm(loc) => {
let bits = loc.bits();
if self.extension.is_sign_extended() {
Some(format!("Simm{bits}"))
} else {
Some(format!("Imm{bits}"))
}
}
Reg(_) => Some(format!("Gpr<R::{}Gpr>", self.mutability.generate_type())),
RegMem(_) => Some(format!("GprMem<R::{}Gpr, R::ReadGpr>", self.mutability.generate_type())),
}
Expand All @@ -22,7 +29,14 @@ impl dsl::Operand {
};
match self.location.kind() {
FixedReg(_) => None,
Imm(loc) => Some(format!("Imm{}", loc.bits())),
Imm(loc) => {
let bits = loc.bits();
if self.extension.is_sign_extended() {
Some(format!("Simm{bits}"))
} else {
Some(format!("Imm{bits}"))
}
}
Reg(_) => Some(format!("Gpr<{pick_ty}>")),
RegMem(_) => Some(format!("GprMem<{pick_ty}, {read_ty}>")),
}
Expand Down Expand Up @@ -58,8 +72,12 @@ impl dsl::Location {
eax => "\"%eax\"".into(),
rax => "\"%rax\"".into(),
imm8 | imm16 | imm32 => {
let variant = extension.generate_variant();
format!("self.{self}.to_string({variant})")
if extension.is_sign_extended() {
let variant = extension.generate_variant();
format!("self.{self}.to_string({variant})")
} else {
format!("self.{self}.to_string()")
}
}
r8 | r16 | r32 | r64 | rm8 | rm16 | rm32 | rm64 => match self.generate_size() {
Some(size) => format!("self.{self}.to_string({size})"),
Expand Down Expand Up @@ -120,7 +138,6 @@ impl dsl::Extension {
SignExtendWord => "Extension::SignExtendWord",
SignExtendLong => "Extension::SignExtendLong",
SignExtendQuad => "Extension::SignExtendQuad",
ZeroExtend => "Extension::ZeroExtend",
}
}
}
12 changes: 11 additions & 1 deletion cranelift/assembler-x64/meta/src/instructions.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
//! Defines x64 instructions using the DSL.

mod add;
mod and;
mod or;
mod sub;
mod xor;

use crate::dsl::Inst;

#[must_use]
pub fn list() -> Vec<Inst> {
and::list()
let mut all = vec![];
all.extend(add::list());
all.extend(and::list());
all.extend(or::list());
all.extend(sub::list());
all.extend(xor::list());
all
}
44 changes: 44 additions & 0 deletions cranelift/assembler-x64/meta/src/instructions/add.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
use crate::dsl::{fmt, inst, r, rex, rw, sxl, sxq};
use crate::dsl::{Feature::*, Inst, LegacyPrefix::*, Location::*};

pub fn list() -> Vec<Inst> {
vec![
inst("addb", fmt("I", [rw(al), r(imm8)]), rex(0x4).ib(), _64b | compat),
inst("addw", fmt("I", [rw(ax), r(imm16)]), rex(0x5).prefix(_66).iw(), _64b | compat),
inst("addl", fmt("I", [rw(eax), r(imm32)]), rex(0x5).id(), _64b | compat),
inst("addq", fmt("I_SXL", [rw(rax), sxq(imm32)]), rex(0x5).w().id(), _64b),
inst("addb", fmt("MI", [rw(rm8), r(imm8)]), rex(0x80).digit(0).ib(), _64b | compat),
inst("addw", fmt("MI", [rw(rm16), r(imm16)]), rex(0x81).prefix(_66).digit(0).iw(), _64b | compat),
inst("addl", fmt("MI", [rw(rm32), r(imm32)]), rex(0x81).digit(0).id(), _64b | compat),
inst("addq", fmt("MI_SXL", [rw(rm64), sxq(imm32)]), rex(0x81).w().digit(0).id(), _64b),
inst("addl", fmt("MI_SXB", [rw(rm32), sxl(imm8)]), rex(0x83).digit(0).ib(), _64b | compat),
inst("addq", fmt("MI_SXB", [rw(rm64), sxq(imm8)]), rex(0x83).w().digit(0).ib(), _64b),
inst("addb", fmt("MR", [rw(rm8), r(r8)]), rex(0x0).r(), _64b | compat),
inst("addw", fmt("MR", [rw(rm16), r(r16)]), rex(0x1).prefix(_66).r(), _64b | compat),
inst("addl", fmt("MR", [rw(rm32), r(r32)]), rex(0x1).r(), _64b | compat),
inst("addq", fmt("MR", [rw(rm64), r(r64)]), rex(0x1).w().r(), _64b),
inst("addb", fmt("RM", [rw(r8), r(rm8)]), rex(0x2).r(), _64b | compat),
inst("addw", fmt("RM", [rw(r16), r(rm16)]), rex(0x3).prefix(_66).r(), _64b | compat),
inst("addl", fmt("RM", [rw(r32), r(rm32)]), rex(0x3).r(), _64b | compat),
inst("addq", fmt("RM", [rw(r64), r(rm64)]), rex(0x3).w().r(), _64b),
// Add with carry.
inst("adcb", fmt("I", [rw(al), r(imm8)]), rex(0x14).ib(), _64b | compat),
inst("adcw", fmt("I", [rw(ax), r(imm16)]), rex(0x15).prefix(_66).iw(), _64b | compat),
inst("adcl", fmt("I", [rw(eax), r(imm32)]), rex(0x15).id(), _64b | compat),
inst("adcq", fmt("I_SXL", [rw(rax), sxq(imm32)]), rex(0x15).w().id(), _64b),
inst("adcb", fmt("MI", [rw(rm8), r(imm8)]), rex(0x80).digit(2).ib(), _64b | compat),
inst("adcw", fmt("MI", [rw(rm16), r(imm16)]), rex(0x81).prefix(_66).digit(2).iw(), _64b | compat),
inst("adcl", fmt("MI", [rw(rm32), r(imm32)]), rex(0x81).digit(2).id(), _64b | compat),
inst("adcq", fmt("MI_SXL", [rw(rm64), sxq(imm32)]), rex(0x81).w().digit(2).id(), _64b),
inst("adcl", fmt("MI_SXB", [rw(rm32), sxl(imm8)]), rex(0x83).digit(2).ib(), _64b | compat),
inst("adcq", fmt("MI_SXB", [rw(rm64), sxq(imm8)]), rex(0x83).w().digit(2).ib(), _64b),
inst("adcb", fmt("MR", [rw(rm8), r(r8)]), rex(0x10).r(), _64b | compat),
inst("adcw", fmt("MR", [rw(rm16), r(r16)]), rex(0x11).prefix(_66).r(), _64b | compat),
inst("adcl", fmt("MR", [rw(rm32), r(r32)]), rex(0x11).r(), _64b | compat),
inst("adcq", fmt("MR", [rw(rm64), r(r64)]), rex(0x11).w().r(), _64b),
inst("adcb", fmt("RM", [rw(r8), r(rm8)]), rex(0x12).r(), _64b | compat),
inst("adcw", fmt("RM", [rw(r16), r(rm16)]), rex(0x13).prefix(_66).r(), _64b | compat),
inst("adcl", fmt("RM", [rw(r32), r(rm32)]), rex(0x13).r(), _64b | compat),
inst("adcq", fmt("RM", [rw(r64), r(rm64)]), rex(0x13).w().r(), _64b),
]
}
25 changes: 25 additions & 0 deletions cranelift/assembler-x64/meta/src/instructions/or.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use crate::dsl::{fmt, inst, r, rex, rw, sxl, sxq};
use crate::dsl::{Feature::*, Inst, LegacyPrefix::*, Location::*};

pub fn list() -> Vec<Inst> {
vec![
inst("orb", fmt("I", [rw(al), r(imm8)]), rex(0x0C).ib(), _64b | compat),
inst("orw", fmt("I", [rw(ax), r(imm16)]), rex(0x0D).prefix(_66).iw(), _64b | compat),
inst("orl", fmt("I", [rw(eax), r(imm32)]), rex(0x0D).id(), _64b | compat),
inst("orq", fmt("I_SXL", [rw(rax), sxq(imm32)]), rex(0x0D).w().id(), _64b),
inst("orb", fmt("MI", [rw(rm8), r(imm8)]), rex(0x80).digit(1).ib(), _64b | compat),
inst("orw", fmt("MI", [rw(rm16), r(imm16)]), rex(0x81).prefix(_66).digit(1).iw(), _64b | compat),
inst("orl", fmt("MI", [rw(rm32), r(imm32)]), rex(0x81).digit(1).id(), _64b | compat),
inst("orq", fmt("MI_SXL", [rw(rm64), sxq(imm32)]), rex(0x81).w().digit(1).id(), _64b),
inst("orl", fmt("MI_SXB", [rw(rm32), sxl(imm8)]), rex(0x83).digit(1).ib(), _64b | compat),
inst("orq", fmt("MI_SXB", [rw(rm64), sxq(imm8)]), rex(0x83).w().digit(1).ib(), _64b),
inst("orb", fmt("MR", [rw(rm8), r(r8)]), rex(0x08).r(), _64b | compat),
inst("orw", fmt("MR", [rw(rm16), r(r16)]), rex(0x09).prefix(_66).r(), _64b | compat),
inst("orl", fmt("MR", [rw(rm32), r(r32)]), rex(0x09).r(), _64b | compat),
inst("orq", fmt("MR", [rw(rm64), r(r64)]), rex(0x09).w().r(), _64b),
inst("orb", fmt("RM", [rw(r8), r(rm8)]), rex(0x0A).r(), _64b | compat),
inst("orw", fmt("RM", [rw(r16), r(rm16)]), rex(0x0B).prefix(_66).r(), _64b | compat),
inst("orl", fmt("RM", [rw(r32), r(rm32)]), rex(0x0B).r(), _64b | compat),
inst("orq", fmt("RM", [rw(r64), r(rm64)]), rex(0x0B).w().r(), _64b),
]
}
44 changes: 44 additions & 0 deletions cranelift/assembler-x64/meta/src/instructions/sub.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
use crate::dsl::{fmt, inst, r, rex, rw, sxl, sxq};
use crate::dsl::{Feature::*, Inst, LegacyPrefix::*, Location::*};

pub fn list() -> Vec<Inst> {
vec![
inst("subb", fmt("I", [rw(al), r(imm8)]), rex(0x2C).ib(), _64b | compat),
inst("subw", fmt("I", [rw(ax), r(imm16)]), rex(0x2D).prefix(_66).iw(), _64b | compat),
inst("subl", fmt("I", [rw(eax), r(imm32)]), rex(0x2D).id(), _64b | compat),
inst("subq", fmt("I_SXL", [rw(rax), sxq(imm32)]), rex(0x2D).w().id(), _64b),
inst("subb", fmt("MI", [rw(rm8), r(imm8)]), rex(0x80).digit(5).ib(), _64b | compat),
inst("subw", fmt("MI", [rw(rm16), r(imm16)]), rex(0x81).prefix(_66).digit(5).iw(), _64b | compat),
inst("subl", fmt("MI", [rw(rm32), r(imm32)]), rex(0x81).digit(5).id(), _64b | compat),
inst("subq", fmt("MI_SXL", [rw(rm64), sxq(imm32)]), rex(0x81).w().digit(5).id(), _64b),
inst("subl", fmt("MI_SXB", [rw(rm32), sxl(imm8)]), rex(0x83).digit(5).ib(), _64b | compat),
inst("subq", fmt("MI_SXB", [rw(rm64), sxq(imm8)]), rex(0x83).w().digit(5).ib(), _64b),
inst("subb", fmt("MR", [rw(rm8), r(r8)]), rex(0x28).r(), _64b | compat),
inst("subw", fmt("MR", [rw(rm16), r(r16)]), rex(0x29).prefix(_66).r(), _64b | compat),
inst("subl", fmt("MR", [rw(rm32), r(r32)]), rex(0x29).r(), _64b | compat),
inst("subq", fmt("MR", [rw(rm64), r(r64)]), rex(0x29).w().r(), _64b),
inst("subb", fmt("RM", [rw(r8), r(rm8)]), rex(0x2A).r(), _64b | compat),
inst("subw", fmt("RM", [rw(r16), r(rm16)]), rex(0x2B).prefix(_66).r(), _64b | compat),
inst("subl", fmt("RM", [rw(r32), r(rm32)]), rex(0x2B).r(), _64b | compat),
inst("subq", fmt("RM", [rw(r64), r(rm64)]), rex(0x2B).w().r(), _64b),
// Subtract with borrow.
inst("sbbb", fmt("I", [rw(al), r(imm8)]), rex(0x1C).ib(), _64b | compat),
inst("sbbw", fmt("I", [rw(ax), r(imm16)]), rex(0x1D).prefix(_66).iw(), _64b | compat),
inst("sbbl", fmt("I", [rw(eax), r(imm32)]), rex(0x1D).id(), _64b | compat),
inst("sbbq", fmt("I_SXL", [rw(rax), sxq(imm32)]), rex(0x1D).w().id(), _64b),
inst("sbbb", fmt("MI", [rw(rm8), r(imm8)]), rex(0x80).digit(3).ib(), _64b | compat),
inst("sbbw", fmt("MI", [rw(rm16), r(imm16)]), rex(0x81).prefix(_66).digit(3).iw(), _64b | compat),
inst("sbbl", fmt("MI", [rw(rm32), r(imm32)]), rex(0x81).digit(3).id(), _64b | compat),
inst("sbbq", fmt("MI_SXL", [rw(rm64), sxq(imm32)]), rex(0x81).w().digit(3).id(), _64b),
inst("sbbl", fmt("MI_SXB", [rw(rm32), sxl(imm8)]), rex(0x83).digit(3).ib(), _64b | compat),
inst("sbbq", fmt("MI_SXB", [rw(rm64), sxq(imm8)]), rex(0x83).w().digit(3).ib(), _64b),
inst("sbbb", fmt("MR", [rw(rm8), r(r8)]), rex(0x18).r(), _64b | compat),
inst("sbbw", fmt("MR", [rw(rm16), r(r16)]), rex(0x19).prefix(_66).r(), _64b | compat),
inst("sbbl", fmt("MR", [rw(rm32), r(r32)]), rex(0x19).r(), _64b | compat),
inst("sbbq", fmt("MR", [rw(rm64), r(r64)]), rex(0x19).w().r(), _64b),
inst("sbbb", fmt("RM", [rw(r8), r(rm8)]), rex(0x1A).r(), _64b | compat),
inst("sbbw", fmt("RM", [rw(r16), r(rm16)]), rex(0x1B).prefix(_66).r(), _64b | compat),
inst("sbbl", fmt("RM", [rw(r32), r(rm32)]), rex(0x1B).r(), _64b | compat),
inst("sbbq", fmt("RM", [rw(r64), r(rm64)]), rex(0x1B).w().r(), _64b),
]
}
Loading
Loading