x64: Refactor and fill out some gpr-vs-xmm bits (#6058)

* x64: Add instruction helpers for `mov{d,q}`

These will soon grow AVX-equivalents so move them to instruction helpers
to have clauses for AVX in the future.

* x64: Don't auto-convert between RegMemImm and XmmMemImm

The previous conversion, `mov_rmi_to_xmm`, would move from GPR registers
to XMM registers which isn't what many of the other `convert` statements
between these newtypes do. This seemed like a possible footgun so I've
removed the auto-conversion and added an explicit helper to go from a
`u32` to an `XmmMemImm`.

* x64: Add AVX encodings of some more GPR-related insns

This commit adds some more support for AVX instructions where GPRs are
in use mixed in with XMM registers. This required a few more variants of
`Inst` to handle the new instructions.

* Fix vpmovmskb encoding

* Fix xmm-to-gpr encoding of vmovd/vmovq

* Fix typo

* Fix rebase conflict

* Fix rebase conflict with tests
This commit is contained in:
Alex Crichton
2023-03-22 09:58:09 -05:00
committed by GitHub
parent a1072007b8
commit 2fde25311e
14 changed files with 695 additions and 83 deletions

View File

@@ -1715,7 +1715,14 @@ impl AvxOpcode {
| AvxOpcode::Vpextrq
| AvxOpcode::Vpblendw
| AvxOpcode::Vmovddup
| AvxOpcode::Vbroadcastss => {
| AvxOpcode::Vbroadcastss
| AvxOpcode::Vmovd
| AvxOpcode::Vmovq
| AvxOpcode::Vmovmskps
| AvxOpcode::Vmovmskpd
| AvxOpcode::Vpmovmskb
| AvxOpcode::Vcvtsi2ss
| AvxOpcode::Vcvtsi2sd => {
smallvec![InstructionSet::AVX]
}

View File

@@ -2515,6 +2515,89 @@ pub(crate) fn emit(
.encode(sink);
}
Inst::XmmToGprVex {
op,
src,
dst,
dst_size,
} => {
let src = allocs.next(src.to_reg());
let dst = allocs.next(dst.to_reg().to_reg());
let (prefix, map, opcode) = match op {
// vmovd/vmovq are differentiated by `w`
AvxOpcode::Vmovd | AvxOpcode::Vmovq => (LegacyPrefixes::_66, OpcodeMap::_0F, 0x7E),
AvxOpcode::Vmovmskps => (LegacyPrefixes::None, OpcodeMap::_0F, 0x50),
AvxOpcode::Vmovmskpd => (LegacyPrefixes::_66, OpcodeMap::_0F, 0x50),
AvxOpcode::Vpmovmskb => (LegacyPrefixes::_66, OpcodeMap::_0F, 0xD7),
_ => unimplemented!("Opcode {:?} not implemented", op),
};
let w = match dst_size {
OperandSize::Size64 => true,
_ => false,
};
let mut vex = VexInstruction::new()
.length(VexVectorLength::V128)
.w(w)
.prefix(prefix)
.map(map)
.opcode(opcode);
vex = match op {
// The `vmovq/vmovd` reverse the order of the destination/source
// relative to other opcodes using this shape of instruction.
AvxOpcode::Vmovd | AvxOpcode::Vmovq => vex
.rm(dst.to_real_reg().unwrap().hw_enc())
.reg(src.to_real_reg().unwrap().hw_enc()),
_ => vex
.rm(src.to_real_reg().unwrap().hw_enc())
.reg(dst.to_real_reg().unwrap().hw_enc()),
};
vex.encode(sink);
}
Inst::GprToXmmVex {
op,
src,
dst,
src_size,
} => {
let dst = allocs.next(dst.to_reg().to_reg());
let src = match src.clone().to_reg_mem().with_allocs(allocs) {
RegMem::Reg { reg } => {
RegisterOrAmode::Register(reg.to_real_reg().unwrap().hw_enc().into())
}
RegMem::Mem { addr } => RegisterOrAmode::Amode(addr.finalize(state, sink)),
};
let (prefix, map, opcode) = match op {
// vmovd/vmovq are differentiated by `w`
AvxOpcode::Vmovd | AvxOpcode::Vmovq => (LegacyPrefixes::_66, OpcodeMap::_0F, 0x6E),
AvxOpcode::Vcvtsi2ss => (LegacyPrefixes::_F3, OpcodeMap::_0F, 0x2A),
AvxOpcode::Vcvtsi2sd => (LegacyPrefixes::_F2, OpcodeMap::_0F, 0x2A),
_ => unimplemented!("Opcode {:?} not implemented", op),
};
let w = match src_size {
OperandSize::Size64 => true,
_ => false,
};
let mut insn = VexInstruction::new()
.length(VexVectorLength::V128)
.w(w)
.prefix(prefix)
.map(map)
.opcode(opcode)
.rm(src)
.reg(dst.to_real_reg().unwrap().hw_enc());
// These opcodes technically take a second operand which is the
// upper bits to preserve during the float conversion. We don't
// actually use this in this backend right now so reuse the
// destination register. This at least matches what LLVM does.
if let AvxOpcode::Vcvtsi2ss | AvxOpcode::Vcvtsi2sd = op {
insn = insn.vvvv(dst.to_real_reg().unwrap().hw_enc());
}
insn.encode(sink);
}
Inst::XmmRmREvex {
op,
src1,

View File

@@ -158,7 +158,9 @@ impl Inst {
| Inst::XmmUnaryRmRImmVex { op, .. }
| Inst::XmmMovRMVex { op, .. }
| Inst::XmmMovRMImmVex { op, .. }
| Inst::XmmToGprImmVex { op, .. } => op.available_from(),
| Inst::XmmToGprImmVex { op, .. }
| Inst::XmmToGprVex { op, .. }
| Inst::GprToXmmVex { op, .. } => op.available_from(),
}
}
}
@@ -1202,6 +1204,18 @@ impl PrettyPrint for Inst {
format!("{} {}, {}", ljustify(op.to_string()), src, dst)
}
Inst::XmmToGprVex {
op,
src,
dst,
dst_size,
} => {
let dst_size = dst_size.to_bytes();
let src = pretty_print_reg(src.to_reg(), 8, allocs);
let dst = pretty_print_reg(dst.to_reg().to_reg(), dst_size, allocs);
format!("{} {src}, {dst}", ljustify(op.to_string()))
}
Inst::XmmToGprImm { op, src, dst, imm } => {
let src = pretty_print_reg(src.to_reg(), 8, allocs);
let dst = pretty_print_reg(dst.to_reg().to_reg(), 8, allocs);
@@ -1225,6 +1239,17 @@ impl PrettyPrint for Inst {
format!("{} {}, {}", ljustify(op.to_string()), src, dst)
}
Inst::GprToXmmVex {
op,
src,
src_size,
dst,
} => {
let dst = pretty_print_reg(dst.to_reg().to_reg(), 8, allocs);
let src = src.pretty_print(src_size.to_bytes(), allocs);
format!("{} {src}, {dst}", ljustify(op.to_string()))
}
Inst::XmmCmpRmR { op, src, dst } => {
let dst = pretty_print_reg(dst.to_reg(), 8, allocs);
let src = src.pretty_print(8, allocs);
@@ -2082,12 +2107,13 @@ fn x64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut OperandCol
collector.reg_fixed_nonallocatable(*dst);
}
Inst::XmmToGpr { src, dst, .. }
| Inst::XmmToGprVex { src, dst, .. }
| Inst::XmmToGprImm { src, dst, .. }
| Inst::XmmToGprImmVex { src, dst, .. } => {
collector.reg_use(src.to_reg());
collector.reg_def(dst.to_writable_reg());
}
Inst::GprToXmm { src, dst, .. } => {
Inst::GprToXmm { src, dst, .. } | Inst::GprToXmmVex { src, dst, .. } => {
collector.reg_def(dst.to_writable_reg());
src.get_operands(collector);
}