summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--.gitmodules4
-rw-r--r--config.example.toml3
-rwxr-xr-xsrc/bootstrap/configure.py1
-rw-r--r--src/bootstrap/src/core/build_steps/compile.rs22
-rw-r--r--src/bootstrap/src/core/build_steps/llvm.rs95
-rw-r--r--src/bootstrap/src/core/builder.rs7
-rw-r--r--src/bootstrap/src/core/config/config.rs8
-rw-r--r--src/bootstrap/src/lib.rs10
-rw-r--r--src/bootstrap/src/utils/change_tracker.rs5
m---------src/tools/enzyme0
-rw-r--r--src/tools/tidy/config/black.toml1
-rw-r--r--src/tools/tidy/config/ruff.toml2
-rw-r--r--src/tools/tidy/src/walk.rs1
13 files changed, 159 insertions, 0 deletions
diff --git a/.gitmodules b/.gitmodules
index b5250d49386..926807336d7 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -47,3 +47,7 @@
 	path = src/tools/rustc-perf
 	url = https://github.com/rust-lang/rustc-perf.git
 	shallow = true
+[submodule "src/tools/enzyme"]
+	path = src/tools/enzyme
+	url = https://github.com/EnzymeAD/Enzyme.git
+	shallow = true
diff --git a/config.example.toml b/config.example.toml
index 13f76933b16..e9433c9c9bd 100644
--- a/config.example.toml
+++ b/config.example.toml
@@ -78,6 +78,9 @@
 # Indicates whether the LLVM plugin is enabled or not
 #plugins = false
 
+# Wheter to build Enzyme as AutoDiff backend.
+#enzyme = false
+
 # Indicates whether ccache is used when building LLVM. Set to `true` to use the first `ccache` in
 # PATH, or set an absolute path to use a specific version.
 #ccache = false
diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py
index 768aac912ce..49d564642bd 100755
--- a/src/bootstrap/configure.py
+++ b/src/bootstrap/configure.py
@@ -71,6 +71,7 @@ v("llvm-libunwind", "rust.llvm-libunwind", "use LLVM libunwind")
 # channel, etc.
 o("optimize-llvm", "llvm.optimize", "build optimized LLVM")
 o("llvm-assertions", "llvm.assertions", "build LLVM with assertions")
+o("llvm-enzyme", "llvm.enzyme", "build LLVM with enzyme")
 o("llvm-plugins", "llvm.plugins", "build LLVM with plugin interface")
 o("debug-assertions", "rust.debug-assertions", "build with debugging assertions")
 o("debug-assertions-std", "rust.debug-assertions-std", "build the standard library with debugging assertions")
diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs
index eef548033f1..8467f2e2111 100644
--- a/src/bootstrap/src/core/build_steps/compile.rs
+++ b/src/bootstrap/src/core/build_steps/compile.rs
@@ -1189,6 +1189,10 @@ pub fn rustc_cargo_env(
         cargo.env("RUSTC_VERIFY_LLVM_IR", "1");
     }
 
+    if builder.config.llvm_enzyme {
+        cargo.rustflag("--cfg=llvm_enzyme");
+    }
+
     // Note that this is disabled if LLVM itself is disabled or we're in a check
     // build. If we are in a check build we still go ahead here presuming we've
     // detected that LLVM is already built and good to go which helps prevent
@@ -1784,6 +1788,24 @@ impl Step for Assemble {
         //        use that to bootstrap this compiler forward.
         let mut build_compiler = builder.compiler(target_compiler.stage - 1, builder.config.build);
 
+        // Build enzyme
+        let enzyme_install = if builder.config.llvm_enzyme {
+            Some(builder.ensure(llvm::Enzyme { target: build_compiler.host }))
+        } else {
+            None
+        };
+
+        if let Some(enzyme_install) = enzyme_install {
+            let lib_ext = std::env::consts::DLL_EXTENSION;
+            let src_lib = enzyme_install.join("build/Enzyme/libEnzyme-19").with_extension(lib_ext);
+            let libdir = builder.sysroot_libdir(build_compiler, build_compiler.host);
+            let target_libdir = builder.sysroot_libdir(target_compiler, target_compiler.host);
+            let dst_lib = libdir.join("libEnzyme-19").with_extension(lib_ext);
+            let target_dst_lib = target_libdir.join("libEnzyme-19").with_extension(lib_ext);
+            builder.copy_link(&src_lib, &dst_lib);
+            builder.copy_link(&src_lib, &target_dst_lib);
+        }
+
         // Build the libraries for this compiler to link to (i.e., the libraries
         // it uses at runtime). NOTE: Crates the target compiler compiles don't
         // link to these. (FIXME: Is that correct? It seems to be correct most
diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs
index e1eea31b3bb..442638d3203 100644
--- a/src/bootstrap/src/core/build_steps/llvm.rs
+++ b/src/bootstrap/src/core/build_steps/llvm.rs
@@ -529,6 +529,7 @@ impl Step for Llvm {
             }
         };
 
+        // FIXME(ZuseZ4): Do we need that for Enzyme too?
         // When building LLVM with LLVM_LINK_LLVM_DYLIB for macOS, an unversioned
         // libLLVM.dylib will be built. However, llvm-config will still look
         // for a versioned path like libLLVM-14.dylib. Manually create a symbolic
@@ -849,6 +850,100 @@ fn get_var(var_base: &str, host: &str, target: &str) -> Option<OsString> {
         .or_else(|| env::var_os(var_base))
 }
 
+#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
+pub struct Enzyme {
+    pub target: TargetSelection,
+}
+
+impl Step for Enzyme {
+    type Output = PathBuf;
+    const ONLY_HOSTS: bool = true;
+
+    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
+        run.path("src/tools/enzyme/enzyme")
+    }
+
+    fn make_run(run: RunConfig<'_>) {
+        run.builder.ensure(Enzyme { target: run.target });
+    }
+
+    /// Compile Enzyme for `target`.
+    fn run(self, builder: &Builder<'_>) -> PathBuf {
+        builder.require_submodule(
+            "src/tools/enzyme",
+            Some("The Enzyme sources are required for autodiff."),
+        );
+        if builder.config.dry_run() {
+            let out_dir = builder.enzyme_out(self.target);
+            return out_dir;
+        }
+        let target = self.target;
+
+        let LlvmResult { llvm_config, .. } = builder.ensure(Llvm { target: self.target });
+
+        static STAMP_HASH_MEMO: OnceLock<String> = OnceLock::new();
+        let smart_stamp_hash = STAMP_HASH_MEMO.get_or_init(|| {
+            generate_smart_stamp_hash(
+                builder,
+                &builder.config.src.join("src/tools/enzyme"),
+                builder.enzyme_info.sha().unwrap_or_default(),
+            )
+        });
+
+        let out_dir = builder.enzyme_out(target);
+        let stamp = out_dir.join("enzyme-finished-building");
+        let stamp = HashStamp::new(stamp, Some(smart_stamp_hash));
+
+        if stamp.is_done() {
+            if stamp.hash.is_none() {
+                builder.info(
+                    "Could not determine the Enzyme submodule commit hash. \
+                     Assuming that an Enzyme rebuild is not necessary.",
+                );
+                builder.info(&format!(
+                    "To force Enzyme to rebuild, remove the file `{}`",
+                    stamp.path.display()
+                ));
+            }
+            return out_dir;
+        }
+
+        builder.info(&format!("Building Enzyme for {}", target));
+        t!(stamp.remove());
+        let _time = helpers::timeit(builder);
+        t!(fs::create_dir_all(&out_dir));
+
+        builder
+            .config
+            .update_submodule(Path::new("src").join("tools").join("enzyme").to_str().unwrap());
+        let mut cfg = cmake::Config::new(builder.src.join("src/tools/enzyme/enzyme/"));
+        // FIXME(ZuseZ4): Find a nicer way to use Enzyme Debug builds
+        //cfg.profile("Debug");
+        //cfg.define("CMAKE_BUILD_TYPE", "Debug");
+        configure_cmake(builder, target, &mut cfg, true, LdFlags::default(), &[]);
+
+        // Re-use the same flags as llvm to control the level of debug information
+        // generated for lld.
+        let profile = match (builder.config.llvm_optimize, builder.config.llvm_release_debuginfo) {
+            (false, _) => "Debug",
+            (true, false) => "Release",
+            (true, true) => "RelWithDebInfo",
+        };
+
+        cfg.out_dir(&out_dir)
+            .profile(profile)
+            .env("LLVM_CONFIG_REAL", &llvm_config)
+            .define("LLVM_ENABLE_ASSERTIONS", "ON")
+            .define("ENZYME_EXTERNAL_SHARED_LIB", "ON")
+            .define("LLVM_DIR", builder.llvm_out(target));
+
+        cfg.build();
+
+        t!(stamp.write());
+        out_dir
+    }
+}
+
 #[derive(Debug, Clone, Hash, PartialEq, Eq)]
 pub struct Lld {
     pub target: TargetSelection,
diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs
index 304fe8da2bf..a28b19d33a6 100644
--- a/src/bootstrap/src/core/builder.rs
+++ b/src/bootstrap/src/core/builder.rs
@@ -798,6 +798,7 @@ impl<'a> Builder<'a> {
                 tool::Miri,
                 tool::CargoMiri,
                 llvm::Lld,
+                llvm::Enzyme,
                 llvm::CrtBeginEnd,
                 tool::RustdocGUITest,
                 tool::OptimizedDist,
@@ -1588,6 +1589,12 @@ impl<'a> Builder<'a> {
             rustflags.arg(sysroot_str);
         }
 
+        // https://rust-lang.zulipchat.com/#narrow/stream/182449-t-compiler.2Fhelp/topic/.E2.9C.94.20link.20new.20library.20into.20stage1.2Frustc
+        if self.config.llvm_enzyme {
+            rustflags.arg("-l");
+            rustflags.arg("Enzyme-19");
+        }
+
         let use_new_symbol_mangling = match self.config.rust_new_symbol_mangling {
             Some(setting) => {
                 // If an explicit setting is given, use that
diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs
index f7cc9ee7219..52c1c462788 100644
--- a/src/bootstrap/src/core/config/config.rs
+++ b/src/bootstrap/src/core/config/config.rs
@@ -221,6 +221,7 @@ pub struct Config {
     // llvm codegen options
     pub llvm_assertions: bool,
     pub llvm_tests: bool,
+    pub llvm_enzyme: bool,
     pub llvm_plugins: bool,
     pub llvm_optimize: bool,
     pub llvm_thin_lto: bool,
@@ -898,6 +899,7 @@ define_config! {
         release_debuginfo: Option<bool> = "release-debuginfo",
         assertions: Option<bool> = "assertions",
         tests: Option<bool> = "tests",
+        enzyme: Option<bool> = "enzyme",
         plugins: Option<bool> = "plugins",
         ccache: Option<StringOrBool> = "ccache",
         static_libstdcpp: Option<bool> = "static-libstdcpp",
@@ -1603,6 +1605,7 @@ impl Config {
         // we'll infer default values for them later
         let mut llvm_assertions = None;
         let mut llvm_tests = None;
+        let mut llvm_enzyme = None;
         let mut llvm_plugins = None;
         let mut debug = None;
         let mut debug_assertions = None;
@@ -1722,6 +1725,8 @@ impl Config {
             config.llvm_tools_enabled = llvm_tools.unwrap_or(true);
             config.rustc_parallel =
                 parallel_compiler.unwrap_or(config.channel == "dev" || config.channel == "nightly");
+            config.llvm_enzyme =
+                llvm_enzyme.unwrap_or(config.channel == "dev" || config.channel == "nightly");
             config.rustc_default_linker = default_linker;
             config.musl_root = musl_root.map(PathBuf::from);
             config.save_toolstates = save_toolstates.map(PathBuf::from);
@@ -1806,6 +1811,7 @@ impl Config {
                 release_debuginfo,
                 assertions,
                 tests,
+                enzyme,
                 plugins,
                 ccache,
                 static_libstdcpp,
@@ -1839,6 +1845,7 @@ impl Config {
             set(&mut config.ninja_in_file, ninja);
             llvm_assertions = assertions;
             llvm_tests = tests;
+            llvm_enzyme = enzyme;
             llvm_plugins = plugins;
             set(&mut config.llvm_optimize, optimize_toml);
             set(&mut config.llvm_thin_lto, thin_lto);
@@ -2055,6 +2062,7 @@ impl Config {
 
         config.llvm_assertions = llvm_assertions.unwrap_or(false);
         config.llvm_tests = llvm_tests.unwrap_or(false);
+        config.llvm_enzyme = llvm_enzyme.unwrap_or(false);
         config.llvm_plugins = llvm_plugins.unwrap_or(false);
         config.rust_optimize = optimize.unwrap_or(RustOptimize::Bool(true));
 
diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs
index 5751c398f30..624246479f7 100644
--- a/src/bootstrap/src/lib.rs
+++ b/src/bootstrap/src/lib.rs
@@ -77,6 +77,9 @@ const LLD_FILE_NAMES: &[&str] = &["ld.lld", "ld64.lld", "lld-link", "wasm-ld"];
 #[allow(clippy::type_complexity)] // It's fine for hard-coded list and type is explained above.
 const EXTRA_CHECK_CFGS: &[(Option<Mode>, &str, Option<&[&'static str]>)] = &[
     (None, "bootstrap", None),
+    (Some(Mode::Rustc), "llvm_enzyme", None),
+    (Some(Mode::Codegen), "llvm_enzyme", None),
+    (Some(Mode::ToolRustc), "llvm_enzyme", None),
     (Some(Mode::Rustc), "parallel_compiler", None),
     (Some(Mode::ToolRustc), "parallel_compiler", None),
     (Some(Mode::ToolRustc), "rust_analyzer", None),
@@ -140,6 +143,7 @@ pub struct Build {
     clippy_info: GitInfo,
     miri_info: GitInfo,
     rustfmt_info: GitInfo,
+    enzyme_info: GitInfo,
     in_tree_llvm_info: GitInfo,
     local_rebuild: bool,
     fail_fast: bool,
@@ -306,6 +310,7 @@ impl Build {
         let clippy_info = GitInfo::new(omit_git_hash, &src.join("src/tools/clippy"));
         let miri_info = GitInfo::new(omit_git_hash, &src.join("src/tools/miri"));
         let rustfmt_info = GitInfo::new(omit_git_hash, &src.join("src/tools/rustfmt"));
+        let enzyme_info = GitInfo::new(omit_git_hash, &src.join("src/tools/enzyme"));
 
         // we always try to use git for LLVM builds
         let in_tree_llvm_info = GitInfo::new(false, &src.join("src/llvm-project"));
@@ -393,6 +398,7 @@ impl Build {
             clippy_info,
             miri_info,
             rustfmt_info,
+            enzyme_info,
             in_tree_llvm_info,
             cc: RefCell::new(HashMap::new()),
             cxx: RefCell::new(HashMap::new()),
@@ -740,6 +746,10 @@ impl Build {
         }
     }
 
+    fn enzyme_out(&self, target: TargetSelection) -> PathBuf {
+        self.out.join(&*target.triple).join("enzyme")
+    }
+
     fn lld_out(&self, target: TargetSelection) -> PathBuf {
         self.out.join(target).join("lld")
     }
diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs
index 3fec2d953d9..80ab09881fe 100644
--- a/src/bootstrap/src/utils/change_tracker.rs
+++ b/src/bootstrap/src/utils/change_tracker.rs
@@ -245,4 +245,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[
         severity: ChangeSeverity::Warning,
         summary: "Removed `rust.split-debuginfo` as it was deprecated long time ago.",
     },
+    ChangeInfo {
+        change_id: 129176,
+        severity: ChangeSeverity::Info,
+        summary: "New option `llvm.enzyme` to control whether the llvm based autodiff tool (Enzyme) is built.",
+    },
 ];
diff --git a/src/tools/enzyme b/src/tools/enzyme
new file mode 160000
+Subproject 2fe5164a2423dd67ef25e2c4fb204fd06362494
diff --git a/src/tools/tidy/config/black.toml b/src/tools/tidy/config/black.toml
index e73847a93ba..a1e1eef987a 100644
--- a/src/tools/tidy/config/black.toml
+++ b/src/tools/tidy/config/black.toml
@@ -12,5 +12,6 @@ extend-exclude = """(\
     src/llvm-project/|\
     src/doc/embedded-book/|\
     src/tools/rustc-perf/|\
+    src/tools/enzyme/|\
     library/backtrace/
     )"""
diff --git a/src/tools/tidy/config/ruff.toml b/src/tools/tidy/config/ruff.toml
index c87c17f7833..2be109010a0 100644
--- a/src/tools/tidy/config/ruff.toml
+++ b/src/tools/tidy/config/ruff.toml
@@ -16,6 +16,7 @@ extend-exclude = [
     "src/llvm-project/",
     "src/doc/embedded-book/",
     "library/backtrace/",
+    "src/tools/enzyme/",
     "src/tools/rustc-perf/",
     # Hack: CI runs from a subdirectory under the main checkout
     "../src/doc/nomicon/",
@@ -29,6 +30,7 @@ extend-exclude = [
     "../src/llvm-project/",
     "../src/doc/embedded-book/",
     "../library/backtrace/",
+    "../src/tools/enzyme/",
     "../src/tools/rustc-perf/",
 ]
 
diff --git a/src/tools/tidy/src/walk.rs b/src/tools/tidy/src/walk.rs
index d4721b5a5fd..4a69022ebdc 100644
--- a/src/tools/tidy/src/walk.rs
+++ b/src/tools/tidy/src/walk.rs
@@ -24,6 +24,7 @@ pub fn filter_dirs(path: &Path) -> bool {
         "src/tools/rust-analyzer",
         "src/tools/rustc-perf",
         "src/tools/rustfmt",
+        "src/tools/enzyme",
         "src/doc/book",
         "src/doc/edition-guide",
         "src/doc/embedded-book",