Skip to main content

cranelift_codegen_meta/
isle.rs

1use std::io::Result;
2
3/// A list of compilations (transformations from ISLE source to
4/// generated Rust source) that exist in the repository.
5///
6/// This list is used either to regenerate the Rust source in-tree (if
7/// the `rebuild-isle` feature is enabled), or to verify that the ISLE
8/// source in-tree corresponds to the ISLE source that was last used
9/// to rebuild the Rust source (if the `rebuild-isle` feature is not
10/// enabled).
11#[derive(Clone, Debug)]
12pub struct IsleCompilations {
13    pub items: Vec<IsleCompilation>,
14}
15
16impl IsleCompilations {
17    pub fn lookup(&self, name: &str) -> Option<&IsleCompilation> {
18        for compilation in &self.items {
19            if compilation.name == name {
20                return Some(compilation);
21            }
22        }
23        None
24    }
25}
26
27#[derive(Clone, Debug)]
28pub struct IsleCompilation {
29    pub name: String,
30    pub output: std::path::PathBuf,
31    pub tracked_inputs: Vec<std::path::PathBuf>,
32    pub untracked_inputs: Vec<std::path::PathBuf>,
33}
34
35impl IsleCompilation {
36    /// All inputs to the computation, tracked or untracked. May contain directories.
37    pub fn inputs(&self) -> Vec<std::path::PathBuf> {
38        self.tracked_inputs
39            .iter()
40            .chain(self.untracked_inputs.iter())
41            .cloned()
42            .collect()
43    }
44
45    /// All path inputs to the compilation. Directory inputs are expanded to the
46    /// list of all ISLE files in the directory.
47    pub fn paths(&self) -> Result<Vec<std::path::PathBuf>> {
48        let mut paths = Vec::new();
49        for input in self.inputs() {
50            paths.extend(Self::expand_paths(&input)?);
51        }
52        Ok(paths)
53    }
54
55    fn expand_paths(input: &std::path::PathBuf) -> Result<Vec<std::path::PathBuf>> {
56        if input.is_file() {
57            return Ok(vec![input.clone()]);
58        }
59
60        if !input.exists() {
61            return Err(std::io::Error::new(
62                std::io::ErrorKind::NotFound,
63                format!("ISLE input does not exist: {}", input.display()),
64            ));
65        }
66
67        let mut paths = Vec::new();
68        for entry in std::fs::read_dir(input).map_err(|e| {
69            std::io::Error::new(
70                e.kind(),
71                format!(
72                    "failed to read ISLE input directory {}: {e}",
73                    input.display()
74                ),
75            )
76        })? {
77            let path = entry?.path();
78            if let Some(ext) = path.extension() {
79                if ext == "isle" {
80                    paths.push(path);
81                }
82            }
83        }
84        Ok(paths)
85    }
86}
87
88pub fn shared_isle_lower_paths(codegen_crate_dir: &std::path::Path) -> Vec<std::path::PathBuf> {
89    let inst_specs_isle = codegen_crate_dir.join("src").join("inst_specs.isle");
90    let prelude_isle = codegen_crate_dir.join("src").join("prelude.isle");
91    let prelude_lower_isle = codegen_crate_dir.join("src").join("prelude_lower.isle");
92    // The shared instruction selector logic.
93    vec![
94        inst_specs_isle.clone(),
95        prelude_isle.clone(),
96        prelude_lower_isle.clone(),
97    ]
98}
99
100/// Construct the list of compilations (transformations from ISLE
101/// source to generated Rust source) that exist in the repository.
102pub fn get_isle_compilations(
103    codegen_crate_dir: &std::path::Path,
104    gen_dir: &std::path::Path,
105) -> IsleCompilations {
106    // Preludes.
107    let numerics_isle = gen_dir.join("numerics.isle");
108    let clif_lower_isle = gen_dir.join("clif_lower.isle");
109    let clif_opt_isle = gen_dir.join("clif_opt.isle");
110    let prelude_isle = codegen_crate_dir.join("src").join("prelude.isle");
111    let prelude_opt_isle = codegen_crate_dir.join("src").join("prelude_opt.isle");
112    let prelude_lower_isle = codegen_crate_dir.join("src").join("prelude_lower.isle");
113    #[cfg(feature = "pulley")]
114    let pulley_gen = gen_dir.join("pulley_gen.isle");
115
116    // Verification spec source files. These define the instruction
117    // semantics consumed by the ISLE verifier and are only needed
118    // when building the verifier tooling (the `spec` feature). They
119    // are excluded from normal codegen builds.
120    let spec_inputs = |extra: &[&str]| -> Vec<std::path::PathBuf> {
121        if !cfg!(feature = "spec") {
122            return vec![];
123        }
124        let spec_dir = codegen_crate_dir.join("src").join("spec");
125        let mut inputs = vec![
126            spec_dir.join("prelude_spec.isle"),
127            spec_dir.join("inst_specs.isle"),
128            spec_dir.join("inst_tags.isle"),
129        ];
130        inputs.extend(extra.iter().map(|f| spec_dir.join(f)));
131        inputs
132    };
133    let lower_spec_inputs = |extra: &[&str]| -> Vec<std::path::PathBuf> {
134        let mut inputs = spec_inputs(extra);
135        if cfg!(feature = "spec") {
136            let spec_dir = codegen_crate_dir.join("src").join("spec");
137            inputs.push(spec_dir.join("prelude_lower_spec.isle"));
138        }
139        inputs
140    };
141
142    // Directory for mid-end optimizations.
143    let src_opts = codegen_crate_dir.join("src").join("opts");
144
145    // Directories for lowering backends.
146    let src_isa_x64 = codegen_crate_dir.join("src").join("isa").join("x64");
147    let src_isa_aarch64 = codegen_crate_dir.join("src").join("isa").join("aarch64");
148    let src_isa_s390x = codegen_crate_dir.join("src").join("isa").join("s390x");
149    let src_isa_risc_v = codegen_crate_dir.join("src").join("isa").join("riscv64");
150    #[cfg(feature = "pulley")]
151    let src_isa_pulley_shared = codegen_crate_dir
152        .join("src")
153        .join("isa")
154        .join("pulley_shared");
155
156    // This is a set of ISLE compilation units.
157    //
158    // The format of each entry is:
159    //
160    //     (output Rust code file, input ISLE source files)
161    //
162    // There should be one entry for each backend that uses ISLE for lowering,
163    // and if/when we replace our peephole optimization passes with ISLE, there
164    // should be an entry for each of those as well.
165    //
166    // N.B.: add any new compilation outputs to
167    // `scripts/force-rebuild-isle.sh` if they do not fit the pattern
168    // `cranelift/codegen/src/isa/*/lower/isle/generated_code.rs`!
169    IsleCompilations {
170        items: vec![
171            // The mid-end optimization rules.
172            IsleCompilation {
173                name: "opt".to_string(),
174                output: gen_dir.join("isle_opt.rs"),
175                tracked_inputs: [
176                    vec![prelude_isle.clone(), prelude_opt_isle],
177                    spec_inputs(&["fpconst.isle", "opt.isle"]),
178                    vec![
179                        src_opts.join("arithmetic.isle"),
180                        src_opts.join("bitops.isle"),
181                        src_opts.join("cprop.isle"),
182                        src_opts.join("extends.isle"),
183                        src_opts.join("icmp.isle"),
184                        src_opts.join("remat.isle"),
185                        src_opts.join("selects.isle"),
186                        src_opts.join("shifts.isle"),
187                        src_opts.join("skeleton.isle"),
188                        src_opts.join("spaceship.isle"),
189                        src_opts.join("spectre.isle"),
190                        src_opts.join("vector.isle"),
191                    ],
192                ]
193                .concat(),
194                untracked_inputs: vec![numerics_isle.clone(), clif_opt_isle],
195            },
196            // The x86-64 instruction selector.
197            IsleCompilation {
198                name: "x64".to_string(),
199                output: gen_dir.join("isle_x64.rs"),
200                tracked_inputs: [
201                    vec![prelude_isle.clone(), prelude_lower_isle.clone()],
202                    lower_spec_inputs(&["fpconst.isle", "state.isle"]),
203                    vec![
204                        src_isa_x64.join("inst.isle"),
205                        src_isa_x64.join("lower.isle"),
206                    ],
207                ]
208                .concat(),
209                untracked_inputs: vec![
210                    numerics_isle.clone(),
211                    clif_lower_isle.clone(),
212                    gen_dir.join("assembler.isle"),
213                ],
214            },
215            // The aarch64 instruction selector.
216            IsleCompilation {
217                name: "aarch64".to_string(),
218                output: gen_dir.join("isle_aarch64.rs"),
219                tracked_inputs: [
220                    vec![prelude_isle.clone(), prelude_lower_isle.clone()],
221                    lower_spec_inputs(&["fpconst.isle", "state.isle"]),
222                    vec![
223                        src_isa_aarch64.join("inst.isle"),
224                        src_isa_aarch64.join("inst_neon.isle"),
225                    ],
226                    // The aarch64-specific spec directory is also verification-only.
227                    if cfg!(feature = "spec") {
228                        vec![src_isa_aarch64.join("spec")]
229                    } else {
230                        vec![]
231                    },
232                    vec![
233                        src_isa_aarch64.join("lower.isle"),
234                        src_isa_aarch64.join("lower_dynamic_neon.isle"),
235                    ],
236                ]
237                .concat(),
238                untracked_inputs: vec![numerics_isle.clone(), clif_lower_isle.clone()],
239            },
240            // The s390x instruction selector.
241            IsleCompilation {
242                name: "s390x".to_string(),
243                output: gen_dir.join("isle_s390x.rs"),
244                tracked_inputs: [
245                    vec![prelude_isle.clone(), prelude_lower_isle.clone()],
246                    lower_spec_inputs(&[]),
247                    vec![
248                        src_isa_s390x.join("inst.isle"),
249                        src_isa_s390x.join("lower.isle"),
250                    ],
251                ]
252                .concat(),
253                untracked_inputs: vec![numerics_isle.clone(), clif_lower_isle.clone()],
254            },
255            // The risc-v instruction selector.
256            IsleCompilation {
257                name: "riscv64".to_string(),
258                output: gen_dir.join("isle_riscv64.rs"),
259                tracked_inputs: [
260                    vec![prelude_isle.clone(), prelude_lower_isle.clone()],
261                    lower_spec_inputs(&[]),
262                    vec![
263                        src_isa_risc_v.join("inst.isle"),
264                        src_isa_risc_v.join("inst_vector.isle"),
265                        src_isa_risc_v.join("lower.isle"),
266                    ],
267                ]
268                .concat(),
269                untracked_inputs: vec![numerics_isle.clone(), clif_lower_isle.clone()],
270            },
271            // The Pulley instruction selector.
272            #[cfg(feature = "pulley")]
273            IsleCompilation {
274                name: "pulley".to_string(),
275                output: gen_dir.join("isle_pulley_shared.rs"),
276                tracked_inputs: vec![
277                    prelude_isle.clone(),
278                    prelude_lower_isle.clone(),
279                    src_isa_pulley_shared.join("inst.isle"),
280                    src_isa_pulley_shared.join("lower.isle"),
281                ],
282                untracked_inputs: vec![
283                    numerics_isle.clone(),
284                    pulley_gen.clone(),
285                    clif_lower_isle.clone(),
286                ],
287            },
288        ],
289    }
290}