}
}
+#[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);
pub(crate) enum AssemblyTextFragment {
Text(String),
ArgIndex(AssemblyArgId),
+ Metavariable(AssemblyMetavariableId),
}
#[derive(Debug, Default, Clone)]
}
}
+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()
..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(),
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();
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),
}
}
// 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},
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 {
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);
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},
});
}
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(_) => {
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;
"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"};
Carry,
#[serde(rename = "overflow")]
Overflow,
+ #[serde(rename = "immediate_s16")]
+ ImmediateS16,
+ #[serde(rename = "immediate_u16")]
+ ImmediateU16,
}
forward_display_to_serde!(InstructionInputRegister);
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)]
}
}
+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
}
}
instructions! {
+ // TODO(programmerjake): finish implementing immediate args
+ /*
+ #[enumerant = AddI]
+ fn add(Ra, ImmediateS16) -> (Rt) {
+ "addi"
+ }
+ */
+
// add
#[enumerant = Add]
fn add(Ra, Rb) -> (Rt) {
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 {
#[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>,
rb: Option<u64>,
#[set = set_rc]
rc: Option<u64>,
+ #[set = set_immediate]
+ immediate: Option<u64>,
#[set = set_carry]
carry: Option<CarryFlags>,
#[set = set_overflow]
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)
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):