Skip to content

Commit

Permalink
Rework data directives. They now use the rust type names (like .u8) i…
Browse files Browse the repository at this point in the history
…nstead of byte/word/dword/qword. There's also both unsigned and signed variants, as well as basic floats.
  • Loading branch information
CensoredUsername committed Oct 1, 2024
1 parent 10786cc commit c225c6a
Show file tree
Hide file tree
Showing 6 changed files with 171 additions and 28 deletions.
5 changes: 3 additions & 2 deletions doc/examples/bf-jit/src/aarch64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
4 changes: 2 additions & 2 deletions doc/examples/hello-world/src/aarch64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
14 changes: 10 additions & 4 deletions doc/langref_common.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<Item=u8>` or `IntoIterator<Item=&u8>` | 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.
Expand Down
81 changes: 75 additions & 6 deletions plugin/src/directive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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()?;
Expand Down Expand Up @@ -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<Stmt>, input: parse::ParseStream, size: Size) -> parse::Result<()> {
fn directive_signed(invocation_context: &mut DynasmContext, stmts: &mut Vec<Stmt>, input: parse::ParseStream, size: Size) -> parse::Result<()> {
// FIXME: this could be replaced by a Punctuated parser?
// parse (expr (, expr)*)?

Expand Down Expand Up @@ -127,6 +135,67 @@ fn directive_const(invocation_context: &mut DynasmContext, stmts: &mut Vec<Stmt>
Ok(())
}

fn directive_unsigned(invocation_context: &mut DynasmContext, stmts: &mut Vec<Stmt>, 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<Stmt>, 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| {
Expand Down
4 changes: 2 additions & 2 deletions testing/tests/aarch64_cache_coherency.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
91 changes: 79 additions & 12 deletions testing/tests/directives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String> = 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<String> = 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<String> = 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<String> = 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<String> = 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<String> = 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]
Expand Down

0 comments on commit c225c6a

Please sign in to comment.