macro_rules! msr {
($op:expr) => {
concat!("msr x, ", stringify!($op))
};
}
macro_rules! el2 {
() => {
"el2"
};
}
the result of msr!(el2!()) is " msr x, el2!() "
i want to use stringify!, and the result is " msr x, el2" , how to do ?
macro_rules! msr {
($op:expr) => {
concat!("msr x, ", stringify!($op))
};
}
macro_rules! el2 {
() => {
"el2"
};
}
the result of msr!(el2!()) is " msr x, el2!() "
i want to use stringify!, and the result is " msr x, el2" , how to do ?
This is a continuation of your previous topic, and this note still stands. Please read it.
You don’t need to get stringify!(el2!())
to give "el2"
. It isn’t possible. stringify!(SOME_TOKENS)
always gives "SOME_TOKENS"
.
Your repeated questions about this are a classic case of the XY problem.
"el2"
)."el2"
).I can’t give you Y because it isn’t possible the way you’re asking.
What is X?
What is your root problem?
Are you not reading the responses because you don’t fully speak English or just because you’re adamant on getting it done the way you would have in C?
If the latter, refer to this post I linked to earlier.
If the former (not really speaking English) I’m sorry about that, but please understand that getting stringify!(some_macro!())
to give "el2"
or any other string is not possible. You might want my asm!
solution from the previous topic.
If that won’t work, please explain why.
thanks for your answer.
The root problem is as follow:
In C, registers are defined and passed in in different ways.
#define register_1 x1
#define register_2 x2
#define str(x) #x
#define write(name) asm ("msr " str(name)", xwz"));
u32 x3;
write(register_1) -> asm ("msr x1, xwz);
write(register_2) ->asm ("msr x2, xwz);
write(x3) ->asm ("msr x3, xwz);
write(el2) ->asm ("msr el2, xwz);
In rust, what should I do
I am still trying to understand what you want to achieve. However, please review the function below to see if it aligns with your expectations.
use std::arch::asm;
const register_1: &str = "x1";
const register_2: &str = "x2";
macro_rules! write {
($name:expr) => {
{
unsafe {
match $name {
register_1 => asm!("msr x1, xwz"),
register_2 => asm!("msr x2, xwz"),
_ => panic!("unexpected match"),
}
}
}
};
}
fn main() {
let _ = write!(register_1);
let _ = write!(register_2);
}
register_1 and register_2 ,It's not just these two.
There may also be register_3 register_4 register_5 ...
For this to result in el2
when $op
is a macro invocation like register1!()
you need some kind of eager macro expansion in order to implement a expand_and_stringify
macro. Unfortunately this feature isn't available yet. There is TokenStream::expand_expr()
, but that is unstable (nightly only) and only works for macros that expand into literals (while I think you macro attempts to expand into an identifier) so it isn't useful in this case.
You could change the definition of el2
so that it gives another macro the value as an argument:
macro_rules! msr {
// Special case macros:
($($other_macro:ident)::* ! ()) => {
$($other_macro)::*! { msr }
};
// Not a macro, so stringify argument:
($op:expr) => {
concat!("msr x, ", stringify!($op))
};
}
macro_rules! el2 {
($($other_macro:ident)::*) => {
$($other_macro)::*! { el2 }
};
}
fn main() {
assert_eq!(msr!(el2!()), "msr x, el2");
assert_eq!(msr!(el2), "msr x, el2");
}
But the easiest workaround is to not use stringify
and just pass around string literals since concat
and other inbuilt macros that take string arguments usually eager expand their arguments:
macro_rules! msr {
($op:expr) => {
concat!("msr x, ", $op)
};
}
macro_rules! el2 { () => { "el2" }; }
fn main() {
assert_eq!(msr!(el2!()), "msr x, el2");
assert_eq!(msr!("el2"), "msr x, el2");
}
I think what you’re trying to do here is flawed in a different way. If xwz
is not declared as an input to the asm!
block, xwz
has an undefined value as specified in the Reference.
Any registers not specified as inputs will contain an undefined value on entry to the asm block.
To give registers consistent values across multiple assembly instructions, use one asm!
block with multiple instructions. This will also cause the verbosity of each instruction to lower, making it simpler to just write out the instruction each time.
This probably could have happened in your C code as well, but it must have never manifested as any bug in the program. In Rust, “just don’t build with -O3
” or “hope the compiler doesn’t optimize this and break the code” is unacceptable.
Thank you very much. this will solve my problem
I formatted it. Please do this whenever you post code, so we can read it more easily.
thank you
If you want multiple arguments then you can use Incremental TT Munchers like this:
/// Helper macro that early expands arguments and then invokes a `callback`
/// macro with the results.
///
/// Assumes the macro invocations in the arguments take "callback" parameters.
macro_rules! _early_expand {
// Handle macro argument:
(
callback = {$($callback:tt)*},
callback_state = {$($callback_state:tt)*},
arguments = {
// Argument (macro):
$($other_macro:ident)::* ! ()
// Other arguments:
$(, $($rest:tt)*)?
},
// Evaluated args:
$($($state:tt)+)?
) => {
$($other_macro)::*! {
callback = {$crate::early_expand},
callback_state = {
callback = {$($callback)*},
callback_state = {$($callback_state)*},
arguments = {
// Remaining arguments:
$($($rest)*)?
},
// Already evaluated arguments:
$($($state)+ ,)?
// other_macro will insert its tokens here at the end
},
}
};
// Handle non-macro argument:
(
callback = {$($callback:tt)*},
callback_state = {$($callback_state:tt)*},
arguments = {
// Argument (value):
$value:expr
// Other arguments:
$(, $($rest:tt)*)?
},
// Evaluated args:
$($($state:tt)+)?
) => {
$crate::early_expand! {
callback = {$($callback)*},
callback_state = {$($callback_state)*},
arguments = {
// Remaining arguments:
$($($rest)*)?
},
// Already evaluated arguments:
$($($state)+ ,)?
// New evaluated argument:
$value
}
};
// All arguments have been handled:
(
callback = {$($callback:tt)*},
callback_state = {$($callback_state:tt)*},
arguments = {
// Arguments is empty, all have been evaluated
},
$($state:tt)*
) => {
$($callback)* ! { $($callback_state)* $($state)* }
};
}
// Allow accessing the macro using normal namespace rules instead of special
// macro name space rules (where macros are only visible to lines later in the
// file)
pub(crate) use _early_expand as early_expand;
macro_rules! _msr {
// After evaluated args:
(@callback $op1:expr, $op2:expr) => {
concat!("msr x, ", stringify!($op1), ", ", stringify!($op2))
};
(@$($fail:tt)*) => {
::core::compile_error!(::core::concat!(
"Failed to expand arguments for \"msr\" macro, remaining token: ",
::core::stringify!(@$($fail)*)
))
};
// Evaluate all arguments
($($args:tt)*) => {
$crate::early_expand! {
callback = {$crate::msr},
callback_state = {@callback},
arguments = { $($args)* },
}
};
}
#[allow(unused_imports)]
pub(crate) use _msr as msr;
macro_rules! el0 {
(
callback = { $($callback:tt)* },
callback_state = { $($callback_state:tt)* },
) => {
$($callback)* ! { $($callback_state)* el01 }
};
}
#[test]
fn it_works() {
// trace_macros!(true); // <- Debug macro expansion
assert_eq!(msr!(el, el0!()), "msr x, el, el01");
}
This gets complicated so you can define a helper macro for creating such macros:
/// Defines a macro that eagerly evaluates its arguments.
macro_rules! _eager_macro {
// Secondly call this arm to create an "eager" macro with no arguments:
(@inner
dollar = {$dollar:tt},
$pub:vis $name:ident() {
$($macro_code:tt)*
}
) => {
// This macro arm is simple since we don't need to eagerly evaluate the
// macro's arguments, we just need to return the result immediately.
paste::paste! {
macro_rules! [<_ $name>] {
// Eager, so inside another macro that we need to call back:
(
@eager_expansion
callback = { $dollar ($dollar callback:tt)* },
callback_state = { $dollar ($dollar callback_state:tt)* },
) => {
$dollar ($dollar callback)* ! { $dollar ($dollar callback_state)* $($macro_code)* }
};
// Not eager:
() => {
$($macro_code)*
};
}
$pub use [<_ $name>] as $name;
}
};
// Secondly call this arm to create an "eager" macro that takes arguments
// that need to be evaluated before the macro's body.
(@inner
dollar = {$dollar:tt},
$pub:vis $name:ident($($param:ident),* $(,)?) {
$($macro_code:tt)*
}
) => {
paste::paste! {
macro_rules! [<_ $name>] {
// After evaluated args (eager, so callback original macro):
(@callback_eager
callback = { $dollar ($dollar callback:tt)* },
callback_state = { $dollar ($dollar callback_state:tt)* },
$(
$dollar $param:expr
),*
) => {
$dollar ($dollar callback)* ! {
$dollar ($dollar callback_state)*
$($macro_code)*
}
};
// After evaluated args:
(@callback
$(
$dollar $param:expr
),*
) => {
$($macro_code)*
};
// Fallback arm when expansion fails (can that happen?)
(@$dollar ($dollar fail:tt)*) => {
::core::compile_error!(::core::concat!(
"Failed to expand arguments for \"",
::core::stringify!($name),
"\" macro, remaining token: ",
::core::stringify!(@$dollar ($dollar fail)*)
))
};
// Invoked from another macro, so call back to it later:
(
@eager_expansion
callback = { $dollar ($dollar callback:tt)* },
callback_state = { $dollar ($dollar callback_state:tt)* },
// This macro's arguments:
$dollar ($dollar args:tt)*
) => {
$crate::early_expand! {
// TODO: callback should really be specified with full
// path to this crated macro, otherwise it can fail when
// invoked from other modules that doesn't have it
// imported. So `msr!()` works but not `path::to::msr!()`
callback = {$name},
callback_state = {
@callback_eager
callback = { $dollar ($dollar callback)* },
callback_state = { $dollar ($dollar callback_state)* },
},
arguments = { $dollar ($dollar args)* },
}
};
// Evaluate all arguments
($dollar ($dollar args:tt)*) => {
$crate::early_expand! {
// TODO: callback should really be specified with full
// path to this crated macro, otherwise it can fail when
// invoked from other modules that doesn't have it
// imported. So `msr!()` works but not `path::to::msr!()`
callback = {$name},
callback_state = {@callback},
arguments = { $dollar ($dollar args)* },
}
};
}
$pub use [<_ $name>] as $name;
}
};
// First call this arm to get a dollar sign symbol (in the future we can use
// `$$` instead of this hack)
($pub:vis $macro_name:ident($($param:ident),* $(,)?) {
$($macro_code:tt)*
}) => {
$crate::eager_macro! {
@inner
dollar = {$},
$pub $macro_name($($param),*) {
$($macro_code)*
}
}
};
}
pub(crate) use _eager_macro as eager_macro;
And then you can use the helper macro like:
eager_macro!(
pub(crate) msr_adv(op1, op2) {
concat!("msr x, ", stringify!($op1), ", ", stringify!($op2))
}
);
eager_macro!(pub(crate) el0_adv() {
el01
});
#[test]
fn adv_macros() {
// trace_macros!(true); // <- Debug macro expansion
assert_eq!(msr_adv!(el, el0_adv!()), "msr x, el, el01");
}
Thank you very much for your patient answer.