From: Jacob Lifshay Date: Fri, 4 Sep 2020 03:18:35 +0000 (-0700) Subject: add addex instruction and code needed for working around LLVM not implementing addex... X-Git-Tag: v0.2.0~18 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=651216c2c90ace7fc9163c20083dbc47a79b637f;p=power-instruction-analyzer.git add addex instruction and code needed for working around LLVM not implementing addex in the assembler --- diff --git a/power-instruction-analyzer-proc-macro/src/inline_assembly.rs b/power-instruction-analyzer-proc-macro/src/inline_assembly.rs index 6378cb7..c96cc2b 100644 --- a/power-instruction-analyzer-proc-macro/src/inline_assembly.rs +++ b/power-instruction-analyzer-proc-macro/src/inline_assembly.rs @@ -303,6 +303,14 @@ impl Assembly { ..Self::default() } } + pub(crate) fn args_without_text(&self) -> Assembly { + Assembly { + text_fragments: Vec::new(), + inputs: self.inputs.clone(), + outputs: self.outputs.clone(), + clobbers: self.clobbers.clone(), + } + } pub(crate) fn to_text(&self) -> String { let mut id_index_map = HashMap::new(); for (index, id) in self diff --git a/power-instruction-analyzer-proc-macro/src/instructions.rs b/power-instruction-analyzer-proc-macro/src/instructions.rs index e1a7988..a53b2c0 100644 --- a/power-instruction-analyzer-proc-macro/src/instructions.rs +++ b/power-instruction-analyzer-proc-macro/src/instructions.rs @@ -9,10 +9,10 @@ use syn::{ braced, bracketed, parenthesized, parse::{Parse, ParseStream}, punctuated::Punctuated, - Error, LitStr, Token, + token, Error, LitStr, Token, }; -trait InstructionArg: Clone + fmt::Debug + ToTokens + Parse { +trait InstructionArgName: Clone + fmt::Debug + ToTokens + Parse { type Enumerant: Copy + Eq + Hash + fmt::Debug; fn enumerant(&self) -> Self::Enumerant; fn span(&self) -> &Span; @@ -67,7 +67,7 @@ macro_rules! ident_enum { )* } - impl InstructionArg for $enum_name { + impl InstructionArgName for $enum_name { type Enumerant = $enum_name<()>; fn enumerant(&self) -> Self::Enumerant { $enum_name::enumerant(self) @@ -143,7 +143,7 @@ macro_rules! ident_enum { ident_enum! { #[parse_error_msg = "unknown instruction input"] - enum InstructionInput { + enum InstructionInputName { Ra, Rb, Rc, @@ -154,7 +154,7 @@ ident_enum! { ident_enum! { #[parse_error_msg = "unknown instruction output"] - enum InstructionOutput { + enum InstructionOutputName { Rt, Carry, Overflow, @@ -169,6 +169,74 @@ ident_enum! { } } +#[derive(Debug)] +struct InstructionArg { + name: T, + register: Option, +} + +impl InstructionArg { + fn error_if_register_is_specified(&self) -> syn::Result<()> { + match &self.register { + None => Ok(()), + Some(register) => Err(Error::new_spanned( + register, + format_args!("register specification not allowed on {}", self.name.name()), + )), + } + } +} + +impl Parse for InstructionArg { + fn parse(input: ParseStream) -> syn::Result { + let name = input.parse()?; + let register = if input.peek(token::Paren) { + let register_tokens; + parenthesized!(register_tokens in input); + let register: LitStr = register_tokens.parse()?; + match &*register.value() { + "r1" => Err("stack pointer (r1) can't be used as instruction argument"), + "r2" => Err("TOC pointer (r2) can't be used as instruction argument"), + "r13" => { + Err("system thread id register (r13) can't be used as instruction argument") + } + "r0" | "r3" | "r4" | "r5" | "r6" | "r7" | "r8" | "r9" | "r10" | "r11" | "r12" + | "r14" | "r15" | "r16" | "r17" | "r18" | "r19" | "r20" | "r21" | "r22" | "r23" + | "r24" | "r25" | "r26" | "r27" | "r28" | "r29" | "r30" | "r31" => Ok(()), + _ => Err("unknown register: valid values are r0, r3..r12, r14..r31"), + } + .map_err(|msg| Error::new_spanned(®ister, msg))?; + Some(register) + } else { + None + }; + Ok(Self { name, register }) + } +} + +type InstructionInput = InstructionArg; +type InstructionOutput = InstructionArg; + +impl InstructionInput { + fn constraint(&self) -> LitStr { + if let Some(register) = &self.register { + LitStr::new(&format!("{{{}}}", register.value()), register.span()) + } else { + LitStr::new("b", Span::call_site()) + } + } +} + +impl InstructionOutput { + fn constraint(&self) -> LitStr { + if let Some(register) = &self.register { + LitStr::new(&format!("=&{{{}}}", register.value()), register.span()) + } else { + LitStr::new("=&b", Span::call_site()) + } + } +} + #[derive(Debug)] struct Instruction { enumerant: Ident, @@ -176,26 +244,27 @@ struct Instruction { inputs: Punctuated, outputs: Punctuated, instruction_name: LitStr, + literal_instruction_text: Option, } -fn check_duplicate_free<'a, T: InstructionArg + 'a>( - args: impl IntoIterator, +fn check_duplicate_free<'a, T: InstructionArgName + 'a>( + args: impl IntoIterator>, ) -> syn::Result<()> { let mut seen_args = HashMap::new(); for arg in args { - if let Some(prev_arg) = seen_args.insert(arg.enumerant(), arg) { + if let Some(prev_arg) = seen_args.insert(arg.name.enumerant(), arg) { let mut error = Error::new( - arg.span().clone(), + arg.name.span().clone(), format_args!( "duplicate instruction argument: {}", - arg.clone().into_ident() + arg.name.clone().into_ident() ), ); error.combine(Error::new( - prev_arg.span().clone(), + prev_arg.name.span().clone(), format_args!( "duplicate instruction argument: {}", - prev_arg.clone().into_ident() + prev_arg.name.clone().into_ident() ), )); return Err(error); @@ -232,12 +301,20 @@ impl Parse for Instruction { let body_tokens; braced!(body_tokens in input); let instruction_name: LitStr = body_tokens.parse()?; + let literal_instruction_text; + if body_tokens.peek(Token!(:)) { + body_tokens.parse::()?; + literal_instruction_text = Some(body_tokens.parse()?); + } else { + literal_instruction_text = None; + } Ok(Self { enumerant, fn_name, inputs, outputs, instruction_name, + literal_instruction_text, }) } } @@ -246,12 +323,12 @@ impl Instruction { fn map_input_registers(&self) -> syn::Result> { let mut retval = Vec::new(); for input in &self.inputs { - retval.push(match input { - InstructionInput::Ra(_) => quote! {InstructionInputRegister::Ra}, - InstructionInput::Rb(_) => quote! {InstructionInputRegister::Rb}, - InstructionInput::Rc(_) => quote! {InstructionInputRegister::Rc}, - InstructionInput::Carry(_) => quote! {InstructionInputRegister::Carry}, - InstructionInput::Overflow(_) => quote! {InstructionInputRegister::Overflow}, + retval.push(match input.name { + InstructionInputName::Ra(_) => quote! {InstructionInputRegister::Ra}, + InstructionInputName::Rb(_) => quote! {InstructionInputRegister::Rb}, + InstructionInputName::Rc(_) => quote! {InstructionInputRegister::Rc}, + InstructionInputName::Carry(_) => quote! {InstructionInputRegister::Carry}, + InstructionInputName::Overflow(_) => quote! {InstructionInputRegister::Overflow}, }); } Ok(retval) @@ -263,8 +340,14 @@ impl Instruction { inputs, outputs, instruction_name, + literal_instruction_text, } = self; - let asm_instr = Assembly::from(instruction_name.value()); + let asm_instr = Assembly::from( + literal_instruction_text + .as_ref() + .unwrap_or(instruction_name) + .value(), + ); let mut asm_instr_args = Vec::new(); let mut before_instr_asm_lines = Vec::::new(); let mut after_instr_asm_lines = Vec::::new(); @@ -274,61 +357,72 @@ impl Instruction { let mut need_overflow_output = false; let mut need_cr_output = false; for output in outputs { - match output { - InstructionOutput::Rt(_) => { + match output.name { + InstructionOutputName::Rt(_) => { before_asm.push(quote! {let rt: u64;}); - asm_instr_args.push(assembly! {"$" output{"=&b"(rt)} }); + let constraint = output.constraint(); + asm_instr_args.push(assembly! {"$" output{#constraint(rt)} }); after_asm.push(quote! {retval.rt = Some(rt);}); } - InstructionOutput::Carry(_) => { + InstructionOutputName::Carry(_) => { + output.error_if_register_is_specified()?; need_carry_output = true; } - InstructionOutput::Overflow(_) => { + InstructionOutputName::Overflow(_) => { + output.error_if_register_is_specified()?; need_overflow_output = true; } - InstructionOutput::CR0(_) => { + InstructionOutputName::CR0(_) => { + output.error_if_register_is_specified()?; need_cr_output = true; after_asm.push(quote! { retval.cr0 = Some(ConditionRegister::from_cr_field(cr, 0)); }); } - InstructionOutput::CR1(_) => { + InstructionOutputName::CR1(_) => { + output.error_if_register_is_specified()?; need_cr_output = true; after_asm.push(quote! { retval.cr1 = Some(ConditionRegister::from_cr_field(cr, 1)); }); } - InstructionOutput::CR2(_) => { + InstructionOutputName::CR2(_) => { + output.error_if_register_is_specified()?; need_cr_output = true; after_asm.push(quote! { retval.cr2 = Some(ConditionRegister::from_cr_field(cr, 2)); }); } - InstructionOutput::CR3(_) => { + InstructionOutputName::CR3(_) => { + output.error_if_register_is_specified()?; need_cr_output = true; after_asm.push(quote! { retval.cr3 = Some(ConditionRegister::from_cr_field(cr, 3)); }); } - InstructionOutput::CR4(_) => { + InstructionOutputName::CR4(_) => { + output.error_if_register_is_specified()?; need_cr_output = true; after_asm.push(quote! { retval.cr4 = Some(ConditionRegister::from_cr_field(cr, 4)); }); } - InstructionOutput::CR5(_) => { + InstructionOutputName::CR5(_) => { + output.error_if_register_is_specified()?; need_cr_output = true; after_asm.push(quote! { retval.cr5 = Some(ConditionRegister::from_cr_field(cr, 5)); }); } - InstructionOutput::CR6(_) => { + InstructionOutputName::CR6(_) => { + output.error_if_register_is_specified()?; need_cr_output = true; after_asm.push(quote! { retval.cr6 = Some(ConditionRegister::from_cr_field(cr, 6)); }); } - InstructionOutput::CR7(_) => { + InstructionOutputName::CR7(_) => { + output.error_if_register_is_specified()?; need_cr_output = true; after_asm.push(quote! { retval.cr7 = Some(ConditionRegister::from_cr_field(cr, 7)); @@ -339,23 +433,28 @@ impl Instruction { let mut need_carry_input = false; let mut need_overflow_input = false; for input in inputs { - match input { - InstructionInput::Ra(_) => { + match input.name { + InstructionInputName::Ra(_) => { before_asm.push(quote! {let ra: u64 = inputs.try_get_ra()?;}); - asm_instr_args.push(assembly! {"$" input{"b"(ra)} }); + let constraint = input.constraint(); + asm_instr_args.push(assembly! {"$" input{#constraint(ra)} }); } - InstructionInput::Rb(_) => { + InstructionInputName::Rb(_) => { before_asm.push(quote! {let rb: u64 = inputs.try_get_rb()?;}); - asm_instr_args.push(assembly! {"$" input{"b"(rb)} }); + let constraint = input.constraint(); + asm_instr_args.push(assembly! {"$" input{#constraint(rb)} }); } - InstructionInput::Rc(_) => { + InstructionInputName::Rc(_) => { before_asm.push(quote! {let rc: u64 = inputs.try_get_rc()?;}); - asm_instr_args.push(assembly! {"$" input{"b"(rc)} }); + let constraint = input.constraint(); + asm_instr_args.push(assembly! {"$" input{#constraint(rc)} }); } - InstructionInput::Carry(_) => { + InstructionInputName::Carry(_) => { + input.error_if_register_is_specified()?; need_carry_input = true; } - InstructionInput::Overflow(_) => { + InstructionInputName::Overflow(_) => { + input.error_if_register_is_specified()?; need_overflow_input = true; } } @@ -430,7 +529,12 @@ impl Instruction { append_assembly!(final_asm; (asm_instr)); let mut separator = " "; for i in asm_instr_args { - append_assembly!(final_asm; (separator) (i)); + if literal_instruction_text.is_some() { + let i = i.args_without_text(); + append_assembly!(final_asm; (i)); + } else { + append_assembly!(final_asm; (separator) (i)); + } separator = ", "; } for i in after_instr_asm_lines { @@ -477,6 +581,7 @@ impl Instructions { inputs: _, outputs: _, instruction_name, + literal_instruction_text: _, } = instruction; fn_names.push(fn_name); enumerants.push(enumerant); diff --git a/src/instr_models.rs b/src/instr_models.rs index 059f1ce..9ee3996 100644 --- a/src/instr_models.rs +++ b/src/instr_models.rs @@ -261,6 +261,31 @@ pub fn subfzeo(inputs: InstructionInput) -> InstructionResult { }) } +pub fn addex(inputs: InstructionInput) -> InstructionResult { + let ra: u64 = inputs.try_get_ra()?; + let rb: u64 = inputs.try_get_ra()?; + let OverflowFlags { + ov: carry_in, so, .. + } = inputs.try_get_overflow()?; + let result_u128 = ra as u128 + rb as u128 + carry_in as u128; + let result32_u128 = ra as u32 as u128 + rb as u32 as u128 + carry_in as u128; + let result = result_u128 as u64; + let carry = u64::try_from(result_u128).is_err(); + let carry32 = u32::try_from(result32_u128).is_err(); + Ok(InstructionOutput { + rt: Some(result), + overflow: Some(propagate_so( + OverflowFlags { + so, + ov: carry, + ov32: carry32, + }, + inputs, + )?), + ..InstructionOutput::default() + }) +} + create_instr_variants_ov_cr!(divde, divdeo, divde_, divdeo_, i64); pub fn divdeo(inputs: InstructionInput) -> InstructionResult { diff --git a/src/lib.rs b/src/lib.rs index 3c7c9bd..7d85cd8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -459,6 +459,12 @@ instructions! { "subfzeo." } + #[enumerant = AddEX] + fn addex(Ra("r3"), Rb("r4"), Overflow) -> (Rt("r5"), Overflow) { + // work around LLVM not supporting addex instruction: + "addex" : ".long 0x7CA32154 # addex r5, r3, r4, 0" + } + // divde #[enumerant = DivDE] fn divde(Ra, Rb) -> (Rt) {