From c225c6aef1e112b38d623d557c7619efcf7b3fc4 Mon Sep 17 00:00:00 2001 From: CensoredUsername Date: Tue, 1 Oct 2024 04:52:32 +0200 Subject: [PATCH] Rework data directives. They now use the rust type names (like .u8) instead of byte/word/dword/qword. There's also both unsigned and signed variants, as well as basic floats. --- doc/examples/bf-jit/src/aarch64.rs | 5 +- doc/examples/hello-world/src/aarch64.rs | 4 +- doc/langref_common.md | 14 ++-- plugin/src/directive.rs | 81 +++++++++++++++++++-- testing/tests/aarch64_cache_coherency.rs | 4 +- testing/tests/directives.rs | 91 ++++++++++++++++++++---- 6 files changed, 171 insertions(+), 28 deletions(-) diff --git a/doc/examples/bf-jit/src/aarch64.rs b/doc/examples/bf-jit/src/aarch64.rs index 7421158753..ea94c2c99c 100644 --- a/doc/examples/bf-jit/src/aarch64.rs +++ b/doc/examples/bf-jit/src/aarch64.rs @@ -77,10 +77,11 @@ impl Program { // literal pool dynasm!(ops + ; .align 8 ; ->getchar: - ; .qword State::getchar as _ + ; .u64 State::getchar as _ ; ->putchar: - ; .qword State::putchar as _ + ; .u64 State::putchar as _ ); let start = prologue!(ops); diff --git a/doc/examples/hello-world/src/aarch64.rs b/doc/examples/hello-world/src/aarch64.rs index 60f4d4e214..3a65c10840 100644 --- a/doc/examples/hello-world/src/aarch64.rs +++ b/doc/examples/hello-world/src/aarch64.rs @@ -11,9 +11,9 @@ fn main() { ; .arch aarch64 ; ->hello: ; .bytes string.as_bytes() - ; .align 4 + ; .align 8 ; ->print: - ; .qword print as _ + ; .u64 print as _ ); let hello = ops.offset(); diff --git a/doc/langref_common.md b/doc/langref_common.md index 2e426cf703..ba4c2e5b34 100644 --- a/doc/langref_common.md +++ b/doc/langref_common.md @@ -55,10 +55,16 @@ Name | Argument format | Description `.feature`| A comma-separated list of identifiers. | Set architectural features that are allowed to be used. `.alias` | An name followed by a register | Defines the name as an alias for the wanted register. `.align` | An expression of type usize | Pushes NOPs until the assembling head has reached the desired alignment. -`.byte` | One or more expressions of the type `i8` | Pushes the values into the assembling buffer. -`.word` | One or more expressions of the type `i16` | Pushes the values into the assembling buffer. -`.dword` | One or more expressions of the type `i32` | Pushes the values into the assembling buffer. -`.qword` | One or more expressions of the type `i64` | Pushes the values into the assembling buffer. +`.u8` | One or more expressions of the type `u8` | Pushes the values into the assembling buffer. +`.u16` | One or more expressions of the type `u16` | Pushes the values into the assembling buffer. +`.u32` | One or more expressions of the type `u32` | Pushes the values into the assembling buffer. +`.u64` | One or more expressions of the type `u64` | Pushes the values into the assembling buffer. +`.i8` | One or more expressions of the type `i8` | Pushes the values into the assembling buffer. +`.i16` | One or more expressions of the type `i16` | Pushes the values into the assembling buffer. +`.i32` | One or more expressions of the type `i32` | Pushes the values into the assembling buffer. +`.i64` | One or more expressions of the type `i64` | Pushes the values into the assembling buffer. +`.f32` | One or more expressions of the type `f32` | Pushes the values into the assembling buffer. +`.f64` | One or more expressions of the type `f64` | Pushes the values into the assembling buffer. `.bytes` | An expression of that implements `IntoIterator` or `IntoIterator` | Extends the assembling buffer with the iterator. Directives are normally local to the current `dynasm!` invocation. However, if the `filelocal` feature is used they will be processed in lexical order over the whole file. This feature only works on a nightly compiler and might be removed in the future. diff --git a/plugin/src/directive.rs b/plugin/src/directive.rs index e0a6c06f79..521a72df7a 100644 --- a/plugin/src/directive.rs +++ b/plugin/src/directive.rs @@ -2,6 +2,8 @@ use std::collections::hash_map::Entry; use syn::parse; use syn::Token; +use syn::spanned::Spanned; +use quote::quote_spanned; use proc_macro2::{TokenTree, Literal}; use proc_macro_error2::emit_error; @@ -43,11 +45,17 @@ pub(crate) fn evaluate_directive(invocation_context: &mut DynasmContext, stmts: } invocation_context.current_arch.set_features(&features); }, - // ; .byte (expr ("," expr)*)? - "byte" => directive_const(invocation_context, stmts, input, Size::BYTE)?, - "word" => directive_const(invocation_context, stmts, input, Size::B_2)?, - "dword" => directive_const(invocation_context, stmts, input, Size::B_4)?, - "qword" => directive_const(invocation_context, stmts, input, Size::B_8)?, + // ; .u8 (expr ("," expr)*)? + "u8" => directive_unsigned(invocation_context, stmts, input, Size::BYTE)?, + "u16" => directive_unsigned(invocation_context, stmts, input, Size::B_2)?, + "u32" => directive_unsigned(invocation_context, stmts, input, Size::B_4)?, + "u64" => directive_unsigned(invocation_context, stmts, input, Size::B_8)?, + "i8" => directive_signed(invocation_context, stmts, input, Size::BYTE)?, + "i16" => directive_signed(invocation_context, stmts, input, Size::B_2)?, + "i32" => directive_signed(invocation_context, stmts, input, Size::B_4)?, + "i64" => directive_signed(invocation_context, stmts, input, Size::B_8)?, + "f32" => directive_float(stmts, input, Size::B_4)?, + "f64" => directive_float(stmts, input, Size::B_8)?, "bytes" => { // ; .bytes expr let iterator: syn::Expr = input.parse()?; @@ -97,7 +105,7 @@ pub(crate) fn evaluate_directive(invocation_context: &mut DynasmContext, stmts: Ok(()) } -fn directive_const(invocation_context: &mut DynasmContext, stmts: &mut Vec, input: parse::ParseStream, size: Size) -> parse::Result<()> { +fn directive_signed(invocation_context: &mut DynasmContext, stmts: &mut Vec, input: parse::ParseStream, size: Size) -> parse::Result<()> { // FIXME: this could be replaced by a Punctuated parser? // parse (expr (, expr)*)? @@ -127,6 +135,67 @@ fn directive_const(invocation_context: &mut DynasmContext, stmts: &mut Vec Ok(()) } +fn directive_unsigned(invocation_context: &mut DynasmContext, stmts: &mut Vec, input: parse::ParseStream, size: Size) -> parse::Result<()> { + // FIXME: this could be replaced by a Punctuated parser? + // parse (expr (, expr)*)? + + if input.is_empty() || input.peek(Token![;]) { + return Ok(()) + } + + if let Some(jump) = input.parse_opt()? { + invocation_context.current_arch.handle_static_reloc(stmts, jump, size); + } else { + let expr: syn::Expr = input.parse()?; + stmts.push(Stmt::ExprUnsigned(delimited(expr), size)); + } + + + while input.peek(Token![,]) { + let _: Token![,] = input.parse()?; + + if let Some(jump) = input.parse_opt()? { + invocation_context.current_arch.handle_static_reloc(stmts, jump, size); + } else { + let expr: syn::Expr = input.parse()?; + stmts.push(Stmt::ExprUnsigned(delimited(expr), size)); + } + } + + Ok(()) +} + +fn directive_float(stmts: &mut Vec, input: parse::ParseStream, size: Size) -> parse::Result<()> { + // FIXME: this could be replaced by a Punctuated parser? + // parse (expr (, expr)*)? + + if input.is_empty() || input.peek(Token![;]) { + return Ok(()) + } + + let expr: syn::Expr = input.parse()?; + let expr = match size { + Size::B_4 => quote_spanned! {expr.span() => f32::to_bits( #expr ) }, + Size::B_8 => quote_spanned! {expr.span() => f64::to_bits( #expr ) }, + _ => unreachable!() + }; + stmts.push(Stmt::ExprUnsigned(delimited(expr), size)); + + while input.peek(Token![,]) { + let _: Token![,] = input.parse()?; + + let expr: syn::Expr = input.parse()?; + let expr = match size { + Size::B_4 => quote_spanned! {expr.span() => f32::to_bits( #expr ) }, + Size::B_8 => quote_spanned! {expr.span() => f64::to_bits( #expr ) }, + _ => unreachable!() + }; + stmts.push(Stmt::ExprUnsigned(delimited(expr), size)); + } + + Ok(()) +} + /// In case a directive is unknown, try to skip up to the next ; and resume parsing. fn skip_until_semicolon(input: parse::ParseStream) { let _ = input.step(|cursor| { diff --git a/testing/tests/aarch64_cache_coherency.rs b/testing/tests/aarch64_cache_coherency.rs index 7799552a9d..746a179390 100644 --- a/testing/tests/aarch64_cache_coherency.rs +++ b/testing/tests/aarch64_cache_coherency.rs @@ -106,9 +106,9 @@ fn test_cache_coherency_other_cores() { ; .arch aarch64 ; .align 8 ; -> first_addr: - ; .qword first_value.as_ptr() as *mut u8 as _ + ; .u64 first_value.as_ptr() as *mut u8 as _ ; -> second_addr: - ; .qword second_value.as_ptr() as *mut u8 as _ + ; .u64 second_value.as_ptr() as *mut u8 as _ ); let start = ops.offset(); dynasm!(ops diff --git a/testing/tests/directives.rs b/testing/tests/directives.rs index 41b550c1d9..0332bf2018 100644 --- a/testing/tests/directives.rs +++ b/testing/tests/directives.rs @@ -124,39 +124,106 @@ fn test_aliases() { } #[test] -fn test_data_directives_unaligned() { +fn test_data_directives_unaligned_unsigned() { let mut ops = dynasmrt::SimpleAssembler::new(); dynasm!(ops - ; .byte 0x00 - ; .word 0x1111 - ; .dword 0x22222222 - ; .qword 0x3333333333333333 + ; .u8 0x88 + ; .u16 0x9999 + ; .u32 0xAAAAAAAA + ; .u64 0xBBBBBBBBBBBBBBBB ); let buf = ops.finalize(); let hex: Vec = buf.iter().map(|x| format!("{:02X}", *x)).collect(); let hex = hex.join(", "); - assert_eq!(hex, "00, 11, 11, 22, 22, 22, 22, 33, 33, 33, 33, 33, 33, 33, 33", "Register aliases"); + assert_eq!(hex, "88, 99, 99, AA, AA, AA, AA, BB, BB, BB, BB, BB, BB, BB, BB", "Data directives"); } #[test] -fn test_data_directives_aligned() { +fn test_data_directives_unaligned_signed() { + let mut ops = dynasmrt::SimpleAssembler::new(); + dynasm!(ops + ; .i8 -0x44 + ; .i16 -0x1111 + ; .i32 -0x22222222 + ; .i64 -0x3333333333333333 + ); + + let buf = ops.finalize(); + let hex: Vec = buf.iter().map(|x| format!("{:02X}", *x)).collect(); + let hex = hex.join(", "); + assert_eq!(hex, "BC, EF, EE, DE, DD, DD, DD, CD, CC, CC, CC, CC, CC, CC, CC", "Data directives"); +} + +#[test] +fn test_data_directives_aligned_unsigned() { + let mut ops = dynasmrt::SimpleAssembler::new(); + dynasm!(ops + ; .arch x64 + ; .u8 0x88 + ; .align 2 + ; .u16 0x9999 + ; .align 8 + ; .u32 0xAAAAAAAA + ; .align 8 + ; .u64 0xBBBBBBBBBBBBBBBB + ); + + let buf = ops.finalize(); + let hex: Vec = buf.iter().map(|x| format!("{:02X}", *x)).collect(); + let hex = hex.join(", "); + assert_eq!(hex, "88, 90, 99, 99, 90, 90, 90, 90, AA, AA, AA, AA, 90, 90, 90, 90, BB, BB, BB, BB, BB, BB, BB, BB", "Data directives"); +} + +#[test] +fn test_data_directives_aligned_signed() { let mut ops = dynasmrt::SimpleAssembler::new(); dynasm!(ops ; .arch x64 - ; .byte 0x00 + ; .i8 -0x44 ; .align 2 - ; .word 0x1111 + ; .i16 -0x1111 ; .align 8 - ; .dword 0x22222222 + ; .i32 -0x22222222 + ; .align 8 + ; .i64 -0x3333333333333333 + ); + + let buf = ops.finalize(); + let hex: Vec = buf.iter().map(|x| format!("{:02X}", *x)).collect(); + let hex = hex.join(", "); + assert_eq!(hex, "BC, 90, EF, EE, 90, 90, 90, 90, DE, DD, DD, DD, 90, 90, 90, 90, CD, CC, CC, CC, CC, CC, CC, CC", "Data directives"); +} + +#[test] +fn test_data_directives_unaligned_float() { + let mut ops = dynasmrt::SimpleAssembler::new(); + dynasm!(ops + ; .arch x64 + ; .f32 3.14159265359 + ; .f64 3.14159265359 + ); + + let buf = ops.finalize(); + let hex: Vec = buf.iter().map(|x| format!("{:02X}", *x)).collect(); + let hex = hex.join(", "); + assert_eq!(hex, "DB, 0F, 49, 40, EA, 2E, 44, 54, FB, 21, 09, 40", "Data directives"); +} + +#[test] +fn test_data_directives_aligned_float() { + let mut ops = dynasmrt::SimpleAssembler::new(); + dynasm!(ops + ; .arch x64 + ; .f32 3.14159265359 ; .align 8 - ; .qword 0x3333333333333333 + ; .f64 3.14159265359 ); let buf = ops.finalize(); let hex: Vec = buf.iter().map(|x| format!("{:02X}", *x)).collect(); let hex = hex.join(", "); - assert_eq!(hex, "00, 90, 11, 11, 90, 90, 90, 90, 22, 22, 22, 22, 90, 90, 90, 90, 33, 33, 33, 33, 33, 33, 33, 33", "Register aliases"); + assert_eq!(hex, "DB, 0F, 49, 40, 90, 90, 90, 90, EA, 2E, 44, 54, FB, 21, 09, 40", "Data directives"); } #[test]