From b31b2fd875d43aab39db07dd7da1aa5e8b39bb8b Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Mon, 12 Oct 2020 20:01:58 -0700 Subject: [PATCH] working on adding support for immediate operands --- .../src/inline_assembly.rs | 8 + .../src/instructions.rs | 168 +++++++++++++++--- src/instr_models.rs | 10 ++ src/lib.rs | 5 +- tests/test_power_instruction_analyzer.py | 2 +- 5 files changed, 164 insertions(+), 29 deletions(-) diff --git a/power-instruction-analyzer-proc-macro/src/inline_assembly.rs b/power-instruction-analyzer-proc-macro/src/inline_assembly.rs index 5b3a382..a0a8bf4 100644 --- a/power-instruction-analyzer-proc-macro/src/inline_assembly.rs +++ b/power-instruction-analyzer-proc-macro/src/inline_assembly.rs @@ -360,6 +360,14 @@ impl Assembly { clobbers: self.clobbers.clone(), } } + pub(crate) fn text_without_args(&self) -> Assembly { + Assembly { + text_fragments: self.text_fragments.clone(), + inputs: Vec::new(), + outputs: Vec::new(), + clobbers: Vec::new(), + } + } pub(crate) fn to_text(&self) -> String { let mut id_index_map = HashMap::new(); for (index, id) in self diff --git a/power-instruction-analyzer-proc-macro/src/instructions.rs b/power-instruction-analyzer-proc-macro/src/instructions.rs index 5ecb91a..4146cb6 100644 --- a/power-instruction-analyzer-proc-macro/src/instructions.rs +++ b/power-instruction-analyzer-proc-macro/src/instructions.rs @@ -160,6 +160,45 @@ enum ImmediateShape { U16, } +impl ImmediateShape { + fn is_signed(self) -> bool { + match self { + ImmediateShape::S16 => true, + ImmediateShape::U16 => false, + } + } + fn bits(self) -> usize { + match self { + ImmediateShape::S16 | ImmediateShape::U16 => 16, + } + } + fn bit_mask(self) -> u64 { + match self.bits() { + 64 => u64::MAX, + bits => (1u64 << bits) - 1, + } + } + fn value_to_string(self, bits: u64) -> String { + let bits = self.normalize_value(bits); + if self.is_signed() { + (bits as i64).to_string() + } else { + bits.to_string() + } + } + fn normalize_value(self, mut bits: u64) -> u64 { + bits &= self.bit_mask(); + if self.is_signed() && (bits >> (self.bits() - 1)) != 0 { + bits |= !self.bit_mask(); + } + bits + } + /// returns all the immediate values starting at zero and incrementing from there + fn values(self) -> impl Iterator { + (0..=self.bit_mask()).map(move |v| self.normalize_value(v)) + } +} + impl InstructionInputName { fn get_immediate_shape(&self) -> Option { match self { @@ -172,6 +211,21 @@ impl InstructionInputName { InstructionInputName::ImmediateU16(_) => Some(ImmediateShape::U16), } } + fn get_instruction_input_register_tokens(&self) -> TokenStream { + match self { + InstructionInputName::Ra(_) => quote! {InstructionInputRegister::Ra}, + InstructionInputName::Rb(_) => quote! {InstructionInputRegister::Rb}, + InstructionInputName::Rc(_) => quote! {InstructionInputRegister::Rc}, + InstructionInputName::ImmediateS16(_) => { + quote! {InstructionInputRegister::ImmediateS16} + } + InstructionInputName::ImmediateU16(_) => { + quote! {InstructionInputRegister::ImmediateU16} + } + InstructionInputName::Carry(_) => quote! {InstructionInputRegister::Carry}, + InstructionInputName::Overflow(_) => quote! {InstructionInputRegister::Overflow}, + } + } } ident_enum! { @@ -338,6 +392,12 @@ impl Parse for Instruction { if body_tokens.peek(Token!(:)) { body_tokens.parse::()?; literal_instruction_text = Some(body_tokens.parse()?); + if found_immediate { + return Err(Error::new_spanned( + &literal_instruction_text, + "literal instruction text is not supported for instructions with immediates", + )); + } } else { literal_instruction_text = None; } @@ -356,19 +416,7 @@ impl Instruction { fn map_input_registers(&self) -> syn::Result> { let mut retval = Vec::new(); for input in &self.inputs { - retval.push(match input.name { - InstructionInputName::Ra(_) => quote! {InstructionInputRegister::Ra}, - InstructionInputName::Rb(_) => quote! {InstructionInputRegister::Rb}, - InstructionInputName::Rc(_) => quote! {InstructionInputRegister::Rc}, - InstructionInputName::ImmediateS16(_) => { - quote! {InstructionInputRegister::Immediate(ImmediateShape::S16)} - } - InstructionInputName::ImmediateU16(_) => { - quote! {InstructionInputRegister::Immediate(ImmediateShape::U16)} - } - InstructionInputName::Carry(_) => quote! {InstructionInputRegister::Carry}, - InstructionInputName::Overflow(_) => quote! {InstructionInputRegister::Overflow}, - }); + retval.push(input.name.get_instruction_input_register_tokens()); } Ok(retval) } @@ -475,7 +523,7 @@ impl Instruction { shape: ImmediateShape, id: AssemblyMetavariableId, } - let mut immediate = None; + let mut immediate: Option = None; for input in inputs { match input.name { InstructionInputName::Ra(_) => { @@ -499,6 +547,14 @@ impl Instruction { let id = AssemblyMetavariableId::new(); assert!(immediate.is_none()); immediate = Some(Immediate { shape, id }); + let mask = shape.bit_mask(); + let instruction_input_register = + input.name.get_instruction_input_register_tokens(); + before_asm.push(quote! { + let immediate: u64 = inputs.try_get_immediate( + #instruction_input_register + )? & #mask; + }); asm_instr_args.push(id.into()); } InstructionInputName::Carry(_) => { @@ -574,24 +630,88 @@ impl Instruction { "mfcr $" output{"=&b"(cr)} clobber{"cr"} }); } - if let Some(Immediate { shape, id }) = immediate { - todo!() - } - let mut final_asm = assembly! {}; - for i in before_instr_asm_lines { - append_assembly! {final_asm; (i) "\n"}; - } - append_assembly!(final_asm; (asm_instr)); + let mut asm_instrs = asm_instr; let mut separator = " "; for i in asm_instr_args { if literal_instruction_text.is_some() { let i = i.args_without_text(); - append_assembly!(final_asm; (i)); + append_assembly!(asm_instrs; (i)); } else { - append_assembly!(final_asm; (separator) (i)); + append_assembly!(asm_instrs; (separator) (i)); } separator = ", "; } + if let Some(Immediate { + shape, + id: immediate_id, + }) = immediate + { + let shape: ImmediateShape = shape; + assert!(literal_instruction_text.is_none()); + // save and restore lr and ctr ourselves since LLVM doesn't handle that properly + // see https://bugs.llvm.org/show_bug.cgi?id=47811 + // and https://bugs.llvm.org/show_bug.cgi?id=47812 + before_asm.push(quote! {let lr_temp: u64;}); + let lr_temp; + before_instr_asm_lines.push(assembly! {"mflr $" output(lr_temp = {"=&b"(lr_temp)})}); + after_instr_asm_lines.push(assembly! {"mtlr $" (lr_temp)}); + before_asm.push(quote! {let ctr_temp: u64;}); + let ctr_temp; + before_instr_asm_lines.push(assembly! {"mfctr $" output(ctr_temp = {"=&b"(ctr_temp)})}); + after_instr_asm_lines.push(assembly! {"mtctr $" (ctr_temp)}); + let template = mem::replace(&mut asm_instrs, assembly! {}); + let target_temp; + before_asm.push(quote! {let target_temp: u64;}); + let target_temp2; + before_asm.push(quote! {let target_temp2: u64;}); + append_assembly! { + asm_instrs; + "bl 3f\n" + "4:\n" + "mulli $" output(target_temp = {"=&b"(target_temp)}) ", $" input{"b"(immediate)} ", 1f - 0f\n" + "addi $" (target_temp) ", $" (target_temp) ", 0f - 4b\n" + "mflr $" output(target_temp2 = {"=&b"(target_temp2)}) "\n" + "add $" (target_temp) ", $" (target_temp) ", $" (target_temp2) "\n" + "mtctr $" (target_temp) "\n" + "bctrl\n" + "b 2f\n" + "3:\n" + "blr\n" + }; + let mut count = 0; + for (index, immediate) in shape.values().enumerate() { + count = index + 1; + match index { + 0 => { + append_assembly! {asm_instrs; "0:\n"}; + } + 1 => { + append_assembly! {asm_instrs; "1:\n"}; + } + _ => {} + } + let expanded_template = template + .replace_metavariables(|id| -> syn::Result<_> { + Ok(if id == immediate_id { + shape.value_to_string(immediate).into() + } else { + id.into() + }) + })? + .text_without_args(); + append_assembly! {asm_instrs; (expanded_template) "\n"}; + append_assembly! {asm_instrs; "blr\n"}; + } + assert!(count >= 1); + append_assembly! {asm_instrs; "2:"}; + let args = template.args_without_text(); + append_assembly! {asm_instrs; (args)}; + } + let mut final_asm = assembly! {}; + for i in before_instr_asm_lines { + append_assembly! {final_asm; (i) "\n"}; + } + append_assembly!(final_asm; (asm_instrs)); for i in after_instr_asm_lines { append_assembly! {final_asm; "\n" (i)}; } diff --git a/src/instr_models.rs b/src/instr_models.rs index 5389383..446e850 100644 --- a/src/instr_models.rs +++ b/src/instr_models.rs @@ -56,6 +56,16 @@ macro_rules! create_instr_variants_cr { }; } +pub fn addi(inputs: InstructionInput) -> InstructionResult { + let ra = inputs.try_get_ra()? as i64; + let immediate = inputs.try_get_immediate_s16()? as i64; + let result = ra.wrapping_add(immediate) as u64; + Ok(InstructionOutput { + rt: Some(result), + ..InstructionOutput::default() + }) +} + create_instr_variants_ov_cr!(add, addo, add_, addo_, i64); pub fn addo(inputs: InstructionInput) -> InstructionResult { diff --git a/src/lib.rs b/src/lib.rs index ba2c6e0..4cbfad9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -304,13 +304,10 @@ pub struct WholeTest { } instructions! { - // TODO(programmerjake): finish implementing immediate args - /* #[enumerant = AddI] - fn add(Ra, ImmediateS16) -> (Rt) { + fn addi(Ra, ImmediateS16) -> (Rt) { "addi" } - */ // add #[enumerant = Add] diff --git a/tests/test_power_instruction_analyzer.py b/tests/test_power_instruction_analyzer.py index 7858168..0bde084 100644 --- a/tests/test_power_instruction_analyzer.py +++ b/tests/test_power_instruction_analyzer.py @@ -227,7 +227,7 @@ class TestInstructionOutput(unittest.TestCase): class TestDivInstrs(unittest.TestCase): def test(self): v = pia.InstructionInput( - ra=0x1234, rb=0x56, rc=0x789, + ra=0x1234, rb=0x56, rc=0x789, immediate=0x54, overflow=pia.OverflowFlags(so=False, ov=True, ov32=True), carry=pia.CarryFlags(ca=True, ca32=False)) for instr in pia.INSTRS: -- 2.30.2