Macro_rules! , Macro Call Macro

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.

  • You want to do X (I’m not sure because you won’t say)
  • You think you can get to X (?) by getting Y (some macro or const being able to expand to the literal text "el2").
  • You don’t know how to do Y because it isn’t feasibly possible but you ask for help on it.
  • People here on URLO are confused because Y is clearly not the root cause of your problem.
  • You don’t read the responses fully and keep asking about Y (some macro or const being able to expand to the literal text "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.

4 Likes

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);
}
1 Like

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");
}
2 Likes

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.

2 Likes

thank you

1 Like

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");
}

Playground with all the code

4 Likes

Thank you very much for your patient answer.