diff --git a/src/Target/x64/asm/instr.rs b/src/Target/x64/asm/instr.rs index 3333190b..3bf68c70 100644 --- a/src/Target/x64/asm/instr.rs +++ b/src/Target/x64/asm/instr.rs @@ -16,6 +16,8 @@ pub struct X64MCInstr { pub op1: Option, /// Second operand pub op2: Option, + /// Third operand + pub op3: Option, // for far calls pub(crate) far: bool, @@ -28,6 +30,7 @@ impl X64MCInstr { mnemonic: mne, op1: None, op2: None, + op3: None, far: false, } } @@ -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, } } @@ -48,6 +52,7 @@ impl X64MCInstr { mnemonic: mne, op1: Some(op), op2: None, + op3: None, far: false, } } @@ -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, } } @@ -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::(Code::Imul_rm64, (*op1).into())? - } else if op1.is_gr32() { - Instruction::with1::(Code::Imul_rm32, (*op1).into())? - } else if op1.is_gr16() { - Instruction::with1::(Code::Imul_rm16, (*op1).into())? - } else if op1.is_gr8() { - Instruction::with1::(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::(Code::Imul_r16_rm16_imm16, (*op1).into(), (*op2).into(), *op3 as i32)? + } else if op1.is_gr32() { + Instruction::with3::(Code::Imul_r32_rm32_imm32, (*op1).into(), (*op2).into(), *op3 as i32)? + } else if op1.is_gr64() { + Instruction::with3::(Code::Imul_r64_rm64_imm32, (*op1).into(), (*op2).into(), *op3 as i32)? + } else { todo!("{}", self)} + } else if op1.is_gr64() { + Instruction::with2::(Code::Imul_r64_rm64, (*op1).into(), (*op2).into())? + } else if op1.is_gr32() { + Instruction::with2::(Code::Imul_r32_rm32, (*op1).into(), (*op2).into())? + } else if op1.is_gr16() { + Instruction::with2::(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::(Code::Imul_r16_rm16_imm16, (*op1).into(), op2.into(), *op3 as i32)? + } else if op1.is_gr32() { + Instruction::with3::(Code::Imul_r32_rm32_imm32, (*op1).into(), op2.into(), *op3 as i32)? + } else if op1.is_gr64() { + Instruction::with3::(Code::Imul_r64_rm64_imm32, (*op1).into(), op2.into(), *op3 as i32)? + } else { todo!("{}", self)} + } else if op1.is_gr64() { + Instruction::with2::(Code::Imul_r64_rm64, (*op1).into(), op2.into())? + } else if op1.is_gr32() { + Instruction::with2::(Code::Imul_r32_rm32, (*op1).into(), op2.into())? + } else if op1.is_gr16() { + Instruction::with2::(Code::Imul_r16_rm16, (*op1).into(), op2.into())? + } else { todo!("{}", self)} + } else { + if op1.is_gr64() { + Instruction::with1::(Code::Imul_rm64, (*op1).into())? + } else if op1.is_gr32() { + Instruction::with1::(Code::Imul_rm32, (*op1).into())? + } else if op1.is_gr16() { + Instruction::with1::(Code::Imul_rm16, (*op1).into())? + } else if op1.is_gr8() { + Instruction::with1::(Code::Imul_rm8, (*op1).into())? + } else { todo!("{}", self)} + } } else if let Some(Operand::Mem(op1)) = &self.op1 { Instruction::with1::(Code::Imul_rm64, op1.into())? } else { todo!("{}", self) } @@ -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)); + } } } diff --git a/src/Target/x64/asm/parser.rs b/src/Target/x64/asm/parser.rs index af98b36c..bd5a5057 100644 --- a/src/Target/x64/asm/parser.rs +++ b/src/Target/x64/asm/parser.rs @@ -46,6 +46,7 @@ impl x64Parser { mnemonic: mne, op1: None, op2: None, + op3: None, far: false, }; @@ -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 @@ -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()))? diff --git a/src/Target/x64/lower/math.rs b/src/Target/x64/lower/math.rs index e3c93aa9..43bb0511 100644 --- a/src/Target/x64/lower/math.rs +++ b/src/Target/x64/lower/math.rs @@ -38,65 +38,47 @@ pub(crate) fn x64_lower_mul(sink: &mut Vec, 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)), + ]); } } diff --git a/src/Target/x64/optimizer.rs b/src/Target/x64/optimizer.rs index 151fc7bf..d32b6b93 100644 --- a/src/Target/x64/optimizer.rs +++ b/src/Target/x64/optimizer.rs @@ -101,6 +101,7 @@ fn X64MergeMove(instr0: &X64MCInstr, instr1: &X64MCInstr) -> Option displ: 0, rip: false, })), + op3: None, far: false, }]) } else if instr0.is_op2_imm() && instr1.is_op2_reg() { @@ -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() { @@ -216,6 +219,7 @@ fn X64MergeAdd(instr0: &X64MCInstr, instr1: &X64MCInstr, instr2: &X64MCInstr) -> displ: imm as isize, rip: false, })), + op3: None, far: false, }]) } else { None } @@ -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, }]) } \ No newline at end of file