Skip to content

Commit 7ea7ebc

Browse files
authored
x64: convert all multiplication-related instructions (#10782)
* asm: add SSE4.1 feature * asm: refactor format constructors with expanding structs * asm: add implicit operands Implicit operands are used by an instruction but not present in its disassembled output. Instructions like `mul`, e.g., will write to the `%rax` and `%rdx` registers, but this is all invisible in disassembly. Implicit operands are always fixed (i.e., the register is known), but not all fixed operands are implicit (i.e., some fixed registers _are_ disassembled). * asm: add `mul*` instructions, vector and scalar * asm: generate ISLE for multi-return constructors--`AssemblerOutputs::RetValueRegs` Certain `mul*` instructions write to multiple registers. For register allocation, Cranelift needs to know about all of these registers. This change uses the pre-existing pattern of returning a `ValueRegs` type to indicate this. This change is limited to what is needed now: the only multi-return needed now uses two fixed registers. * x64: convert SSE multiplications * x64: convert scalar multiplications * x64: remove old `Inst::Mul*` variants * winch: convert multiplications This does not include any special "small immediate resizing" rules for Winch, so the Winch disassembly tests gain a few bytes (e.g., some immediates that _could_ fit in 8 bits are emitted as the full 32 bits). * refactor: move `i32`-related extractors to `prelude.isle` * review: print the implicit registers as well * fix: the instruction size has changed (again)
1 parent 90ac295 commit 7ea7ebc

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+571
-692
lines changed

cranelift/assembler-x64/meta/src/dsl.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ pub use encoding::{
1313
};
1414
pub use encoding::{rex, vex};
1515
pub use features::{ALL_FEATURES, Feature, Features};
16-
pub use format::{Extension, Format, Location, Mutability, Operand, OperandKind};
17-
pub use format::{align, fmt, r, rw, sxl, sxq, sxw, w};
16+
pub use format::{Extension, Format, Location, Mutability, Operand, OperandKind, RegClass};
17+
pub use format::{align, fmt, implicit, r, rw, sxl, sxq, sxw, w};
1818

1919
/// Abbreviated constructor for an x64 instruction.
2020
pub fn inst(

cranelift/assembler-x64/meta/src/dsl/features.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ pub enum Feature {
6565
sse,
6666
sse2,
6767
ssse3,
68+
sse41,
6869
}
6970

7071
/// List all CPU features.
@@ -80,6 +81,7 @@ pub const ALL_FEATURES: &[Feature] = &[
8081
Feature::sse,
8182
Feature::sse2,
8283
Feature::ssse3,
84+
Feature::sse41,
8385
];
8486

8587
impl fmt::Display for Feature {
@@ -90,6 +92,7 @@ impl fmt::Display for Feature {
9092
Feature::sse => write!(f, "sse"),
9193
Feature::sse2 => write!(f, "sse2"),
9294
Feature::ssse3 => write!(f, "ssse3"),
95+
Feature::sse41 => write!(f, "sse41"),
9396
}
9497
}
9598
}

cranelift/assembler-x64/meta/src/dsl/format.rs

Lines changed: 46 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,12 @@ pub fn fmt(
3434
/// This function panics if the location is an immediate (i.e., an immediate
3535
/// cannot be written to).
3636
#[must_use]
37-
pub fn rw(location: Location) -> Operand {
38-
assert!(!matches!(location.kind(), OperandKind::Imm(_)));
37+
pub fn rw(op: impl Into<Operand>) -> Operand {
38+
let op = op.into();
39+
assert!(!matches!(op.location.kind(), OperandKind::Imm(_)));
3940
Operand {
40-
location,
4141
mutability: Mutability::ReadWrite,
42-
extension: Extension::default(),
43-
align: false,
42+
..op
4443
}
4544
}
4645

@@ -54,23 +53,30 @@ pub fn r(op: impl Into<Operand>) -> Operand {
5453

5554
/// An abbreviated constructor for a "write" operand.
5655
#[must_use]
57-
pub fn w(location: Location) -> Operand {
56+
pub fn w(op: impl Into<Operand>) -> Operand {
57+
let op = op.into();
5858
Operand {
59-
location,
6059
mutability: Mutability::Write,
61-
extension: Extension::None,
62-
align: false,
60+
..op
6361
}
6462
}
6563

6664
/// An abbreviated constructor for a memory operand that requires alignment.
6765
pub fn align(location: Location) -> Operand {
6866
assert!(location.uses_memory());
6967
Operand {
70-
location,
71-
mutability: Mutability::Read,
72-
extension: Extension::None,
7368
align: true,
69+
..Operand::from(location)
70+
}
71+
}
72+
73+
/// An abbreviated constructor for an operand that is used by the instruction
74+
/// but not visible in its disassembly.
75+
pub fn implicit(location: Location) -> Operand {
76+
assert!(matches!(location.kind(), OperandKind::FixedReg(_)));
77+
Operand {
78+
implicit: true,
79+
..Operand::from(location)
7480
}
7581
}
7682

@@ -84,10 +90,8 @@ pub fn align(location: Location) -> Operand {
8490
pub fn sxq(location: Location) -> Operand {
8591
assert!(location.bits() <= 64);
8692
Operand {
87-
location,
88-
mutability: Mutability::Read,
8993
extension: Extension::SignExtendQuad,
90-
align: false,
94+
..Operand::from(location)
9195
}
9296
}
9397

@@ -101,10 +105,8 @@ pub fn sxq(location: Location) -> Operand {
101105
pub fn sxl(location: Location) -> Operand {
102106
assert!(location.bits() <= 32);
103107
Operand {
104-
location,
105-
mutability: Mutability::Read,
106108
extension: Extension::SignExtendLong,
107-
align: false,
109+
..Operand::from(location)
108110
}
109111
}
110112

@@ -118,10 +120,8 @@ pub fn sxl(location: Location) -> Operand {
118120
pub fn sxw(location: Location) -> Operand {
119121
assert!(location.bits() <= 16);
120122
Operand {
121-
location,
122-
mutability: Mutability::Read,
123123
extension: Extension::SignExtendWord,
124-
align: false,
124+
..Operand::from(location)
125125
}
126126
}
127127

@@ -204,6 +204,9 @@ pub struct Operand {
204204
/// address used in the operand must align to the size of the operand (e.g.,
205205
/// `m128` must be 16-byte aligned).
206206
pub align: bool,
207+
/// Some register operands are implicit: that is, they do not appear in the
208+
/// disassembled output even though they are used in the instruction.
209+
pub implicit: bool,
207210
}
208211

209212
impl core::fmt::Display for Operand {
@@ -213,6 +216,7 @@ impl core::fmt::Display for Operand {
213216
mutability,
214217
extension,
215218
align,
219+
implicit,
216220
} = self;
217221
write!(f, "{location}")?;
218222
let mut flags = vec![];
@@ -225,6 +229,9 @@ impl core::fmt::Display for Operand {
225229
if *align != false {
226230
flags.push("align".to_owned());
227231
}
232+
if *implicit {
233+
flags.push("implicit".to_owned());
234+
}
228235
if !flags.is_empty() {
229236
write!(f, "[{}]", flags.join(","))?;
230237
}
@@ -237,11 +244,13 @@ impl From<Location> for Operand {
237244
let mutability = Mutability::default();
238245
let extension = Extension::default();
239246
let align = false;
247+
let implicit = false;
240248
Self {
241249
location,
242250
mutability,
243251
extension,
244252
align,
253+
implicit,
245254
}
246255
}
247256
}
@@ -270,6 +279,9 @@ pub enum Location {
270279
ax,
271280
eax,
272281
rax,
282+
dx,
283+
edx,
284+
rdx,
273285
cl,
274286

275287
// Immediate values.
@@ -307,9 +319,9 @@ impl Location {
307319
use Location::*;
308320
match self {
309321
al | cl | imm8 | r8 | rm8 | m8 => 8,
310-
ax | imm16 | r16 | rm16 | m16 => 16,
311-
eax | imm32 | r32 | rm32 | m32 | xmm_m32 => 32,
312-
rax | r64 | rm64 | m64 | xmm_m64 => 64,
322+
ax | dx | imm16 | r16 | rm16 | m16 => 16,
323+
eax | edx | imm32 | r32 | rm32 | m32 | xmm_m32 => 32,
324+
rax | rdx | r64 | rm64 | m64 | xmm_m64 => 64,
313325
xmm | xmm_m128 => 128,
314326
}
315327
}
@@ -325,7 +337,8 @@ impl Location {
325337
pub fn uses_memory(&self) -> bool {
326338
use Location::*;
327339
match self {
328-
al | cl | ax | eax | rax | imm8 | imm16 | imm32 | r8 | r16 | r32 | r64 | xmm => false,
340+
al | ax | eax | rax | cl | dx | edx | rdx | imm8 | imm16 | imm32 | r8 | r16 | r32
341+
| r64 | xmm => false,
329342
rm8 | rm16 | rm32 | rm64 | xmm_m32 | xmm_m64 | xmm_m128 | m8 | m16 | m32 | m64 => true,
330343
}
331344
}
@@ -337,8 +350,8 @@ impl Location {
337350
use Location::*;
338351
match self {
339352
imm8 | imm16 | imm32 => false,
340-
al | ax | eax | rax | cl | r8 | r16 | r32 | r64 | rm8 | rm16 | rm32 | rm64 | xmm
341-
| xmm_m32 | xmm_m64 | xmm_m128 | m8 | m16 | m32 | m64 => true,
353+
al | ax | eax | rax | cl | dx | edx | rdx | r8 | r16 | r32 | r64 | rm8 | rm16
354+
| rm32 | rm64 | xmm | xmm_m32 | xmm_m64 | xmm_m128 | m8 | m16 | m32 | m64 => true,
342355
}
343356
}
344357

@@ -347,7 +360,7 @@ impl Location {
347360
pub fn kind(&self) -> OperandKind {
348361
use Location::*;
349362
match self {
350-
al | ax | eax | rax | cl => OperandKind::FixedReg(*self),
363+
al | ax | eax | rax | cl | dx | edx | rdx => OperandKind::FixedReg(*self),
351364
imm8 | imm16 | imm32 => OperandKind::Imm(*self),
352365
r8 | r16 | r32 | r64 | xmm => OperandKind::Reg(*self),
353366
rm8 | rm16 | rm32 | rm64 | xmm_m32 | xmm_m64 | xmm_m128 => OperandKind::RegMem(*self),
@@ -364,9 +377,8 @@ impl Location {
364377
use Location::*;
365378
match self {
366379
imm8 | imm16 | imm32 | m8 | m16 | m32 | m64 => None,
367-
al | ax | eax | rax | cl | r8 | r16 | r32 | r64 | rm8 | rm16 | rm32 | rm64 => {
368-
Some(RegClass::Gpr)
369-
}
380+
al | ax | eax | rax | cl | dx | edx | rdx | r8 | r16 | r32 | r64 | rm8 | rm16
381+
| rm32 | rm64 => Some(RegClass::Gpr),
370382
xmm | xmm_m32 | xmm_m64 | xmm_m128 => Some(RegClass::Xmm),
371383
}
372384
}
@@ -385,6 +397,9 @@ impl core::fmt::Display for Location {
385397
eax => write!(f, "eax"),
386398
rax => write!(f, "rax"),
387399
cl => write!(f, "cl"),
400+
dx => write!(f, "dx"),
401+
edx => write!(f, "edx"),
402+
rdx => write!(f, "rdx"),
388403

389404
r8 => write!(f, "r8"),
390405
r16 => write!(f, "r16"),

cranelift/assembler-x64/meta/src/generate/format.rs

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,33 @@ impl dsl::Format {
1414
/// once Cranelift has switched to using this assembler predominantly
1515
/// (TODO).
1616
#[must_use]
17-
pub fn generate_att_style_operands(&self) -> String {
17+
pub(crate) fn generate_att_style_operands(&self) -> String {
1818
let ordered_ops: Vec<_> = self
1919
.operands
2020
.iter()
21+
.filter(|o| !o.implicit)
2122
.rev()
2223
.map(|o| format!("{{{}}}", o.location))
2324
.collect();
2425
ordered_ops.join(", ")
2526
}
2627

27-
pub fn generate_rex_encoding(&self, f: &mut Formatter, rex: &dsl::Rex) {
28+
#[must_use]
29+
pub(crate) fn generate_implicit_operands(&self) -> String {
30+
let ops: Vec<_> = self
31+
.operands
32+
.iter()
33+
.filter(|o| o.implicit)
34+
.map(|o| format!("{{{}}}", o.location))
35+
.collect();
36+
if ops.is_empty() {
37+
String::new()
38+
} else {
39+
format!(" ;; implicit: {}", ops.join(", "))
40+
}
41+
}
42+
43+
pub(crate) fn generate_rex_encoding(&self, f: &mut Formatter, rex: &dsl::Rex) {
2844
self.generate_prefixes(f, rex);
2945
self.generate_rex_prefix(f, rex);
3046
self.generate_opcodes(f, rex);
@@ -91,12 +107,17 @@ impl dsl::Format {
91107
fmtln!(f, "let dst = self.{dst}.enc();");
92108
fmtln!(f, "let rex = RexPrefix::two_op(digit, dst, {bits});");
93109
}
110+
[FixedReg(_), RegMem(mem)] | [FixedReg(_), FixedReg(_), RegMem(mem)] => {
111+
let digit = rex.digit.unwrap();
112+
fmtln!(f, "let digit = 0x{digit:x};");
113+
fmtln!(f, "let rex = self.{mem}.as_rex_prefix(digit, {bits});");
114+
}
94115
[Mem(dst), Imm(_)] | [RegMem(dst), Imm(_)] | [RegMem(dst)] => {
95116
let digit = rex.digit.unwrap();
96117
fmtln!(f, "let digit = 0x{digit:x};");
97118
fmtln!(f, "let rex = self.{dst}.as_rex_prefix(digit, {bits});");
98119
}
99-
[Reg(dst), RegMem(src)] => {
120+
[Reg(dst), RegMem(src)] | [Reg(dst), RegMem(src), Imm(_)] => {
100121
fmtln!(f, "let dst = self.{dst}.enc();");
101122
fmtln!(f, "let rex = self.{src}.as_rex_prefix(dst, {bits});");
102123
}
@@ -135,12 +156,17 @@ impl dsl::Format {
135156
fmtln!(f, "let digit = 0x{digit:x};");
136157
fmtln!(f, "self.{reg}.encode_modrm(buf, digit);");
137158
}
138-
[Mem(mem), Imm(_)] | [RegMem(mem), Imm(_)] | [RegMem(mem)] => {
159+
[Mem(mem), Imm(_)]
160+
| [RegMem(mem), Imm(_)]
161+
| [RegMem(mem)]
162+
| [FixedReg(_), RegMem(mem)]
163+
| [FixedReg(_), FixedReg(_), RegMem(mem)] => {
139164
let digit = rex.digit.unwrap();
140165
fmtln!(f, "let digit = 0x{digit:x};");
141166
fmtln!(f, "self.{mem}.encode_rex_suffixes(buf, off, digit, 0);");
142167
}
143168
[Reg(reg), RegMem(mem)]
169+
| [Reg(reg), RegMem(mem), Imm(_)]
144170
| [Mem(mem), Reg(reg)]
145171
| [RegMem(mem), Reg(reg)]
146172
| [RegMem(mem), Reg(reg), Imm(_)]

cranelift/assembler-x64/meta/src/generate/inst.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ impl dsl::Inst {
194194
f.add_block(
195195
"fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result",
196196
|f| {
197-
for op in &self.format.operands {
197+
for op in self.format.operands.iter() {
198198
let location = op.location;
199199
let to_string = location.generate_to_string(op.extension);
200200
fmtln!(f, "let {location} = {to_string};");
@@ -207,7 +207,8 @@ impl dsl::Inst {
207207
&self.mnemonic
208208
};
209209
let ordered_ops = self.format.generate_att_style_operands();
210-
fmtln!(f, "write!(f, \"{inst_name} {ordered_ops}\")");
210+
let implicit_ops = self.format.generate_implicit_operands();
211+
fmtln!(f, "write!(f, \"{inst_name} {ordered_ops}{implicit_ops}\")");
211212
},
212213
);
213214
},

cranelift/assembler-x64/meta/src/generate/operand.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,11 @@ impl dsl::Operand {
1414
format!("Imm{bits}")
1515
}
1616
}
17-
al | ax | eax | rax | cl => {
17+
al | ax | eax | rax | cl | dx | edx | rdx => {
1818
let enc = match self.location {
1919
al | ax | eax | rax => "{ gpr::enc::RAX }",
2020
cl => "{ gpr::enc::RCX }",
21+
dx | edx | rdx => "{ gpr::enc::RDX }",
2122
_ => unreachable!(),
2223
};
2324
format!("Fixed<R::{mut_}Gpr, {enc}>")
@@ -44,6 +45,9 @@ impl dsl::Location {
4445
eax => "\"%eax\"".into(),
4546
rax => "\"%rax\"".into(),
4647
cl => "\"%cl\"".into(),
48+
dx => "\"%dx\"".into(),
49+
edx => "\"%edx\"".into(),
50+
rdx => "\"%rdx\"".into(),
4751
imm8 | imm16 | imm32 => {
4852
if extension.is_sign_extended() {
4953
let variant = extension.generate_variant();
@@ -67,7 +71,7 @@ impl dsl::Location {
6771
fn generate_size(&self) -> Option<&str> {
6872
use dsl::Location::*;
6973
match self {
70-
al | ax | eax | rax | cl | imm8 | imm16 | imm32 => None,
74+
al | ax | eax | rax | cl | dx | edx | rdx | imm8 | imm16 | imm32 => None,
7175
r8 | rm8 => Some("Size::Byte"),
7276
r16 | rm16 => Some("Size::Word"),
7377
r32 | rm32 => Some("Size::Doubleword"),

cranelift/assembler-x64/meta/src/instructions.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
mod add;
44
mod and;
55
mod cvt;
6+
mod mul;
67
mod neg;
78
mod or;
89
mod shift;
@@ -17,6 +18,7 @@ pub fn list() -> Vec<Inst> {
1718
all.extend(add::list());
1819
all.extend(and::list());
1920
all.extend(cvt::list());
21+
all.extend(mul::list());
2022
all.extend(neg::list());
2123
all.extend(or::list());
2224
all.extend(shift::list());

0 commit comments

Comments
 (0)