working on adding support for immediate operands
authorJacob Lifshay <programmerjake@gmail.com>
Tue, 13 Oct 2020 03:01:58 +0000 (20:01 -0700)
committerJacob Lifshay <programmerjake@gmail.com>
Tue, 13 Oct 2020 03:01:58 +0000 (20:01 -0700)
power-instruction-analyzer-proc-macro/src/inline_assembly.rs
power-instruction-analyzer-proc-macro/src/instructions.rs
src/instr_models.rs
src/lib.rs
tests/test_power_instruction_analyzer.py

index 5b3a3827acedd9a158e0748063fab2f0f035a87a..a0a8bf492fabbc6001ed997d7b92f6c1d56e8525 100644 (file)
@@ -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
index 5ecb91a73580cd16004b2f8ee0b5cae92b8f7bdb..4146cb6af132497f3fc302fa3313a80742b65f2f 100644 (file)
@@ -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<Item = u64> {
+        (0..=self.bit_mask()).map(move |v| self.normalize_value(v))
+    }
+}
+
 impl InstructionInputName {
     fn get_immediate_shape(&self) -> Option<ImmediateShape> {
         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::<Token!(:)>()?;
             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<Vec<TokenStream>> {
         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<Immediate> = 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)};
         }
index 5389383f4c92ad6fc5969fbd964d20c907318109..446e850b4b7493abc923eada780cf3b2ac6d496f 100644 (file)
@@ -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 {
index ba2c6e0cfa2cf223a0275fc17eb537524a5c3cc2..4cbfad981b417a24bbb0fc92dc5c0ec0aa45e20b 100644 (file)
@@ -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]
index 785816848ecdf3f4f76cef171c79605d9fe7d17a..0bde084b102dbbc4ed359bfb0dd2dba4a0a28505 100644 (file)
@@ -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: