Cranelift: do not check in generated ISLE code; regenerate on every compile. (#4143)
This PR fixes #4066: it modifies the Cranelift `build.rs` workflow to invoke the ISLE DSL compiler on every compilation, rather than only when the user specifies a special "rebuild ISLE" feature. The main benefit of this change is that it vastly simplifies the mental model required of developers, and removes a bunch of failure modes we have tried to work around in other ways. There is now just one "source of truth", the ISLE source itself, in the repository, and so there is no need to understand a special "rebuild" step and how to handle merge errors. There is no special process needed to develop the compiler when modifying the DSL. And there is no "noise" in the git history produced by constantly-regenerated files. The two main downsides we discussed in #4066 are: - Compile time could increase, by adding more to the "meta" step before the main build; - It becomes less obvious where the source definitions are (everything becomes more "magic"), which makes exploration and debugging harder. This PR addresses each of these concerns: 1. To maintain reasonable compile time, it includes work to cut down the dependencies of the `cranelift-isle` crate to *nothing* (only the Rust stdlib), in the default build. It does this by putting the error-reporting bits (`miette` crate) under an optional feature, and the logging (`log` crate) under a feature-controlled macro, and manually writing an `Error` impl rather than using `thiserror`. This completely avoids proc macros and the `syn` build slowness. The user can still get nice errors out of `miette`: this is enabled by specifying a Cargo feature `--features isle-errors`. 2. To allow the user to optionally inspect the generated source, which nominally lives in a hard-to-find path inside `target/` now, this PR adds a feature `isle-in-source-tree` that, as implied by the name, moves the target for ISLE generated source into the source tree, at `cranelift/codegen/isle_generated_source/`. It seems reasonable to do this when an explicit feature (opt-in) is specified because this is how ISLE regeneration currently works as well. To prevent surprises, if the feature is *not* specified, the build fails if this directory exists.
This commit is contained in:
@@ -35,8 +35,8 @@ criterion = "0.3"
|
||||
|
||||
[build-dependencies]
|
||||
cranelift-codegen-meta = { path = "meta", version = "0.85.0" }
|
||||
cranelift-isle = { path = "../isle/isle", version = "=0.85.0", optional = true }
|
||||
miette = { version = "3", features = ["fancy"], optional = true }
|
||||
cranelift-isle = { path = "../isle/isle", version = "=0.85.0" }
|
||||
miette = { version = "4.7.0", features = ["fancy"], optional = true }
|
||||
|
||||
[features]
|
||||
default = ["std", "unwind"]
|
||||
@@ -84,12 +84,12 @@ enable-serde = [
|
||||
# Enable support for the Souper harvester.
|
||||
souper-harvest = ["souper-ir", "souper-ir/stringify"]
|
||||
|
||||
# Recompile ISLE DSL source files into their generated Rust code.
|
||||
rebuild-isle = ["cranelift-isle", "miette", "cranelift-codegen-meta/rebuild-isle"]
|
||||
# Provide fancy Miette-produced errors for ISLE.
|
||||
isle-errors = ["miette", "cranelift-isle/miette-errors"]
|
||||
|
||||
# A hack to skip the ISLE-rebuild logic when testing for determinism
|
||||
# with the "Meta deterministic check" CI job.
|
||||
completely-skip-isle-for-ci-deterministic-check = []
|
||||
# Put ISLE generated files in isle_generated_code/, for easier
|
||||
# inspection, rather than inside of target/.
|
||||
isle-in-source-tree = []
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "experimental" }
|
||||
|
||||
@@ -51,11 +51,42 @@ fn main() {
|
||||
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
|
||||
if let Err(err) = meta::generate(&isas, &out_dir, crate_dir) {
|
||||
let explicit_isle_dir = &crate_dir.join("isle_generated_code");
|
||||
#[cfg(feature = "isle-in-source-tree")]
|
||||
let isle_dir = explicit_isle_dir;
|
||||
#[cfg(not(feature = "isle-in-source-tree"))]
|
||||
let isle_dir = std::path::Path::new(&out_dir);
|
||||
|
||||
#[cfg(feature = "isle-in-source-tree")]
|
||||
{
|
||||
std::fs::create_dir_all(isle_dir).expect("Could not create ISLE source directory");
|
||||
}
|
||||
#[cfg(not(feature = "isle-in-source-tree"))]
|
||||
{
|
||||
if explicit_isle_dir.is_dir() {
|
||||
eprintln!(concat!(
|
||||
"Error: directory isle_generated_code/ exists but is only used when\n",
|
||||
"`--feature isle-in-source-tree` is specified. To prevent confusion,\n",
|
||||
"this build script requires the directory to be removed when reverting\n",
|
||||
"to the usual generated code in target/. Please delete the directory and\n",
|
||||
"re-run this build.\n",
|
||||
));
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if let Err(err) = meta::generate(&isas, &out_dir, isle_dir.to_str().unwrap()) {
|
||||
eprintln!("Error: {}", err);
|
||||
process::exit(1);
|
||||
}
|
||||
|
||||
if &std::env::var("SKIP_ISLE").unwrap_or("0".to_string()) != "1" {
|
||||
if let Err(err) = build_isle(crate_dir, isle_dir) {
|
||||
eprintln!("Error: {}", err);
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if env::var("CRANELIFT_VERBOSE").is_ok() {
|
||||
for isa in &isas {
|
||||
println!("cargo:warning=Includes support for {} ISA", isa.to_string());
|
||||
@@ -67,18 +98,6 @@ fn main() {
|
||||
println!("cargo:warning=Generated files are in {}", out_dir);
|
||||
}
|
||||
|
||||
// The "Meta deterministic check" CI job runs this build script N
|
||||
// times to ensure it produces the same output
|
||||
// consistently. However, it runs the script in a fresh directory,
|
||||
// without any of the source tree present; this breaks our
|
||||
// manifest check (we need the ISLE source to be present). To keep
|
||||
// things simple, we just disable all ISLE-related logic for this
|
||||
// specific CI job.
|
||||
#[cfg(not(feature = "completely-skip-isle-for-ci-deterministic-check"))]
|
||||
{
|
||||
maybe_rebuild_isle(crate_dir).expect("Unhandled failure in ISLE rebuild");
|
||||
}
|
||||
|
||||
let pkg_version = env::var("CARGO_PKG_VERSION").unwrap();
|
||||
let mut cmd = std::process::Command::new("git");
|
||||
cmd.arg("rev-parse")
|
||||
@@ -149,71 +168,15 @@ struct IsleCompilation {
|
||||
inputs: Vec<std::path::PathBuf>,
|
||||
}
|
||||
|
||||
impl IsleCompilation {
|
||||
/// Compute the manifest filename for the given generated Rust file.
|
||||
fn manifest_filename(&self) -> std::path::PathBuf {
|
||||
self.output.with_extension("manifest")
|
||||
}
|
||||
|
||||
/// Compute the content of the source manifest for all ISLE source
|
||||
/// files that go into the compilation of one Rust file.
|
||||
///
|
||||
/// We store this alongside the `<generated_filename>.rs` file as
|
||||
/// `<generated_filename>.manifest` and use it to verify that a
|
||||
/// rebuild was done if necessary.
|
||||
fn compute_manifest(&self) -> Result<String, Box<dyn std::error::Error + 'static>> {
|
||||
// We use the deprecated SipHasher from std::hash in order to verify
|
||||
// that ISLE sources haven't changed since the generated source was
|
||||
// last regenerated.
|
||||
//
|
||||
// We use this instead of a stronger and more usual content hash, like
|
||||
// SHA-{160,256,512}, because it's built into the standard library and
|
||||
// we don't want to pull in a separate crate. We try to keep Cranelift
|
||||
// crate dependencies as intentionally small as possible. In fact, we
|
||||
// used to use the `sha2` crate for SHA-512 and this turns out to be
|
||||
// undesirable for downstream consumers (see #3609).
|
||||
//
|
||||
// Why not the recommended replacement
|
||||
// `std::collections::hash_map::DefaultHasher`? Because we need the
|
||||
// hash to be deterministic, both between runs (i.e., not seeded with
|
||||
// random state) and across Rust versions.
|
||||
//
|
||||
// If `SipHasher` is ever actually removed from `std`, we'll need to
|
||||
// find a new option, either a very small crate or something else
|
||||
// that's built-in.
|
||||
#![allow(deprecated)]
|
||||
use std::fmt::Write;
|
||||
use std::hash::{Hasher, SipHasher};
|
||||
|
||||
let mut manifest = String::new();
|
||||
|
||||
for filename in &self.inputs {
|
||||
// Our source must be valid UTF-8 for this to work, else user
|
||||
// will get an error on build. This is not expected to be an
|
||||
// issue.
|
||||
let content = std::fs::read_to_string(filename)?;
|
||||
// On Windows, source is checked out with line-endings changed
|
||||
// to `\r\n`; canonicalize the source that we hash to
|
||||
// Unix-style (`\n`) so hashes will match.
|
||||
let content = content.replace("\r\n", "\n");
|
||||
// One line in the manifest: <filename> <siphash>.
|
||||
let mut hasher = SipHasher::new_with_keys(0, 0); // fixed keys for determinism
|
||||
hasher.write(content.as_bytes());
|
||||
let filename = format!("{}", filename.display()).replace("\\", "/");
|
||||
writeln!(&mut manifest, "{} {:x}", filename, hasher.finish())?;
|
||||
}
|
||||
|
||||
Ok(manifest)
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct the list of compilations (transformations from ISLE
|
||||
/// source to generated Rust source) that exist in the repository.
|
||||
fn get_isle_compilations(crate_dir: &std::path::Path) -> Result<IsleCompilations, std::io::Error> {
|
||||
fn get_isle_compilations(
|
||||
crate_dir: &std::path::Path,
|
||||
out_dir: &std::path::Path,
|
||||
) -> Result<IsleCompilations, std::io::Error> {
|
||||
let cur_dir = std::env::current_dir()?;
|
||||
|
||||
let clif_isle =
|
||||
make_isle_source_path_relative(&cur_dir, crate_dir.join("src").join("clif.isle"));
|
||||
let clif_isle = out_dir.join("clif.isle");
|
||||
let prelude_isle =
|
||||
make_isle_source_path_relative(&cur_dir, crate_dir.join("src").join("prelude.isle"));
|
||||
let src_isa_x64 =
|
||||
@@ -240,10 +203,7 @@ fn get_isle_compilations(crate_dir: &std::path::Path) -> Result<IsleCompilations
|
||||
items: vec![
|
||||
// The x86-64 instruction selector.
|
||||
IsleCompilation {
|
||||
output: src_isa_x64
|
||||
.join("lower")
|
||||
.join("isle")
|
||||
.join("generated_code.rs"),
|
||||
output: out_dir.join("isle_x64.rs"),
|
||||
inputs: vec![
|
||||
clif_isle.clone(),
|
||||
prelude_isle.clone(),
|
||||
@@ -253,10 +213,7 @@ fn get_isle_compilations(crate_dir: &std::path::Path) -> Result<IsleCompilations
|
||||
},
|
||||
// The aarch64 instruction selector.
|
||||
IsleCompilation {
|
||||
output: src_isa_aarch64
|
||||
.join("lower")
|
||||
.join("isle")
|
||||
.join("generated_code.rs"),
|
||||
output: out_dir.join("isle_aarch64.rs"),
|
||||
inputs: vec![
|
||||
clif_isle.clone(),
|
||||
prelude_isle.clone(),
|
||||
@@ -266,10 +223,7 @@ fn get_isle_compilations(crate_dir: &std::path::Path) -> Result<IsleCompilations
|
||||
},
|
||||
// The s390x instruction selector.
|
||||
IsleCompilation {
|
||||
output: src_isa_s390x
|
||||
.join("lower")
|
||||
.join("isle")
|
||||
.join("generated_code.rs"),
|
||||
output: out_dir.join("isle_s390x.rs"),
|
||||
inputs: vec![
|
||||
clif_isle.clone(),
|
||||
prelude_isle.clone(),
|
||||
@@ -281,170 +235,116 @@ fn get_isle_compilations(crate_dir: &std::path::Path) -> Result<IsleCompilations
|
||||
})
|
||||
}
|
||||
|
||||
/// Check the manifest for the ISLE generated code, which documents
|
||||
/// what ISLE source went into generating the Rust, and if there is a
|
||||
/// mismatch, either invoke the ISLE compiler (if we have the
|
||||
/// `rebuild-isle` feature) or exit with an error (if not).
|
||||
///
|
||||
/// We do this by computing a hash of the ISLE source and checking it
|
||||
/// against a "manifest" that is also checked into git, alongside the
|
||||
/// generated Rust.
|
||||
///
|
||||
/// (Why not include the `rebuild-isle` feature by default? Because
|
||||
/// the build process must not modify the checked-in source by
|
||||
/// default; any checked-in source is a human-managed bit of data, and
|
||||
/// we can only act as an agent of the human developer when explicitly
|
||||
/// requested to do so. This manifest check is a middle ground that
|
||||
/// ensures this explicit control while also avoiding the easy footgun
|
||||
/// of "I changed the ISLE, why isn't the compiler updated?!".)
|
||||
fn maybe_rebuild_isle(
|
||||
fn build_isle(
|
||||
crate_dir: &std::path::Path,
|
||||
isle_dir: &std::path::Path,
|
||||
) -> Result<(), Box<dyn std::error::Error + 'static>> {
|
||||
let isle_compilations = get_isle_compilations(crate_dir)?;
|
||||
let mut rebuild_compilations = vec![];
|
||||
let isle_compilations = get_isle_compilations(crate_dir, isle_dir)?;
|
||||
|
||||
let mut had_error = false;
|
||||
for compilation in &isle_compilations.items {
|
||||
for file in &compilation.inputs {
|
||||
println!("cargo:rerun-if-changed={}", file.display());
|
||||
}
|
||||
|
||||
let manifest =
|
||||
std::fs::read_to_string(compilation.manifest_filename()).unwrap_or(String::new());
|
||||
// Canonicalize Windows line-endings into Unix line-endings in
|
||||
// the manifest text itself.
|
||||
let manifest = manifest.replace("\r\n", "\n");
|
||||
let expected_manifest = compilation.compute_manifest()?.replace("\r\n", "\n");
|
||||
if manifest != expected_manifest {
|
||||
rebuild_compilations.push((compilation, expected_manifest));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rebuild-isle")]
|
||||
{
|
||||
if !rebuild_compilations.is_empty() {
|
||||
set_miette_hook();
|
||||
}
|
||||
let mut had_error = false;
|
||||
for (compilation, expected_manifest) in rebuild_compilations {
|
||||
if let Err(e) = rebuild_isle(compilation, &expected_manifest) {
|
||||
eprintln!("Error building ISLE files: {:?}", e);
|
||||
let mut source = e.source();
|
||||
while let Some(e) = source {
|
||||
eprintln!("{:?}", e);
|
||||
source = e.source();
|
||||
}
|
||||
had_error = true;
|
||||
if let Err(e) = run_compilation(compilation) {
|
||||
eprintln!("Error building ISLE files: {:?}", e);
|
||||
let mut source = e.source();
|
||||
while let Some(e) = source {
|
||||
eprintln!("{:?}", e);
|
||||
source = e.source();
|
||||
}
|
||||
}
|
||||
|
||||
if had_error {
|
||||
std::process::exit(1);
|
||||
had_error = true;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "rebuild-isle"))]
|
||||
{
|
||||
if !rebuild_compilations.is_empty() {
|
||||
for (compilation, _) in rebuild_compilations {
|
||||
eprintln!("");
|
||||
eprintln!(
|
||||
"Error: the ISLE source files that resulted in the generated Rust source"
|
||||
);
|
||||
eprintln!("");
|
||||
eprintln!(" * {}", compilation.output.display());
|
||||
eprintln!("");
|
||||
eprintln!(
|
||||
"have changed but the generated source was not rebuilt! These ISLE source"
|
||||
);
|
||||
eprintln!("files are:");
|
||||
eprintln!("");
|
||||
for file in &compilation.inputs {
|
||||
eprintln!(" * {}", file.display());
|
||||
}
|
||||
}
|
||||
|
||||
eprintln!("");
|
||||
eprintln!("Please add `--features rebuild-isle` to your `cargo build` command");
|
||||
eprintln!("if you wish to rebuild the generated source, then include these changes");
|
||||
eprintln!("in any git commits you make that include the changes to the ISLE.");
|
||||
eprintln!("");
|
||||
eprintln!("For example:");
|
||||
eprintln!("");
|
||||
eprintln!(" $ cargo build -p cranelift-codegen --features rebuild-isle");
|
||||
eprintln!("");
|
||||
eprintln!("(This build script cannot do this for you by default because we cannot");
|
||||
eprintln!("modify checked-into-git source without your explicit opt-in.)");
|
||||
eprintln!("");
|
||||
std::process::exit(1);
|
||||
}
|
||||
if had_error {
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
println!("cargo:rustc-env=ISLE_DIR={}", isle_dir.to_str().unwrap());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "rebuild-isle")]
|
||||
fn set_miette_hook() {
|
||||
use std::sync::Once;
|
||||
static SET_MIETTE_HOOK: Once = Once::new();
|
||||
SET_MIETTE_HOOK.call_once(|| {
|
||||
let _ = miette::set_hook(Box::new(|_| {
|
||||
Box::new(
|
||||
miette::MietteHandlerOpts::new()
|
||||
// This is necessary for `miette` to properly display errors
|
||||
// until https://github.com/zkat/miette/issues/93 is fixed.
|
||||
.force_graphical(true)
|
||||
.build(),
|
||||
)
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
/// Rebuild ISLE DSL source text into generated Rust code.
|
||||
/// Build ISLE DSL source text into generated Rust code.
|
||||
///
|
||||
/// NB: This must happen *after* the `cranelift-codegen-meta` functions, since
|
||||
/// it consumes files generated by them.
|
||||
#[cfg(feature = "rebuild-isle")]
|
||||
fn rebuild_isle(
|
||||
fn run_compilation(
|
||||
compilation: &IsleCompilation,
|
||||
manifest: &str,
|
||||
) -> Result<(), Box<dyn std::error::Error + 'static>> {
|
||||
use cranelift_isle as isle;
|
||||
|
||||
println!("Rebuilding {}", compilation.output.display());
|
||||
|
||||
// First, remove the manifest, if any; we will recreate it
|
||||
// below if the compilation is successful. Ignore error if no
|
||||
// manifest was present.
|
||||
let manifest_filename = compilation.manifest_filename();
|
||||
let _ = std::fs::remove_file(&manifest_filename);
|
||||
|
||||
let code = (|| {
|
||||
let lexer = isle::lexer::Lexer::from_files(&compilation.inputs[..])?;
|
||||
let defs = isle::parser::parse(lexer)?;
|
||||
isle::compile::compile(&defs)
|
||||
|
||||
let mut options = isle::codegen::CodegenOptions::default();
|
||||
// Because we include!() the generated ISLE source, we cannot
|
||||
// put the global pragmas (`#![allow(...)]`) in the ISLE
|
||||
// source itself; we have to put them in the source that
|
||||
// include!()s it. (See
|
||||
// https://github.com/rust-lang/rust/issues/47995.)
|
||||
options.exclude_global_allow_pragmas = true;
|
||||
|
||||
isle::compile::compile(&defs, &options)
|
||||
})()
|
||||
.map_err(|e| {
|
||||
// Make sure to include the source snippets location info along with
|
||||
// the error messages.
|
||||
|
||||
let report = miette::Report::new(e);
|
||||
return DebugReport(report);
|
||||
#[cfg(feature = "isle-errors")]
|
||||
{
|
||||
let report = miette::Report::new(e);
|
||||
return DebugReport(report);
|
||||
|
||||
struct DebugReport(miette::Report);
|
||||
struct DebugReport(miette::Report);
|
||||
|
||||
impl std::fmt::Display for DebugReport {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
self.0.handler().debug(&*self.0, f)
|
||||
impl std::fmt::Display for DebugReport {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
self.0.handler().debug(&*self.0, f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for DebugReport {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
std::fmt::Display::fmt(self, f)
|
||||
impl std::fmt::Debug for DebugReport {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
std::fmt::Display::fmt(self, f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for DebugReport {}
|
||||
impl std::error::Error for DebugReport {}
|
||||
}
|
||||
#[cfg(not(feature = "isle-errors"))]
|
||||
{
|
||||
return DebugReport(format!("{}", e));
|
||||
|
||||
struct DebugReport(String);
|
||||
|
||||
impl std::fmt::Display for DebugReport {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
writeln!(f, "ISLE errors:\n\n{}\n", self.0)?;
|
||||
writeln!(f, "To see a more detailed error report, run: ")?;
|
||||
writeln!(f, "")?;
|
||||
writeln!(
|
||||
f,
|
||||
" $ cargo check -p cranelift-codegen --features isle-errors"
|
||||
)?;
|
||||
writeln!(f, "")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for DebugReport {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
std::fmt::Display::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for DebugReport {}
|
||||
}
|
||||
})?;
|
||||
|
||||
let code = rustfmt(&code).unwrap_or_else(|e| {
|
||||
@@ -461,39 +361,32 @@ fn rebuild_isle(
|
||||
);
|
||||
std::fs::write(&compilation.output, code)?;
|
||||
|
||||
// Write the manifest so that, in the default build configuration
|
||||
// without the `rebuild-isle` feature, we can at least verify that
|
||||
// no changes were made that will not be picked up. Note that we
|
||||
// only write this *after* we write the source above, so no
|
||||
// manifest is produced if there was an error.
|
||||
std::fs::write(&manifest_filename, manifest)?;
|
||||
|
||||
return Ok(());
|
||||
|
||||
fn rustfmt(code: &str) -> std::io::Result<String> {
|
||||
use std::io::Write;
|
||||
|
||||
let mut rustfmt = std::process::Command::new("rustfmt")
|
||||
.stdin(std::process::Stdio::piped())
|
||||
.stdout(std::process::Stdio::piped())
|
||||
.spawn()?;
|
||||
|
||||
let mut stdin = rustfmt.stdin.take().unwrap();
|
||||
stdin.write_all(code.as_bytes())?;
|
||||
drop(stdin);
|
||||
|
||||
let mut stdout = rustfmt.stdout.take().unwrap();
|
||||
let mut data = vec![];
|
||||
stdout.read_to_end(&mut data)?;
|
||||
|
||||
let status = rustfmt.wait()?;
|
||||
if !status.success() {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
format!("`rustfmt` exited with status {}", status),
|
||||
));
|
||||
}
|
||||
|
||||
Ok(String::from_utf8(data).expect("rustfmt always writs utf-8 to stdout"))
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn rustfmt(code: &str) -> std::io::Result<String> {
|
||||
use std::io::Write;
|
||||
|
||||
let mut rustfmt = std::process::Command::new("rustfmt")
|
||||
.stdin(std::process::Stdio::piped())
|
||||
.stdout(std::process::Stdio::piped())
|
||||
.spawn()?;
|
||||
|
||||
let mut stdin = rustfmt.stdin.take().unwrap();
|
||||
stdin.write_all(code.as_bytes())?;
|
||||
drop(stdin);
|
||||
|
||||
let mut stdout = rustfmt.stdout.take().unwrap();
|
||||
let mut data = vec![];
|
||||
stdout.read_to_end(&mut data)?;
|
||||
|
||||
let status = rustfmt.wait()?;
|
||||
if !status.success() {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
format!("`rustfmt` exited with status {}", status),
|
||||
));
|
||||
}
|
||||
|
||||
Ok(String::from_utf8(data).expect("rustfmt always writs utf-8 to stdout"))
|
||||
}
|
||||
|
||||
@@ -17,6 +17,3 @@ cranelift-codegen-shared = { path = "../shared", version = "0.85.0" }
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "experimental" }
|
||||
|
||||
[features]
|
||||
rebuild-isle = []
|
||||
|
||||
@@ -113,7 +113,6 @@ pub(crate) enum OperandKindFields {
|
||||
|
||||
impl OperandKindFields {
|
||||
/// Return the [EnumValues] for this field if it is an `enum`.
|
||||
#[cfg(feature = "rebuild-isle")]
|
||||
pub(crate) fn enum_values(&self) -> Option<&EnumValues> {
|
||||
match self {
|
||||
OperandKindFields::ImmEnum(map) => Some(map),
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
//! Generate instruction data (including opcodes, formats, builders, etc.).
|
||||
use std::fmt;
|
||||
use std::path::Path;
|
||||
|
||||
use cranelift_codegen_shared::constant_hash;
|
||||
|
||||
@@ -1085,7 +1084,6 @@ fn gen_inst_builder(inst: &Instruction, format: &InstructionFormat, fmt: &mut Fo
|
||||
fmtln!(fmt, "}")
|
||||
}
|
||||
|
||||
#[cfg(feature = "rebuild-isle")]
|
||||
fn gen_isle(formats: &[&InstructionFormat], instructions: &AllInstructions, fmt: &mut Formatter) {
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::fmt::Write;
|
||||
@@ -1341,7 +1339,6 @@ fn gen_isle(formats: &[&InstructionFormat], instructions: &AllInstructions, fmt:
|
||||
}
|
||||
|
||||
/// Generate an `enum` immediate in ISLE.
|
||||
#[cfg(feature = "rebuild-isle")]
|
||||
fn gen_isle_enum(name: &str, mut variants: Vec<&str>, fmt: &mut Formatter) {
|
||||
variants.sort();
|
||||
let prefix = format!(";;;; Enumerated Immediate: {} ", name);
|
||||
@@ -1407,7 +1404,7 @@ pub(crate) fn generate(
|
||||
inst_builder_filename: &str,
|
||||
isle_filename: &str,
|
||||
out_dir: &str,
|
||||
crate_dir: &Path,
|
||||
isle_dir: &str,
|
||||
) -> Result<(), error::Error> {
|
||||
// Opcodes.
|
||||
let mut fmt = Formatter::new();
|
||||
@@ -1424,18 +1421,9 @@ pub(crate) fn generate(
|
||||
fmt.update_file(opcode_filename, out_dir)?;
|
||||
|
||||
// ISLE DSL.
|
||||
#[cfg(feature = "rebuild-isle")]
|
||||
{
|
||||
let mut fmt = Formatter::new();
|
||||
gen_isle(&formats, all_inst, &mut fmt);
|
||||
let crate_src_dir = crate_dir.join("src");
|
||||
fmt.update_file(isle_filename, &crate_src_dir.display().to_string())?;
|
||||
}
|
||||
#[cfg(not(feature = "rebuild-isle"))]
|
||||
{
|
||||
// Silence unused variable warnings.
|
||||
let _ = (isle_filename, crate_dir);
|
||||
}
|
||||
let mut fmt = Formatter::new();
|
||||
gen_isle(&formats, all_inst, &mut fmt);
|
||||
fmt.update_file(isle_filename, isle_dir)?;
|
||||
|
||||
// Instruction builder.
|
||||
let mut fmt = Formatter::new();
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
//! This crate generates Rust sources for use by
|
||||
//! [`cranelift_codegen`](../cranelift_codegen/index.html).
|
||||
|
||||
use std::path::Path;
|
||||
#[macro_use]
|
||||
mod cdsl;
|
||||
mod srcgen;
|
||||
@@ -23,7 +22,7 @@ pub fn isa_from_arch(arch: &str) -> Result<isa::Isa, String> {
|
||||
}
|
||||
|
||||
/// Generates all the Rust source files used in Cranelift from the meta-language.
|
||||
pub fn generate(isas: &[isa::Isa], out_dir: &str, crate_dir: &Path) -> Result<(), error::Error> {
|
||||
pub fn generate(isas: &[isa::Isa], out_dir: &str, isle_dir: &str) -> Result<(), error::Error> {
|
||||
// Create all the definitions:
|
||||
// - common definitions.
|
||||
let mut shared_defs = shared::define();
|
||||
@@ -50,7 +49,7 @@ pub fn generate(isas: &[isa::Isa], out_dir: &str, crate_dir: &Path) -> Result<()
|
||||
"inst_builder.rs",
|
||||
"clif.isle",
|
||||
&out_dir,
|
||||
crate_dir,
|
||||
isle_dir,
|
||||
)?;
|
||||
|
||||
for isa in target_isas {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +0,0 @@
|
||||
src/clif.isle 443b34b797fc8ace
|
||||
src/prelude.isle e6c91b0115343ab9
|
||||
src/isa/aarch64/inst.isle 21a43af20be377d2
|
||||
src/isa/aarch64/lower.isle 75ad8450963e3829
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +0,0 @@
|
||||
src/clif.isle 443b34b797fc8ace
|
||||
src/prelude.isle e6c91b0115343ab9
|
||||
src/isa/s390x/inst.isle 36c2500563cdd4e6
|
||||
src/isa/s390x/lower.isle e5c946ab8a265b77
|
||||
13835
cranelift/codegen/src/isa/s390x/lower/isle/generated_code.rs
generated
13835
cranelift/codegen/src/isa/s390x/lower/isle/generated_code.rs
generated
File diff suppressed because it is too large
Load Diff
@@ -1,4 +0,0 @@
|
||||
src/clif.isle 443b34b797fc8ace
|
||||
src/prelude.isle e6c91b0115343ab9
|
||||
src/isa/x64/inst.isle 833710d359126637
|
||||
src/isa/x64/lower.isle 4c567e9157f84afb
|
||||
12337
cranelift/codegen/src/isa/x64/lower/isle/generated_code.rs
generated
12337
cranelift/codegen/src/isa/x64/lower/isle/generated_code.rs
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user