From 5faf0899a8c09d86a81ee1446d443c06909ec4bf Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Fri, 9 Jan 2026 02:03:59 -0800 Subject: [PATCH] fully parse powerisa-instructions.xml --- crates/cpu/Cargo.toml | 5 + crates/cpu/src/powerisa.rs | 1963 ++++++++++++++++++++++++++++++++++-- 2 files changed, 1869 insertions(+), 99 deletions(-) diff --git a/crates/cpu/Cargo.toml b/crates/cpu/Cargo.toml index ccff0d7..0450556 100644 --- a/crates/cpu/Cargo.toml +++ b/crates/cpu/Cargo.toml @@ -26,3 +26,8 @@ hex-literal.workspace = true parse_powerisa_pdf.workspace = true sha2.workspace = true ureq.workspace = true + +[dev-dependencies] +base16ct.workspace = true +hex-literal.workspace = true +sha2.workspace = true diff --git a/crates/cpu/src/powerisa.rs b/crates/cpu/src/powerisa.rs index ad3f7d3..4839a50 100644 --- a/crates/cpu/src/powerisa.rs +++ b/crates/cpu/src/powerisa.rs @@ -2,7 +2,7 @@ // See Notices.txt for copyright information use roxmltree::{Attribute, Document, Node, NodeType}; -use std::{fmt, panic::Location, sync::OnceLock}; +use std::{backtrace::Backtrace, fmt, sync::OnceLock}; const POWERISA_INSTRUCTIONS_XML: &str = include_str!(concat!(env!("OUT_DIR"), "/powerisa-instructions.xml")); @@ -10,38 +10,42 @@ const POWERISA_INSTRUCTIONS_XML: &str = enum Error<'a> { XmlError(roxmltree::Error), Unexpected { - loc: &'static Location<'static>, + backtrace: Backtrace, node: Node<'a, 'static>, }, BodyTooShort { - loc: &'static Location<'static>, + backtrace: Backtrace, node: Node<'a, 'static>, }, ExpectedTag { - loc: &'static Location<'static>, + backtrace: Backtrace, expected_tag_name: &'a str, got_element: Node<'a, 'static>, }, MissingAttribute { - loc: &'static Location<'static>, + backtrace: Backtrace, attribute_name: &'a str, element: Node<'a, 'static>, }, ExpectedAttribute { - loc: &'static Location<'static>, + backtrace: Backtrace, expected_attribute_name: &'a str, attribute: Attribute<'a, 'static>, element: Node<'a, 'static>, }, UnexpectedAttribute { - loc: &'static Location<'static>, + backtrace: Backtrace, attribute: Attribute<'a, 'static>, element: Node<'a, 'static>, }, IsSubsetMustBeFalse { - loc: &'static Location<'static>, + backtrace: Backtrace, is_subset: Attribute<'a, 'static>, }, + UnknownValue { + name: &'static str, + value_formatted: String, + }, } impl From for Error<'_> { @@ -54,50 +58,70 @@ impl fmt::Display for Error<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Error::XmlError(v) => v.fmt(f), - Error::Unexpected { loc, node } => write!(f, "at {loc}: unexpected node: {node:?}"), - Error::BodyTooShort { loc, node } => { - write!(f, "at {loc}: node's body is too short: {node:?}") + Error::Unexpected { backtrace, node } => { + writeln!(f, "unexpected node: {node:?}")?; + backtrace.fmt(f) + } + Error::BodyTooShort { backtrace, node } => { + writeln!(f, "node's body is too short: {node:?}")?; + backtrace.fmt(f) } Error::ExpectedTag { - loc, + backtrace, expected_tag_name, got_element, - } => write!( - f, - "at {loc}: expected tag {expected_tag_name:?} but got: {got_element:?}" - ), + } => { + writeln!( + f, + "expected tag {expected_tag_name:?} but got: {got_element:?}" + )?; + backtrace.fmt(f) + } Error::MissingAttribute { - loc, + backtrace, attribute_name, element, - } => write!( - f, - "at {loc}: missing attribute {attribute_name:?}: {element:?}" - ), + } => { + writeln!(f, "missing attribute {attribute_name:?}: {element:?}")?; + backtrace.fmt(f) + } Error::ExpectedAttribute { - loc, + backtrace, expected_attribute_name, attribute, element, - } => write!( - f, - "at {loc}: expected attribute with name {expected_attribute_name:?}: {attribute:?}\n\ - in element: {element:?}" - ), + } => { + writeln!( + f, + "expected attribute with name {expected_attribute_name:?}: {attribute:?}\n\ + in element: {element:?}" + )?; + backtrace.fmt(f) + } Error::UnexpectedAttribute { - loc, + backtrace, attribute, element, - } => write!( - f, - "at {loc}: unexpected attribute: {attribute:?}\n\ - in element: {element:?}" - ), - Error::IsSubsetMustBeFalse { loc, is_subset } => { - write!( + } => { + writeln!( f, - "at {loc}: `is-subset` attribute must be `False`: {is_subset:?}" - ) + "unexpected attribute: {attribute:?}\n\ + in element: {element:?}" + )?; + backtrace.fmt(f) + } + Error::IsSubsetMustBeFalse { + backtrace, + is_subset, + } => { + writeln!(f, "`is-subset` attribute must be `False`: {is_subset:?}")?; + backtrace.fmt(f) + } + Error::UnknownValue { + name, + value_formatted: value, + } => { + write!(f, "unknown value for {name}: {value}") } } } @@ -147,9 +171,6 @@ impl<'a> Parser<'a> { self.cur_node = cur_node.next_sibling(); } } - fn element_body_todo(&mut self) { - self.cur_node = None; - } fn peek(&self) -> bool { T::peek(self) } @@ -184,7 +205,7 @@ impl<'a> Parser<'a> { self.parse_any_element(|element, parser| { if !element.has_tag_name(tag_name) { return Err(Error::ExpectedTag { - loc: Location::caller(), + backtrace: Backtrace::capture(), expected_tag_name: tag_name, got_element: element, }); @@ -194,14 +215,14 @@ impl<'a> Parser<'a> { for i in 0..N { let Some(attr) = attrs_iter.next() else { return Err(Error::MissingAttribute { - loc: Location::caller(), + backtrace: Backtrace::capture(), attribute_name: attr_names[i], element, }); }; if (attr.namespace(), attr.name()) != (None, attr_names[i]) { return Err(Error::ExpectedAttribute { - loc: Location::caller(), + backtrace: Backtrace::capture(), expected_attribute_name: attr_names[i], attribute: attr, element, @@ -211,7 +232,7 @@ impl<'a> Parser<'a> { } if let Some(attribute) = attrs_iter.next() { return Err(Error::UnexpectedAttribute { - loc: Location::caller(), + backtrace: Backtrace::capture(), attribute, element, }); @@ -227,13 +248,13 @@ impl<'a> Parser<'a> { self.skip_ws_and_comments(); let Some(element) = self.cur_node else { return Err(Error::BodyTooShort { - loc: Location::caller(), + backtrace: Backtrace::capture(), node: self.parent, }); }; let NodeType::Element = element.node_type() else { return Err(Error::Unexpected { - loc: Location::caller(), + backtrace: Backtrace::capture(), node: element, }); }; @@ -245,7 +266,7 @@ impl<'a> Parser<'a> { parser.skip_ws_and_comments(); if let Some(node) = parser.cur_node { Err(Error::Unexpected { - loc: Location::caller(), + backtrace: Backtrace::capture(), node, }) } else { @@ -263,7 +284,7 @@ impl<'a> Parser<'a> { parser.skip_ws_and_comments(); if let Some(node) = parser.cur_node { Err(Error::Unexpected { - loc: Location::caller(), + backtrace: Backtrace::capture(), node, }) } else { @@ -277,6 +298,43 @@ trait Parse: Sized { fn parse<'a>(parser: &mut Parser<'a>) -> Result>; } +impl Parse for Box { + fn peek<'a>(_parser: &Parser<'a>) -> bool { + true + } + fn parse<'a>(parser: &mut Parser<'a>) -> Result> { + Self::parse_with_options(parser, false) + } +} + +impl ParseTextLine for Box { + fn parse_with_options<'a>( + parser: &mut Parser<'a>, + remove_leading_nl: bool, + ) -> Result> { + let mut retval = String::new(); + loop { + parser.skip_comments(); + let Some(node) = parser.cur_node else { + break; + }; + if !node.is_text() { + break; + } + retval.extend(node.text()); + parser.cur_node = node.next_sibling(); + } + if remove_leading_nl { + if retval.starts_with("\r\n") { + retval.replace_range(0..2, ""); + } else if retval.starts_with(&['\r', '\n']) { + retval.remove(0); + } + } + Ok(retval.into_boxed_str()) + } +} + impl Parse for Box<[T]> { fn peek<'a>(_parser: &Parser<'a>) -> bool { true @@ -350,6 +408,41 @@ impl Parse for T { } } +trait ParseElementEnum: + ParseElement + Copy + Eq + std::hash::Hash + fmt::Debug + 'static + Send + Sync +{ + const NAME_FOR_ERRORS: &'static str; + const TAG_NAME: &'static str; + type ParseValue: Parse + fmt::Debug; + type Value: Copy; + fn values() -> &'static [Self]; + fn value_eq(l: &Self::ParseValue, r: Self::Value) -> bool; + fn value(self) -> Self::Value; +} + +impl ParseElement for T { + type AttributeNames = (); + const TAG_NAME: &'static str = ::TAG_NAME; + const ATTRIBUTE_NAMES: Self::AttributeNames = (); + + fn parse_element<'a>( + _element: Node<'a, 'static>, + _attributes: ::Attributes<'a>, + parser: &mut Parser<'a>, + ) -> Result> { + let parse_value: T::ParseValue = parser.parse()?; + for &value in T::values() { + if T::value_eq(&parse_value, value.value()) { + return Ok(value); + } + } + Err(Error::UnknownValue { + name: T::NAME_FOR_ERRORS, + value_formatted: format!("{parse_value:?}"), + }) + } +} + impl Instructions { pub fn instructions(&self) -> &[Instruction] { &self.instructions @@ -383,7 +476,7 @@ impl ParseElement for Instructions { let [is_subset] = attributes; if is_subset.value() != "False" { return Err(Error::IsSubsetMustBeFalse { - loc: Location::caller(), + backtrace: Backtrace::capture(), is_subset, }); } @@ -397,7 +490,7 @@ pub struct Instruction { header: Box<[InstructionHeader]>, code: Option, description: Option, - special_registers_altered: Option, + special_registers_altered: Option, } struct FlattenedOption<'a, T>(&'a Option); @@ -441,7 +534,7 @@ impl Instruction { pub fn description(&self) -> Option<&InstructionDescription> { self.description.as_ref() } - pub fn special_registers_altered(&self) -> Option<&InstructionSpecialRegistersAltered> { + pub fn special_registers_altered(&self) -> Option<&InsnSpRegsAltered> { self.special_registers_altered.as_ref() } } @@ -503,7 +596,7 @@ impl ParseElement for InstructionHeader { } pub struct InstructionTitle { - text_lines: TextLines, + text_lines: TextLines>, } impl fmt::Debug for InstructionTitle { @@ -514,10 +607,10 @@ impl fmt::Debug for InstructionTitle { } impl InstructionTitle { - pub fn text_lines(&self) -> &TextLines { + pub fn text_lines(&self) -> &TextLines> { &self.text_lines } - pub fn lines(&self) -> &[TextLine] { + pub fn lines(&self) -> &[Box] { self.text_lines.lines() } } @@ -539,7 +632,7 @@ impl ParseElement for InstructionTitle { } pub struct InstructionMnemonics { - text_lines: TextLines, + text_lines: TextLines>, } impl fmt::Debug for InstructionMnemonics { @@ -550,10 +643,10 @@ impl fmt::Debug for InstructionMnemonics { } impl InstructionMnemonics { - pub fn text_lines(&self) -> &TextLines { + pub fn text_lines(&self) -> &TextLines> { &self.text_lines } - pub fn lines(&self) -> &[TextLine] { + pub fn lines(&self) -> &[Box] { self.text_lines.lines() } } @@ -575,7 +668,273 @@ impl ParseElement for InstructionMnemonics { } #[derive(Debug)] -pub struct InstructionBitFields {} +pub struct InstructionBitFieldName { + text_line: TextLine, +} + +impl InstructionBitFieldName { + pub fn text_line(&self) -> &TextLine { + &self.text_line + } +} + +impl ParseElement for InstructionBitFieldName { + type AttributeNames = (); + const TAG_NAME: &'static str = "name"; + const ATTRIBUTE_NAMES: Self::AttributeNames = (); + + fn parse_element<'a>( + _element: Node<'a, 'static>, + _attributes: ::Attributes<'a>, + parser: &mut Parser<'a>, + ) -> Result> { + Ok(Self { + text_line: parser.parse()?, + }) + } +} + +#[derive(Debug)] +pub struct InstructionBitFieldBitNumber { + text: Box, +} + +impl InstructionBitFieldBitNumber { + pub fn text(&self) -> &str { + &self.text + } +} + +impl ParseElement for InstructionBitFieldBitNumber { + type AttributeNames = (); + const TAG_NAME: &'static str = "bit-number"; + const ATTRIBUTE_NAMES: Self::AttributeNames = (); + + fn parse_element<'a>( + _element: Node<'a, 'static>, + _attributes: ::Attributes<'a>, + parser: &mut Parser<'a>, + ) -> Result> { + Ok(Self { + text: parser.parse()?, + }) + } +} + +pub struct InstructionBitField { + name: InstructionBitFieldName, + bit_number: InstructionBitFieldBitNumber, +} + +impl fmt::Debug for InstructionBitField { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { + name: InstructionBitFieldName { text_line }, + bit_number: InstructionBitFieldBitNumber { text }, + } = self; + f.debug_struct("InstructionBitField") + .field("name", text_line) + .field("bit_number", text) + .finish() + } +} + +impl InstructionBitField { + pub fn name(&self) -> &InstructionBitFieldName { + &self.name + } + pub fn bit_number(&self) -> &InstructionBitFieldBitNumber { + &self.bit_number + } +} + +impl ParseElement for InstructionBitField { + type AttributeNames = (); + const TAG_NAME: &'static str = "field"; + const ATTRIBUTE_NAMES: Self::AttributeNames = (); + + fn parse_element<'a>( + _element: Node<'a, 'static>, + _attributes: ::Attributes<'a>, + parser: &mut Parser<'a>, + ) -> Result> { + Ok(Self { + name: parser.parse()?, + bit_number: parser.parse()?, + }) + } +} + +#[derive(Debug)] +pub struct InstructionBitFieldsPrefixText { + text_line: TextLine, +} + +impl InstructionBitFieldsPrefixText { + pub fn text_line(&self) -> &TextLine { + &self.text_line + } +} + +impl ParseElement for InstructionBitFieldsPrefixText { + type AttributeNames = (); + const TAG_NAME: &'static str = "prefix-text"; + const ATTRIBUTE_NAMES: Self::AttributeNames = (); + + fn parse_element<'a>( + _element: Node<'a, 'static>, + _attributes: ::Attributes<'a>, + parser: &mut Parser<'a>, + ) -> Result> { + Ok(Self { + text_line: parser.parse()?, + }) + } +} + +#[derive(Debug)] +pub struct InstructionBitFieldsSuffixText { + text_line: TextLine, +} + +impl InstructionBitFieldsSuffixText { + pub fn text_line(&self) -> &TextLine { + &self.text_line + } +} + +impl ParseElement for InstructionBitFieldsSuffixText { + type AttributeNames = (); + const TAG_NAME: &'static str = "suffix-text"; + const ATTRIBUTE_NAMES: Self::AttributeNames = (); + + fn parse_element<'a>( + _element: Node<'a, 'static>, + _attributes: ::Attributes<'a>, + parser: &mut Parser<'a>, + ) -> Result> { + Ok(Self { + text_line: parser.parse()?, + }) + } +} + +pub struct InstructionBitFieldsPrefix { + prefix_text: InstructionBitFieldsPrefixText, + fields_inner: InstructionBitFieldsInner, + suffix_text: InstructionBitFieldsSuffixText, +} + +impl InstructionBitFieldsPrefix { + pub fn prefix_text(&self) -> &InstructionBitFieldsPrefixText { + &self.prefix_text + } + pub fn fields_inner(&self) -> &InstructionBitFieldsInner { + &self.fields_inner + } + pub fn fields(&self) -> &[InstructionBitField] { + self.fields_inner.fields() + } + pub fn suffix_text(&self) -> &InstructionBitFieldsSuffixText { + &self.suffix_text + } +} + +impl fmt::Debug for InstructionBitFieldsPrefix { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { + prefix_text: + InstructionBitFieldsPrefixText { + text_line: prefix_text, + }, + fields_inner: InstructionBitFieldsInner { fields }, + suffix_text: + InstructionBitFieldsSuffixText { + text_line: suffix_text, + }, + } = self; + f.debug_struct("InstructionBitFieldsPrefix") + .field("prefix_text", prefix_text) + .field("fields", fields) + .field("suffix_text", suffix_text) + .finish() + } +} + +impl ParseElement for InstructionBitFieldsPrefix { + type AttributeNames = (); + const TAG_NAME: &'static str = "prefix"; + const ATTRIBUTE_NAMES: Self::AttributeNames = (); + + fn parse_element<'a>( + _element: Node<'a, 'static>, + _attributes: ::Attributes<'a>, + parser: &mut Parser<'a>, + ) -> Result> { + Ok(Self { + prefix_text: parser.parse()?, + fields_inner: parser.parse()?, + suffix_text: parser.parse()?, + }) + } +} + +#[derive(Debug)] +pub struct InstructionBitFieldsInner { + fields: Box<[InstructionBitField]>, +} + +impl InstructionBitFieldsInner { + pub fn fields(&self) -> &[InstructionBitField] { + &self.fields + } +} + +impl ParseElement for InstructionBitFieldsInner { + type AttributeNames = (); + const TAG_NAME: &'static str = "fields"; + const ATTRIBUTE_NAMES: Self::AttributeNames = (); + + fn parse_element<'a>( + _element: Node<'a, 'static>, + _attributes: ::Attributes<'a>, + parser: &mut Parser<'a>, + ) -> Result> { + Ok(Self { + fields: parser.parse()?, + }) + } +} + +pub struct InstructionBitFields { + prefix: Option, + fields_inner: InstructionBitFieldsInner, +} + +impl fmt::Debug for InstructionBitFields { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { + prefix, + fields_inner: InstructionBitFieldsInner { fields }, + } = self; + f.debug_struct("InstructionBitFields") + .field("prefix", &FlattenedOption(prefix)) + .field("fields", fields) + .finish() + } +} + +impl InstructionBitFields { + pub fn prefix(&self) -> Option<&InstructionBitFieldsPrefix> { + self.prefix.as_ref() + } + pub fn fields_inner(&self) -> &InstructionBitFieldsInner { + &self.fields_inner + } + pub fn fields(&self) -> &[InstructionBitField] { + self.fields_inner.fields() + } +} impl ParseElement for InstructionBitFields { type AttributeNames = (); @@ -587,18 +946,19 @@ impl ParseElement for InstructionBitFields { _attributes: ::Attributes<'a>, parser: &mut Parser<'a>, ) -> Result> { - // TODO - parser.element_body_todo(); - Ok(Self {}) + Ok(Self { + prefix: parser.parse()?, + fields_inner: parser.parse()?, + }) } } pub struct InstructionCode { - text_lines: TextLines, + text_lines: TextLines, } impl InstructionCode { - pub fn text_lines(&self) -> &TextLines { + pub fn text_lines(&self) -> &TextLines { &self.text_lines } pub fn lines(&self) -> &[TextLine] { @@ -630,11 +990,11 @@ impl ParseElement for InstructionCode { } pub struct InstructionDescription { - text_lines: TextLines, + text_lines: TextLines, } impl InstructionDescription { - pub fn text_lines(&self) -> &TextLines { + pub fn text_lines(&self) -> &TextLines { &self.text_lines } pub fn lines(&self) -> &[TextLine] { @@ -665,10 +1025,436 @@ impl ParseElement for InstructionDescription { } } -#[derive(Debug)] -pub struct InstructionSpecialRegistersAltered {} +#[derive(Copy, Clone, PartialEq, Eq, Hash, Default)] +pub struct InsnSpRegsAlteredTitle; -impl ParseElement for InstructionSpecialRegistersAltered { +impl fmt::Debug for InsnSpRegsAlteredTitle { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("InsnSpRegsAlteredTitle") + .field("text_line", self.text_line()) + .finish() + } +} + +impl InsnSpRegsAlteredTitle { + pub fn text_line(self) -> &'static TextLine { + static TEXT_LINE: OnceLock = OnceLock::new(); + TEXT_LINE.get_or_init( + #[cold] + || TextLine { + items: Box::new([TextLineItem::Bold(Box::new([TextLineItem::Text( + "Special Registers Altered:".into(), + )]))]), + }, + ) + } +} + +impl ParseElementEnum for InsnSpRegsAlteredTitle { + const NAME_FOR_ERRORS: &'static str = "InsnSpRegsAlteredTitle"; + const TAG_NAME: &'static str = "title"; + type ParseValue = TextLine; + type Value = &'static TextLine; + + fn values() -> &'static [Self] { + &[Self] + } + + fn value_eq(l: &Self::ParseValue, r: Self::Value) -> bool { + l == r + } + + fn value(self) -> Self::Value { + self.text_line() + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +#[non_exhaustive] +pub enum InsnSpRegsAlteredSpecialText { + DependentOnTheSystemService, + None, + SeeAbove, + SeeTable5_1, +} + +impl fmt::Debug for InsnSpRegsAlteredSpecialText { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.value().fmt(f) + } +} + +macro_rules! inst_sp_regs_altered_special_text { + ($($Variant:ident => $value:literal,)*) => { + impl InsnSpRegsAlteredSpecialText { + const VALUES: &[Self] = &[ + $(Self::$Variant,)* + ]; + pub const fn text(self) -> &'static str { + match self { + $(Self::$Variant => $value,)* + } + } + } + }; +} + +inst_sp_regs_altered_special_text! { + DependentOnTheSystemService => "Dependent on the system service", + None => "None", + SeeAbove => "See above.", + SeeTable5_1 => "See Table 5.1", +} + +impl ParseElementEnum for InsnSpRegsAlteredSpecialText { + const NAME_FOR_ERRORS: &'static str = "InsnSpRegsAlteredSpecialText"; + const TAG_NAME: &'static str = "special-text"; + type ParseValue = Box; + type Value = &'static str; + fn values() -> &'static [Self] { + Self::VALUES + } + + fn value_eq(l: &Self::ParseValue, r: Self::Value) -> bool { + &**l == r + } + + fn value(self) -> Self::Value { + self.text() + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Default)] +pub struct InsnSpRegsAlteredTableHeaderReg; + +impl fmt::Debug for InsnSpRegsAlteredTableHeaderReg { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("InsnSpRegsAlteredTableHeaderReg") + .field("text_line", self.text_line()) + .finish() + } +} + +impl InsnSpRegsAlteredTableHeaderReg { + pub fn text_line(self) -> &'static TextLine { + static TEXT_LINE: OnceLock = OnceLock::new(); + TEXT_LINE.get_or_init( + #[cold] + || TextLine { + items: Box::new([TextLineItem::Bold(Box::new([TextLineItem::Italic( + Box::new([TextLineItem::Text("Register".into())]), + )]))]), + }, + ) + } +} + +impl ParseElementEnum for InsnSpRegsAlteredTableHeaderReg { + const NAME_FOR_ERRORS: &'static str = "InsnSpRegsAlteredTableHeaderReg"; + const TAG_NAME: &'static str = "table-header-register"; + type ParseValue = TextLine; + type Value = &'static TextLine; + + fn values() -> &'static [Self] { + &[Self] + } + + fn value_eq(l: &Self::ParseValue, r: Self::Value) -> bool { + l == r + } + + fn value(self) -> Self::Value { + self.text_line() + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Default)] +pub struct InsnSpRegsAlteredTableHeaderFields; + +impl fmt::Debug for InsnSpRegsAlteredTableHeaderFields { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let text_line = self.text_line(); + f.debug_struct("InsnSpRegsAlteredTableHeaderFields") + .field("text_line", text_line) + .finish() + } +} + +impl InsnSpRegsAlteredTableHeaderFields { + pub fn text_line(self) -> &'static TextLine { + static TEXT_LINE: OnceLock = OnceLock::new(); + TEXT_LINE.get_or_init( + #[cold] + || TextLine { + items: Box::new([TextLineItem::Bold(Box::new([TextLineItem::Italic( + Box::new([TextLineItem::Text("Field(s)".into())]), + )]))]), + }, + ) + } +} + +impl ParseElementEnum for InsnSpRegsAlteredTableHeaderFields { + const NAME_FOR_ERRORS: &'static str = "InsnSpRegsAlteredTableHeaderFields"; + const TAG_NAME: &'static str = "table-header-fields"; + type ParseValue = TextLine; + type Value = &'static TextLine; + + fn values() -> &'static [Self] { + &[Self] + } + + fn value_eq(l: &Self::ParseValue, r: Self::Value) -> bool { + l == r + } + + fn value(self) -> Self::Value { + self.text_line() + } +} + +#[derive(Debug)] +pub struct InsnSpRegsAlteredEntryReg { + text: Box, +} + +impl InsnSpRegsAlteredEntryReg { + pub fn text(&self) -> &str { + &self.text + } +} + +impl ParseElement for InsnSpRegsAlteredEntryReg { + type AttributeNames = (); + const TAG_NAME: &'static str = "register"; + const ATTRIBUTE_NAMES: Self::AttributeNames = (); + + fn parse_element<'a>( + _element: Node<'a, 'static>, + _attributes: ::Attributes<'a>, + parser: &mut Parser<'a>, + ) -> Result> { + Ok(Self { + text: parser.parse()?, + }) + } +} + +pub struct InsnSpRegsAlteredEntryFields { + text_lines: TextLines, +} + +impl fmt::Debug for InsnSpRegsAlteredEntryFields { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { text_lines } = self; + text_lines.debug_fmt("InsnSpRegsAlteredEntryFields", f) + } +} + +impl InsnSpRegsAlteredEntryFields { + pub fn text_lines(&self) -> &TextLines { + &self.text_lines + } + pub fn lines(&self) -> &[TextLine] { + self.text_lines.lines() + } +} + +impl ParseElement for InsnSpRegsAlteredEntryFields { + type AttributeNames = (); + const TAG_NAME: &'static str = "fields"; + const ATTRIBUTE_NAMES: Self::AttributeNames = (); + + fn parse_element<'a>( + _element: Node<'a, 'static>, + _attributes: ::Attributes<'a>, + parser: &mut Parser<'a>, + ) -> Result> { + Ok(Self { + text_lines: parser.parse()?, + }) + } +} + +pub struct InsnSpRegsAlteredEntryConds { + text_lines: TextLines, +} + +impl fmt::Debug for InsnSpRegsAlteredEntryConds { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { text_lines } = self; + text_lines.debug_fmt("InsnSpRegsAlteredEntryConds", f) + } +} + +impl InsnSpRegsAlteredEntryConds { + pub fn text_lines(&self) -> &TextLines { + &self.text_lines + } + pub fn lines(&self) -> &[TextLine] { + self.text_lines.lines() + } +} + +impl ParseElement for InsnSpRegsAlteredEntryConds { + type AttributeNames = (); + const TAG_NAME: &'static str = "conditions"; + const ATTRIBUTE_NAMES: Self::AttributeNames = (); + + fn parse_element<'a>( + _element: Node<'a, 'static>, + _attributes: ::Attributes<'a>, + parser: &mut Parser<'a>, + ) -> Result> { + Ok(Self { + text_lines: parser.parse()?, + }) + } +} + +pub struct InsnSpRegsAlteredEntry { + register: InsnSpRegsAlteredEntryReg, + fields: InsnSpRegsAlteredEntryFields, + conditions: InsnSpRegsAlteredEntryConds, +} + +impl fmt::Debug for InsnSpRegsAlteredEntry { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { + register: InsnSpRegsAlteredEntryReg { text: register }, + fields, + conditions, + } = self; + f.debug_struct("InsnSpRegsAlteredEntry") + .field("register", register) + .field("fields", fields) + .field("conditions", conditions) + .finish() + } +} + +impl InsnSpRegsAlteredEntry { + pub fn register(&self) -> &InsnSpRegsAlteredEntryReg { + &self.register + } + pub fn fields(&self) -> &InsnSpRegsAlteredEntryFields { + &self.fields + } + pub fn conditions(&self) -> &InsnSpRegsAlteredEntryConds { + &self.conditions + } +} + +impl ParseElement for InsnSpRegsAlteredEntry { + type AttributeNames = (); + const TAG_NAME: &'static str = "entry"; + const ATTRIBUTE_NAMES: Self::AttributeNames = (); + + fn parse_element<'a>( + _element: Node<'a, 'static>, + _attributes: ::Attributes<'a>, + parser: &mut Parser<'a>, + ) -> Result> { + Ok(Self { + register: parser.parse()?, + fields: parser.parse()?, + conditions: parser.parse()?, + }) + } +} + +pub enum InsnSpRegsAltered { + Special { + title: InsnSpRegsAlteredTitle, + special_text: InsnSpRegsAlteredSpecialText, + }, + Table { + title: InsnSpRegsAlteredTitle, + table_header_reg: InsnSpRegsAlteredTableHeaderReg, + table_header_fields: InsnSpRegsAlteredTableHeaderFields, + entries: Box<[InsnSpRegsAlteredEntry]>, + }, +} + +impl fmt::Debug for InsnSpRegsAltered { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Special { + title, + special_text, + } => f + .debug_struct("Special") + .field("title", title) + .field("special_text", special_text) + .finish(), + Self::Table { + title, + table_header_reg, + table_header_fields, + entries, + } => f + .debug_struct("Table") + .field("title", title) + .field("table_header_reg", table_header_reg) + .field("table_header_fields", table_header_fields) + .field("entries", entries) + .finish(), + } + } +} + +impl InsnSpRegsAltered { + pub fn title(&self) -> InsnSpRegsAlteredTitle { + match *self { + Self::Special { title, .. } | Self::Table { title, .. } => title, + } + } + pub fn special_text(&self) -> Option { + match *self { + InsnSpRegsAltered::Special { special_text, .. } => Some(special_text), + InsnSpRegsAltered::Table { + title: _, + table_header_reg: _, + table_header_fields: _, + entries: _, + } => None, + } + } + pub fn table_header_reg(&self) -> Option { + match *self { + InsnSpRegsAltered::Special { + title: _, + special_text: _, + } => None, + InsnSpRegsAltered::Table { + table_header_reg, .. + } => Some(table_header_reg), + } + } + pub fn table_header_fields(&self) -> Option { + match *self { + InsnSpRegsAltered::Special { + title: _, + special_text: _, + } => None, + InsnSpRegsAltered::Table { + table_header_fields, + .. + } => Some(table_header_fields), + } + } + pub fn entries(&self) -> Option<&[InsnSpRegsAlteredEntry]> { + match self { + InsnSpRegsAltered::Special { + title: _, + special_text: _, + } => None, + InsnSpRegsAltered::Table { entries, .. } => Some(entries), + } + } +} + +impl ParseElement for InsnSpRegsAltered { type AttributeNames = (); const TAG_NAME: &'static str = "special-registers-altered"; const ATTRIBUTE_NAMES: Self::AttributeNames = (); @@ -678,12 +1464,24 @@ impl ParseElement for InstructionSpecialRegistersAltered { _attributes: ::Attributes<'a>, parser: &mut Parser<'a>, ) -> Result> { - // TODO - parser.element_body_todo(); - Ok(Self {}) + let title = parser.parse()?; + if let Some(special_text) = parser.parse()? { + Ok(Self::Special { + title, + special_text, + }) + } else { + Ok(Self::Table { + title, + table_header_reg: parser.parse()?, + table_header_fields: parser.parse()?, + entries: parser.parse()?, + }) + } } } +#[derive(PartialEq, Eq)] pub enum TextLineItem { Text(Box), Code(Box<[TextLineItem]>), @@ -829,7 +1627,7 @@ impl Parse for TextLineItem { .transpose()? else { return Err(Error::BodyTooShort { - loc: Location::caller(), + backtrace: Backtrace::capture(), node: parser.parent, }); }; @@ -837,6 +1635,7 @@ impl Parse for TextLineItem { } } +#[derive(PartialEq, Eq)] pub struct TextLine { items: Box<[TextLineItem]>, } @@ -844,8 +1643,7 @@ pub struct TextLine { impl fmt::Debug for TextLine { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let Self { items } = self; - f.write_str("TextLine ")?; - items.fmt(f) + write!(f, "TextLine {items:?}") } } @@ -853,6 +1651,9 @@ impl TextLine { pub fn items(&self) -> &[TextLineItem] { &self.items } +} + +impl ParseTextLine for TextLine { fn parse_with_options<'a>( parser: &mut Parser<'a>, remove_leading_nl: bool, @@ -895,38 +1696,48 @@ impl Parse for TextLine { } } -pub struct TextLines { - lines: Box<[TextLine]>, +pub struct TextLines { + lines: Box<[Text]>, } -impl fmt::Debug for TextLines { +impl fmt::Debug for TextLines { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.debug_fmt("TextLines", f) } } -impl TextLines { - pub fn lines(&self) -> &[TextLine] { +impl TextLines { + pub fn lines(&self) -> &[Text] { &self.lines } - fn debug_fmt(&self, name: &str, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fn debug_fmt(&self, name: &str, f: &mut fmt::Formatter<'_>) -> fmt::Result + where + Text: fmt::Debug, + { let Self { lines } = self; f.write_str(name)?; fmt::Debug::fmt(lines, f) } } -impl Parse for TextLines { - fn peek<'a>(parser: &Parser<'a>) -> bool { - parser.peek_element("br").is_some() || TextLine::peek(parser) +trait ParseTextLine: Sized { + fn parse_with_options<'a>( + parser: &mut Parser<'a>, + remove_leading_nl: bool, + ) -> Result>; +} + +impl Parse for TextLines { + fn peek<'a>(_parser: &Parser<'a>) -> bool { + true } fn parse<'a>(parser: &mut Parser<'a>) -> Result> { let mut lines = Vec::new(); - lines.push(TextLine::parse(parser)?); + lines.push(Text::parse_with_options(parser, false)?); while parser.peek_element("br").is_some() { parser.parse_element("br", [], |_element, [], _parser| Ok(()))?; - lines.push(TextLine::parse_with_options(parser, true)?); + lines.push(Text::parse_with_options(parser, true)?); } Ok(Self { lines: lines.into_boxed_slice(), @@ -935,20 +1746,974 @@ impl Parse for TextLines { } #[cfg(test)] -#[test] -fn test_instructions_parses() { +mod tests { + use super::*; + use base16ct::HexDisplay; + use hex_literal::hex; + use sha2::Digest; use std::fmt::Write; - let instructions = Instructions::get(); - let mut written = String::new(); - for (i, instruction) in instructions.instructions().iter().enumerate() { - written.clear(); - write!(written, "{instruction:#?}").expect("known to not error"); - println!("------\n{written}\n------"); - let expected: &str = match i { - #[cfg(todo)] - 0 => "", - _ => continue, - }; - assert!(written == expected); + + #[test] + fn test_instructions_parses() -> Result<(), &'static str> { + #[derive(Clone, Copy)] + struct ExpectedInsnHash { + loc: &'static std::panic::Location<'static>, + expected_mnemonic: &'static str, + expected_hash: HexDisplay<'static>, + } + impl ExpectedInsnHash { + const fn empty() -> Self { + Self { + loc: std::panic::Location::caller(), + expected_mnemonic: "", + expected_hash: HexDisplay(&[]), + } + } + } + impl Default for ExpectedInsnHash { + fn default() -> Self { + Self::empty() + } + } + macro_rules! insn { + ($expected_mnemonic:literal, $expected_hash:literal) => { + ExpectedInsnHash { + loc: std::panic::Location::caller(), + expected_mnemonic: $expected_mnemonic, + expected_hash: HexDisplay({ + let v = &hex!($expected_hash); + assert!(v.len() >= 5); + v + }), + } + }; + () => { + ExpectedInsnHash::empty() + }; + } + static EXPECTED_HASHES: &[ExpectedInsnHash] = &[ + // Book 1 Branch + insn!("b", "05a4beffce"), + insn!("bc", "3487c6e08d"), + insn!("bclr", "e9e2e6ea43"), + insn!("bcctr", "0e55af907e"), + insn!("bctar", "ee91bf35e9"), + insn!("crand", "9bc66adf04"), + insn!("cror", "bb21baf028"), + insn!("crnand", "a6052066e3"), + insn!("crxor", "58a03c02a8"), + insn!("crnor", "d884219927"), + insn!("crandc", "c5d5aced85"), + insn!("creqv", "662590bf07"), + insn!("crorc", "14324bf398"), + insn!("mcrf", "f89641dc3c"), + insn!("sc", "685e6b3217"), + // Book 1 Fixed-Point + insn!("lbz", "c10dbcb96a"), + insn!("lbzx", "30c924d957"), + insn!("lbzu", "bcfdebd312"), + insn!("lbzux", "49f61eb762"), + insn!("lhz", "21a49348c8"), + insn!("lhzx", "49c4d116e8"), + insn!("lhzu", "48ee40ca93"), + insn!("lhzux", "d49f2ea84c"), + insn!("lha", "d05f6222e6"), + insn!("lhax", "486d712f77"), + insn!("lhau", "2ffda0f14b"), + insn!("lhaux", "a5492cf215"), + insn!("lwz", "8aec6eee9f"), + insn!("lwzx", "b024d64496"), + insn!("lwzu", "9a9b8bf164"), + insn!("lwzux", "7261cd36c9"), + insn!("lwa", "7ec6a105bc"), + insn!("lwax", "31d7a2bce9"), + insn!("lwaux", "bb62e25b3d"), + insn!("ld", "22cb1e7e22"), + insn!("ldx", "b4e5b63aba"), + insn!("ldu", "0697131c3c"), + insn!("ldux", "abd5b69d53"), + insn!("stb", "1da5aff76f"), + insn!("stbx", "59c2579957"), + insn!("stbu", "e69fd5da44"), + insn!("stbux", "d6078c481d"), + insn!("sth", "028879812a"), + insn!("sthx", "821d276106"), + insn!("sthu", "f467aa4926"), + insn!("sthux", "c662054a44"), + insn!("stw", "2fac68137e"), + insn!("stwx", "85ac5a7477"), + insn!("stwu", "105d53d7f9"), + insn!("stwux", "c600f25c69"), + insn!("std", "ed15d338ff"), + insn!("stdx", "c6de43c8c1"), + insn!("stdu", "b4dc89336a"), + insn!("stdux", "246d93cb11"), + insn!("lq", "d843b52568"), + insn!("stq", "0ed25a72b6"), + insn!("lhbrx", "5f2972ad56"), + insn!("sthbrx", "406a8cceb5"), + insn!("lwbrx", "24e2c59b12"), + insn!("stwbrx", "4bd14ba7ce"), + insn!("ldbrx", "f5b0703cad"), + insn!("stdbrx", "61ca2fce57"), + insn!("lmw", "9f0df5634f"), + insn!("stmw", "a907f7dfea"), + insn!("lswi", "00bcf243bc"), + insn!("lswx", "64fbec6450"), + insn!("stswi", "14684359ed"), + insn!("stswx", "2b8451ff5f"), + insn!("addi", "8b9c9ad540"), + insn!("addis", "8230aeb50d"), + insn!("addpcis", "ef2e4a9cc0"), + insn!("add", "1e188c4039"), + insn!("addic", "4ddd1cdbd9"), + insn!("addic.", "a545624df0"), + insn!("subf", "a1a4755310"), + insn!("subfic", "9038f7dbc6"), + insn!("addc", "6f58df2480"), + insn!("subfc", "3447db7b2e"), + insn!("adde", "32fed9e1ae"), + insn!("subfe", "a842d17ea0"), + insn!("addme", "a4b4644ecc"), + insn!("addze", "e8f0dddb47"), + insn!("subfme", "1d7b046ba1"), + insn!("subfze", "3e15f250f2"), + insn!("addex", "b8b3ff6ea9"), + insn!("neg", "7c824f5f8b"), + insn!("mulli", "170828b06d"), + insn!("mullw", "403186f819"), + insn!("mulhw", "dd1458b0d2"), + insn!("mulhwu", "ae9e5333ad"), + insn!("divw", "f588f40ac0"), + insn!("divwu", "80992471e6"), + insn!("divwe", "0b27818a42"), + insn!("divweu", "8a80f5b7ee"), + insn!("modsw", "feab80b9f4"), + insn!("moduw", "86bc11c0c8"), + insn!("darn", "4f9b829036"), + insn!("mulld", "336db32d97"), + insn!("mulhd", "9698fca65b"), + insn!("mulhdu", "c799c73ee2"), + insn!("maddhd", "367541e7e8"), + insn!("maddhdu", "46b5721042"), + insn!("maddld", "6baf51f955"), + insn!("divd", "4c75f5ae9c"), + insn!("divdu", "d8997f826d"), + insn!("divde", "980a477d2b"), + insn!("divdeu", "b8be82059f"), + insn!("modsd", "197c2d4fcc"), + insn!("modud", "8b0fd7d432"), + insn!("cmpi", "efc4f43fec"), + insn!("cmp", "b7b98db957"), + insn!("cmpli", "45f703e788"), + insn!("cmpl", "9d13a8c007"), + insn!("cmprb", "ad56077216"), + insn!("cmpeqb", "ba6836995f"), + insn!("twi", "0e0b75304a"), + insn!("tw", "2cf066dc36"), + insn!("tdi", "c9195f9974"), + insn!("td", "833cec86af"), + insn!("isel", "20df2457b6"), + insn!("andi.", "0c2e70e962"), + insn!("andis.", "63db245579"), + insn!("ori", "7aed28e6d5"), + insn!("oris", "fc3017ddea"), + insn!("xori", "f3caa0800e"), + insn!("xoris", "ce5ede0c81"), + insn!("and", "157dfa649f"), + insn!("xor", "4db3912d43"), + insn!("nand", "da7b75d4da"), + insn!("or", "46b6957818"), + insn!("orc", "6c9e0b5542"), + insn!("nor", "b2e58cb2d8"), + insn!("eqv", "c25614dede"), + insn!("andc", "1acc43e893"), + insn!("extsb", "5042f8137f"), + insn!("extsh", "30c6eb6ce9"), + insn!("cmpb", "937d763c11"), + insn!("cntlzw", "c01fe89ea2"), + insn!("cnttzw", "094b58148a"), + insn!("popcntb", "4191552bce"), + insn!("popcntw", "74b04ec702"), + insn!("prtyw", "a83bb73c14"), + insn!("extsw", "eb656a70e2"), + insn!("popcntd", "d64c1aec4f"), + insn!("prtyd", "f74ebccfdf"), + insn!("cntlzd", "e698cf73d3"), + insn!("cnttzd", "34588a7ce8"), + insn!("cntlzdm", "c0018d2d5b"), + insn!("cnttzdm", "09cc319caa"), + insn!("bpermd", "b2ee3589be"), + insn!("cfuged", "eda5da126c"), + insn!("pextd", "d42a1a777e"), + insn!("pdepd", "a7c9a9a7ba"), + insn!("rlwinm", "21a8c08fac"), + insn!("rlwnm", "f62cb77d8a"), + insn!("rlwimi", "9e3282b5ba"), + insn!("rldicl", "f22d9c12fd"), + insn!("rldicr", "d301784be5"), + insn!("rldic", "fd1ee6f0b3"), + insn!("rldcl", "364d285e33"), + insn!("rldcr", "dd5ead8ff4"), + insn!("rldimi", "605b8907f0"), + insn!("slw", "4093e47ea1"), + insn!("srw", "85a5fa5c36"), + insn!("srawi", "3ea22aabc1"), + insn!("sraw", "df33782ba4"), + insn!("sld", "e1da1538dd"), + insn!("sradi", "b8394e72aa"), + insn!("srd", "8ac3790243"), + insn!("srad", "e128b7e88d"), + insn!("extswsli", "1ca36ec0eb"), + insn!("cdtbcd", "13229b66a7"), + insn!("cbcdtd", "6fd233fe83"), + insn!("addg6s", "47caba2f9a"), + insn!("brh", "d91d4c6a00"), + insn!("brw", "45f937f47a"), + insn!("brd", "72a1255fd0"), + insn!("hashst", "a4c4c6f3d3"), + insn!("hashchk", "8c8f828bef"), + insn!("mfvsrd", "c7c71821c9"), + insn!("mfvsrld", "adb28df8a6"), + insn!("mfvsrwz", "37475acd00"), + insn!("mtvsrd", "9007aeb28e"), + insn!("mtvsrwa", "4b3e576548"), + insn!("mtvsrwz", "dbaa8e068f"), + insn!("mtvsrdd", "d1a47c1dc9"), + insn!("mtvsrws", "9b72624c7e"), + insn!("mtspr", "dbfae704f2"), + insn!("mfspr", "9a025cd08b"), + insn!("mcrxrx", "d7b6bb65d3"), + insn!("mtocrf", "80409a8951"), + insn!("mtcrf", "36af5db069"), + insn!("mfocrf", "8e3a6c2be2"), + insn!("mfcr", "e173a7be12"), + insn!("setb", "89fcdf508f"), + insn!("setbc", "3f2956521b"), + insn!("setbcr", "4795d46cae"), + insn!("setnbc", "e48c9e03d3"), + insn!("setnbcr", "de5002c7dc"), + insn!("pnop", "5478e5eb86"), + // Book 1 Floating-Point + insn!("lfs", "7331af45a6"), + insn!("lfsx", "d152f026f7"), + insn!("lfsu", "807d2999e1"), + insn!("lfsux", "b6b085af25"), + insn!("lfd", "b0a824af70"), + insn!("lfdx", "7576336546"), + insn!("lfdu", "4338f082d4"), + insn!("lfdux", "ac4e1c427b"), + insn!("lfiwax", "47fb8bd8b2"), + insn!("lfiwzx", "1971663968"), + insn!("stfs", "28e5ef4037"), + insn!("stfsx", "4e4d15543d"), + insn!("stfsu", "fdd1d5ef23"), + insn!("stfsux", "31f4aba0c0"), + insn!("stfd", "e699a03b30"), + insn!("stfdx", "b109ee3cee"), + insn!("stfdu", "e924c1b1ae"), + insn!("stfdux", "450d9d0db4"), + insn!("stfiwx", "34d205b697"), + insn!("lfdp", "52d86d153f"), + insn!("lfdpx", "65c7b797db"), + insn!("stfdp", "50d883692e"), + insn!("stfdpx", "9db9a09e7f"), + insn!("fmr", "1f28c1f4aa"), + insn!("fneg", "d2891238be"), + insn!("fabs", "43c0bd9da4"), + insn!("fnabs", "bd98ba993a"), + insn!("fcpsgn", "2f37d5875e"), + insn!("fmrgew", "8579f99f43"), + insn!("fmrgow", "9053658532"), + insn!("fadd", "68a59e6088"), + insn!("fsub", "809a73e375"), + insn!("fmul", "3df95ce519"), + insn!("fdiv", "8cd3692743"), + insn!("fsqrt", "4be9f5ada7"), + insn!("fre", "11d162eb90"), + insn!("frsqrte", "2b1a1f5d4f"), + insn!("ftdiv", "69b2527dad"), + insn!("ftsqrt", "ca9654eb62"), + insn!("fmadd", "435d24b73f"), + insn!("fmsub", "b3681250be"), + insn!("fnmadd", "24213814bf"), + insn!("fnmsub", "4ba4a4fe3f"), + insn!("frsp", "c43c7dba7b"), + insn!("fctid", "fe47191faf"), + insn!("fctidz", "a4e04126bf"), + insn!("fctidu", "4b92323ab3"), + insn!("fctiduz", "5dcbee6a1b"), + insn!("fctiw", "86a8710dd0"), + insn!("fctiwz", "100ccae019"), + insn!("fctiwu", "9782676ac5"), + insn!("fctiwuz", "450bc3a1c0"), + insn!("fcfid", "4d3a700267"), + insn!("fcfidu", "58fa858b7e"), + insn!("fcfids", "e9bbc818e9"), + insn!("fcfidus", "1f74eadc12"), + insn!("frin", "9133040b7d"), + insn!("friz", "f9a6e029ef"), + insn!("frip", "ce7a1a8dcf"), + insn!("frim", "c68b1668e1"), + insn!("fcmpu", "e9405bb558"), + insn!("fcmpo", "19295db629"), + insn!("fsel", "07883e27b0"), + insn!("mffs", "4217bef65e"), + insn!("mffsce", "8421c9f1d3"), + insn!("mffscdrn", "eef2922c65"), + insn!("mffscdrni", "d72b044dcd"), + insn!("mffscrn", "324b7604ce"), + insn!("mffscrni", "2d238783bd"), + insn!("mffsl", "46ffe2f0c2"), + insn!("mcrfs", "a8666f60f6"), + insn!("mtfsfi", "bf724ad53f"), + insn!("mtfsf", "c935bccde6"), + insn!("mtfsb0", "e73cbe7824"), + insn!("mtfsb1", "861e8142e8"), + // Book 1 DFP + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + // Book 1 Vector + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + // Book 1 VSX + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + // Book 2 Storage Control + insn!("icbi", "badb3cb30d"), + insn!("icbt", "1a9daa98c4"), + insn!("dcbz", "4b47e901a1"), + insn!("dcbst", "a9440cc687"), + insn!("dcbf", "d1ea08dec4"), + insn!("copy", "b82add82ad"), + insn!("paste.", "23ec6d671b"), + insn!("cpabort", "baee1d76a5"), + insn!("lwat", "d29905d0c2"), + insn!("ldat", "c2c7ac55ea"), + insn!("stwat", "6256c289e8"), + insn!("stdat", "17bc31faa3"), + insn!("isync", "ec505a42cd"), + insn!("lbarx", "df6b36f57f"), + insn!("lharx", "89b0f37c8d"), + insn!("lwarx", "83096e31ad"), + insn!("stbcx.", "2ad639d1c0"), + insn!("sthcx.", "73c922c413"), + insn!("stwcx.", "d84b8ea285"), + insn!("ldarx", "4819e395b5"), + insn!("stdcx.", "4a4c88a9d3"), + insn!("stqcx.", "a9927504d9"), + insn!("sync", "79391e4e86"), + insn!("wait", "2470682a01"), + // Book 2 Time Base + insn!("mftb", "2a2681e7fb"), + // Book 2 Branch History Rolling Buffer + insn!(), + insn!(), + // Book 3 Branch + insn!("sc", "3fb7fbeb7c"), + insn!("scv", "6c4d291ac5"), + insn!("rfscv", "442adf033a"), + insn!("rfid", "06191c8a3a"), + insn!("hrfid", "ed6f16b138"), + insn!("urfid", "1972956623"), + insn!("stop", "f3f09e9e80"), + // Book 3 Fixed-Point + insn!("lbzcix", "e226b904c7"), + insn!("lhzcix", "6fa23c2105"), + insn!("lwzcix", "59a852aba1"), + insn!("ldcix", "b8700488c0"), + insn!("stbcix", "1d0911b0f7"), + insn!("sthcix", "5f4eb5ca03"), + insn!("stwcix", "5d0a5b1c33"), + insn!("stdcix", "fb2bd7f0e3"), + insn!("hashstp", "89dee31207"), + insn!("hashchkp", "e3f6d311ee"), + insn!("mtspr", "b7fa29ee9d"), + insn!("mfspr", "399912a6d1"), + insn!("mtmsr", "cee82ab56c"), + insn!("mtmsrd", "b8871fa740"), + insn!("mfmsr", "b1d9d57b48"), + // Book 3 Storage Control + insn!("slbie", "da256f258d"), + insn!("slbieg", "15abe15598"), + insn!("slbia", "a79bfdc74f"), + insn!("slbiag", "67accf6b2c"), + insn!("slbmfev", "d06921f3c9"), + insn!("slbmfee", "2a201787e3"), + insn!("slbfee.", "473bf7ac9b"), + insn!("slbsync", "a3c4b68e12"), + insn!("tlbsync", "7b160e133f"), + // Book 3 Processor Control + insn!("msgsndu", "dad7de6355"), + insn!("msgclru", "c39ad4540b"), + insn!("msgsnd", "09224d9fe9"), + insn!("msgclr", "986ab4a2ea"), + insn!("msgsndp", "8eeecc7f0a"), + insn!("msgclrp", "62c5fe6115"), + insn!("msgsync", "e30602807b"), + ]; + let instructions = Instructions::get(); + let mut written = String::new(); + let mut insn_macros = String::new(); + for (insn_index, instruction) in instructions.instructions().iter().enumerate() { + written.clear(); + write!(written, "{instruction:#?}").expect("known to not error"); + println!("------\n{written}\n------"); + let hash = sha2::Sha256::digest(&written); + let hash = HexDisplay(&hash); + let ExpectedInsnHash { + loc, + expected_mnemonic, + expected_hash, + } = EXPECTED_HASHES.get(insn_index).copied().unwrap_or_default(); + println!("insn_index: {insn_index} hash: {hash:x}"); + let short_expected_hash = HexDisplay(&hash.0[0..5]); + println!("short hash: {short_expected_hash:x}"); + println!("expected_hash: {expected_hash:x}"); + println!("expected_mnemonic: {expected_mnemonic:?}"); + println!("loc: {loc}"); + let mnemonic_prefix = instruction.header()[0].mnemonics().lines()[0] + .split_whitespace() + .next(); + println!("mnemonic_prefix: {:?}", FlattenedOption(&mnemonic_prefix)); + if let Some(mnemonic_prefix) = mnemonic_prefix { + writeln!( + insn_macros, + " insn!({mnemonic_prefix:?}, \"{short_expected_hash:x}\")," + ) + .unwrap(); + } + if !expected_hash.0.is_empty() { + if mnemonic_prefix != Some(expected_mnemonic) + || &hash.0[..expected_hash.0.len()] != expected_hash.0 + { + return Err("mismatch"); + } + } + } + println!("------\n{insn_macros}------"); + assert_eq!(instructions.instructions().len(), EXPECTED_HASHES.len()); + Ok(()) } }