Skip to main content

ab_riscv_macros_impl/
lib.rs

1//! See and use `ab-riscv-macros` crate instead, this is its implementation detail
2
3mod instruction;
4mod instruction_execution;
5
6use proc_macro::TokenStream;
7
8/// Processes `#[instruction]` attribute on both enum definitions and implementations.
9///
10/// # Enum definition
11///
12/// When applied to the enum definition, it can be used as simply `#[instruction]` to make an enum
13/// with instructions available for inheritance.
14///
15/// More complex syntax is used when inheriting instructions:
16/// ```rust,ignore
17/// #[instruction(
18///     reorder = [C, Add],
19///     ignore = [E],
20///     inherit = [BaseInstruction],
21///     reorder = [D, A],
22/// )]
23/// struct Extended<Reg> {
24///     A(Reg),
25///     B(Reg),
26///     C(Reg),
27///     D(Reg),
28///     E(Reg),
29/// }
30/// ```
31///
32/// This will generate an enum with both `BaseInstruction` and `Extended` instructions, while also
33/// reordering them according to the specified order. So the eventual enum will look like this:
34/// ```rust,ignore
35/// struct Extended<Reg> {
36///     C(Reg),
37///     Add { rd: Reg, rs1: Reg, rs2: Reg },
38///     // Any other instructions from `BaseInstruction` that were not mentioned explicitly
39///     D(Reg),
40///     A(Reg),
41///     B(Reg),
42/// }
43/// ```
44///
45/// Note that both `reorder`, `ignore` and `inherit` attributes can be specified multiple times, and
46/// reordering can reference any variant from both the `BaseInstruction` and `Extended` enums.
47///
48/// This, of course, only works when enums have compatible generics.
49///
50/// All instruction enums in the project must have unique names. Individual instructions can be
51/// repeated between inherited enums, but they must have the same exact variant definition and are
52/// assumed to be 100% compatible.
53///
54/// Here is how the attributes are processed:
55/// * first, all own and inherited enum variants are collected into a set
56/// * then each attribute is processed in order of declaration
57///   * `reorder` indicated where the corresponding variant needs to be included
58///   * `ignore` removed individual variants or the whole enum from a set mentioned earlier (but
59///     instructions that were already "reordered" before will remain). Ignored list may contain
60///     enums that are not in the list of inherited enums.
61///   * `inherit` includes all remaining variants of the corresponding enum that were not explicitly
62///     reordered or ignored earlier
63///   * own variants that were not explicitly reordered or ignored are placed at the end of the enum
64///
65/// # Enum decoding implementation
66///
67/// For enum decoding implementation, the macro is applied to the implementation of `Instruction`
68/// trait and affects its `try_decode()` method:
69/// ```rust,ignore
70/// #[instruction]
71/// impl<Reg> const Instruction for Rv64MInstruction<Reg>
72/// where
73///     Reg: [const] Register<Type = u64>,
74/// {
75/// ```
76///
77/// `try_decode()` implementation will end up containing decoding logic for the full extended enum
78/// as mentioned above. The two major restrictions are that `return` is not allowed in the
79/// `try_decode()` method and enum variants must be constructed using `Self::`. The implementation
80/// is quite fragile, so if you're calling internal functions, they might have to be re-exported
81/// since the macro will simply copy-paste the decoding logic as is. Similarly with missing imports,
82/// etc. Compiler should be able to guide you through errors reasonably well.
83///
84/// # Enum display implementation
85///
86/// For enum display implementation, the macro is applied to the implementation of
87/// `core::fmt::Display` trait and affects its `fmt()` method:
88/// ```rust,ignore
89/// #[instruction]
90/// impl<Reg> fmt::Display for Rv64MInstruction<Reg>
91/// where
92///     Reg: fmt::Display + Copy,
93/// {
94/// ```
95/// `fmt()` implementation will end up containing decoding logic for the full extended enum as
96/// mentioned above. The three major restrictions are that an enum must be generic over `Reg`
97/// register type, field types must have `Copy` bounds on them (like `Reg` in the example above),
98/// and the method body must consist of a single `match` statement.
99///
100/// # `process_instruction_macros()`
101///
102/// What this macro "does" is impossible to do in Rust macros. So for completeness,
103/// `ab_riscv_macros::process_instruction_macros()` must be called from `build.rs` in a
104/// crate that uses `#[instruction]` macro to generate a bunch of special filed, which the macro
105/// uses to replace the original code with. This is the only way to get the desired ergonomics
106/// withing current constraints of what macros are allowed to do.
107///
108/// # [package.links]
109///
110/// `package` section of `Cargo.toml` must contain `links = "crate-name"` in order for metadata to
111/// be successfully exported to dependent crates.
112#[proc_macro_attribute]
113pub fn instruction(attr: TokenStream, item: TokenStream) -> TokenStream {
114    instruction::instruction(attr.into(), item.into())
115        .unwrap_or_else(|error| error.to_compile_error())
116        .into()
117}
118
119/// Processes `#[instruction_execution]` attribute on both enum execution implementations.
120///
121/// It must be applied to enum, whose definition is already annotated with `#[instruction]` macro.
122///
123/// Similarly to that macro, this macro will process the contents of the `ExecutableInstruction`
124/// trait implementation. `execute()` implementation will end up containing both inherited and own
125/// execution logic according to the ordering set in `#[instruction]`.
126///
127/// There are constraints on the `execute()` method body, it must have one or both (but nothing
128/// else) of the following:
129/// * matching in the following style: `match self { Self::Variant { .. } }`
130///   * note that `Self` must be used instead of the explicit type name, such that it works when
131///     inherited
132/// * `Ok(ControlFlow::Continue(()))` expression
133#[proc_macro_attribute]
134pub fn instruction_execution(attr: TokenStream, item: TokenStream) -> TokenStream {
135    instruction_execution::instruction_execution(attr.into(), item.into())
136        .unwrap_or_else(|error| error.to_compile_error())
137        .into()
138}