diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index dd536cb7b02..5706b8f9e7c 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -143,6 +143,8 @@ pub struct Config { pub rust_new_symbol_mangling: bool, pub rust_profile_use: Option, pub rust_profile_generate: Option, + pub llvm_profile_use: Option, + pub llvm_profile_generate: bool, pub build: TargetSelection, pub hosts: Vec, @@ -605,6 +607,8 @@ impl Config { if let Some(value) = flags.deny_warnings { config.deny_warnings = value; } + config.llvm_profile_use = flags.llvm_profile_use; + config.llvm_profile_generate = flags.llvm_profile_generate; if config.dry_run { let dir = config.out.join("tmp-dry-run"); diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs index 64075e18366..d7d511c1c92 100644 --- a/src/bootstrap/dist.rs +++ b/src/bootstrap/dist.rs @@ -2157,10 +2157,16 @@ impl Step for ReproducibleArtifacts { } fn run(self, builder: &Builder<'_>) -> Self::Output { - let path = builder.config.rust_profile_use.as_ref()?; - + let mut added_anything = false; let tarball = Tarball::new(builder, "reproducible-artifacts", &self.target.triple); - tarball.add_file(path, ".", 0o644); - Some(tarball.generate()) + if let Some(path) = builder.config.rust_profile_use.as_ref() { + tarball.add_file(path, ".", 0o644); + added_anything = true; + } + if let Some(path) = builder.config.llvm_profile_use.as_ref() { + tarball.add_file(path, ".", 0o644); + added_anything = true; + } + if added_anything { Some(tarball.generate()) } else { None } } } diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs index 80c33fa4d7c..2fddda74a28 100644 --- a/src/bootstrap/flags.rs +++ b/src/bootstrap/flags.rs @@ -71,6 +71,13 @@ pub struct Flags { pub rust_profile_use: Option, pub rust_profile_generate: Option, + + pub llvm_profile_use: Option, + // LLVM doesn't support a custom location for generating profile + // information. + // + // llvm_out/build/profiles/ is the location this writes to. + pub llvm_profile_generate: bool, } pub enum Subcommand { @@ -222,8 +229,15 @@ To learn more about a subcommand, run `./x.py -h`", VALUE overrides the skip-rebuild option in config.toml.", "VALUE", ); - opts.optopt("", "rust-profile-generate", "generate PGO profile with rustc build", "FORMAT"); - opts.optopt("", "rust-profile-use", "use PGO profile for rustc build", "FORMAT"); + opts.optopt( + "", + "rust-profile-generate", + "generate PGO profile with rustc build", + "PROFILE", + ); + opts.optopt("", "rust-profile-use", "use PGO profile for rustc build", "PROFILE"); + opts.optflag("", "llvm-profile-generate", "generate PGO profile with llvm built for rustc"); + opts.optopt("", "llvm-profile-use", "use PGO profile for llvm build", "PROFILE"); // We can't use getopt to parse the options until we have completed specifying which // options are valid, but under the current implementation, some options are conditional on @@ -687,6 +701,8 @@ Arguments: .expect("`color` should be `always`, `never`, or `auto`"), rust_profile_use: matches.opt_str("rust-profile-use"), rust_profile_generate: matches.opt_str("rust-profile-generate"), + llvm_profile_use: matches.opt_str("llvm-profile-use"), + llvm_profile_generate: matches.opt_present("llvm-profile-generate"), } } } diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs index d1397394be7..2172b01706d 100644 --- a/src/bootstrap/native.rs +++ b/src/bootstrap/native.rs @@ -189,6 +189,14 @@ impl Step for Llvm { .define("LLVM_TARGET_ARCH", target_native.split('-').next().unwrap()) .define("LLVM_DEFAULT_TARGET_TRIPLE", target_native); + if builder.config.llvm_profile_generate { + cfg.define("LLVM_BUILD_INSTRUMENTED", "IR"); + cfg.define("LLVM_BUILD_RUNTIME", "No"); + } + if let Some(path) = builder.config.llvm_profile_use.as_ref() { + cfg.define("LLVM_PROFDATA_FILE", &path); + } + if target != "aarch64-apple-darwin" && !target.contains("windows") { cfg.define("LLVM_ENABLE_ZLIB", "ON"); } else { diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile index 8242e091cd4..e29d990f0f9 100644 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile @@ -82,6 +82,12 @@ COPY host-x86_64/dist-x86_64-linux/build-clang.sh /tmp/ RUN ./build-clang.sh ENV CC=clang CXX=clang++ +ENV PERF_COMMIT 1e19fc4c6168d2f7596e512f42f358f245d8f09d +RUN curl -LS -o perf.zip https://github.com/rust-lang/rustc-perf/archive/$PERF_COMMIT.zip && \ + unzip perf.zip && \ + mv rustc-perf-$PERF_COMMIT rustc-perf && \ + rm perf.zip + COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/build-clang.sh b/src/ci/docker/host-x86_64/dist-x86_64-linux/build-clang.sh index 40a637616d3..ed5edfec4e1 100755 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/build-clang.sh +++ b/src/ci/docker/host-x86_64/dist-x86_64-linux/build-clang.sh @@ -20,14 +20,18 @@ cd clang-build # include path, /rustroot/include, to clang's default include path. INC="/rustroot/include:/usr/include" +# We need compiler-rt for the profile runtime (used later to PGO the LLVM build) +# but sanitizers aren't currently building. Since we don't need those, just +# disable them. hide_output \ cmake ../llvm \ -DCMAKE_C_COMPILER=/rustroot/bin/gcc \ -DCMAKE_CXX_COMPILER=/rustroot/bin/g++ \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_PREFIX=/rustroot \ + -DCOMPILER_RT_BUILD_SANITIZERS=OFF \ -DLLVM_TARGETS_TO_BUILD=X86 \ - -DLLVM_ENABLE_PROJECTS="clang;lld" \ + -DLLVM_ENABLE_PROJECTS="clang;lld;compiler-rt" \ -DC_INCLUDE_DIRS="$INC" hide_output make -j$(nproc) diff --git a/src/ci/pgo.sh b/src/ci/pgo.sh index aa009a4eac6..e35e3e670cc 100755 --- a/src/ci/pgo.sh +++ b/src/ci/pgo.sh @@ -5,61 +5,59 @@ set -euxo pipefail rm -rf /tmp/rustc-pgo python3 ../x.py build --target=$PGO_HOST --host=$PGO_HOST \ - --stage 2 library/std --rust-profile-generate=/tmp/rustc-pgo + --stage 2 library/std \ + --rust-profile-generate=/tmp/rustc-pgo \ + --llvm-profile-generate +# Profile libcore compilation in opt-level=0 and opt-level=3 RUSTC_BOOTSTRAP=1 ./build/$PGO_HOST/stage2/bin/rustc --edition=2018 \ --crate-type=lib ../library/core/src/lib.rs +RUSTC_BOOTSTRAP=1 ./build/$PGO_HOST/stage2/bin/rustc --edition=2018 \ + --crate-type=lib -Copt-level=3 ../library/core/src/lib.rs -# Download and build a single-file stress test benchmark on perf.rust-lang.org. -function pgo_perf_benchmark { - local PERF=1e19fc4c6168d2f7596e512f42f358f245d8f09d - local github_prefix=https://raw.githubusercontent.com/rust-lang/rustc-perf/$PERF - local name=$1 - local edition=$2 - curl -o /tmp/$name.rs $github_prefix/collector/benchmarks/$name/src/lib.rs +cp -r /tmp/rustc-perf ./ +chown -R $(whoami): ./rustc-perf +cd rustc-perf - RUSTC_BOOTSTRAP=1 ./build/$PGO_HOST/stage2/bin/rustc --edition=$edition \ - --crate-type=lib /tmp/$name.rs -} +# Build the collector ahead of time, which is needed to make sure the rustc-fake +# binary used by the collector is present. +RUSTC=/checkout/obj/build/$PGO_HOST/stage0/bin/rustc \ +RUSTC_BOOTSTRAP=1 \ +/checkout/obj/build/$PGO_HOST/stage0/bin/cargo build -p collector -pgo_perf_benchmark externs 2018 -pgo_perf_benchmark ctfe-stress-4 2018 -pgo_perf_benchmark inflate 2015 +# benchmark using profile_local with eprintln, which essentially just means +# don't actually benchmark -- just make sure we run rustc a bunch of times. +RUST_LOG=collector=debug \ +RUSTC=/checkout/obj/build/$PGO_HOST/stage0/bin/rustc \ +RUSTC_BOOTSTRAP=1 \ +/checkout/obj/build/$PGO_HOST/stage0/bin/cargo run -p collector --bin collector -- \ + profile_local \ + eprintln \ + /checkout/obj/build/$PGO_HOST/stage2/bin/rustc \ + Test \ + --builds Check,Debug,Opt \ + --cargo /checkout/obj/build/$PGO_HOST/stage0/bin/cargo \ + --runs All \ + --include externs,ctfe-stress-4,inflate,cargo,token-stream-stress,match-stress-enum -cp -pri ../src/tools/cargo /tmp/cargo - -# The Cargo repository does not have a Cargo.lock in it, as it relies on the -# lockfile already present in the rust-lang/rust monorepo. This decision breaks -# down when Cargo is built outside the monorepo though (like in this case), -# resulting in a build without any dependency locking. -# -# To ensure Cargo is built with locked dependencies even during PGO profiling -# the following command copies the monorepo's lockfile into the Cargo temporary -# directory. Cargo will *not* keep that lockfile intact, as it will remove all -# the dependencies Cargo itself doesn't rely on. Still, it will prevent -# building Cargo with arbitrary dependency versions. -# -# See #81378 for the bug that prompted adding this. -cp -p ../Cargo.lock /tmp/cargo - -# Build cargo (with some flags) -function pgo_cargo { - RUSTC=./build/$PGO_HOST/stage2/bin/rustc \ - ./build/$PGO_HOST/stage0/bin/cargo $@ \ - --manifest-path /tmp/cargo/Cargo.toml -} - -# Build a couple different variants of Cargo -CARGO_INCREMENTAL=1 pgo_cargo check -echo 'pub fn barbarbar() {}' >> /tmp/cargo/src/cargo/lib.rs -CARGO_INCREMENTAL=1 pgo_cargo check -touch /tmp/cargo/src/cargo/lib.rs -CARGO_INCREMENTAL=1 pgo_cargo check -pgo_cargo build --release +cd /checkout/obj # Merge the profile data we gathered ./build/$PGO_HOST/llvm/bin/llvm-profdata \ merge -o /tmp/rustc-pgo.profdata /tmp/rustc-pgo +# Merge the profile data we gathered for LLVM +# Note that this uses the profdata from the clang we used to build LLVM, +# which likely has a different version than our in-tree clang. +/rustroot/bin/llvm-profdata \ + merge -o /tmp/llvm-pgo.profdata ./build/$PGO_HOST/llvm/build/profiles + +# Rustbuild currently doesn't support rebuilding LLVM when PGO options +# change (or any other llvm-related options); so just clear out the relevant +# directories ourselves. +rm -r ./build/$PGO_HOST/llvm ./build/$PGO_HOST/lld + # This produces the actual final set of artifacts. -$@ --rust-profile-use=/tmp/rustc-pgo.profdata +$@ \ + --rust-profile-use=/tmp/rustc-pgo.profdata \ + --llvm-profile-use=/tmp/llvm-pgo.profdata