From 996181fc09cbe1771c5097eaa4712d4b37051374 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Sun, 11 Oct 2020 18:24:00 -0700 Subject: [PATCH] working on adding instructions that take immediates --- .../src/inline_assembly.rs | 57 ++++++++++++++++++ .../src/instructions.rs | 59 ++++++++++++++++++- src/lib.rs | 33 +++++++++++ src/main.rs | 12 ++++ src/python.rs | 6 +- tests/test_power_instruction_analyzer.py | 8 ++- 6 files changed, 169 insertions(+), 6 deletions(-) diff --git a/power-instruction-analyzer-proc-macro/src/inline_assembly.rs b/power-instruction-analyzer-proc-macro/src/inline_assembly.rs index c96cc2b..5b3a382 100644 --- a/power-instruction-analyzer-proc-macro/src/inline_assembly.rs +++ b/power-instruction-analyzer-proc-macro/src/inline_assembly.rs @@ -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 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( + &self, + mut f: impl FnMut(AssemblyMetavariableId) -> Result, + ) -> Result { + 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), } } diff --git a/power-instruction-analyzer-proc-macro/src/instructions.rs b/power-instruction-analyzer-proc-macro/src/instructions.rs index a53b2c0..5ecb91a 100644 --- a/power-instruction-analyzer-proc-macro/src/instructions.rs +++ b/power-instruction-analyzer-proc-macro/src/instructions.rs @@ -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 { + 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::)>()?; 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"}; diff --git a/src/lib.rs b/src/lib.rs index 81d8fed..ba2c6e0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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, + #[serde( + default, + skip_serializing_if = "Option::is_none", + with = "serde_hex::SerdeHex" + )] + pub immediate: Option, #[serde(default, skip_serializing_if = "Option::is_none", flatten)] pub carry: Option, #[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 { + self.immediate.ok_or(MissingInstructionInput { input }) + } + pub fn try_get_immediate_u16(self) -> Result { + Ok(self.try_get_immediate(InstructionInputRegister::ImmediateU16)? as u16) + } + pub fn try_get_immediate_s16(self) -> Result { + 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) { diff --git a/src/main.rs b/src/main.rs index 15ee093..167d759 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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 { diff --git a/src/python.rs b/src/python.rs index 502353c..b12af3c 100644 --- a/src/python.rs +++ b/src/python.rs @@ -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, @@ -350,6 +350,8 @@ fn power_instruction_analyzer(_py: Python, m: &PyModule) -> PyResult<()> { rb: Option, #[set = set_rc] rc: Option, + #[set = set_immediate] + immediate: Option, #[set = set_carry] carry: Option, #[set = set_overflow] diff --git a/tests/test_power_instruction_analyzer.py b/tests/test_power_instruction_analyzer.py index 1fc97a6..7858168 100644 --- a/tests/test_power_instruction_analyzer.py +++ b/tests/test_power_instruction_analyzer.py @@ -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): -- 2.30.2