machinst x64: commute operands of integer operations if one input is an immediate;
This commit is contained in:
@@ -135,12 +135,14 @@ fn matches_input<C: LowerCtx<I = Inst>>(c: &mut C, input: InsnInput, op: Opcode)
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Put the given input into a register, and mark it as used (side-effect).
|
||||||
fn input_to_reg(ctx: Ctx, spec: InsnInput) -> Reg {
|
fn input_to_reg(ctx: Ctx, spec: InsnInput) -> Reg {
|
||||||
let inputs = ctx.get_input(spec.insn, spec.input);
|
let inputs = ctx.get_input(spec.insn, spec.input);
|
||||||
ctx.use_input_reg(inputs);
|
ctx.use_input_reg(inputs);
|
||||||
inputs.reg
|
inputs.reg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An extension specification for `extend_input_to_reg`.
|
||||||
enum ExtSpec {
|
enum ExtSpec {
|
||||||
ZeroExtendTo32,
|
ZeroExtendTo32,
|
||||||
ZeroExtendTo64,
|
ZeroExtendTo64,
|
||||||
@@ -148,6 +150,8 @@ enum ExtSpec {
|
|||||||
SignExtendTo64,
|
SignExtendTo64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Put the given input into a register, marking it as used, and do a zero- or signed- extension if
|
||||||
|
/// required. (This obviously causes side-effects.)
|
||||||
fn extend_input_to_reg(ctx: Ctx, spec: InsnInput, ext_spec: ExtSpec) -> Reg {
|
fn extend_input_to_reg(ctx: Ctx, spec: InsnInput, ext_spec: ExtSpec) -> Reg {
|
||||||
let requested_size = match ext_spec {
|
let requested_size = match ext_spec {
|
||||||
ExtSpec::ZeroExtendTo32 | ExtSpec::SignExtendTo32 => 32,
|
ExtSpec::ZeroExtendTo32 | ExtSpec::SignExtendTo32 => 32,
|
||||||
@@ -188,15 +192,17 @@ fn extend_input_to_reg(ctx: Ctx, spec: InsnInput, ext_spec: ExtSpec) -> Reg {
|
|||||||
dst.to_reg()
|
dst.to_reg()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Put the given input into a register or a memory operand.
|
||||||
|
/// Effectful: may mark the given input as used, when returning the register form.
|
||||||
fn input_to_reg_mem(ctx: Ctx, spec: InsnInput) -> RegMem {
|
fn input_to_reg_mem(ctx: Ctx, spec: InsnInput) -> RegMem {
|
||||||
// TODO handle memory.
|
// TODO handle memory.
|
||||||
RegMem::reg(input_to_reg(ctx, spec))
|
RegMem::reg(input_to_reg(ctx, spec))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Try to use an immediate for constant inputs, and a register otherwise.
|
/// Returns whether the given input is an immediate that can be properly sign-extended, without any
|
||||||
/// TODO: handle memory as well!
|
/// possible side-effect.
|
||||||
fn input_to_reg_mem_imm(ctx: Ctx, spec: InsnInput) -> RegMemImm {
|
fn input_to_sext_imm(ctx: Ctx, spec: InsnInput) -> Option<u32> {
|
||||||
let imm = ctx.get_input(spec.insn, spec.input).constant.and_then(|x| {
|
ctx.get_input(spec.insn, spec.input).constant.and_then(|x| {
|
||||||
// For i64 instructions (prefixed with REX.W), require that the immediate will sign-extend
|
// For i64 instructions (prefixed with REX.W), require that the immediate will sign-extend
|
||||||
// to 64 bits. For other sizes, it doesn't matter and we can just use the plain
|
// to 64 bits. For other sizes, it doesn't matter and we can just use the plain
|
||||||
// constant.
|
// constant.
|
||||||
@@ -205,10 +211,18 @@ fn input_to_reg_mem_imm(ctx: Ctx, spec: InsnInput) -> RegMemImm {
|
|||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
match imm {
|
}
|
||||||
|
|
||||||
|
/// Put the given input into an immediate, a register or a memory operand.
|
||||||
|
/// Effectful: may mark the given input as used, when returning the register form.
|
||||||
|
fn input_to_reg_mem_imm(ctx: Ctx, spec: InsnInput) -> RegMemImm {
|
||||||
|
match input_to_sext_imm(ctx, spec) {
|
||||||
Some(x) => RegMemImm::imm(x),
|
Some(x) => RegMemImm::imm(x),
|
||||||
None => RegMemImm::reg(input_to_reg(ctx, spec)),
|
None => match input_to_reg_mem(ctx, spec) {
|
||||||
|
RegMem::Reg { reg } => RegMemImm::reg(reg),
|
||||||
|
RegMem::Mem { addr } => RegMemImm::mem(addr),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -369,8 +383,6 @@ fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||||||
| Opcode::Band
|
| Opcode::Band
|
||||||
| Opcode::Bor
|
| Opcode::Bor
|
||||||
| Opcode::Bxor => {
|
| Opcode::Bxor => {
|
||||||
// TODO For commutative operations (add, mul, and, or, xor), try to commute the
|
|
||||||
// operands if one is an immediate.
|
|
||||||
let ty = ty.unwrap();
|
let ty = ty.unwrap();
|
||||||
if ty.lane_count() > 1 {
|
if ty.lane_count() > 1 {
|
||||||
let sse_op = match op {
|
let sse_op = match op {
|
||||||
@@ -413,8 +425,32 @@ fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||||||
Opcode::Bxor => AluRmiROpcode::Xor,
|
Opcode::Bxor => AluRmiROpcode::Xor,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
let lhs = input_to_reg(ctx, inputs[0]);
|
|
||||||
let rhs = input_to_reg_mem_imm(ctx, inputs[1]);
|
let (lhs, rhs) = match op {
|
||||||
|
Opcode::Iadd
|
||||||
|
| Opcode::IaddIfcout
|
||||||
|
| Opcode::Imul
|
||||||
|
| Opcode::Band
|
||||||
|
| Opcode::Bor
|
||||||
|
| Opcode::Bxor => {
|
||||||
|
// For commutative operations, try to commute operands if one is an
|
||||||
|
// immediate.
|
||||||
|
if let Some(imm) = input_to_sext_imm(ctx, inputs[0]) {
|
||||||
|
(input_to_reg(ctx, inputs[1]), RegMemImm::imm(imm))
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
input_to_reg(ctx, inputs[0]),
|
||||||
|
input_to_reg_mem_imm(ctx, inputs[1]),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Opcode::Isub => (
|
||||||
|
input_to_reg(ctx, inputs[0]),
|
||||||
|
input_to_reg_mem_imm(ctx, inputs[1]),
|
||||||
|
),
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
let dst = output_to_reg(ctx, outputs[0]);
|
let dst = output_to_reg(ctx, outputs[0]);
|
||||||
ctx.emit(Inst::mov_r_r(true, lhs, dst));
|
ctx.emit(Inst::mov_r_r(true, lhs, dst));
|
||||||
ctx.emit(Inst::alu_rmi_r(is_64, alu_op, rhs, dst));
|
ctx.emit(Inst::alu_rmi_r(is_64, alu_op, rhs, dst));
|
||||||
|
|||||||
Reference in New Issue
Block a user