1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
//! Macros used to generate programs for Flowistry's benchmarks
mod utils;

use proc_macro::TokenStream;
use proc_macro2::{Ident, Span};
use quote::quote;
use utils::{
  parse_arr_assign, parse_struct_assign, ArrayAssign, StructAssign, TreeLevel,
};

/// Repeatedly assigns to a variable to increase the number of locations
/// while keeping the number of places constant. For example:
/// ```rust
/// use bench_utils::generate_locations;
/// generate_locations!(foo: [i32; 10] = 1);
/// ```
/// generates a program which assigns `foo = 1` 10 times.
#[proc_macro]
pub fn generate_locations(input: TokenStream) -> TokenStream {
  let ArrayAssign {
    var_name,
    var_val,
    num_locations,
  } = parse_arr_assign(input);

  let var_iter = std::iter::repeat(&var_name).take(num_locations);

  quote! {
    let mut #var_name = #var_val;
    #( #var_iter = #var_val; )*
  }
  .into()
}

/// Repeatedly borrows the same variable to create many places, locations, and lifetimes.
/// For example:
/// ```rust
/// use bench_utils::generate_unique_lifetimes;
/// generate_unique_lifetimes!(foo: [i32; 10] = 1);
/// ```
/// generates a program which creates 10 "borrow" variables, each assigned to `&foo`:
/// ```rust
/// let foo = 1;
/// let borrow_1 = &foo;
/// let borrow_2 = &foo;
/// // ...
/// ```
#[proc_macro]
pub fn generate_unique_lifetimes(input: TokenStream) -> TokenStream {
  let ArrayAssign {
    var_name,
    var_val,
    num_locations,
  } = parse_arr_assign(input);

  let idents = (1 ..= num_locations)
    .map(|num| Ident::new(format!("borrow_{num}").as_str(), Span::call_site()));

  quote! {
    let #var_name = #var_val;
    #( let #idents = &#var_name; )*
  }
  .into()
}

/// Assigns to a "main" variable and repeatedly creates temporary variables
/// which use the "main" variable as an input. Each temporary uses its value
/// to assign back to the "main" variable, generating infoflow between each temporary.
/// For example:
/// ```rust
/// use bench_utils::generate_flow;
/// generate_flow!(foo: [i32; 10] = 1);
/// ```
/// generates
/// ```rust
/// let mut foo = 1;
/// let temp_1 = foo;
/// foo = temp_1;
/// let temp_2 = foo;
/// foo = temp_2;
/// // ...
/// ```
#[proc_macro]
pub fn generate_flow(input: TokenStream) -> TokenStream {
  let ArrayAssign {
    var_name,
    var_val,
    num_locations,
  } = parse_arr_assign(input);

  let idents = (1 ..= num_locations)
    .map(|num| Ident::new(format!("temp_{num}").as_str(), Span::call_site()));

  quote! {
    let mut #var_name = #var_val;

    #(
      let #idents = #var_name;
      #var_name = #idents;
    )*
  }
  .into()
}

/// Creates a struct with many fields, generating many places while
/// keeping the number of locations constant. For example:
/// ```rust
/// use bench_utils::generate_places;
/// generate_places!(foo: PlaceStruct<[i32; 3]> = 1);
/// ```
/// generates a struct called `PlaceStruct` with 3 `i32` fields and assigns
/// `foo` to an instantiation of the `PlaceStruct` where each field is `1`:
#[proc_macro]
pub fn generate_places(input: TokenStream) -> TokenStream {
  let StructAssign {
    var_name,
    struct_name,
    field_val,
    field_ty,
    num_fields,
  } = parse_struct_assign(input);

  let fields = (1 ..= num_fields)
    .map(|num| Ident::new(format!("field_{num}").as_str(), Span::call_site()))
    .collect::<Vec<_>>();

  quote! {
    struct #struct_name {
      #(#fields: #field_ty,)*
    }

    let #var_name = #struct_name {
      #(#fields: #field_val,)*
    };
  }
  .into()
}

/// Creates a struct with many fields, each having the type `&'a <type>`,
/// creating many places with one lifetime. For example:
/// ```rust
/// use bench_utils::generate_same_lifetime;
/// generate_same_lifetime!(foo: LifetimesStruct<[i32; 3]> = 1);
/// ```
/// generates a `LifetimesStruct<'a>` struct with 3 `&'a i32` fields and assigns
/// `foo` to an instantiation of the struct, with each field having the value `&1`.
#[proc_macro]
pub fn generate_same_lifetime(input: TokenStream) -> TokenStream {
  let lt_ident = syn::Lifetime::new("'a", Span::call_site());

  let StructAssign {
    var_name,
    struct_name,
    field_val,
    field_ty,
    num_fields,
  } = parse_struct_assign(input);

  let fields = (1 ..= num_fields)
    .map(|num| Ident::new(format!("field_{num}").as_str(), Span::call_site()))
    .collect::<Vec<_>>();

  quote! {
    struct #struct_name<#lt_ident> {
      #(#fields: &#lt_ident #field_ty,)*
    }

    let #var_name = #struct_name {
      #(#fields: &#field_val,)*
    };
  }
  .into()
}

/// Creates a struct with deeply-nested fields. For example:
/// ```rust
/// use bench_utils::generate_nested_struct;
/// generate_nested_struct!(foo: NestedStruct<[i32; 3]> = 1);
/// ```
/// generates 3 structs for each "level" of the nesting, with 3 fields each:
/// ```rust
/// struct struct_1 {
///   field_1: i32,
///   // ...
/// }
/// struct struct_2 {
///   field_1: struct_1,
///   // ...
/// }
/// struct struct_3 {
///   field_1: struct_2,
///   // ...
/// }
/// ```
/// the macro then instantiates each level of the tree, resulting in a final struct
/// with nⁿ `i32` fields.
#[proc_macro]
pub fn generate_nested_struct(input: TokenStream) -> TokenStream {
  let StructAssign {
    field_val,
    field_ty,
    num_fields,
    ..
  } = parse_struct_assign(input);

  let fields = (1 ..= num_fields)
    .map(|num| Ident::new(format!("field_{num}").as_str(), Span::call_site()))
    .collect::<Vec<_>>();

  // Create and instantiate structs for each "level" of the nested struct
  let mut levels = vec![];
  for level_num in (0 ..= num_fields).rev() {
    let level = TreeLevel::new(
      level_num,
      fields.clone(),
      levels.last(),
      &field_val,
      &field_ty,
    );
    levels.push(level);
  }

  let defs = levels.iter().map(|level| level.def.clone());
  let instants = levels.iter().map(|level| level.instantiation.clone());
  quote! {
    #(#defs)*

    #(#instants)*
  }
  .into()
}