bench_utils/
lib.rs

1//! Macros used to generate programs for Flowistry's benchmarks
2mod utils;
3
4use proc_macro::TokenStream;
5use proc_macro2::{Ident, Span};
6use quote::quote;
7use utils::{
8  ArrayAssign, StructAssign, TreeLevel, parse_arr_assign, parse_struct_assign,
9};
10
11/// Repeatedly assigns to a variable to increase the number of locations
12/// while keeping the number of places constant. For example:
13/// ```rust
14/// use bench_utils::generate_locations;
15/// generate_locations!(foo: [i32; 10] = 1);
16/// ```
17/// generates a program which assigns `foo = 1` 10 times.
18#[proc_macro]
19pub fn generate_locations(input: TokenStream) -> TokenStream {
20  let ArrayAssign {
21    var_name,
22    var_val,
23    num_locations,
24  } = parse_arr_assign(input);
25
26  let var_iter = std::iter::repeat_n(&var_name, num_locations);
27
28  quote! {
29    let mut #var_name = #var_val;
30    #( #var_iter = #var_val; )*
31  }
32  .into()
33}
34
35/// Repeatedly borrows the same variable to create many places, locations, and lifetimes.
36/// For example:
37/// ```rust
38/// use bench_utils::generate_unique_lifetimes;
39/// generate_unique_lifetimes!(foo: [i32; 10] = 1);
40/// ```
41/// generates a program which creates 10 "borrow" variables, each assigned to `&foo`:
42/// ```rust
43/// let foo = 1;
44/// let borrow_1 = &foo;
45/// let borrow_2 = &foo;
46/// // ...
47/// ```
48#[proc_macro]
49pub fn generate_unique_lifetimes(input: TokenStream) -> TokenStream {
50  let ArrayAssign {
51    var_name,
52    var_val,
53    num_locations,
54  } = parse_arr_assign(input);
55
56  let idents = (1 ..= num_locations)
57    .map(|num| Ident::new(format!("borrow_{num}").as_str(), Span::call_site()));
58
59  quote! {
60    let #var_name = #var_val;
61    #( let #idents = &#var_name; )*
62  }
63  .into()
64}
65
66/// Assigns to a "main" variable and repeatedly creates temporary variables
67/// which use the "main" variable as an input. Each temporary uses its value
68/// to assign back to the "main" variable, generating infoflow between each temporary.
69/// For example:
70/// ```rust
71/// use bench_utils::generate_flow;
72/// generate_flow!(foo: [i32; 10] = 1);
73/// ```
74/// generates
75/// ```rust
76/// let mut foo = 1;
77/// let temp_1 = foo;
78/// foo = temp_1;
79/// let temp_2 = foo;
80/// foo = temp_2;
81/// // ...
82/// ```
83#[proc_macro]
84pub fn generate_flow(input: TokenStream) -> TokenStream {
85  let ArrayAssign {
86    var_name,
87    var_val,
88    num_locations,
89  } = parse_arr_assign(input);
90
91  let idents = (1 ..= num_locations)
92    .map(|num| Ident::new(format!("temp_{num}").as_str(), Span::call_site()));
93
94  quote! {
95    let mut #var_name = #var_val;
96
97    #(
98      let #idents = #var_name;
99      #var_name = #idents;
100    )*
101  }
102  .into()
103}
104
105/// Creates a struct with many fields, generating many places while
106/// keeping the number of locations constant. For example:
107/// ```rust
108/// use bench_utils::generate_places;
109/// generate_places!(foo: PlaceStruct<[i32; 3]> = 1);
110/// ```
111/// generates a struct called `PlaceStruct` with 3 `i32` fields and assigns
112/// `foo` to an instantiation of the `PlaceStruct` where each field is `1`:
113#[proc_macro]
114pub fn generate_places(input: TokenStream) -> TokenStream {
115  let StructAssign {
116    var_name,
117    struct_name,
118    field_val,
119    field_ty,
120    num_fields,
121  } = parse_struct_assign(input);
122
123  let fields = (1 ..= num_fields)
124    .map(|num| Ident::new(format!("field_{num}").as_str(), Span::call_site()))
125    .collect::<Vec<_>>();
126
127  quote! {
128    struct #struct_name {
129      #(#fields: #field_ty,)*
130    }
131
132    let #var_name = #struct_name {
133      #(#fields: #field_val,)*
134    };
135  }
136  .into()
137}
138
139/// Creates a struct with many fields, each having the type `&'a <type>`,
140/// creating many places with one lifetime. For example:
141/// ```rust
142/// use bench_utils::generate_same_lifetime;
143/// generate_same_lifetime!(foo: LifetimesStruct<[i32; 3]> = 1);
144/// ```
145/// generates a `LifetimesStruct<'a>` struct with 3 `&'a i32` fields and assigns
146/// `foo` to an instantiation of the struct, with each field having the value `&1`.
147#[proc_macro]
148pub fn generate_same_lifetime(input: TokenStream) -> TokenStream {
149  let lt_ident = syn::Lifetime::new("'a", Span::call_site());
150
151  let StructAssign {
152    var_name,
153    struct_name,
154    field_val,
155    field_ty,
156    num_fields,
157  } = parse_struct_assign(input);
158
159  let fields = (1 ..= num_fields)
160    .map(|num| Ident::new(format!("field_{num}").as_str(), Span::call_site()))
161    .collect::<Vec<_>>();
162
163  quote! {
164    struct #struct_name<#lt_ident> {
165      #(#fields: &#lt_ident #field_ty,)*
166    }
167
168    let #var_name = #struct_name {
169      #(#fields: &#field_val,)*
170    };
171  }
172  .into()
173}
174
175/// Creates a struct with deeply-nested fields. For example:
176/// ```rust
177/// use bench_utils::generate_nested_struct;
178/// generate_nested_struct!(foo: NestedStruct<[i32; 3]> = 1);
179/// ```
180/// generates 3 structs for each "level" of the nesting, with 3 fields each:
181/// ```rust
182/// struct struct_1 {
183///   field_1: i32,
184///   // ...
185/// }
186/// struct struct_2 {
187///   field_1: struct_1,
188///   // ...
189/// }
190/// struct struct_3 {
191///   field_1: struct_2,
192///   // ...
193/// }
194/// ```
195/// the macro then instantiates each level of the tree, resulting in a final struct
196/// with nⁿ `i32` fields.
197#[proc_macro]
198pub fn generate_nested_struct(input: TokenStream) -> TokenStream {
199  let StructAssign {
200    field_val,
201    field_ty,
202    num_fields,
203    ..
204  } = parse_struct_assign(input);
205
206  let fields = (1 ..= num_fields)
207    .map(|num| Ident::new(format!("field_{num}").as_str(), Span::call_site()))
208    .collect::<Vec<_>>();
209
210  // Create and instantiate structs for each "level" of the nested struct
211  let mut levels = vec![];
212  for level_num in (0 ..= num_fields).rev() {
213    let level = TreeLevel::new(
214      level_num,
215      fields.clone(),
216      levels.last(),
217      &field_val,
218      &field_ty,
219    );
220    levels.push(level);
221  }
222
223  let defs = levels.iter().map(|level| level.def.clone());
224  let instants = levels.iter().map(|level| level.instantiation.clone());
225  quote! {
226    #(#defs)*
227
228    #(#instants)*
229  }
230  .into()
231}