working on adding instructions that take immediates
authorJacob Lifshay <programmerjake@gmail.com>
Mon, 12 Oct 2020 01:24:00 +0000 (18:24 -0700)
committerJacob Lifshay <programmerjake@gmail.com>
Mon, 12 Oct 2020 01:24:00 +0000 (18:24 -0700)
power-instruction-analyzer-proc-macro/src/inline_assembly.rs
power-instruction-analyzer-proc-macro/src/instructions.rs
src/lib.rs
src/main.rs
src/python.rs
tests/test_power_instruction_analyzer.py

index c96cc2b427e21195015f23ae7947b12457d1a85c..5b3a3827acedd9a158e0748063fab2f0f035a87a 100644 (file)
@@ -146,6 +146,25 @@ impl ToAssembly for String {
     }
 }
 
+#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
+pub(crate) struct AssemblyMetavariableId(u64);
+
+impl AssemblyMetavariableId {
+    pub(crate) fn new() -> Self {
+        // don't start at zero to help avoid confusing id with indexes
+        static NEXT_ID: AtomicU64 = AtomicU64::new(10000);
+        AssemblyMetavariableId(NEXT_ID.fetch_add(1, Ordering::Relaxed))
+    }
+}
+
+impl ToAssembly for AssemblyMetavariableId {
+    fn append_to(&self, retval: &mut Assembly) {
+        retval
+            .text_fragments
+            .push(AssemblyTextFragment::Metavariable(*self));
+    }
+}
+
 #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
 pub(crate) struct AssemblyArgId(u64);
 
@@ -229,6 +248,7 @@ impl_assembly_arg! {
 pub(crate) enum AssemblyTextFragment {
     Text(String),
     ArgIndex(AssemblyArgId),
+    Metavariable(AssemblyMetavariableId),
 }
 
 #[derive(Debug, Default, Clone)]
@@ -269,6 +289,21 @@ impl From<&'_ AssemblyArgId> for Assembly {
     }
 }
 
+impl From<AssemblyMetavariableId> for Assembly {
+    fn from(arg_id: AssemblyMetavariableId) -> Self {
+        Self {
+            text_fragments: vec![AssemblyTextFragment::Metavariable(arg_id)],
+            ..Self::default()
+        }
+    }
+}
+
+impl From<&'_ AssemblyMetavariableId> for Assembly {
+    fn from(arg_id: &AssemblyMetavariableId) -> Self {
+        Self::from(*arg_id)
+    }
+}
+
 impl Assembly {
     pub(crate) fn new() -> Self {
         Self::default()
@@ -303,6 +338,20 @@ impl Assembly {
             ..Self::default()
         }
     }
+    pub(crate) fn replace_metavariables<R>(
+        &self,
+        mut f: impl FnMut(AssemblyMetavariableId) -> Result<Assembly, R>,
+    ) -> Result<Assembly, R> {
+        let mut retval = self.args_without_text();
+        for text_fragment in &self.text_fragments {
+            match text_fragment {
+                AssemblyTextFragment::Text(text) => text.append_to(&mut retval),
+                AssemblyTextFragment::ArgIndex(id) => id.append_to(&mut retval),
+                AssemblyTextFragment::Metavariable(id) => f(*id)?.append_to(&mut retval),
+            }
+        }
+        Ok(retval)
+    }
     pub(crate) fn args_without_text(&self) -> Assembly {
         Assembly {
             text_fragments: Vec::new(),
@@ -331,6 +380,13 @@ impl Assembly {
         for text_fragment in &self.text_fragments {
             match text_fragment {
                 AssemblyTextFragment::Text(text) => retval += text,
+                AssemblyTextFragment::Metavariable(id) => {
+                    panic!(
+                        "metavariables are not allowed when converting \
+                            assembly to text: metavariable id={:?}\n{:#?}",
+                        id, self
+                    );
+                }
                 AssemblyTextFragment::ArgIndex(id) => {
                     if let Some(index) = id_index_map.get(id) {
                         write!(retval, "{}", index).unwrap();
@@ -353,6 +409,7 @@ impl ToAssembly for Assembly {
         for text_fragment in &self.text_fragments {
             match *text_fragment {
                 AssemblyTextFragment::Text(ref text) => text.append_to(retval),
+                AssemblyTextFragment::Metavariable(id) => id.append_to(retval),
                 AssemblyTextFragment::ArgIndex(id) => id.append_to(retval),
             }
         }
index a53b2c06a67bd71438188f4bf7850c0b90c35037..5ecb91a73580cd16004b2f8ee0b5cae92b8f7bdb 100644 (file)
@@ -1,10 +1,10 @@
 // SPDX-License-Identifier: LGPL-2.1-or-later
 // See Notices.txt for copyright information
 
-use crate::inline_assembly::{Assembly, AssemblyWithTextSpan};
+use crate::inline_assembly::{Assembly, AssemblyMetavariableId, AssemblyWithTextSpan};
 use proc_macro2::{Ident, Span, TokenStream};
 use quote::{quote, ToTokens, TokenStreamExt};
-use std::{collections::HashMap, fmt, hash::Hash};
+use std::{collections::HashMap, fmt, hash::Hash, mem};
 use syn::{
     braced, bracketed, parenthesized,
     parse::{Parse, ParseStream},
@@ -147,11 +147,33 @@ ident_enum! {
         Ra,
         Rb,
         Rc,
+        ImmediateS16,
+        ImmediateU16,
         Carry,
         Overflow,
     }
 }
 
+#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
+enum ImmediateShape {
+    S16,
+    U16,
+}
+
+impl InstructionInputName {
+    fn get_immediate_shape(&self) -> Option<ImmediateShape> {
+        match self {
+            InstructionInputName::Ra(_)
+            | InstructionInputName::Rb(_)
+            | InstructionInputName::Rc(_)
+            | InstructionInputName::Carry(_)
+            | InstructionInputName::Overflow(_) => None,
+            InstructionInputName::ImmediateS16(_) => Some(ImmediateShape::S16),
+            InstructionInputName::ImmediateU16(_) => Some(ImmediateShape::U16),
+        }
+    }
+}
+
 ident_enum! {
     #[parse_error_msg = "unknown instruction output"]
     enum InstructionOutputName {
@@ -293,6 +315,17 @@ impl Parse for Instruction {
         parenthesized!(inputs_tokens in input);
         let inputs = inputs_tokens.parse_terminated(InstructionInput::parse)?;
         check_duplicate_free(&inputs)?;
+        let mut found_immediate = false;
+        for input in &inputs {
+            if input.name.get_immediate_shape().is_some() {
+                if mem::replace(&mut found_immediate, true) {
+                    return Err(Error::new_spanned(
+                        &input.name,
+                        "multiple immediates for an instruction are not supported",
+                    ));
+                }
+            }
+        }
         input.parse::<Token!(->)>()?;
         let outputs_tokens;
         parenthesized!(outputs_tokens in input);
@@ -327,6 +360,12 @@ impl Instruction {
                 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},
             });
@@ -432,6 +471,11 @@ impl Instruction {
         }
         let mut need_carry_input = false;
         let mut need_overflow_input = false;
+        struct Immediate {
+            shape: ImmediateShape,
+            id: AssemblyMetavariableId,
+        }
+        let mut immediate = None;
         for input in inputs {
             match input.name {
                 InstructionInputName::Ra(_) => {
@@ -449,6 +493,14 @@ impl Instruction {
                     let constraint = input.constraint();
                     asm_instr_args.push(assembly! {"$" input{#constraint(rc)} });
                 }
+                InstructionInputName::ImmediateS16(_) | InstructionInputName::ImmediateU16(_) => {
+                    input.error_if_register_is_specified()?;
+                    let shape = input.name.get_immediate_shape().unwrap();
+                    let id = AssemblyMetavariableId::new();
+                    assert!(immediate.is_none());
+                    immediate = Some(Immediate { shape, id });
+                    asm_instr_args.push(id.into());
+                }
                 InstructionInputName::Carry(_) => {
                     input.error_if_register_is_specified()?;
                     need_carry_input = true;
@@ -522,6 +574,9 @@ 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"};
index 81d8fed04583aaa30ccf51f8aa6d07625a7b7d36..ba2c6e0cfa2cf223a0275fc17eb537524a5c3cc2 100644 (file)
@@ -189,6 +189,10 @@ pub enum InstructionInputRegister {
     Carry,
     #[serde(rename = "overflow")]
     Overflow,
+    #[serde(rename = "immediate_s16")]
+    ImmediateS16,
+    #[serde(rename = "immediate_u16")]
+    ImmediateU16,
 }
 
 forward_display_to_serde!(InstructionInputRegister);
@@ -213,6 +217,12 @@ pub struct InstructionInput {
         with = "serde_hex::SerdeHex"
     )]
     pub rc: Option<u64>,
+    #[serde(
+        default,
+        skip_serializing_if = "Option::is_none",
+        with = "serde_hex::SerdeHex"
+    )]
+    pub immediate: Option<u64>,
     #[serde(default, skip_serializing_if = "Option::is_none", flatten)]
     pub carry: Option<CarryFlags>,
     #[serde(default, skip_serializing_if = "Option::is_none", flatten)]
@@ -255,6 +265,21 @@ impl_instr_try_get! {
     }
 }
 
+impl InstructionInput {
+    fn try_get_immediate(
+        self,
+        input: InstructionInputRegister,
+    ) -> Result<u64, MissingInstructionInput> {
+        self.immediate.ok_or(MissingInstructionInput { input })
+    }
+    pub fn try_get_immediate_u16(self) -> Result<u16, MissingInstructionInput> {
+        Ok(self.try_get_immediate(InstructionInputRegister::ImmediateU16)? as u16)
+    }
+    pub fn try_get_immediate_s16(self) -> Result<i16, MissingInstructionInput> {
+        Ok(self.try_get_immediate(InstructionInputRegister::ImmediateS16)? as i16)
+    }
+}
+
 fn is_false(v: &bool) -> bool {
     !v
 }
@@ -279,6 +304,14 @@ pub struct WholeTest {
 }
 
 instructions! {
+    // TODO(programmerjake): finish implementing immediate args
+    /*
+    #[enumerant = AddI]
+    fn add(Ra, ImmediateS16) -> (Rt) {
+        "addi"
+    }
+    */
+
     // add
     #[enumerant = Add]
     fn add(Ra, Rb) -> (Rt) {
index 15ee093ef8af538692ade04fe1a07bbe6a023cfb..167d759e83d937bce60c742048c64abe4a20aff6 100644 (file)
@@ -51,6 +51,18 @@ fn call_with_inputs(
                     call_with_inputs(inputs, input_registers, f)?;
                 }
             }
+            InstructionInputRegister::ImmediateS16 => {
+                for &i in TEST_VALUES {
+                    inputs.immediate = Some(i as i16 as u64);
+                    call_with_inputs(inputs, input_registers, f)?;
+                }
+            }
+            InstructionInputRegister::ImmediateU16 => {
+                for &i in TEST_VALUES {
+                    inputs.immediate = Some(i as u16 as u64);
+                    call_with_inputs(inputs, input_registers, f)?;
+                }
+            }
             InstructionInputRegister::Carry => {
                 for &ca in BOOL_VALUES {
                     for &ca32 in BOOL_VALUES {
index 502353cbd6f6032794d0bec04d49f2198c0ceef2..b12af3c691fbc3c6bc0aad26ce160a8972702048 100644 (file)
@@ -341,8 +341,8 @@ fn power_instruction_analyzer(_py: Python, m: &PyModule) -> PyResult<()> {
         #[pymodule(m)]
         #[pyclass(name = InstructionInput)]
         #[wrapped(value: InstructionInput)]
-        #[args(ra="None", rb="None", rc="None", carry="None", overflow="None")]
-        #[text_signature = "(ra=None, rb=None, rc=None, carry=None, overflow=None)"]
+        #[args(ra="None", rb="None", rc="None", immediate="None", carry="None", overflow="None")]
+        #[text_signature = "(ra=None, rb=None, rc=None, immediate=None, carry=None, overflow=None)"]
         struct PyInstructionInput {
             #[set = set_ra]
             ra: Option<u64>,
@@ -350,6 +350,8 @@ fn power_instruction_analyzer(_py: Python, m: &PyModule) -> PyResult<()> {
             rb: Option<u64>,
             #[set = set_rc]
             rc: Option<u64>,
+            #[set = set_immediate]
+            immediate: Option<u64>,
             #[set = set_carry]
             carry: Option<CarryFlags>,
             #[set = set_overflow]
index 1fc97a607843cf841cc307922b5a757013d8a9a1..785816848ecdf3f4f76cef171c79605d9fe7d17a 100644 (file)
@@ -140,7 +140,8 @@ class TestConditionRegister(unittest.TestCase):
 class TestInstructionInput(unittest.TestCase):
     def test_text_signature(self):
         self.assertEqual(pia.InstructionInput.__text_signature__,
-                         "(ra=None, rb=None, rc=None, carry=None, overflow=None)")
+                         "(ra=None, rb=None, rc=None, immediate=None, "
+                         "carry=None, overflow=None)")
 
     def test_fields(self):
         v = pia.InstructionInput(ra=123, rb=456, rc=789)
@@ -153,13 +154,16 @@ class TestInstructionInput(unittest.TestCase):
         self.assertEqual(v.rb, 4567)
         v.rc = 7890
         self.assertEqual(v.rc, 7890)
+        v.immediate = 890
+        self.assertEqual(v.immediate, 890)
 
     def test_str_repr(self):
         v = pia.InstructionInput(ra=123, rb=456, rc=789)
         self.assertEqual(str(v),
                          '{"ra":"0x7B","rb":"0x1C8","rc":"0x315"}')
         self.assertEqual(repr(v),
-                         "InstructionInput(ra=123, rb=456, rc=789, carry=None, overflow=None)")
+                         "InstructionInput(ra=123, rb=456, rc=789, "
+                         "immediate=None, carry=None, overflow=None)")
 
 
 class TestInstructionOutput(unittest.TestCase):