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/// ```
78///
79/// `try_decode()` implementation will end up containing decoding logic for the full extended enum
80/// as mentioned above. The two major restrictions are that `return` is not allowed in the
81/// `try_decode()` method and enum variants must be constructed using `Self::`. The implementation
82/// is quite fragile, so if you're calling internal functions, they might have to be re-exported
83/// since the macro will simply copy-paste the decoding logic as is. Similarly with missing imports,
84/// etc. Compiler should be able to guide you through errors reasonably well.
85///
86/// # Enum display implementation
87///
88/// For enum display implementation, the macro is applied to the implementation of
89/// `core::fmt::Display` trait and affects its `fmt()` method:
90/// ```rust,ignore
91/// #[instruction]
92/// impl<Reg> fmt::Display for Rv64MInstruction<Reg>
93/// where
94///     Reg: fmt::Display + Copy,
95/// {
96///     // ...
97/// }
98/// ```
99/// `fmt()` implementation will end up containing decoding logic for the full extended enum as
100/// mentioned above. The three major restrictions are that an enum must be generic over `Reg`
101/// register type, field types must have `Copy` bounds on them (like `Reg` in the example above),
102/// and the method body must consist of a single `match` statement.
103///
104/// # `process_instruction_macros()`
105///
106/// What this macro "does" is impossible to do in Rust macros. So for completeness,
107/// `ab_riscv_macros::process_instruction_macros()` must be called from `build.rs` in a
108/// crate that uses `#[instruction]` macro to generate a bunch of special filed, which the macro
109/// uses to replace the original code with. This is the only way to get the desired ergonomics
110/// withing current constraints of what macros are allowed to do.
111///
112/// # [package.links]
113///
114/// `package` section of `Cargo.toml` must contain `links = "crate-name"` in order for metadata to
115/// be successfully exported to dependent crates.
116#[proc_macro_attribute]
117pub fn instruction(attr: TokenStream, item: TokenStream) -> TokenStream {
118    instruction::instruction(attr.into(), item.into())
119        .unwrap_or_else(|error| error.to_compile_error())
120        .into()
121}
122
123/// Processes `#[instruction_execution]` attribute on both enum execution implementations.
124///
125/// It must be applied to enum, whose definition is already annotated with `#[instruction]` macro.
126///
127/// Similarly to that macro, this macro will process the contents of the `ExecutableInstruction`
128/// trait implementation. `execute()` implementation will end up containing both inherited and own
129/// execution logic according to the ordering set in `#[instruction]`.
130///
131/// There are constraints on the `execute()` method body, it must have one or both (but nothing
132/// else) of the following:
133/// * matching in the following style: `match self { Self::Variant { .. } }`
134///   * note that `Self` must be used instead of the explicit type name, such that it works when
135///     inherited
136/// * `Ok(ControlFlow::Continue(()))` expression
137#[proc_macro_attribute]
138pub fn instruction_execution(attr: TokenStream, item: TokenStream) -> TokenStream {
139    instruction_execution::instruction_execution(attr.into(), item.into())
140        .unwrap_or_else(|error| error.to_compile_error())
141        .into()
142}