Skip to content
This repository was archived by the owner on Sep 8, 2025. It is now read-only.

Commit 79cf7e8

Browse files
authored
x64: Migrate cmov* to the new assembler (#11095)
This commit adds all `cmov*` variants from the Intel manual to the new assembler. This then additionally removes the `Cmove` pseudo-inst in favor of these new instructions. One difference from before is that the naming in the `CC` enum does not exactly match what mnemonics Capstone uses to disassemble. For example `CC.NB` in ISLE corresponds to the Intel instruction `CMOVNB`. This instruction, however, has the same encoding as `CMOVAE` and Capstone disassembles as `CMOVAE`. This means that the instruction selection in ISLE isn't a 1:1 match with mnemonics. This additionally adds support in the assembler ISLE generation to understand that instructions which read EFLAGS generate a `ConsumesFlags` variant in their instruction helpers.
1 parent 0de0089 commit 79cf7e8

File tree

28 files changed

+435
-435
lines changed

28 files changed

+435
-435
lines changed

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -601,6 +601,15 @@ pub enum Eflags {
601601
}
602602

603603
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+
604613
/// Returns whether this represents a writes to any bit in the EFLAGS
605614
/// register.
606615
pub fn is_write(&self) -> bool {

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ mod and;
77
mod atomic;
88
mod avg;
99
mod bitmanip;
10+
mod cmov;
1011
mod cmp;
1112
mod cvt;
1213
mod div;
@@ -41,6 +42,7 @@ pub fn list() -> Vec<Inst> {
4142
all.extend(atomic::list());
4243
all.extend(avg::list());
4344
all.extend(bitmanip::list());
45+
all.extend(cmov::list());
4446
all.extend(cmp::list());
4547
all.extend(cvt::list());
4648
all.extend(div::list());
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
use crate::dsl::{Eflags::*, Feature::*, Inst, Location::*};
2+
use crate::dsl::{fmt, inst, r, rex, rw};
3+
4+
#[rustfmt::skip] // Keeps instructions on a single line.
5+
pub fn list() -> Vec<Inst> {
6+
vec![
7+
// Note that the Intel manual lists many mnemonics for this family of
8+
// instructions which are duplicates of other mnemonics. The order here
9+
// matches the order in the manual and comments are left when variants
10+
// are omitted due to the instructions being duplicates of another.
11+
inst("cmovaw", fmt("RM", [rw(r16), r(rm16)]).flags(R), rex([0x66, 0x0f, 0x47]).r(), _64b | compat),
12+
inst("cmoval", fmt("RM", [rw(r32), r(rm32)]).flags(R), rex([0x0f, 0x47]).r(), _64b | compat),
13+
inst("cmovaq", fmt("RM", [rw(r64), r(rm64)]).flags(R), rex([0x0f, 0x47]).w().r(), _64b),
14+
inst("cmovaew", fmt("RM", [rw(r16), r(rm16)]).flags(R), rex([0x66, 0x0f, 0x43]).r(), _64b | compat),
15+
inst("cmovael", fmt("RM", [rw(r32), r(rm32)]).flags(R), rex([0x0f, 0x43]).r(), _64b | compat),
16+
inst("cmovaeq", fmt("RM", [rw(r64), r(rm64)]).flags(R), rex([0x0f, 0x43]).w().r(), _64b),
17+
inst("cmovbw", fmt("RM", [rw(r16), r(rm16)]).flags(R), rex([0x66, 0x0f, 0x42]).r(), _64b | compat),
18+
inst("cmovbl", fmt("RM", [rw(r32), r(rm32)]).flags(R), rex([0x0f, 0x42]).r(), _64b | compat),
19+
inst("cmovbq", fmt("RM", [rw(r64), r(rm64)]).flags(R), rex([0x0f, 0x42]).w().r(), _64b),
20+
inst("cmovbew", fmt("RM", [rw(r16), r(rm16)]).flags(R), rex([0x66, 0x0f, 0x46]).r(), _64b | compat),
21+
inst("cmovbel", fmt("RM", [rw(r32), r(rm32)]).flags(R), rex([0x0f, 0x46]).r(), _64b | compat),
22+
inst("cmovbeq", fmt("RM", [rw(r64), r(rm64)]).flags(R), rex([0x0f, 0x46]).w().r(), _64b),
23+
// NB: cmovc* is omitted here as it has the same encoding as cmovb*
24+
inst("cmovew", fmt("RM", [rw(r16), r(rm16)]).flags(R), rex([0x66, 0x0f, 0x44]).r(), _64b | compat),
25+
inst("cmovel", fmt("RM", [rw(r32), r(rm32)]).flags(R), rex([0x0f, 0x44]).r(), _64b | compat),
26+
inst("cmoveq", fmt("RM", [rw(r64), r(rm64)]).flags(R), rex([0x0f, 0x44]).w().r(), _64b),
27+
inst("cmovgw", fmt("RM", [rw(r16), r(rm16)]).flags(R), rex([0x66, 0x0f, 0x4f]).r(), _64b | compat),
28+
inst("cmovgl", fmt("RM", [rw(r32), r(rm32)]).flags(R), rex([0x0f, 0x4f]).r(), _64b | compat),
29+
inst("cmovgq", fmt("RM", [rw(r64), r(rm64)]).flags(R), rex([0x0f, 0x4f]).w().r(), _64b),
30+
inst("cmovgew", fmt("RM", [rw(r16), r(rm16)]).flags(R), rex([0x66, 0x0f, 0x4d]).r(), _64b | compat),
31+
inst("cmovgel", fmt("RM", [rw(r32), r(rm32)]).flags(R), rex([0x0f, 0x4d]).r(), _64b | compat),
32+
inst("cmovgeq", fmt("RM", [rw(r64), r(rm64)]).flags(R), rex([0x0f, 0x4d]).w().r(), _64b),
33+
inst("cmovlw", fmt("RM", [rw(r16), r(rm16)]).flags(R), rex([0x66, 0x0f, 0x4c]).r(), _64b | compat),
34+
inst("cmovll", fmt("RM", [rw(r32), r(rm32)]).flags(R), rex([0x0f, 0x4c]).r(), _64b | compat),
35+
inst("cmovlq", fmt("RM", [rw(r64), r(rm64)]).flags(R), rex([0x0f, 0x4c]).w().r(), _64b),
36+
inst("cmovlew", fmt("RM", [rw(r16), r(rm16)]).flags(R), rex([0x66, 0x0f, 0x4e]).r(), _64b | compat),
37+
inst("cmovlel", fmt("RM", [rw(r32), r(rm32)]).flags(R), rex([0x0f, 0x4e]).r(), _64b | compat),
38+
inst("cmovleq", fmt("RM", [rw(r64), r(rm64)]).flags(R), rex([0x0f, 0x4e]).w().r(), _64b),
39+
// NB: cmovna* is omitted here as it has the same encoding as cmovbe*
40+
// NB: cmovnb* is omitted here as it has the same encoding as cmovae*
41+
// NB: cmovnbe* is omitted here as it has the same encoding as cmova*
42+
// NB: cmovnc* is omitted here as it has the same encoding as cmovae*
43+
inst("cmovnew", fmt("RM", [rw(r16), r(rm16)]).flags(R), rex([0x66, 0x0f, 0x45]).r(), _64b | compat),
44+
inst("cmovnel", fmt("RM", [rw(r32), r(rm32)]).flags(R), rex([0x0f, 0x45]).r(), _64b | compat),
45+
inst("cmovneq", fmt("RM", [rw(r64), r(rm64)]).flags(R), rex([0x0f, 0x45]).w().r(), _64b),
46+
// NB: cmovng* is omitted here as it has the same encoding as cmovle*
47+
// NB: cmovnge* is omitted here as it has the same encoding as cmovl*
48+
// NB: cmovnl* is omitted here as it has the same encoding as cmovge*
49+
// NB: cmovnle* is omitted here as it has the same encoding as cmovg*
50+
inst("cmovnow", fmt("RM", [rw(r16), r(rm16)]).flags(R), rex([0x66, 0x0f, 0x41]).r(), _64b | compat),
51+
inst("cmovnol", fmt("RM", [rw(r32), r(rm32)]).flags(R), rex([0x0f, 0x41]).r(), _64b | compat),
52+
inst("cmovnoq", fmt("RM", [rw(r64), r(rm64)]).flags(R), rex([0x0f, 0x41]).w().r(), _64b),
53+
inst("cmovnpw", fmt("RM", [rw(r16), r(rm16)]).flags(R), rex([0x66, 0x0f, 0x4b]).r(), _64b | compat),
54+
inst("cmovnpl", fmt("RM", [rw(r32), r(rm32)]).flags(R), rex([0x0f, 0x4b]).r(), _64b | compat),
55+
inst("cmovnpq", fmt("RM", [rw(r64), r(rm64)]).flags(R), rex([0x0f, 0x4b]).w().r(), _64b),
56+
inst("cmovnsw", fmt("RM", [rw(r16), r(rm16)]).flags(R), rex([0x66, 0x0f, 0x49]).r(), _64b | compat),
57+
inst("cmovnsl", fmt("RM", [rw(r32), r(rm32)]).flags(R), rex([0x0f, 0x49]).r(), _64b | compat),
58+
inst("cmovnsq", fmt("RM", [rw(r64), r(rm64)]).flags(R), rex([0x0f, 0x49]).w().r(), _64b),
59+
// NB: cmovnz* is omitted here as it has the same encoding as cmovne*
60+
inst("cmovow", fmt("RM", [rw(r16), r(rm16)]).flags(R), rex([0x66, 0x0f, 0x40]).r(), _64b | compat),
61+
inst("cmovol", fmt("RM", [rw(r32), r(rm32)]).flags(R), rex([0x0f, 0x40]).r(), _64b | compat),
62+
inst("cmovoq", fmt("RM", [rw(r64), r(rm64)]).flags(R), rex([0x0f, 0x40]).w().r(), _64b),
63+
inst("cmovpw", fmt("RM", [rw(r16), r(rm16)]).flags(R), rex([0x66, 0x0f, 0x4a]).r(), _64b | compat),
64+
inst("cmovpl", fmt("RM", [rw(r32), r(rm32)]).flags(R), rex([0x0f, 0x4a]).r(), _64b | compat),
65+
inst("cmovpq", fmt("RM", [rw(r64), r(rm64)]).flags(R), rex([0x0f, 0x4a]).w().r(), _64b),
66+
// NB: cmovpe* is omitted here as it has the same encoding as cmovp*
67+
// NB: cmovpo* is omitted here as it has the same encoding as cmovnp*
68+
inst("cmovsw", fmt("RM", [rw(r16), r(rm16)]).flags(R), rex([0x66, 0x0f, 0x48]).r(), _64b | compat),
69+
inst("cmovsl", fmt("RM", [rw(r32), r(rm32)]).flags(R), rex([0x0f, 0x48]).r(), _64b | compat),
70+
inst("cmovsq", fmt("RM", [rw(r64), r(rm64)]).flags(R), rex([0x0f, 0x48]).w().r(), _64b),
71+
// NB: cmovz* is omitted here as it has the same encoding as cmove*
72+
]
73+
}

cranelift/codegen/meta/src/gen_asm.rs

Lines changed: 66 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,10 @@ enum IsleConstructor {
301301
/// This constructor produces no results, but the flags register is written,
302302
/// so a `ProducesFlags` value is returned with a side effect.
303303
ProducesFlagsSideEffect,
304+
305+
/// This instructions reads EFLAGS, and returns a single gpr, so this
306+
/// creates `ConsumesFlags.ConsumesFlagsReturnsReg`.
307+
ConsumesFlagsReturnsGpr,
304308
}
305309

306310
impl IsleConstructor {
@@ -314,6 +318,7 @@ impl IsleConstructor {
314318
"SideEffectNoResult"
315319
}
316320
IsleConstructor::ProducesFlagsSideEffect => "ProducesFlags",
321+
IsleConstructor::ConsumesFlagsReturnsGpr => "ConsumesFlags",
317322
}
318323
}
319324

@@ -328,6 +333,7 @@ impl IsleConstructor {
328333
IsleConstructor::RetXmm => "emit_ret_xmm",
329334
IsleConstructor::RetValueRegs => "emit_ret_value_regs",
330335
IsleConstructor::ProducesFlagsSideEffect => "asm_produce_flags_side_effect",
336+
IsleConstructor::ConsumesFlagsReturnsGpr => "asm_consumes_flags_returns_gpr",
331337
}
332338
}
333339

@@ -339,7 +345,8 @@ impl IsleConstructor {
339345
| IsleConstructor::RetXmm
340346
| IsleConstructor::RetValueRegs
341347
| IsleConstructor::NoReturnSideEffect
342-
| IsleConstructor::ProducesFlagsSideEffect => "",
348+
| IsleConstructor::ProducesFlagsSideEffect
349+
| IsleConstructor::ConsumesFlagsReturnsGpr => "",
343350
}
344351
}
345352

@@ -355,7 +362,8 @@ impl IsleConstructor {
355362
| IsleConstructor::RetXmm
356363
| IsleConstructor::RetValueRegs
357364
| IsleConstructor::NoReturnSideEffect
358-
| IsleConstructor::ProducesFlagsSideEffect => false,
365+
| IsleConstructor::ProducesFlagsSideEffect
366+
| IsleConstructor::ConsumesFlagsReturnsGpr => false,
359367
}
360368
}
361369
}
@@ -371,7 +379,7 @@ fn isle_param_for_ctor(op: &Operand, ctor: IsleConstructor) -> String {
371379
OperandKind::RegMem(_) if op.mutability.is_write() => match ctor {
372380
IsleConstructor::RetMemorySideEffect => "SyntheticAmode".to_string(),
373381
IsleConstructor::NoReturnSideEffect => "".to_string(),
374-
IsleConstructor::RetGpr => "Gpr".to_string(),
382+
IsleConstructor::RetGpr | IsleConstructor::ConsumesFlagsReturnsGpr => "Gpr".to_string(),
375383
IsleConstructor::RetXmm => "Xmm".to_string(),
376384
IsleConstructor::RetValueRegs => "ValueRegs".to_string(),
377385
IsleConstructor::ProducesFlagsSideEffect => todo!(),
@@ -411,45 +419,66 @@ fn isle_constructors(format: &Format) -> Vec<IsleConstructor> {
411419
// One read/write register output? Output the instruction
412420
// and that register.
413421
Reg(r) | FixedReg(r) => match r.reg_class().unwrap() {
414-
RegClass::Xmm => vec![IsleConstructor::RetXmm],
415-
RegClass::Gpr => vec![IsleConstructor::RetGpr],
422+
RegClass::Xmm => {
423+
assert!(!format.eflags.is_read());
424+
vec![IsleConstructor::RetXmm]
425+
}
426+
RegClass::Gpr => {
427+
if format.eflags.is_read() {
428+
vec![IsleConstructor::ConsumesFlagsReturnsGpr]
429+
} else {
430+
vec![IsleConstructor::RetGpr]
431+
}
432+
}
416433
},
417434
// One read/write memory operand? Output a side effect.
418-
Mem(_) => vec![IsleConstructor::RetMemorySideEffect],
435+
Mem(_) => {
436+
assert!(!format.eflags.is_read());
437+
vec![IsleConstructor::RetMemorySideEffect]
438+
}
419439
// One read/write reg-mem output? We need constructors for
420440
// both variants.
421-
RegMem(rm) => match rm.reg_class().unwrap() {
422-
RegClass::Xmm => vec![
423-
IsleConstructor::RetXmm,
424-
IsleConstructor::RetMemorySideEffect,
425-
],
426-
RegClass::Gpr => vec![
427-
IsleConstructor::RetGpr,
428-
IsleConstructor::RetMemorySideEffect,
429-
],
430-
},
441+
RegMem(rm) => {
442+
assert!(!format.eflags.is_read());
443+
match rm.reg_class().unwrap() {
444+
RegClass::Xmm => vec![
445+
IsleConstructor::RetXmm,
446+
IsleConstructor::RetMemorySideEffect,
447+
],
448+
RegClass::Gpr => vec![
449+
IsleConstructor::RetGpr,
450+
IsleConstructor::RetMemorySideEffect,
451+
],
452+
}
453+
}
431454
},
432455
},
433-
[one, two] => match (one.location.kind(), two.location.kind()) {
434-
(FixedReg(_) | Reg(_), FixedReg(_) | Reg(_)) => {
435-
vec![IsleConstructor::RetValueRegs]
436-
}
437-
(Reg(r), Mem(_)) | (Mem(_) | RegMem(_), Reg(r) | FixedReg(r)) => {
438-
assert!(matches!(r.reg_class().unwrap(), RegClass::Gpr));
439-
vec![IsleConstructor::RetGpr]
456+
[one, two] => {
457+
assert!(!format.eflags.is_read());
458+
match (one.location.kind(), two.location.kind()) {
459+
(FixedReg(_) | Reg(_), FixedReg(_) | Reg(_)) => {
460+
vec![IsleConstructor::RetValueRegs]
461+
}
462+
(Reg(r), Mem(_)) | (Mem(_) | RegMem(_), Reg(r) | FixedReg(r)) => {
463+
assert!(matches!(r.reg_class().unwrap(), RegClass::Gpr));
464+
vec![IsleConstructor::RetGpr]
465+
}
466+
other => panic!("unsupported number of write operands {other:?}"),
440467
}
441-
other => panic!("unsupported number of write operands {other:?}"),
442-
},
443-
[one, two, three] => match (
444-
one.location.kind(),
445-
two.location.kind(),
446-
three.location.kind(),
447-
) {
448-
(FixedReg(_), FixedReg(_), Mem(_)) => {
449-
vec![IsleConstructor::RetValueRegs]
468+
}
469+
[one, two, three] => {
470+
assert!(!format.eflags.is_read());
471+
match (
472+
one.location.kind(),
473+
two.location.kind(),
474+
three.location.kind(),
475+
) {
476+
(FixedReg(_), FixedReg(_), Mem(_)) => {
477+
vec![IsleConstructor::RetValueRegs]
478+
}
479+
other => panic!("unsupported number of write operands {other:?}"),
450480
}
451-
other => panic!("unsupported number of write operands {other:?}"),
452-
},
481+
}
453482

454483
other => panic!("unsupported number of write operands {other:?}"),
455484
}
@@ -563,7 +592,9 @@ fn generate_isle_inst_decls(f: &mut Formatter, inst: &Inst) {
563592
IsleConstructor::RetMemorySideEffect | IsleConstructor::NoReturnSideEffect => {
564593
unreachable!()
565594
}
566-
IsleConstructor::RetGpr => "(temp_writable_gpr)",
595+
IsleConstructor::RetGpr | IsleConstructor::ConsumesFlagsReturnsGpr => {
596+
"(temp_writable_gpr)"
597+
}
567598
IsleConstructor::RetXmm => "(temp_writable_xmm)",
568599
IsleConstructor::RetValueRegs | IsleConstructor::ProducesFlagsSideEffect => {
569600
todo!()

0 commit comments

Comments
 (0)