Skip to content

Commit

Permalink
[X64] fixing optimization of mul
Browse files Browse the repository at this point in the history
  • Loading branch information
Cr0a3 committed Oct 28, 2024
1 parent 272c8fe commit 25eee30
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 56 deletions.
72 changes: 63 additions & 9 deletions src/Target/x64/asm/instr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ pub struct X64MCInstr {
pub op1: Option<Operand>,
/// Second operand
pub op2: Option<Operand>,
/// Third operand
pub op3: Option<Operand>,

// for far calls
pub(crate) far: bool,
Expand All @@ -28,6 +30,7 @@ impl X64MCInstr {
mnemonic: mne,
op1: None,
op2: None,
op3: None,
far: false,
}
}
Expand All @@ -38,6 +41,7 @@ impl X64MCInstr {
mnemonic: self.mnemonic.to_owned(),
op1: self.op1.to_owned(),
op2: self.op2.to_owned(),
op3: self.op3.to_owned(),
far: true,
}
}
Expand All @@ -48,6 +52,7 @@ impl X64MCInstr {
mnemonic: mne,
op1: Some(op),
op2: None,
op3: None,
far: false,
}
}
Expand All @@ -58,6 +63,18 @@ impl X64MCInstr {
mnemonic: mne,
op1: Some(op1),
op2: Some(op2),
op3: None,
far: false,
}
}

/// Creates the instruction with 3 operands
pub fn with3(mne: Mnemonic, op1: Operand, op2: Operand, op3: Operand) -> Self {
Self {
mnemonic: mne,
op1: Some(op1),
op2: Some(op2),
op3: Some(op3),
far: false,
}
}
Expand Down Expand Up @@ -563,15 +580,49 @@ impl X64MCInstr {
Mnemonic::Ret => Instruction::with(Code::Retnq),
Mnemonic::Imul => {
if let Some(Operand::Reg(op1)) = &self.op1 {
if op1.is_gr64() {
Instruction::with1::<Register>(Code::Imul_rm64, (*op1).into())?
} else if op1.is_gr32() {
Instruction::with1::<Register>(Code::Imul_rm32, (*op1).into())?
} else if op1.is_gr16() {
Instruction::with1::<Register>(Code::Imul_rm16, (*op1).into())?
} else if op1.is_gr8() {
Instruction::with1::<Register>(Code::Imul_rm8, (*op1).into())?
} else { todo!("{}", self)}
if let Some(Operand::Reg(op2)) = &self.op2 {
if let Some(Operand::Imm(op3)) = &self.op3 {
if op1.is_gr16() {
Instruction::with3::<Register, Register, i32>(Code::Imul_r16_rm16_imm16, (*op1).into(), (*op2).into(), *op3 as i32)?
} else if op1.is_gr32() {
Instruction::with3::<Register, Register, i32>(Code::Imul_r32_rm32_imm32, (*op1).into(), (*op2).into(), *op3 as i32)?
} else if op1.is_gr64() {
Instruction::with3::<Register, Register, i32>(Code::Imul_r64_rm64_imm32, (*op1).into(), (*op2).into(), *op3 as i32)?
} else { todo!("{}", self)}
} else if op1.is_gr64() {
Instruction::with2::<Register, Register>(Code::Imul_r64_rm64, (*op1).into(), (*op2).into())?
} else if op1.is_gr32() {
Instruction::with2::<Register, Register>(Code::Imul_r32_rm32, (*op1).into(), (*op2).into())?
} else if op1.is_gr16() {
Instruction::with2::<Register, Register>(Code::Imul_r16_rm16, (*op1).into(), (*op2).into())?
} else { todo!("{}", self)}
} else if let Some(Operand::Mem(op2)) = &self.op2 {
if let Some(Operand::Imm(op3)) = &self.op3 {
if op1.is_gr16() {
Instruction::with3::<Register, MemoryOperand, i32>(Code::Imul_r16_rm16_imm16, (*op1).into(), op2.into(), *op3 as i32)?
} else if op1.is_gr32() {
Instruction::with3::<Register, MemoryOperand, i32>(Code::Imul_r32_rm32_imm32, (*op1).into(), op2.into(), *op3 as i32)?
} else if op1.is_gr64() {
Instruction::with3::<Register, MemoryOperand, i32>(Code::Imul_r64_rm64_imm32, (*op1).into(), op2.into(), *op3 as i32)?
} else { todo!("{}", self)}
} else if op1.is_gr64() {
Instruction::with2::<Register, MemoryOperand>(Code::Imul_r64_rm64, (*op1).into(), op2.into())?
} else if op1.is_gr32() {
Instruction::with2::<Register, MemoryOperand>(Code::Imul_r32_rm32, (*op1).into(), op2.into())?
} else if op1.is_gr16() {
Instruction::with2::<Register, MemoryOperand>(Code::Imul_r16_rm16, (*op1).into(), op2.into())?
} else { todo!("{}", self)}
} else {
if op1.is_gr64() {
Instruction::with1::<Register>(Code::Imul_rm64, (*op1).into())?
} else if op1.is_gr32() {
Instruction::with1::<Register>(Code::Imul_rm32, (*op1).into())?
} else if op1.is_gr16() {
Instruction::with1::<Register>(Code::Imul_rm16, (*op1).into())?
} else if op1.is_gr8() {
Instruction::with1::<Register>(Code::Imul_rm8, (*op1).into())?
} else { todo!("{}", self)}
}
} else if let Some(Operand::Mem(op1)) = &self.op1 {
Instruction::with1::<MemoryOperand>(Code::Imul_rm64, op1.into())?
} else { todo!("{}", self) }
Expand Down Expand Up @@ -1247,6 +1298,9 @@ impl Display for X64MCInstr {
string.push_str(&format!(" {}", op1));
if let Some(op2) = &self.op2 {
string.push_str(&format!(", {}", op2));
if let Some(op3) = &self.op3 {
string.push_str(&format!(", {}", op3));
}
}
}

Expand Down
28 changes: 25 additions & 3 deletions src/Target/x64/asm/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ impl x64Parser {
mnemonic: mne,
op1: None,
op2: None,
op3: None,
far: false,
};

Expand All @@ -68,6 +69,7 @@ impl x64Parser {
self.tokens.pop_front(); // advance over ]
}

let mut second_op = false;
if first_op {
if let Some(Token::Comma) = self.tokens.front() {
self.tokens.pop_front(); // advance
Expand All @@ -83,11 +85,31 @@ impl x64Parser {
self.tokens.pop_front(); // advance
} else if let Some(Token::L_Bracket) = self.tokens.front() {
instr.op2 = Some(Operand::Mem(self.parse_mem()?));
}else {
} else {
Err(ParsingError::CommaWithoutOperandAfter)?
}
}
second_op = true;
}

if second_op {
if let Some(Token::Comma) = self.tokens.front() {
self.tokens.pop_front(); // advance
if let Some(Token::Num(n)) = self.tokens.front() {
instr.op3 = Some(Operand::Imm(*n));
self.tokens.pop_front(); // advance
} else if let Some(Token::Ident(reg)) = self.tokens.front() {
if let Some(reg) = X64Reg::parse(reg.to_string()) {
instr.op3 = Some(Operand::Reg(reg))
} else {
Err(ParsingError::UnknownRegOrUnexpectedIdent(reg.to_string()))?
}
self.tokens.pop_front(); // advance
} else if let Some(Token::L_Bracket) = self.tokens.front() {
instr.op3 = Some(Operand::Mem(self.parse_mem()?));
} else {
Err(ParsingError::CommaWithoutOperandAfter)?
}
} else if self.tokens.len() > 0 {
Err(ParsingError::UnexpectedTokens(self.tokens.clone().into()))?
}
} else if self.tokens.len() > 0 {
Err(ParsingError::UnexpectedTokens(self.tokens.clone().into()))?
Expand Down
70 changes: 26 additions & 44 deletions src/Target/x64/lower/math.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,65 +38,47 @@ pub(crate) fn x64_lower_mul(sink: &mut Vec<X64MCInstr>, instr: &MachineInstr) {
let op2: Operand = (*op2).into();
let out: Operand = out.into();

if op1.is_reg() && op2.is_imm() && out.is_reg() {
let Operand::Reg(op) = op1 else { unreachable!() };
if !op1.is_imm() && op2.is_imm() && out.is_reg() {
let Operand::Imm(displ) = op2 else { unreachable!() };

sink.push(X64MCInstr::with2(Mnemonic::Lea, out, Operand::Mem(MemOp {
base: Some(op),
index: None,
scale: 1,
displ: displ as isize,
rip: false,
})));
sink.push(X64MCInstr::with3(Mnemonic::Imul, out, op1, Operand::Imm(displ)));

return;
}

if op1.is_imm() && op2.is_reg() && out.is_reg() {
let Operand::Reg(op) = op2 else { unreachable!() };
let Operand::Imm(displ) = op1 else { unreachable!() };

sink.push(X64MCInstr::with2(Mnemonic::Lea, out, Operand::Mem(MemOp {
base: Some(op),
index: None,
scale: 1,
displ: displ as isize,
rip: false,
})));
if op1.is_imm() && op2.is_imm() { // theoraticly we could precalculate it here but if the user wanted us to do this he would use `-O` flag
sink.extend_from_slice(&[
X64MCInstr::with2(Mnemonic::Mov, Operand::Reg(X64Reg::Rax.sub_ty(instr.meta)), op1),
X64MCInstr::with2(Mnemonic::Mov, Operand::Reg(X64Reg::Rbx.sub_ty(instr.meta)), op2),
X64MCInstr::with2(Mnemonic::Imul, Operand::Reg(X64Reg::Rax.sub_ty(instr.meta)), Operand::Reg(X64Reg::Rbx.sub_ty(instr.meta))),
X64MCInstr::with2(Mnemonic::Mov, out, Operand::Reg(X64Reg::Rax.sub_ty(instr.meta))),
]);

return;
}

let mnemonic = if instr.meta.signed() {
Mnemonic::Imul
} else {
Mnemonic::Mul
};

if !op2.is_imm() && op1.is_imm() && out.is_reg() {
let Operand::Imm(displ) = op1 else { unreachable!() };

if out != Operand::Reg(X64Reg::Rdx.sub_ty(instr.meta)) {
sink.push( X64MCInstr::with1(Mnemonic::Push, Operand::Reg(X64Reg::Rdx)).into() );
sink.push(X64MCInstr::with3(Mnemonic::Imul, out, op2, Operand::Imm(displ)));

return;
}

let rax = || Operand::Reg(X64Reg::Rax.sub_ty(instr.meta));

sink.push(X64MCInstr::with2(Mnemonic::Mov, rax(), op1));
let tmp = X64Reg::Rax.sub_ty(instr.meta);

// mul/imul only accept r/m
if let Operand::Imm(_) = op2 {
sink.push(X64MCInstr::with2(Mnemonic::Mov, Operand::Reg(X64Reg::Rbx.sub_ty(instr.meta)), op2));
sink.push(X64MCInstr::with1(mnemonic, Operand::Reg(X64Reg::Rbx.sub_ty(instr.meta))));
} else if let Operand::Mem(_) = op2 {
sink.push(X64MCInstr::with2(Mnemonic::Mov, Operand::Reg(X64Reg::Rbx.sub_ty(instr.meta)), op2));
sink.push(X64MCInstr::with1(mnemonic, Operand::Reg(X64Reg::Rbx.sub_ty(instr.meta))));
if op2 != out && out.is_reg() {
sink.extend_from_slice(&[
X64MCInstr::with2(Mnemonic::Mov, out.clone(), op1),
X64MCInstr::with2(Mnemonic::Imul, out, op2),
]);
} else {
sink.push(X64MCInstr::with1(mnemonic, op2));
}

sink.push(X64MCInstr::with2(Mnemonic::Mov, out.to_owned(), rax()));

if out != Operand::Reg(X64Reg::Rdx.sub_ty(instr.meta)) {
sink.push( X64MCInstr::with1(Mnemonic::Pop, Operand::Reg(X64Reg::Rdx)).into() );
sink.extend_from_slice(&[
X64MCInstr::with2(Mnemonic::Mov, Operand::Reg(tmp), op1),
X64MCInstr::with2(Mnemonic::Imul, Operand::Reg(tmp), op2),
X64MCInstr::with2(Mnemonic::Mov, out, Operand::Reg(tmp)),
]);
}
}

Expand Down
5 changes: 5 additions & 0 deletions src/Target/x64/optimizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ fn X64MergeMove(instr0: &X64MCInstr, instr1: &X64MCInstr) -> Option<Vec<X64MCIns
mnemonic: Mnemonic::Mov,
op1: instr1.op1.clone(),
op2: instr0.op2.clone(),
op3: None,
far: false,
}])
}
Expand Down Expand Up @@ -186,6 +187,7 @@ fn X64MergeAdd(instr0: &X64MCInstr, instr1: &X64MCInstr, instr2: &X64MCInstr) ->
displ: 0,
rip: false,
})),
op3: None,
far: false,
}])
} else if instr0.is_op2_imm() && instr1.is_op2_reg() {
Expand All @@ -201,6 +203,7 @@ fn X64MergeAdd(instr0: &X64MCInstr, instr1: &X64MCInstr, instr2: &X64MCInstr) ->
displ: imm as isize,
rip: false,
})),
op3: None,
far: false,
}])
} else if instr0.is_op2_reg() && instr1.is_op2_imm() {
Expand All @@ -216,6 +219,7 @@ fn X64MergeAdd(instr0: &X64MCInstr, instr1: &X64MCInstr, instr2: &X64MCInstr) ->
displ: imm as isize,
rip: false,
})),
op3: None,
far: false,
}])
} else { None }
Expand Down Expand Up @@ -260,6 +264,7 @@ fn X64MergeBrCond(instr0: &X64MCInstr, instr1: &X64MCInstr, instr2: &X64MCInstr)
mnemonic: jmp_mne,
op1: instr2.op1.to_owned(),
op2: None,
op3: None,
far: false,
}])
}

0 comments on commit 25eee30

Please sign in to comment.