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
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
use std::io::Result;
/// A list of compilations (transformations from ISLE source to
/// generated Rust source) that exist in the repository.
///
/// This list is used either to regenerate the Rust source in-tree (if
/// the `rebuild-isle` feature is enabled), or to verify that the ISLE
/// source in-tree corresponds to the ISLE source that was last used
/// to rebuild the Rust source (if the `rebuild-isle` feature is not
/// enabled).
#[derive(Clone, Debug)]
pub struct IsleCompilations {
pub items: Vec<IsleCompilation>,
}
impl IsleCompilations {
pub fn lookup(&self, name: &str) -> Option<&IsleCompilation> {
for compilation in &self.items {
if compilation.name == name {
return Some(compilation);
}
}
None
}
}
#[derive(Clone, Debug)]
pub struct IsleCompilation {
pub name: String,
pub output: std::path::PathBuf,
pub tracked_inputs: Vec<std::path::PathBuf>,
pub untracked_inputs: Vec<std::path::PathBuf>,
}
impl IsleCompilation {
/// All inputs to the computation, tracked or untracked. May contain directories.
pub fn inputs(&self) -> Vec<std::path::PathBuf> {
self.tracked_inputs
.iter()
.chain(self.untracked_inputs.iter())
.cloned()
.collect()
}
/// All path inputs to the compilation. Directory inputs are expanded to the
/// list of all ISLE files in the directory.
pub fn paths(&self) -> Result<Vec<std::path::PathBuf>> {
let mut paths = Vec::new();
for input in self.inputs() {
paths.extend(Self::expand_paths(&input)?);
}
Ok(paths)
}
fn expand_paths(input: &std::path::PathBuf) -> Result<Vec<std::path::PathBuf>> {
if input.is_file() {
return Ok(vec![input.clone()]);
}
if !input.exists() {
return Err(std::io::Error::new(
std::io::ErrorKind::NotFound,
format!("ISLE input does not exist: {}", input.display()),
));
}
let mut paths = Vec::new();
for entry in std::fs::read_dir(input).map_err(|e| {
std::io::Error::new(
e.kind(),
format!(
"failed to read ISLE input directory {}: {e}",
input.display()
),
)
})? {
let path = entry?.path();
if let Some(ext) = path.extension() {
if ext == "isle" {
paths.push(path);
}
}
}
Ok(paths)
}
}
pub fn shared_isle_lower_paths(codegen_crate_dir: &std::path::Path) -> Vec<std::path::PathBuf> {
let inst_specs_isle = codegen_crate_dir.join("src").join("inst_specs.isle");
let prelude_isle = codegen_crate_dir.join("src").join("prelude.isle");
let prelude_lower_isle = codegen_crate_dir.join("src").join("prelude_lower.isle");
// The shared instruction selector logic.
vec![
inst_specs_isle.clone(),
prelude_isle.clone(),
prelude_lower_isle.clone(),
]
}
/// Construct the list of compilations (transformations from ISLE
/// source to generated Rust source) that exist in the repository.
pub fn get_isle_compilations(
codegen_crate_dir: &std::path::Path,
gen_dir: &std::path::Path,
) -> IsleCompilations {
// Preludes.
let numerics_isle = gen_dir.join("numerics.isle");
let clif_lower_isle = gen_dir.join("clif_lower.isle");
let clif_opt_isle = gen_dir.join("clif_opt.isle");
let prelude_isle = codegen_crate_dir.join("src").join("prelude.isle");
let prelude_opt_isle = codegen_crate_dir.join("src").join("prelude_opt.isle");
let prelude_lower_isle = codegen_crate_dir.join("src").join("prelude_lower.isle");
#[cfg(feature = "pulley")]
let pulley_gen = gen_dir.join("pulley_gen.isle");
// Verification spec source files. These define the instruction
// semantics consumed by the ISLE verifier and are only needed
// when building the verifier tooling (the `spec` feature). They
// are excluded from normal codegen builds.
let spec_inputs = |extra: &[&str]| -> Vec<std::path::PathBuf> {
if !cfg!(feature = "spec") {
return vec![];
}
let spec_dir = codegen_crate_dir.join("src").join("spec");
let mut inputs = vec![
spec_dir.join("prelude_spec.isle"),
spec_dir.join("inst_specs.isle"),
spec_dir.join("inst_tags.isle"),
];
inputs.extend(extra.iter().map(|f| spec_dir.join(f)));
inputs
};
// Directory for mid-end optimizations.
let src_opts = codegen_crate_dir.join("src").join("opts");
// Directories for lowering backends.
let src_isa_x64 = codegen_crate_dir.join("src").join("isa").join("x64");
let src_isa_aarch64 = codegen_crate_dir.join("src").join("isa").join("aarch64");
let src_isa_s390x = codegen_crate_dir.join("src").join("isa").join("s390x");
let src_isa_risc_v = codegen_crate_dir.join("src").join("isa").join("riscv64");
#[cfg(feature = "pulley")]
let src_isa_pulley_shared = codegen_crate_dir
.join("src")
.join("isa")
.join("pulley_shared");
// This is a set of ISLE compilation units.
//
// The format of each entry is:
//
// (output Rust code file, input ISLE source files)
//
// There should be one entry for each backend that uses ISLE for lowering,
// and if/when we replace our peephole optimization passes with ISLE, there
// should be an entry for each of those as well.
//
// N.B.: add any new compilation outputs to
// `scripts/force-rebuild-isle.sh` if they do not fit the pattern
// `cranelift/codegen/src/isa/*/lower/isle/generated_code.rs`!
IsleCompilations {
items: vec![
// The mid-end optimization rules.
IsleCompilation {
name: "opt".to_string(),
output: gen_dir.join("isle_opt.rs"),
tracked_inputs: [
vec![prelude_isle.clone(), prelude_opt_isle],
spec_inputs(&[]),
vec![
src_opts.join("arithmetic.isle"),
src_opts.join("bitops.isle"),
src_opts.join("cprop.isle"),
src_opts.join("extends.isle"),
src_opts.join("icmp.isle"),
src_opts.join("remat.isle"),
src_opts.join("selects.isle"),
src_opts.join("shifts.isle"),
src_opts.join("skeleton.isle"),
src_opts.join("spaceship.isle"),
src_opts.join("spectre.isle"),
src_opts.join("vector.isle"),
],
]
.concat(),
untracked_inputs: vec![numerics_isle.clone(), clif_opt_isle],
},
// The x86-64 instruction selector.
IsleCompilation {
name: "x64".to_string(),
output: gen_dir.join("isle_x64.rs"),
tracked_inputs: [
vec![prelude_isle.clone(), prelude_lower_isle.clone()],
spec_inputs(&["state.isle"]),
vec![
src_isa_x64.join("inst.isle"),
src_isa_x64.join("lower.isle"),
],
]
.concat(),
untracked_inputs: vec![
numerics_isle.clone(),
clif_lower_isle.clone(),
gen_dir.join("assembler.isle"),
],
},
// The aarch64 instruction selector.
IsleCompilation {
name: "aarch64".to_string(),
output: gen_dir.join("isle_aarch64.rs"),
tracked_inputs: [
vec![prelude_isle.clone(), prelude_lower_isle.clone()],
spec_inputs(&["fpconst.isle", "state.isle"]),
vec![
src_isa_aarch64.join("inst.isle"),
src_isa_aarch64.join("inst_neon.isle"),
],
// The aarch64-specific spec directory is also verification-only.
if cfg!(feature = "spec") {
vec![src_isa_aarch64.join("spec")]
} else {
vec![]
},
vec![
src_isa_aarch64.join("lower.isle"),
src_isa_aarch64.join("lower_dynamic_neon.isle"),
],
]
.concat(),
untracked_inputs: vec![numerics_isle.clone(), clif_lower_isle.clone()],
},
// The s390x instruction selector.
IsleCompilation {
name: "s390x".to_string(),
output: gen_dir.join("isle_s390x.rs"),
tracked_inputs: [
vec![prelude_isle.clone(), prelude_lower_isle.clone()],
spec_inputs(&[]),
vec![
src_isa_s390x.join("inst.isle"),
src_isa_s390x.join("lower.isle"),
],
]
.concat(),
untracked_inputs: vec![numerics_isle.clone(), clif_lower_isle.clone()],
},
// The risc-v instruction selector.
IsleCompilation {
name: "riscv64".to_string(),
output: gen_dir.join("isle_riscv64.rs"),
tracked_inputs: [
vec![prelude_isle.clone(), prelude_lower_isle.clone()],
spec_inputs(&[]),
vec![
src_isa_risc_v.join("inst.isle"),
src_isa_risc_v.join("inst_vector.isle"),
src_isa_risc_v.join("lower.isle"),
],
]
.concat(),
untracked_inputs: vec![numerics_isle.clone(), clif_lower_isle.clone()],
},
// The Pulley instruction selector.
#[cfg(feature = "pulley")]
IsleCompilation {
name: "pulley".to_string(),
output: gen_dir.join("isle_pulley_shared.rs"),
tracked_inputs: vec![
prelude_isle.clone(),
prelude_lower_isle.clone(),
src_isa_pulley_shared.join("inst.isle"),
src_isa_pulley_shared.join("lower.isle"),
],
untracked_inputs: vec![
numerics_isle.clone(),
pulley_gen.clone(),
clif_lower_isle.clone(),
],
},
],
}
}