Merge commit 'fda0bb9588912a3e0606e880ca9f6e913cf8a5a4' into subtree-update_cg_gcc_2025-06-18

This commit is contained in:
Guillaume Gomez 2025-06-18 15:11:44 +02:00
parent 265f4a7b63
commit efb79975f6
54 changed files with 11917 additions and 10357 deletions

27
.cspell.json Normal file
View File

@ -0,0 +1,27 @@
{
"allowCompoundWords": true,
"dictionaries": ["cpp", "rust-extra", "rustc_codegen_gcc"],
"dictionaryDefinitions": [
{
"name": "rust-extra",
"path": "tools/cspell_dicts/rust.txt",
"addWords": true
},
{
"name": "rustc_codegen_gcc",
"path": "tools/cspell_dicts/rustc_codegen_gcc.txt",
"addWords": true
}
],
"files": [
"src/**/*.rs"
],
"ignorePaths": [
"src/intrinsic/archs.rs",
"src/intrinsic/llvm.rs"
],
"ignoreRegExpList": [
"/(FIXME|NOTE|TODO)\\([^)]+\\)/",
"__builtin_\\w*"
]
}

View File

@ -12,6 +12,8 @@ permissions:
env:
# Enable backtraces for easier debugging
RUST_BACKTRACE: 1
# For the run-make tests.
LLVM_BIN_DIR: /usr/bin
jobs:
build:
@ -48,7 +50,7 @@ jobs:
- name: Install packages
# `llvm-14-tools` is needed to install the `FileCheck` binary which is used for asm tests.
run: sudo apt-get install ninja-build ripgrep llvm-14-tools
run: sudo apt-get install ninja-build ripgrep llvm-14-tools llvm
- name: Install rustfmt & clippy
run: rustup component add rustfmt clippy
@ -61,11 +63,15 @@ jobs:
sudo dpkg --force-overwrite -i ${{ matrix.libgccjit_version.gcc }}
echo 'gcc-path = "/usr/lib/"' > config.toml
# Some run-make tests fail if we use our forked GCC because it doesn't
# bundle libstdc++, so we switch to gcc-14 to have a GCC that has
# libstdc++.
- name: Set default GCC to gcc-14
run: sudo update-alternatives --install /usr/bin/cc cc /usr/bin/gcc-14 30
- name: Set env
run: |
echo "workspace="$GITHUB_WORKSPACE >> $GITHUB_ENV
echo "LIBRARY_PATH=/usr/lib" >> $GITHUB_ENV
echo "LD_LIBRARY_PATH=/usr/lib" >> $GITHUB_ENV
#- name: Cache rust repository
## We only clone the rust repository for rustc tests
@ -76,12 +82,22 @@ jobs:
#path: rust
#key: ${{ runner.os }}-packages-${{ hashFiles('rust/.git/HEAD') }}
- name: Prepare
run: ./y.sh prepare --only-libcore
- name: Check formatting
run: ./y.sh fmt --check
- name: clippy
run: |
cargo clippy --all-targets -- -D warnings
cargo clippy --all-targets --no-default-features -- -D warnings
cargo clippy --manifest-path build_system/Cargo.toml --all-targets -- -D warnings
- name: Build
run: |
./y.sh prepare --only-libcore
./y.sh build --sysroot
./y.sh test --mini-tests
cargo test
./y.sh test --cargo-tests
- name: Run y.sh cargo build
run: |
@ -101,20 +117,19 @@ jobs:
run: |
./y.sh test --release --clean --build-sysroot ${{ matrix.commands }}
- name: Check formatting
run: ./y.sh fmt --check
- name: clippy
run: |
cargo clippy --all-targets -- -D warnings
cargo clippy --all-targets --features master -- -D warnings
duplicates:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- run: python tools/check_intrinsics_duplicates.py
spell_check:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: crate-ci/typos@v1.32.0
- uses: streetsidesoftware/cspell-action@v7
build_system:
runs-on: ubuntu-24.04
steps:

View File

@ -66,8 +66,8 @@ jobs:
run: |
sudo dpkg --force-overwrite -i gcc-15.deb
echo 'gcc-path = "/usr/lib"' > config.toml
echo "LIBRARY_PATH=/usr/lib" >> $GITHUB_ENV
echo "LD_LIBRARY_PATH=/usr/lib" >> $GITHUB_ENV
- name: Set env
run: |

View File

@ -65,8 +65,8 @@ jobs:
- name: Set env
run: |
echo "workspace="$GITHUB_WORKSPACE >> $GITHUB_ENV
echo "LIBRARY_PATH=/usr/lib" >> $GITHUB_ENV
echo "LD_LIBRARY_PATH=/usr/lib" >> $GITHUB_ENV
#- name: Cache rust repository
## We only clone the rust repository for rustc tests
@ -95,7 +95,7 @@ jobs:
./y.sh prepare --only-libcore --cross
./y.sh build --sysroot --features compiler_builtins/no-f16-f128 --target-triple m68k-unknown-linux-gnu
./y.sh test --mini-tests
CG_GCC_TEST_TARGET=m68k-unknown-linux-gnu cargo test
CG_GCC_TEST_TARGET=m68k-unknown-linux-gnu ./y.sh test --cargo-tests
./y.sh clean all
- name: Prepare dependencies

View File

@ -12,6 +12,8 @@ permissions:
env:
# Enable backtraces for easier debugging
RUST_BACKTRACE: 1
# For the run-make tests.
LLVM_BIN_DIR: /usr/bin
jobs:
build:
@ -36,7 +38,8 @@ jobs:
uses: Swatinem/rust-cache@v2
- name: Install packages
run: sudo apt-get install ninja-build ripgrep
# `llvm-14-tools` is needed to install the `FileCheck` binary which is used for run-make tests.
run: sudo apt-get install ninja-build ripgrep llvm-14-tools llvm
- name: Download artifact
run: curl -LO https://github.com/rust-lang/gcc/releases/latest/download/gcc-15.deb
@ -46,18 +49,21 @@ jobs:
sudo dpkg --force-overwrite -i gcc-15.deb
echo 'gcc-path = "/usr/lib/"' > config.toml
# Some run-make tests fail if we use our forked GCC because it doesn't
# bundle libstdc++, so we switch to gcc-14 to have a GCC that has
# libstdc++.
- name: Set default GCC to gcc-14
run: sudo update-alternatives --install /usr/bin/cc cc /usr/bin/gcc-14 30
- name: Set env
run: |
echo "workspace="$GITHUB_WORKSPACE >> $GITHUB_ENV
echo "LIBRARY_PATH=/usr/lib" >> $GITHUB_ENV
echo "LD_LIBRARY_PATH=/usr/lib" >> $GITHUB_ENV
- name: Build
run: |
./y.sh prepare --only-libcore
EMBED_LTO_BITCODE=1 ./y.sh build --sysroot --release --release-sysroot
./y.sh test --mini-tests
cargo test
./y.sh test --cargo-tests
./y.sh clean all
- name: Prepare dependencies

View File

@ -90,7 +90,7 @@ jobs:
if: ${{ !matrix.cargo_runner }}
run: |
./y.sh test --release --clean --release-sysroot --build-sysroot --mini-tests --std-tests --test-libcore
cargo test
./y.sh test --cargo-tests
- name: Run stdarch tests
if: ${{ !matrix.cargo_runner }}

3
.gitignore vendored
View File

@ -19,4 +19,5 @@ tools/llvmint-2
llvm
build_system/target
config.toml
build
build
rustlantis

View File

@ -33,7 +33,7 @@ To run specific tests, use appropriate flags such as:
- `./y.sh test --test-libcore`
- `./y.sh test --std-tests`
- `cargo test -- <name of test>`
- `./y.sh test --cargo-tests -- <name of test>`
Additionally, you can run the tests of `libgccjit`:

View File

@ -81,6 +81,18 @@ dependencies = [
"unicode-width",
]
[[package]]
name = "getrandom"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
dependencies = [
"cfg-if",
"libc",
"r-efi",
"wasi",
]
[[package]]
name = "hermit-abi"
version = "0.3.1"
@ -111,9 +123,9 @@ checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d"
[[package]]
name = "linux-raw-sys"
version = "0.4.14"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
[[package]]
name = "memchr"
@ -137,6 +149,12 @@ version = "1.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
[[package]]
name = "r-efi"
version = "5.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
[[package]]
name = "regex"
version = "1.8.4"
@ -166,9 +184,9 @@ dependencies = [
[[package]]
name = "rustix"
version = "0.38.42"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85"
checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266"
dependencies = [
"bitflags",
"errno",
@ -188,12 +206,12 @@ dependencies = [
[[package]]
name = "tempfile"
version = "3.14.0"
version = "3.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c"
checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1"
dependencies = [
"cfg-if",
"fastrand",
"getrandom",
"once_cell",
"rustix",
"windows-sys",
@ -242,6 +260,15 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "wasi"
version = "0.14.2+wasi-0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
dependencies = [
"wit-bindgen-rt",
]
[[package]]
name = "winapi"
version = "0.3.9"
@ -345,3 +372,12 @@ name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "wit-bindgen-rt"
version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
dependencies = [
"bitflags",
]

View File

@ -31,7 +31,7 @@ gccjit = "2.7"
[dev-dependencies]
boml = "0.3.1"
lang_tester = "0.8.0"
tempfile = "3.7.1"
tempfile = "3.20"
[profile.dev]
# By compiling dependencies with optimizations, performing tests gets much faster.

9
_typos.toml Normal file
View File

@ -0,0 +1,9 @@
[default.extend-words]
ba = "ba"
hsa = "hsa"
olt = "olt"
seh = "seh"
typ = "typ"
[files]
extend-exclude = ["src/intrinsic/archs.rs"]

View File

@ -1,24 +1,24 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
version = 4
[[package]]
name = "addr2line"
version = "0.22.0"
version = "0.24.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678"
checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
dependencies = [
"compiler_builtins",
"gimli 0.29.0",
"gimli",
"rustc-std-workspace-alloc",
"rustc-std-workspace-core",
]
[[package]]
name = "adler"
version = "1.0.2"
name = "adler2"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
dependencies = [
"compiler_builtins",
"rustc-std-workspace-core",
@ -33,10 +33,21 @@ dependencies = [
]
[[package]]
name = "allocator-api2"
version = "0.2.18"
name = "alloctests"
version = "0.0.0"
dependencies = [
"rand",
"rand_xorshift",
]
[[package]]
name = "cc"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
checksum = "1aeb932158bd710538c73702db6945cb68a8fb08c519e6e12706b94263b36db8"
dependencies = [
"shlex",
]
[[package]]
name = "cfg-if"
@ -50,10 +61,11 @@ dependencies = [
[[package]]
name = "compiler_builtins"
version = "0.1.118"
version = "0.1.160"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92afe7344b64cccf3662ca26d5d1c0828ab826f04206b97d856e3625e390e4b5"
checksum = "6376049cfa92c0aa8b9ac95fae22184b981c658208d4ed8a1dc553cd83612895"
dependencies = [
"cc",
"rustc-std-workspace-core",
]
@ -61,11 +73,19 @@ dependencies = [
name = "core"
version = "0.0.0"
[[package]]
name = "coretests"
version = "0.0.0"
dependencies = [
"rand",
"rand_xorshift",
]
[[package]]
name = "dlmalloc"
version = "0.2.6"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3264b043b8e977326c1ee9e723da2c1f8d09a99df52cacf00b4dbce5ac54414d"
checksum = "8cff88b751e7a276c4ab0e222c3f355190adc6dde9ce39c851db39da34990df7"
dependencies = [
"cfg-if",
"compiler_builtins",
@ -97,20 +117,9 @@ dependencies = [
[[package]]
name = "gimli"
version = "0.29.0"
version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd"
dependencies = [
"compiler_builtins",
"rustc-std-workspace-alloc",
"rustc-std-workspace-core",
]
[[package]]
name = "gimli"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2e1d97fbe9722ba9bbd0c97051c2956e726562b61f86a25a4360398a40edfc9"
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
dependencies = [
"compiler_builtins",
"rustc-std-workspace-alloc",
@ -119,11 +128,10 @@ dependencies = [
[[package]]
name = "hashbrown"
version = "0.14.5"
version = "0.15.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3"
dependencies = [
"allocator-api2",
"compiler_builtins",
"rustc-std-workspace-alloc",
"rustc-std-workspace-core",
@ -131,9 +139,9 @@ dependencies = [
[[package]]
name = "hermit-abi"
version = "0.4.0"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc"
checksum = "f154ce46856750ed433c8649605bf7ed2de3bc35fd9d2a9f30cddd873c80cb08"
dependencies = [
"compiler_builtins",
"rustc-std-workspace-alloc",
@ -142,9 +150,9 @@ dependencies = [
[[package]]
name = "libc"
version = "0.2.155"
version = "0.2.172"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
dependencies = [
"rustc-std-workspace-core",
]
@ -161,11 +169,11 @@ dependencies = [
[[package]]
name = "miniz_oxide"
version = "0.7.4"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08"
checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
dependencies = [
"adler",
"adler2",
"compiler_builtins",
"rustc-std-workspace-alloc",
"rustc-std-workspace-core",
@ -173,9 +181,9 @@ dependencies = [
[[package]]
name = "object"
version = "0.36.3"
version = "0.36.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27b64972346851a39438c60b341ebc01bba47464ae329e55cf343eb93964efd9"
checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
dependencies = [
"compiler_builtins",
"memchr",
@ -188,7 +196,6 @@ name = "panic_abort"
version = "0.0.0"
dependencies = [
"alloc",
"cfg-if",
"compiler_builtins",
"core",
"libc",
@ -211,14 +218,22 @@ name = "proc_macro"
version = "0.0.0"
dependencies = [
"core",
"rustc-literal-escaper",
"std",
]
[[package]]
name = "profiler_builtins"
version = "0.0.0"
dependencies = [
"cc",
]
[[package]]
name = "r-efi"
version = "4.5.0"
version = "5.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9e935efc5854715dfc0a4c9ef18dc69dee0ec3bf9cc3ab740db831c0fdd86a3"
checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
dependencies = [
"compiler_builtins",
"rustc-std-workspace-core",
@ -226,15 +241,39 @@ dependencies = [
[[package]]
name = "r-efi-alloc"
version = "1.0.0"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31d6f09fe2b6ad044bc3d2c34ce4979796581afd2f1ebc185837e02421e02fd7"
checksum = "e43c53ff1a01d423d1cb762fd991de07d32965ff0ca2e4f80444ac7804198203"
dependencies = [
"compiler_builtins",
"r-efi",
"rustc-std-workspace-core",
]
[[package]]
name = "rand"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97"
dependencies = [
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
[[package]]
name = "rand_xorshift"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a"
dependencies = [
"rand_core",
]
[[package]]
name = "rustc-demangle"
version = "0.1.24"
@ -245,6 +284,15 @@ dependencies = [
"rustc-std-workspace-core",
]
[[package]]
name = "rustc-literal-escaper"
version = "0.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0041b6238913c41fe704213a4a9329e2f685a156d1781998128b4149c230ad04"
dependencies = [
"rustc-std-workspace-std",
]
[[package]]
name = "rustc-std-workspace-alloc"
version = "1.99.0"
@ -266,6 +314,12 @@ dependencies = [
"std",
]
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "std"
version = "0.0.0"
@ -286,10 +340,13 @@ dependencies = [
"panic_unwind",
"r-efi",
"r-efi-alloc",
"rand",
"rand_xorshift",
"rustc-demangle",
"std_detect",
"unwind",
"wasi",
"windows-targets 0.0.0",
]
[[package]]
@ -298,6 +355,7 @@ version = "0.1.5"
dependencies = [
"cfg-if",
"compiler_builtins",
"libc",
"rustc-std-workspace-alloc",
"rustc-std-workspace-core",
]
@ -306,10 +364,8 @@ dependencies = [
name = "sysroot"
version = "0.0.0"
dependencies = [
"alloc",
"compiler_builtins",
"core",
"proc_macro",
"profiler_builtins",
"std",
"test",
]
@ -326,9 +382,9 @@ dependencies = [
[[package]]
name = "unicode-width"
version = "0.1.13"
version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d"
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
dependencies = [
"compiler_builtins",
"rustc-std-workspace-core",
@ -348,12 +404,12 @@ dependencies = [
[[package]]
name = "unwinding"
version = "0.2.2"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc55842d0db6329a669d55a623c674b02d677b16bfb2d24857d4089d41eba882"
checksum = "8393f2782b6060a807337ff353780c1ca15206f9ba2424df18cb6e733bd7b345"
dependencies = [
"compiler_builtins",
"gimli 0.30.0",
"gimli",
"rustc-std-workspace-core",
]
@ -370,13 +426,17 @@ dependencies = [
[[package]]
name = "windows-sys"
version = "0.52.0"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets",
"windows-targets 0.52.6",
]
[[package]]
name = "windows-targets"
version = "0.0.0"
[[package]]
name = "windows-targets"
version = "0.52.6"

View File

@ -33,7 +33,7 @@ impl BuildArg {
}
arg => {
if !build_arg.config_info.parse_argument(arg, &mut args)? {
return Err(format!("Unknown argument `{}`", arg));
return Err(format!("Unknown argument `{arg}`"));
}
}
}
@ -105,14 +105,14 @@ pub fn create_build_sysroot_content(start_dir: &Path) -> Result<(), String> {
if !start_dir.is_dir() {
create_dir(start_dir)?;
}
copy_file("build_system/build_sysroot/Cargo.toml", &start_dir.join("Cargo.toml"))?;
copy_file("build_system/build_sysroot/Cargo.lock", &start_dir.join("Cargo.lock"))?;
copy_file("build_system/build_sysroot/Cargo.toml", start_dir.join("Cargo.toml"))?;
copy_file("build_system/build_sysroot/Cargo.lock", start_dir.join("Cargo.lock"))?;
let src_dir = start_dir.join("src");
if !src_dir.is_dir() {
create_dir(&src_dir)?;
}
copy_file("build_system/build_sysroot/lib.rs", &start_dir.join("src/lib.rs"))
copy_file("build_system/build_sysroot/lib.rs", start_dir.join("src/lib.rs"))
}
pub fn build_sysroot(env: &HashMap<String, String>, config: &ConfigInfo) -> Result<(), String> {
@ -169,7 +169,7 @@ pub fn build_sysroot(env: &HashMap<String, String>, config: &ConfigInfo) -> Resu
run_command(&[&"cp", &"-r", &dir_to_copy, &sysroot_path], None).map(|_| ())
};
walk_dir(
start_dir.join(&format!("target/{}/{}/deps", config.target_triple, channel)),
start_dir.join(format!("target/{}/{}/deps", config.target_triple, channel)),
&mut copier.clone(),
&mut copier,
false,

View File

@ -17,12 +17,12 @@ enum CleanArg {
impl CleanArg {
fn new() -> Result<Self, String> {
// We skip the binary and the "clean" option.
for arg in std::env::args().skip(2) {
if let Some(arg) = std::env::args().nth(2) {
return match arg.as_str() {
"all" => Ok(Self::All),
"ui-tests" => Ok(Self::UiTests),
"--help" => Ok(Self::Help),
a => Err(format!("Unknown argument `{}`", a)),
a => Err(format!("Unknown argument `{a}`")),
};
}
Ok(Self::default())

View File

@ -43,7 +43,7 @@ impl Args {
}
arg => {
if !command_args.config_info.parse_argument(arg, &mut args)? {
return Err(format!("Unknown option {}", arg));
return Err(format!("Unknown option {arg}"));
}
}
}
@ -52,7 +52,7 @@ impl Args {
Some(p) => p.into(),
None => PathBuf::from("./gcc"),
};
return Ok(Some(command_args));
Ok(Some(command_args))
}
}
@ -64,7 +64,7 @@ pub fn run() -> Result<(), String> {
let result = git_clone("https://github.com/rust-lang/gcc", Some(&args.out_path), false)?;
if result.ran_clone {
let gcc_commit = args.config_info.get_gcc_commit()?;
println!("Checking out GCC commit `{}`...", gcc_commit);
println!("Checking out GCC commit `{gcc_commit}`...");
run_command_with_output(
&[&"git", &"checkout", &gcc_commit],
Some(Path::new(&result.repo_dir)),

View File

@ -66,7 +66,7 @@ impl ConfigFile {
"Expected a boolean for `download-gccjit`",
);
}
_ => return failed_config_parsing(config_file, &format!("Unknown key `{}`", key)),
_ => return failed_config_parsing(config_file, &format!("Unknown key `{key}`")),
}
}
match (config.gcc_path.as_mut(), config.download_gccjit) {
@ -86,9 +86,7 @@ impl ConfigFile {
let path = Path::new(gcc_path);
*gcc_path = path
.canonicalize()
.map_err(|err| {
format!("Failed to get absolute path of `{}`: {:?}", gcc_path, err)
})?
.map_err(|err| format!("Failed to get absolute path of `{gcc_path}`: {err:?}"))?
.display()
.to_string();
}
@ -175,7 +173,7 @@ impl ConfigInfo {
"--sysroot-panic-abort" => self.sysroot_panic_abort = true,
"--gcc-path" => match args.next() {
Some(arg) if !arg.is_empty() => {
self.gcc_path = Some(arg.into());
self.gcc_path = Some(arg);
}
_ => {
return Err("Expected a value after `--gcc-path`, found nothing".to_string());
@ -244,7 +242,7 @@ impl ConfigInfo {
let libgccjit_so = output_dir.join(libgccjit_so_name);
if !libgccjit_so.is_file() && !self.no_download {
// Download time!
let tempfile_name = format!("{}.download", libgccjit_so_name);
let tempfile_name = format!("{libgccjit_so_name}.download");
let tempfile = output_dir.join(&tempfile_name);
let is_in_ci = std::env::var("GITHUB_ACTIONS").is_ok();
@ -262,14 +260,14 @@ impl ConfigInfo {
)
})?;
println!("Downloaded libgccjit.so version {} successfully!", commit);
println!("Downloaded libgccjit.so version {commit} successfully!");
// We need to create a link named `libgccjit.so.0` because that's what the linker is
// looking for.
create_symlink(&libgccjit_so, output_dir.join(&format!("{}.0", libgccjit_so_name)))?;
create_symlink(&libgccjit_so, output_dir.join(format!("{libgccjit_so_name}.0")))?;
}
let gcc_path = output_dir.display().to_string();
println!("Using `{}` as path for libgccjit", gcc_path);
println!("Using `{gcc_path}` as path for libgccjit");
self.gcc_path = Some(gcc_path);
Ok(())
}
@ -286,8 +284,7 @@ impl ConfigInfo {
// since we already have everything we need.
if let Some(gcc_path) = &self.gcc_path {
println!(
"`--gcc-path` was provided, ignoring config file. Using `{}` as path for libgccjit",
gcc_path
"`--gcc-path` was provided, ignoring config file. Using `{gcc_path}` as path for libgccjit"
);
return Ok(());
}
@ -343,7 +340,7 @@ impl ConfigInfo {
self.dylib_ext = match os_name.as_str() {
"Linux" => "so",
"Darwin" => "dylib",
os => return Err(format!("unsupported OS `{}`", os)),
os => return Err(format!("unsupported OS `{os}`")),
}
.to_string();
let rustc = match env.get("RUSTC") {
@ -355,10 +352,10 @@ impl ConfigInfo {
None => return Err("no host found".to_string()),
};
if self.target_triple.is_empty() {
if let Some(overwrite) = env.get("OVERWRITE_TARGET_TRIPLE") {
self.target_triple = overwrite.clone();
}
if self.target_triple.is_empty()
&& let Some(overwrite) = env.get("OVERWRITE_TARGET_TRIPLE")
{
self.target_triple = overwrite.clone();
}
if self.target_triple.is_empty() {
self.target_triple = self.host_triple.clone();
@ -378,7 +375,7 @@ impl ConfigInfo {
}
let current_dir =
std_env::current_dir().map_err(|error| format!("`current_dir` failed: {:?}", error))?;
std_env::current_dir().map_err(|error| format!("`current_dir` failed: {error:?}"))?;
let channel = if self.channel == Channel::Release {
"release"
} else if let Some(channel) = env.get("CHANNEL") {
@ -391,15 +388,15 @@ impl ConfigInfo {
self.cg_backend_path = current_dir
.join("target")
.join(channel)
.join(&format!("librustc_codegen_gcc.{}", self.dylib_ext))
.join(format!("librustc_codegen_gcc.{}", self.dylib_ext))
.display()
.to_string();
self.sysroot_path =
current_dir.join(&get_sysroot_dir()).join("sysroot").display().to_string();
current_dir.join(get_sysroot_dir()).join("sysroot").display().to_string();
if let Some(backend) = &self.backend {
// This option is only used in the rust compiler testsuite. The sysroot is handled
// by its build system directly so no need to set it ourselves.
rustflags.push(format!("-Zcodegen-backend={}", backend));
rustflags.push(format!("-Zcodegen-backend={backend}"));
} else {
rustflags.extend_from_slice(&[
"--sysroot".to_string(),
@ -412,10 +409,10 @@ impl ConfigInfo {
// We have a different environment variable than RUSTFLAGS to make sure those flags are
// only sent to rustc_codegen_gcc and not the LLVM backend.
if let Some(cg_rustflags) = env.get("CG_RUSTFLAGS") {
rustflags.extend_from_slice(&split_args(&cg_rustflags)?);
rustflags.extend_from_slice(&split_args(cg_rustflags)?);
}
if let Some(test_flags) = env.get("TEST_FLAGS") {
rustflags.extend_from_slice(&split_args(&test_flags)?);
rustflags.extend_from_slice(&split_args(test_flags)?);
}
if let Some(linker) = linker {
@ -438,8 +435,8 @@ impl ConfigInfo {
env.insert("RUSTC_LOG".to_string(), "warn".to_string());
let sysroot = current_dir
.join(&get_sysroot_dir())
.join(&format!("sysroot/lib/rustlib/{}/lib", self.target_triple));
.join(get_sysroot_dir())
.join(format!("sysroot/lib/rustlib/{}/lib", self.target_triple));
let ld_library_path = format!(
"{target}:{sysroot}:{gcc_path}",
target = self.cargo_target_dir,
@ -505,7 +502,7 @@ fn download_gccjit(
with_progress_bar: bool,
) -> Result<(), String> {
let url = if std::env::consts::OS == "linux" && std::env::consts::ARCH == "x86_64" {
format!("https://github.com/rust-lang/gcc/releases/download/master-{}/libgccjit.so", commit)
format!("https://github.com/rust-lang/gcc/releases/download/master-{commit}/libgccjit.so")
} else {
eprintln!(
"\
@ -518,7 +515,7 @@ to `download-gccjit = false` and set `gcc-path` to the appropriate directory."
));
};
println!("Downloading `{}`...", url);
println!("Downloading `{url}`...");
// Try curl. If that fails and we are on windows, fallback to PowerShell.
let mut ret = run_command_with_output(
@ -538,7 +535,7 @@ to `download-gccjit = false` and set `gcc-path` to the appropriate directory."
if with_progress_bar { &"--progress-bar" } else { &"-s" },
&url.as_str(),
],
Some(&output_dir),
Some(output_dir),
);
if ret.is_err() && cfg!(windows) {
eprintln!("Fallback to PowerShell");
@ -549,12 +546,11 @@ to `download-gccjit = false` and set `gcc-path` to the appropriate directory."
&"-Command",
&"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12;",
&format!(
"(New-Object System.Net.WebClient).DownloadFile('{}', '{}')",
url, tempfile_name,
"(New-Object System.Net.WebClient).DownloadFile('{url}', '{tempfile_name}')",
)
.as_str(),
],
Some(&output_dir),
Some(output_dir),
);
}
ret

View File

@ -16,21 +16,21 @@ fn show_usage() {
pub fn run() -> Result<(), String> {
let mut check = false;
// We skip binary name and the `info` command.
let mut args = std::env::args().skip(2);
while let Some(arg) = args.next() {
let args = std::env::args().skip(2);
for arg in args {
match arg.as_str() {
"--help" => {
show_usage();
return Ok(());
}
"--check" => check = true,
_ => return Err(format!("Unknown option {}", arg)),
_ => return Err(format!("Unknown option {arg}")),
}
}
let cmd: &[&dyn AsRef<OsStr>] =
if check { &[&"cargo", &"fmt", &"--check"] } else { &[&"cargo", &"fmt"] };
run_command_with_output(cmd, Some(&Path::new(".")))?;
run_command_with_output(cmd, Some(&Path::new("build_system")))
run_command_with_output(cmd, Some(Path::new(".")))?;
run_command_with_output(cmd, Some(Path::new("build_system")))
}

289
build_system/src/fuzz.rs Normal file
View File

@ -0,0 +1,289 @@
use std::ffi::OsStr;
use std::path::Path;
mod reduce;
use crate::utils::run_command_with_output;
fn show_usage() {
println!(
r#"
`fuzz` command help:
--reduce : Reduces a file generated by rustlantis
--help : Show this help
--start : Start of the fuzzed range
--count : The number of cases to fuzz
-j --jobs : The number of threads to use during fuzzing"#
);
}
pub fn run() -> Result<(), String> {
// We skip binary name and the `fuzz` command.
let mut args = std::env::args().skip(2);
let mut start = 0;
let mut count = 100;
let mut threads =
std::thread::available_parallelism().map(|threads| threads.get()).unwrap_or(1);
while let Some(arg) = args.next() {
match arg.as_str() {
"--reduce" => {
let Some(path) = args.next() else {
return Err("--reduce must be provided with a path".into());
};
if !std::fs::exists(&path).unwrap_or(false) {
return Err("--reduce must be provided with a valid path".into());
}
reduce::reduce(&path);
return Ok(());
}
"--help" => {
show_usage();
return Ok(());
}
"--start" => {
start =
str::parse(&args.next().ok_or_else(|| "Fuzz start not provided!".to_string())?)
.map_err(|err| (format!("Fuzz start not a number {err:?}!")))?;
}
"--count" => {
count =
str::parse(&args.next().ok_or_else(|| "Fuzz count not provided!".to_string())?)
.map_err(|err| (format!("Fuzz count not a number {err:?}!")))?;
}
"-j" | "--jobs" => {
threads = str::parse(
&args.next().ok_or_else(|| "Fuzz thread count not provided!".to_string())?,
)
.map_err(|err| (format!("Fuzz thread count not a number {err:?}!")))?;
}
_ => return Err(format!("Unknown option {arg}")),
}
}
// Ensure that we have a cloned version of rustlantis on hand.
crate::utils::git_clone(
"https://github.com/cbeuw/rustlantis.git",
Some("clones/rustlantis".as_ref()),
true,
)
.map_err(|err| (format!("Git clone failed with message: {err:?}!")))?;
// Ensure that we are on the newest rustlantis commit.
let cmd: &[&dyn AsRef<OsStr>] = &[&"git", &"pull", &"origin"];
run_command_with_output(cmd, Some(Path::new("clones/rustlantis")))?;
// Build the release version of rustlantis
let cmd: &[&dyn AsRef<OsStr>] = &[&"cargo", &"build", &"--release"];
run_command_with_output(cmd, Some(Path::new("clones/rustlantis")))?;
// Fuzz a given range
fuzz_range(start, start + count, threads);
Ok(())
}
/// Fuzzes a range `start..end` with `threads`.
fn fuzz_range(start: u64, end: u64, threads: usize) {
use std::sync::Arc;
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::{Duration, Instant};
// Total amount of files to fuzz
let total = end - start;
// Currently fuzzed element
let start = Arc::new(AtomicU64::new(start));
// Count time during fuzzing
let start_time = Instant::now();
let mut workers = Vec::with_capacity(threads);
// Spawn `threads`..
for _ in 0..threads {
let start = start.clone();
// .. which each will ..
workers.push(std::thread::spawn(move || {
// ... grab the next fuzz seed ...
while start.load(Ordering::Relaxed) < end {
let next = start.fetch_add(1, Ordering::Relaxed);
// .. test that seed .
match test(next, false) {
Err(err) => {
// If the test failed at compile-time...
println!("test({next}) failed because {err:?}");
// ... copy that file to the directory `target/fuzz/compiletime_error`...
let mut out_path: std::path::PathBuf =
"target/fuzz/compiletime_error".into();
std::fs::create_dir_all(&out_path).unwrap();
// .. into a file named `fuzz{seed}.rs`.
out_path.push(format!("fuzz{next}.rs"));
std::fs::copy(err, out_path).unwrap();
}
Ok(Err(err)) => {
// If the test failed at run-time...
println!("The LLVM and GCC results don't match for {err:?}");
// ... generate a new file, which prints temporaries(instead of hashing them)...
let mut out_path: std::path::PathBuf = "target/fuzz/runtime_error".into();
std::fs::create_dir_all(&out_path).unwrap();
let Ok(Err(tmp_print_err)) = test(next, true) else {
// ... if that file does not reproduce the issue...
// ... save the original sample in a file named `fuzz{seed}.rs`...
out_path.push(format!("fuzz{next}.rs"));
std::fs::copy(err, &out_path).unwrap();
continue;
};
// ... if that new file still produces the issue, copy it to `fuzz{seed}.rs`..
out_path.push(format!("fuzz{next}.rs"));
std::fs::copy(tmp_print_err, &out_path).unwrap();
// ... and start reducing it, using some properties of `rustlantis` to speed up the process.
reduce::reduce(&out_path);
}
// If the test passed, do nothing
Ok(Ok(())) => (),
}
}
}));
}
// The "manager" thread loop.
while start.load(Ordering::Relaxed) < end || !workers.iter().all(|t| t.is_finished()) {
// Every 500 ms...
let five_hundred_millis = Duration::from_millis(500);
std::thread::sleep(five_hundred_millis);
// ... calculate the remaining fuzz iters ...
let remaining = end - start.load(Ordering::Relaxed);
// ... fix the count(the start counter counts the cases that
// begun fuzzing, and not only the ones that are done)...
let fuzzed = (total - remaining).saturating_sub(threads as u64);
// ... and the fuzz speed ...
let iter_per_sec = fuzzed as f64 / start_time.elapsed().as_secs_f64();
// .. and use them to display fuzzing stats.
println!(
"fuzzed {fuzzed} cases({}%), at rate {iter_per_sec} iter/s, remaining ~{}s",
(100 * fuzzed) as f64 / total as f64,
(remaining as f64) / iter_per_sec
)
}
drop(workers);
}
/// Builds & runs a file with LLVM.
fn debug_llvm(path: &std::path::Path) -> Result<Vec<u8>, String> {
// Build a file named `llvm_elf`...
let exe_path = path.with_extension("llvm_elf");
// ... using the LLVM backend ...
let output = std::process::Command::new("rustc")
.arg(path)
.arg("-o")
.arg(&exe_path)
.output()
.map_err(|err| format!("{err:?}"))?;
// ... check that the compilation succeeded ...
if !output.status.success() {
return Err(format!("LLVM compilation failed:{output:?}"));
}
// ... run the resulting executable ...
let output =
std::process::Command::new(&exe_path).output().map_err(|err| format!("{err:?}"))?;
// ... check it run normally ...
if !output.status.success() {
return Err(format!(
"The program at {path:?}, compiled with LLVM, exited unsuccessfully:{output:?}"
));
}
// ... cleanup that executable ...
std::fs::remove_file(exe_path).map_err(|err| format!("{err:?}"))?;
// ... and return the output(stdout + stderr - this allows UB checks to fire).
let mut res = output.stdout;
res.extend(output.stderr);
Ok(res)
}
/// Builds & runs a file with GCC.
fn release_gcc(path: &std::path::Path) -> Result<Vec<u8>, String> {
// Build a file named `gcc_elf`...
let exe_path = path.with_extension("gcc_elf");
// ... using the GCC backend ...
let output = std::process::Command::new("./y.sh")
.arg("rustc")
.arg(path)
.arg("-O")
.arg("-o")
.arg(&exe_path)
.output()
.map_err(|err| format!("{err:?}"))?;
// ... check that the compilation succeeded ...
if !output.status.success() {
return Err(format!("GCC compilation failed:{output:?}"));
}
// ... run the resulting executable ..
let output =
std::process::Command::new(&exe_path).output().map_err(|err| format!("{err:?}"))?;
// ... check it run normally ...
if !output.status.success() {
return Err(format!(
"The program at {path:?}, compiled with GCC, exited unsuccessfully:{output:?}"
));
}
// ... cleanup that executable ...
std::fs::remove_file(exe_path).map_err(|err| format!("{err:?}"))?;
// ... and return the output(stdout + stderr - this allows UB checks to fire).
let mut res = output.stdout;
res.extend(output.stderr);
Ok(res)
}
type ResultCache = Option<(Vec<u8>, Vec<u8>)>;
/// Generates a new rustlantis file, & compares the result of running it with GCC and LLVM.
fn test(seed: u64, print_tmp_vars: bool) -> Result<Result<(), std::path::PathBuf>, String> {
// Generate a Rust source...
let source_file = generate(seed, print_tmp_vars)?;
test_file(&source_file, true)
}
/// Tests a file with a cached LLVM result. Used for reduction, when it is known
/// that a given transformation should not change the execution result.
fn test_cached(
source_file: &Path,
remove_tmps: bool,
cache: &mut ResultCache,
) -> Result<Result<(), std::path::PathBuf>, String> {
// Test `source_file` with release GCC ...
let gcc_res = release_gcc(source_file)?;
if cache.is_none() {
// ...test `source_file` with debug LLVM ...
*cache = Some((debug_llvm(source_file)?, gcc_res.clone()));
}
let (llvm_res, old_gcc) = cache.as_ref().unwrap();
// ... compare the results ...
if *llvm_res != gcc_res && gcc_res == *old_gcc {
// .. if they don't match, report an error.
Ok(Err(source_file.to_path_buf()))
} else {
if remove_tmps {
std::fs::remove_file(source_file).map_err(|err| format!("{err:?}"))?;
}
Ok(Ok(()))
}
}
fn test_file(
source_file: &Path,
remove_tmps: bool,
) -> Result<Result<(), std::path::PathBuf>, String> {
let mut uncached = None;
test_cached(source_file, remove_tmps, &mut uncached)
}
/// Generates a new rustlantis file for us to run tests on.
fn generate(seed: u64, print_tmp_vars: bool) -> Result<std::path::PathBuf, String> {
use std::io::Write;
let mut out_path = std::env::temp_dir();
out_path.push(format!("fuzz{seed}.rs"));
// We need to get the command output here.
let mut generate = std::process::Command::new("cargo");
generate
.args(["run", "--release", "--bin", "generate"])
.arg(format!("{seed}"))
.current_dir("clones/rustlantis");
if print_tmp_vars {
generate.arg("--debug");
}
let out = generate.output().map_err(|err| format!("{err:?}"))?;
// Stuff the rustlantis output in a source file.
std::fs::File::create(&out_path)
.map_err(|err| format!("{err:?}"))?
.write_all(&out.stdout)
.map_err(|err| format!("{err:?}"))?;
Ok(out_path)
}

View File

@ -0,0 +1,432 @@
use std::io::Write;
use std::path::{Path, PathBuf};
use super::ResultCache;
/// Saves a reduced file for a given `stage`
fn save_reduction(lines: &[String], path: &Path, stage: &str) {
let mut path = path.to_path_buf();
path.set_extension(format!("rs.{stage}"));
let mut file = std::fs::File::create(&path).expect("Could not create the reduced example file");
for line in lines {
file.write_all(line.as_bytes()).expect("Could not save the reduced example");
}
}
/// Checks if a given reduction is valid.
fn test_reduction(lines: &[String], path: &Path, cache: &mut ResultCache) -> bool {
let mut path = path.to_path_buf();
path.set_extension("rs_reduced");
let mut file = std::fs::File::create(&path).expect("Could not create the reduced example file");
for line in lines {
file.write_all(line.as_bytes()).expect("Could not save the reduced example");
}
let res = super::test_cached(&path, false, cache);
let Ok(Err(_)) = res else {
return false;
};
true
}
/// Removes duplicate assignments in bulk.
/// If a line A = B is followed directly by A = C,
/// then removing the first line ought to be fully sound,
/// and not change the behaviour of the program at all. Detect & remove such lines.
fn remove_dup_assign(
file: &mut Vec<String>,
path: &PathBuf,
starts: usize,
ends: usize,
cache: &mut ResultCache,
) {
let mut file_copy = file.clone();
let mut reduction_count = 0;
// Not worth it.
if ends - starts < 8 {
return;
}
for index in starts..ends {
let Some((prefix, _)) = file_copy[index].split_once('=') else {
continue;
};
let Some((prefix2, postifx2)) = file_copy[index + 1].split_once('=') else {
continue;
};
let prefix = prefix.trim();
let prefix2 = prefix2.trim();
// FIXME: Right now, remove_dup_assign cares about assignments to the exact same place.
// However, given an assigemnt like this:
// ```
// A.0 = 1_u32;
// A = (2_u32, 3.0);
// ```
// The first assignment could be safely omitted.
// Additionally, we try to check if the second assignment could depend on the first one.
// In such cases, the result is likely to change, so we bail.
if prefix == prefix2 && !postifx2.contains(prefix) {
file_copy[index] = "".into();
reduction_count += 1;
}
}
// We have removed no lines - no point in testing.
if reduction_count == 0 {
return;
}
// Check if the removed lines affected the execution result in any way, shape or form.
if test_reduction(&file_copy, path, cache) {
println!("Reduced {path:?} by {reduction_count} lines `remove_dup_assign`");
*file = file_copy;
} else {
// The execution result changed.
// This can occur if the second assignment depended on the first one.
// Eg.
// ```
// a = b + c;
// a = a + d;
// ```
remove_dup_assign(file, path, starts, (starts + ends) / 2, cache);
remove_dup_assign(file, path, (starts + ends) / 2, ends, cache);
}
save_reduction(file, path, "remove_dup_assign");
}
/// Removes all the unneeded calls to `dump_var`. This is not something tools like `cvise` can do,
/// but it greately speeds up MIR interpretation + native execution.
fn remove_dump_var(file: &mut Vec<String>, path: &PathBuf) {
let mut curr = 0;
// ... try disabling `dump_vars` one by one, until only the necessary ones are left.
while curr < file.len() {
let Some(line) = file[curr..].iter().position(|line| line.contains("dump_var")) else {
// No more `dump_var`s to remove - exit early.
break;
};
// Make the line absolute again.
let line = line + curr;
let mut file_copy = file.clone();
// Try removing 3 consecutive lines(the call, block end and block beginning). This effectively removes a `dump_var`.
file_copy.remove(line);
file_copy.remove(line);
file_copy.remove(line);
// Not cached - the execution result can change.
let mut uncached = None;
// Check if this reduction is valid.
if test_reduction(&file_copy, path, &mut uncached) {
println!("Reduced {path:?} by 3 lines `remove_dump_var`");
*file = file_copy;
curr = line;
} else {
curr = line + 1;
}
}
save_reduction(file, path, "remove_dump_var");
}
/// Replaces matches with gotos where possible.
/// This exploits some properties of rustlantis(match arm order),
/// and is only soundly applicable to MIR generated by it.
/// Still, it is not something `cvise` can do, but it simplifies the code a ton.
fn match_to_goto(file: &mut Vec<String>, path: &PathBuf, cache: &mut ResultCache) {
let mut curr = 0;
while curr < file.len() {
let Some(match_starts) = file[curr..].iter().position(|line| line.contains("match")) else {
// No more `match`es to remove - exit early.
break;
};
let match_starts = match_starts + curr;
// Find the end of the match
let Some(match_ends) = file[match_starts..].iter().position(|line| line.contains('}'))
else {
// Can't find match end - exit early.
break;
};
let match_ends = match_ends + match_starts;
let match_body = &file[match_starts..match_ends];
// Find where this match should normally jump to.
// This *should* be the second-last arm of the match, as per the paper(the remaining blocks are decoys).
// If this ever changes, this reduction may not always be sound.
// This is not a problem, however: we NEED to use MIRI for reduction anwyway,
// and it will catch this issue.
let jumps_to = &match_body[match_body.len() - 2].trim();
let Some((_, bb_ident)) = jumps_to.split_once("bb") else {
break;
};
// We now have the number of the block we jump to at runtime.
let bb_ident = bb_ident.trim_matches(',');
// Try replacing this match with an unconditional jump.
let mut file_copy = file.clone();
for _ in match_starts..(match_ends + 1) {
file_copy.remove(match_starts);
}
file_copy.insert(match_starts, format!("Goto(bb{bb_ident})\n"));
if test_reduction(&file_copy, path, cache) {
println!("Reduced {path:?} by {} lines `match_to_goto`", match_ends - match_starts);
*file = file_copy;
curr = match_starts;
} else {
curr = match_ends;
}
}
save_reduction(file, path, "match_to_goto");
}
/// At this point, we can try "killing" blocks, by replacing their bodies with calls to `abort`.
/// This is always sound(the program aborts, so no UB can occur after the block),
/// and allows us to safely remove *a lot* of unneeded blocks.
fn block_abort(file: &mut Vec<String>, path: &PathBuf, cache: &mut ResultCache) {
let mut curr = 0;
while curr < file.len() {
let Some(block_starts) = file[curr..]
.iter()
.position(|line| line.starts_with("bb") && line.trim_end().ends_with(" = {"))
else {
// No more `block`s to kill - exit early.
break;
};
let block_starts = block_starts + curr;
// Find the beginning of the next block to find the end of this block.
let Some(block_ends) = file[(block_starts + 1)..]
.iter()
.position(|line| line.starts_with("bb") && line.trim_end().ends_with(" = {"))
else {
// No more `block`s to kill - exit early.
break;
};
let block_ends = block_starts + block_ends;
let block_starts = block_starts + 1;
let mut file_copy = file.clone();
// Remove the block body...
for _ in block_starts..(block_ends) {
file_copy.remove(block_starts);
}
// ..and insert an unconditional call to abort.
file_copy.insert(
block_starts,
"Call(tmp = core::intrinsics::abort(), ReturnTo(bb1), UnwindUnreachable())\n"
.to_string(),
);
file_copy.insert(block_starts, "let tmp = ();\n".to_string());
if test_reduction(&file_copy, path, cache) {
println!("Reduced {path:?} by {} lines `block_abort`", block_ends - block_starts - 2);
*file = file_copy;
curr = block_starts;
} else {
curr = block_ends;
}
}
save_reduction(file, path, "block_abort");
}
/// Removes unreachable basic blocks
fn remove_block(file: &mut Vec<String>, path: &PathBuf, cache: &mut ResultCache) {
let mut curr = 0;
// Next, we try to outright remove blocks.
while curr < file.len() {
let Some(block_starts) = file[curr..]
.iter()
.position(|line| line.starts_with("bb") && line.trim_end().ends_with(" = {"))
else {
// No more `block`s to remove - exit early.
break;
};
let block_starts = block_starts + curr;
// Find the beginning of the next block to find the end of this block.
let Some(block_ends) = file[(block_starts + 1)..]
.iter()
.position(|line| line.starts_with("bb") && line.trim_end().ends_with(" = {"))
else {
// No more `block`s to remove - exit early.
break;
};
let block_ends = block_starts + block_ends + 1;
// Large blocks are likely to be necessary.
if block_ends - block_starts > 6 {
curr = block_starts + 1;
continue;
}
let mut file_copy = file.clone();
file_copy.drain(block_starts..block_ends);
if test_reduction(&file_copy, path, cache) {
println!("Reduced {path:?} by {} lines `remove_blocks`", block_ends - block_starts);
*file = file_copy;
curr = block_starts;
} else {
curr = block_starts + 1;
}
}
save_reduction(file, path, "remove_block");
}
/// Merges blocks ending with unconditional jumps.
fn linearize_cf(file: &mut Vec<String>, path: &PathBuf, cache: &mut ResultCache) {
let mut curr = 0;
// Next, we try to linearize the control flow. What the does that mean?
// Given a sequence like this:
// Goto(bb22)
// }
// bb22 = {
// We remove those 3 lines, merging the blocks together. This is not something `cvise` can do,
// and it makes other transformations easier.
while curr < file.len() {
let Some(block_starts) = file[curr..]
.iter()
.position(|line| line.starts_with("bb") && line.trim_end().ends_with(" = {"))
else {
// No more `block`s to remove - exit early.
break;
};
let block_starts = block_starts + curr;
// Extract the block id.
let Some((block, _)) = file[block_starts].split_once('=') else {
curr = block_starts + 1;
continue;
};
let block = block.trim();
if file[block_starts - 2].trim() != format!("Goto({block})") {
curr = block_starts + 1;
continue;
}
let mut file_copy = file.clone();
// Try removing 3 consecutive lines(the goto, block end and block beginning). This effectively removes a `Goto(next)`.
file_copy.remove(block_starts - 2);
file_copy.remove(block_starts - 2);
file_copy.remove(block_starts - 2);
// Check if this reduction is valid.
if test_reduction(&file_copy, path, cache) {
println!("Reduced {path:?} by 3 lines `linearize_cf`");
*file = file_copy;
curr = block_starts;
} else {
curr = block_starts + 1;
}
}
save_reduction(file, path, "linearize_cf");
}
/// Replaces a call to a given function with a 0 assignment to the destination place, and a Goto.
/// This is always sound, because:
/// 1. All the functions arguments are always initialized
/// 2. and point to initialized memory(the operand of &raw must be an initialized place in rustlantis).
fn remove_fn_calls(file: &mut Vec<String>, path: &PathBuf, cache: &mut ResultCache) {
let mut curr = 0;
while curr < file.len() {
let Some(fn_call) =
file[curr..].iter().position(|line| line.contains("Call(") && line.contains(" = fn"))
else {
// No more calls to remove - exit early.
break;
};
let fn_call = fn_call + curr;
let line = file[fn_call].trim();
// Skip the Call(
let line = &line["Call(".len()..];
// Extract the destination place
let Some((place, line)) = line.split_once('=') else {
curr = fn_call + 1;
continue;
};
// Skip till the return block id.
let Some((_, line)) = line.split_once("ReturnTo(") else {
curr = fn_call + 1;
continue;
};
// Extract the full return block
let Some((block, _)) = line.split_once(')') else {
curr = fn_call + 1;
continue;
};
let mut file_copy = file.clone();
// Remove the call.
file_copy.remove(fn_call);
file_copy.insert(fn_call, format!("Goto({block})\n"));
file_copy.insert(fn_call, format!("{place} = 0;\n"));
// Check if this reduction is valid.
if test_reduction(&file_copy, path, cache) {
println!("Reduced {path:?} using `remove_fn_calls` {cache:?}");
*file = file_copy;
curr = fn_call;
} else {
curr = fn_call + 1;
}
}
save_reduction(file, path, "remove_fn_calls");
}
/// Fully removes unreachable functions.
fn remove_fns(file: &mut Vec<String>, path: &PathBuf, cache: &mut ResultCache) {
let mut curr = 0;
while curr < file.len() {
// Find a function start
let Some(fn_start) = file[curr..].iter().position(|line| {
line.contains("#[custom_mir(dialect = \"runtime\", phase = \"initial\")]")
}) else {
// No more functions to remove - exit early.
break;
};
// Find the next function(and use that to find the end of this one).
// FIXME: this check is flawed: it will never remove the very last function(the one before main).
// The other checks will turn that function into a single call to abort, but it is still annoying that it is kept.
let fn_start = fn_start + curr;
let Some(fn_end) = file[(fn_start + 3)..].iter().position(|line| line.contains("fn fn"))
else {
// No more functions to remove - exit early.
break;
};
let fn_end = fn_start + 2 + fn_end;
let mut file_copy = file.clone();
// Remove the function.\\
file_copy.drain(fn_start..fn_end);
// Check if this reduction is valid.
if test_reduction(&file_copy, path, cache) {
println!("Reduced {path:?} by {} lines `remove_fns`", fn_end - fn_start);
*file = file_copy;
} else {
curr = fn_start + 1;
}
}
save_reduction(file, path, "remove_fns");
}
pub(super) fn reduce(path: impl AsRef<Path>) {
let path = path.as_ref().to_owned();
// ... read the file to a buffer ..
let file = std::fs::read_to_string(&path).expect("Could not open the file to reduce");
let mut file: Vec<_> = file.split_inclusive('\n').map(|s| s.to_string()).collect();
// ... and run reduction passes.
println!("running `remove_dump_var` on {path:?}.");
remove_dump_var(&mut file, &path);
// After `dump_var`, the execution results ought not to change. Cache them.
let mut cache = None;
// Fill the cache
assert!(
test_reduction(&file, &path, &mut cache),
"Reduction error: check that the input file is a valid reproducer."
);
println!("cache:{cache:?}");
println!("running `remove_fn_calls` on {path:?}.");
remove_fn_calls(&mut file, &path, &mut cache);
println!("running `remove_fns` on {path:?}.");
remove_fns(&mut file, &path, &mut cache);
let len = file.len();
println!("running `remove_dup_assign` on {path:?}.");
remove_dup_assign(&mut file, &path, 0, len, &mut cache);
file.retain(|line| !line.is_empty());
println!("running `match_to_goto` on {path:?}.");
match_to_goto(&mut file, &path, &mut cache);
println!("running `block_abort` on {path:?}.");
block_abort(&mut file, &path, &mut cache);
println!("running `remove_block` on {path:?}.");
remove_block(&mut file, &path, &mut cache);
println!("running `linearize_cf` on {path:?}.");
linearize_cf(&mut file, &path, &mut cache);
let mut out = std::fs::File::create(&path).expect("Could not save the reduction result.");
let file = file.into_iter().collect::<String>();
out.write_all(file.as_bytes()).expect("failed to write into file");
}

View File

@ -15,7 +15,7 @@ pub fn run() -> Result<(), String> {
config.no_download = true;
config.setup_gcc_path()?;
if let Some(gcc_path) = config.gcc_path {
println!("{}", gcc_path);
println!("{gcc_path}");
}
Ok(())
}

View File

@ -5,6 +5,7 @@ mod clean;
mod clone_gcc;
mod config;
mod fmt;
mod fuzz;
mod info;
mod prepare;
mod rust_tools;
@ -42,7 +43,8 @@ Commands:
test : Runs tests for the project.
info : Displays information about the build environment and project configuration.
clone-gcc : Clones the GCC compiler from a specified source.
fmt : Runs rustfmt"
fmt : Runs rustfmt
fuzz : Fuzzes `cg_gcc` using rustlantis"
);
}
@ -56,6 +58,7 @@ pub enum Command {
Test,
Info,
Fmt,
Fuzz,
}
fn main() {
@ -75,6 +78,7 @@ fn main() {
Some("info") => Command::Info,
Some("clone-gcc") => Command::CloneGcc,
Some("fmt") => Command::Fmt,
Some("fuzz") => Command::Fuzz,
Some("--help") => {
usage();
process::exit(0);
@ -97,6 +101,7 @@ fn main() {
Command::Info => info::run(),
Command::CloneGcc => clone_gcc::run(),
Command::Fmt => fmt::run(),
Command::Fuzz => fuzz::run(),
} {
eprintln!("Command failed to run: {e}");
process::exit(1);

View File

@ -18,9 +18,9 @@ fn prepare_libcore(
if let Some(path) = sysroot_source {
rustlib_dir = Path::new(&path)
.canonicalize()
.map_err(|error| format!("Failed to canonicalize path: {:?}", error))?;
.map_err(|error| format!("Failed to canonicalize path: {error:?}"))?;
if !rustlib_dir.is_dir() {
return Err(format!("Custom sysroot path {:?} not found", rustlib_dir));
return Err(format!("Custom sysroot path {rustlib_dir:?} not found"));
}
} else {
let rustc_path = match get_rustc_path() {
@ -36,17 +36,17 @@ fn prepare_libcore(
rustlib_dir = parent
.join("../lib/rustlib/src/rust")
.canonicalize()
.map_err(|error| format!("Failed to canonicalize path: {:?}", error))?;
.map_err(|error| format!("Failed to canonicalize path: {error:?}"))?;
if !rustlib_dir.is_dir() {
return Err("Please install `rust-src` component".to_string());
}
}
let sysroot_dir = sysroot_path.join("sysroot_src");
if sysroot_dir.is_dir() {
if let Err(error) = fs::remove_dir_all(&sysroot_dir) {
return Err(format!("Failed to remove `{}`: {:?}", sysroot_dir.display(), error,));
}
if sysroot_dir.is_dir()
&& let Err(error) = fs::remove_dir_all(&sysroot_dir)
{
return Err(format!("Failed to remove `{}`: {:?}", sysroot_dir.display(), error,));
}
let sysroot_library_dir = sysroot_dir.join("library");
@ -122,7 +122,7 @@ fn prepare_rand() -> Result<(), String> {
// Apply patch for the rand crate.
let file_path = "patches/crates/0001-Remove-deny-warnings.patch";
let rand_dir = Path::new("build/rand");
println!("[GIT] apply `{}`", file_path);
println!("[GIT] apply `{file_path}`");
let path = Path::new("../..").join(file_path);
run_command_with_output(&[&"git", &"apply", &path], Some(rand_dir))?;
run_command_with_output(&[&"git", &"add", &"-A"], Some(rand_dir))?;
@ -149,7 +149,7 @@ fn clone_and_setup<F>(repo_url: &str, checkout_commit: &str, extra: Option<F>) -
where
F: Fn(&Path) -> Result<(), String>,
{
let clone_result = git_clone_root_dir(repo_url, &Path::new(crate::BUILD_DIR), false)?;
let clone_result = git_clone_root_dir(repo_url, Path::new(crate::BUILD_DIR), false)?;
if !clone_result.ran_clone {
println!("`{}` has already been cloned", clone_result.repo_name);
}

View File

@ -1,24 +1,22 @@
use std::collections::HashMap;
use std::ffi::OsStr;
#[cfg(unix)]
use std::os::unix::process::CommandExt;
use std::path::PathBuf;
use crate::config::ConfigInfo;
use crate::utils::{
get_toolchain, run_command_with_output_and_env_no_err, rustc_toolchain_version_info,
rustc_version_info,
};
use crate::utils::{get_toolchain, rustc_toolchain_version_info, rustc_version_info};
fn args(command: &str) -> Result<Option<Vec<String>>, String> {
// We skip the binary and the "cargo"/"rustc" option.
if let Some("--help") = std::env::args().skip(2).next().as_deref() {
if let Some("--help") = std::env::args().nth(2).as_deref() {
usage(command);
return Ok(None);
}
let args = std::env::args().skip(2).collect::<Vec<_>>();
if args.is_empty() {
return Err(format!(
"Expected at least one argument for `{}` subcommand, found none",
command
"Expected at least one argument for `{command}` subcommand, found none"
));
}
Ok(Some(args))
@ -27,12 +25,11 @@ fn args(command: &str) -> Result<Option<Vec<String>>, String> {
fn usage(command: &str) {
println!(
r#"
`{}` command help:
`{command}` command help:
[args] : Arguments to be passed to the cargo command
--help : Show this help
"#,
command,
)
}
@ -51,10 +48,10 @@ impl RustcTools {
// expected.
let current_dir = std::env::current_dir()
.and_then(|path| path.canonicalize())
.map_err(|error| format!("Failed to get current directory path: {:?}", error))?;
.map_err(|error| format!("Failed to get current directory path: {error:?}"))?;
let current_exe = std::env::current_exe()
.and_then(|path| path.canonicalize())
.map_err(|error| format!("Failed to get current exe path: {:?}", error))?;
.map_err(|error| format!("Failed to get current exe path: {error:?}"))?;
let mut parent_dir =
current_exe.components().map(|comp| comp.as_os_str()).collect::<Vec<_>>();
// We run this script from "build_system/target/release/y", so we need to remove these elements.
@ -68,7 +65,7 @@ impl RustcTools {
));
}
}
let parent_dir = PathBuf::from(parent_dir.join(&OsStr::new("/")));
let parent_dir = PathBuf::from(parent_dir.join(OsStr::new("/")));
std::env::set_current_dir(&parent_dir).map_err(|error| {
format!("Failed to go to `{}` folder: {:?}", parent_dir.display(), error)
})?;
@ -92,11 +89,31 @@ impl RustcTools {
std::env::set_current_dir(&current_dir).map_err(|error| {
format!("Failed to go back to `{}` folder: {:?}", current_dir.display(), error)
})?;
let toolchain = format!("+{}", toolchain);
let toolchain = format!("+{toolchain}");
Ok(Some(Self { toolchain, args, env, config }))
}
}
fn exec(input: &[&dyn AsRef<OsStr>], env: &HashMap<String, String>) -> Result<(), String> {
#[cfg(unix)]
{
// We use `exec` to call the `execvp` syscall instead of creating a new process where the
// command will be executed because very few signals can actually kill a current process,
// so if segmentation fault (SIGSEGV signal) happens and we raise to the current process,
// it will simply do nothing and we won't have the nice error message for the shell.
let error = crate::utils::get_command_inner(input, None, Some(env)).exec();
eprintln!("execvp syscall failed: {error:?}");
std::process::exit(1);
}
#[cfg(not(unix))]
{
if crate::utils::run_command_with_output_and_env_no_err(input, None, Some(env)).is_err() {
std::process::exit(1);
}
Ok(())
}
}
pub fn run_cargo() -> Result<(), String> {
let Some(mut tools) = RustcTools::new("cargo")? else { return Ok(()) };
let rustflags = tools.env.get("RUSTFLAGS").cloned().unwrap_or_default();
@ -105,11 +122,7 @@ pub fn run_cargo() -> Result<(), String> {
for arg in &tools.args {
command.push(arg);
}
if run_command_with_output_and_env_no_err(&command, None, Some(&tools.env)).is_err() {
std::process::exit(1);
}
Ok(())
exec(&command, &tools.env)
}
pub fn run_rustc() -> Result<(), String> {
@ -118,8 +131,5 @@ pub fn run_rustc() -> Result<(), String> {
for arg in &tools.args {
command.push(arg);
}
if run_command_with_output_and_env_no_err(&command, None, Some(&tools.env)).is_err() {
std::process::exit(1);
}
Ok(())
exec(&command, &tools.env)
}

View File

@ -9,8 +9,8 @@ use crate::build;
use crate::config::{Channel, ConfigInfo};
use crate::utils::{
create_dir, get_sysroot_dir, get_toolchain, git_clone, git_clone_root_dir, remove_file,
run_command, run_command_with_env, run_command_with_output_and_env, rustc_version_info,
split_args, walk_dir,
run_command, run_command_with_env, run_command_with_output, run_command_with_output_and_env,
rustc_version_info, split_args, walk_dir,
};
type Env = HashMap<String, String>;
@ -42,7 +42,7 @@ fn get_runners() -> Runners {
);
runners.insert("--extended-regex-tests", ("Run extended regex tests", extended_regex_tests));
runners.insert("--mini-tests", ("Run mini tests", mini_tests));
runners.insert("--cargo-tests", ("Run cargo tests", cargo_tests));
runners
}
@ -53,9 +53,9 @@ fn get_number_after_arg(
match args.next() {
Some(nb) if !nb.is_empty() => match usize::from_str(&nb) {
Ok(nb) => Ok(nb),
Err(_) => Err(format!("Expected a number after `{}`, found `{}`", option, nb)),
Err(_) => Err(format!("Expected a number after `{option}`, found `{nb}`")),
},
_ => Err(format!("Expected a number after `{}`, found nothing", option)),
_ => Err(format!("Expected a number after `{option}`, found nothing")),
}
}
@ -76,8 +76,8 @@ fn show_usage() {
for (option, (doc, _)) in get_runners() {
// FIXME: Instead of using the hard-coded `23` value, better to compute it instead.
let needed_spaces = 23_usize.saturating_sub(option.len());
let spaces: String = std::iter::repeat(' ').take(needed_spaces).collect();
println!(" {}{}: {}", option, spaces, doc);
let spaces: String = std::iter::repeat_n(' ', needed_spaces).collect();
println!(" {option}{spaces}: {doc}");
}
println!(" --help : Show this help");
}
@ -88,6 +88,8 @@ struct TestArg {
use_system_gcc: bool,
runners: Vec<String>,
flags: Vec<String>,
/// Additional arguments, to be passed to commands like `cargo test`.
test_args: Vec<String>,
nb_parts: Option<usize>,
current_part: Option<usize>,
sysroot_panic_abort: bool,
@ -137,13 +139,14 @@ impl TestArg {
test_arg.sysroot_features.push(feature);
}
_ => {
return Err(format!("Expected an argument after `{}`, found nothing", arg));
return Err(format!("Expected an argument after `{arg}`, found nothing"));
}
},
"--help" => {
show_usage();
return Ok(None);
}
"--" => test_arg.test_args.extend(&mut args),
x if runners.contains_key(x)
&& !test_arg.runners.iter().any(|runner| runner == x) =>
{
@ -151,7 +154,7 @@ impl TestArg {
}
arg => {
if !test_arg.config_info.parse_argument(arg, &mut args)? {
return Err(format!("Unknown option {}", arg));
return Err(format!("Unknown option {arg}"));
}
}
}
@ -189,7 +192,7 @@ fn build_if_no_backend(env: &Env, args: &TestArg) -> Result<(), String> {
command.push(&"--release");
&tmp_env
} else {
&env
env
};
for flag in args.flags.iter() {
command.push(flag);
@ -203,6 +206,33 @@ fn clean(_env: &Env, args: &TestArg) -> Result<(), String> {
create_dir(&path)
}
fn cargo_tests(test_env: &Env, test_args: &TestArg) -> Result<(), String> {
// First, we call `mini_tests` to build minicore for us. This ensures we are testing with a working `minicore`,
// and that any changes we have made affect `minicore`(since it would get rebuilt).
mini_tests(test_env, test_args)?;
// Then, we copy some of the env vars from `test_env`
// We don't want to pass things like `RUSTFLAGS`, since they contain the -Zcodegen-backend flag.
// That would force `cg_gcc` to *rebuild itself* and only then run tests, which is undesirable.
let mut env = HashMap::new();
env.insert(
"LD_LIBRARY_PATH".into(),
test_env.get("LD_LIBRARY_PATH").expect("LD_LIBRARY_PATH missing!").to_string(),
);
env.insert(
"LIBRARY_PATH".into(),
test_env.get("LIBRARY_PATH").expect("LIBRARY_PATH missing!").to_string(),
);
env.insert(
"CG_RUSTFLAGS".into(),
test_env.get("CG_RUSTFLAGS").map(|s| s.as_str()).unwrap_or("").to_string(),
);
// Pass all the default args + the user-specified ones.
let mut args: Vec<&dyn AsRef<OsStr>> = vec![&"cargo", &"test"];
args.extend(test_args.test_args.iter().map(|s| s as &dyn AsRef<OsStr>));
run_command_with_output_and_env(&args, None, Some(&env))?;
Ok(())
}
fn mini_tests(env: &Env, args: &TestArg) -> Result<(), String> {
// FIXME: create a function "display_if_not_quiet" or something along the line.
println!("[BUILD] mini_core");
@ -222,7 +252,7 @@ fn mini_tests(env: &Env, args: &TestArg) -> Result<(), String> {
&"--target",
&args.config_info.target_triple,
]);
run_command_with_output_and_env(&command, None, Some(&env))?;
run_command_with_output_and_env(&command, None, Some(env))?;
// FIXME: create a function "display_if_not_quiet" or something along the line.
println!("[BUILD] example");
@ -234,7 +264,7 @@ fn mini_tests(env: &Env, args: &TestArg) -> Result<(), String> {
&"--target",
&args.config_info.target_triple,
]);
run_command_with_output_and_env(&command, None, Some(&env))?;
run_command_with_output_and_env(&command, None, Some(env))?;
// FIXME: create a function "display_if_not_quiet" or something along the line.
println!("[AOT] mini_core_hello_world");
@ -249,14 +279,14 @@ fn mini_tests(env: &Env, args: &TestArg) -> Result<(), String> {
&"--target",
&args.config_info.target_triple,
]);
run_command_with_output_and_env(&command, None, Some(&env))?;
run_command_with_output_and_env(&command, None, Some(env))?;
let command: &[&dyn AsRef<OsStr>] = &[
&Path::new(&args.config_info.cargo_target_dir).join("mini_core_hello_world"),
&"abc",
&"bcd",
];
maybe_run_command_in_vm(&command, env, args)?;
maybe_run_command_in_vm(command, env, args)?;
Ok(())
}
@ -454,22 +484,47 @@ fn setup_rustc(env: &mut Env, args: &TestArg) -> Result<PathBuf, String> {
} else {
run_command_with_output_and_env(&[&"git", &"checkout"], rust_dir, Some(env))?;
}
let mut patches = Vec::new();
walk_dir(
"patches/tests",
&mut |_| Ok(()),
&mut |file_path: &Path| {
patches.push(file_path.to_path_buf());
Ok(())
},
false,
)?;
patches.sort();
// TODO: remove duplication with prepare.rs by creating a apply_patch function in the utils
// module.
for file_path in patches {
println!("[GIT] apply `{}`", file_path.display());
let path = Path::new("../..").join(file_path);
run_command_with_output(&[&"git", &"apply", &path], rust_dir)?;
run_command_with_output(&[&"git", &"add", &"-A"], rust_dir)?;
run_command_with_output(
&[&"git", &"commit", &"--no-gpg-sign", &"-m", &format!("Patch {}", path.display())],
rust_dir,
)?;
}
let cargo = String::from_utf8(
run_command_with_env(&[&"rustup", &"which", &"cargo"], rust_dir, Some(env))?.stdout,
)
.map_err(|error| format!("Failed to retrieve cargo path: {:?}", error))
.map_err(|error| format!("Failed to retrieve cargo path: {error:?}"))
.and_then(|cargo| {
let cargo = cargo.trim().to_owned();
if cargo.is_empty() { Err(format!("`cargo` path is empty")) } else { Ok(cargo) }
if cargo.is_empty() { Err("`cargo` path is empty".to_string()) } else { Ok(cargo) }
})?;
let rustc = String::from_utf8(
run_command_with_env(&[&"rustup", &toolchain, &"which", &"rustc"], rust_dir, Some(env))?
.stdout,
)
.map_err(|error| format!("Failed to retrieve rustc path: {:?}", error))
.map_err(|error| format!("Failed to retrieve rustc path: {error:?}"))
.and_then(|rustc| {
let rustc = rustc.trim().to_owned();
if rustc.is_empty() { Err(format!("`rustc` path is empty")) } else { Ok(rustc) }
if rustc.is_empty() { Err("`rustc` path is empty".to_string()) } else { Ok(rustc) }
})?;
let llvm_filecheck = match run_command_with_env(
&[
@ -479,7 +534,8 @@ fn setup_rustc(env: &mut Env, args: &TestArg) -> Result<PathBuf, String> {
which FileCheck-11 || \
which FileCheck-12 || \
which FileCheck-13 || \
which FileCheck-14",
which FileCheck-14 || \
which FileCheck",
],
rust_dir,
Some(env),
@ -487,13 +543,15 @@ fn setup_rustc(env: &mut Env, args: &TestArg) -> Result<PathBuf, String> {
Ok(cmd) => String::from_utf8_lossy(&cmd.stdout).to_string(),
Err(_) => {
eprintln!("Failed to retrieve LLVM FileCheck, ignoring...");
// FIXME: the test tests/run-make/no-builtins-attribute will fail if we cannot find
// FileCheck.
String::new()
}
};
let file_path = rust_dir_path.join("config.toml");
std::fs::write(
&file_path,
&format!(
format!(
r#"change-id = 115898
[rust]
@ -532,7 +590,7 @@ fn asm_tests(env: &Env, args: &TestArg) -> Result<(), String> {
let codegen_backend_path = format!(
"{pwd}/target/{channel}/librustc_codegen_gcc.{dylib_ext}",
pwd = std::env::current_dir()
.map_err(|error| format!("`current_dir` failed: {:?}", error))?
.map_err(|error| format!("`current_dir` failed: {error:?}"))?
.display(),
channel = args.config_info.channel.as_str(),
dylib_ext = args.config_info.dylib_ext,
@ -587,11 +645,11 @@ where
F: Fn(&[&dyn AsRef<OsStr>], Option<&Path>, &Env) -> Result<(), String>,
{
let toolchain = get_toolchain()?;
let toolchain_arg = format!("+{}", toolchain);
let toolchain_arg = format!("+{toolchain}");
let rustc_version = String::from_utf8(
run_command_with_env(&[&args.config_info.rustc_command[0], &"-V"], cwd, Some(env))?.stdout,
)
.map_err(|error| format!("Failed to retrieve rustc version: {:?}", error))?;
.map_err(|error| format!("Failed to retrieve rustc version: {error:?}"))?;
let rustc_toolchain_version = String::from_utf8(
run_command_with_env(
&[&args.config_info.rustc_command[0], &toolchain_arg, &"-V"],
@ -600,20 +658,19 @@ where
)?
.stdout,
)
.map_err(|error| format!("Failed to retrieve rustc +toolchain version: {:?}", error))?;
.map_err(|error| format!("Failed to retrieve rustc +toolchain version: {error:?}"))?;
if rustc_version != rustc_toolchain_version {
eprintln!(
"rustc_codegen_gcc is built for `{}` but the default rustc version is `{}`.",
rustc_toolchain_version, rustc_version,
"rustc_codegen_gcc is built for `{rustc_toolchain_version}` but the default rustc version is `{rustc_version}`.",
);
eprintln!("Using `{}`.", rustc_toolchain_version);
eprintln!("Using `{rustc_toolchain_version}`.");
}
let mut env = env.clone();
let rustflags = env.get("RUSTFLAGS").cloned().unwrap_or_default();
env.insert("RUSTDOCFLAGS".to_string(), rustflags);
let mut cargo_command: Vec<&dyn AsRef<OsStr>> = vec![&"cargo", &toolchain_arg];
cargo_command.extend_from_slice(&command);
cargo_command.extend_from_slice(command);
callback(&cargo_command, cwd, &env)
}
@ -680,7 +737,15 @@ fn test_libcore(env: &Env, args: &TestArg) -> Result<(), String> {
println!("[TEST] libcore");
let path = get_sysroot_dir().join("sysroot_src/library/coretests");
let _ = remove_dir_all(path.join("target"));
run_cargo_command(&[&"test"], Some(&path), env, args)?;
// TODO(antoyo): run in release mode when we fix the failures.
// TODO(antoyo): remove the --skip f16::test_total_cmp when this issue is fixed:
// https://github.com/rust-lang/rust/issues/141503
run_cargo_command(
&[&"test", &"--", &"--skip", &"f16::test_total_cmp"],
Some(&path),
env,
args,
)?;
Ok(())
}
@ -818,7 +883,7 @@ fn contains_ui_error_patterns(file_path: &Path, keep_lto_tests: bool) -> Result<
// Tests generating errors.
let file = File::open(file_path)
.map_err(|error| format!("Failed to read `{}`: {:?}", file_path.display(), error))?;
for line in BufReader::new(file).lines().filter_map(|line| line.ok()) {
for line in BufReader::new(file).lines().map_while(Result::ok) {
let line = line.trim();
if line.is_empty() {
continue;
@ -887,7 +952,7 @@ where
if !prepare_files_callback(&rust_path)? {
// FIXME: create a function "display_if_not_quiet" or something along the line.
println!("Keeping all {} tests", test_type);
println!("Keeping all {test_type} tests");
}
if test_type == "ui" {
@ -919,8 +984,7 @@ where
"borrowck",
"test-attrs",
]
.iter()
.any(|name| *name == dir_name)
.contains(&dir_name)
{
remove_dir_all(dir).map_err(|error| {
format!("Failed to remove folder `{}`: {:?}", dir.display(), error)
@ -975,10 +1039,7 @@ where
if nb_parts > 0 {
let current_part = args.current_part.unwrap();
// FIXME: create a function "display_if_not_quiet" or something along the line.
println!(
"Splitting ui_test into {} parts (and running part {})",
nb_parts, current_part
);
println!("Splitting ui_test into {nb_parts} parts (and running part {current_part})");
let out = String::from_utf8(
run_command(
&[
@ -996,7 +1057,7 @@ where
)?
.stdout,
)
.map_err(|error| format!("Failed to retrieve output of find command: {:?}", error))?;
.map_err(|error| format!("Failed to retrieve output of find command: {error:?}"))?;
let mut files = out
.split('\n')
.map(|line| line.trim())
@ -1016,7 +1077,7 @@ where
}
// FIXME: create a function "display_if_not_quiet" or something along the line.
println!("[TEST] rustc {} test suite", test_type);
println!("[TEST] rustc {test_type} test suite");
env.insert("COMPILETEST_FORCE_STAGE0".to_string(), "1".to_string());
let extra =
@ -1040,7 +1101,7 @@ where
&"always",
&"--stage",
&"0",
&format!("tests/{}", test_type),
&format!("tests/{test_type}"),
&"--compiletest-rustc-args",
&rustc_args,
],
@ -1051,19 +1112,18 @@ where
}
fn test_rustc(env: &Env, args: &TestArg) -> Result<(), String> {
//test_rustc_inner(env, args, |_| Ok(false), false, "run-make")?;
test_rustc_inner(env, args, |_| Ok(false), false, "run-make")?;
test_rustc_inner(env, args, |_| Ok(false), false, "ui")
}
fn test_failing_rustc(env: &Env, args: &TestArg) -> Result<(), String> {
let result1 = Ok(());
/*test_rustc_inner(
let result1 = test_rustc_inner(
env,
args,
retain_files_callback("tests/failing-run-make-tests.txt", "run-make"),
false,
"run-make",
)*/
);
let result2 = test_rustc_inner(
env,
@ -1084,14 +1144,13 @@ fn test_successful_rustc(env: &Env, args: &TestArg) -> Result<(), String> {
false,
"ui",
)?;
Ok(())
/*test_rustc_inner(
test_rustc_inner(
env,
args,
remove_files_callback("tests/failing-run-make-tests.txt", "run-make"),
false,
"run-make",
)*/
)
}
fn test_failing_ui_pattern_tests(env: &Env, args: &TestArg) -> Result<(), String> {
@ -1118,7 +1177,7 @@ fn retain_files_callback<'a>(
run_command(
&[
&"find",
&format!("tests/{}", test_type),
&format!("tests/{test_type}"),
&"-mindepth",
&"1",
&"-type",
@ -1137,7 +1196,7 @@ fn retain_files_callback<'a>(
run_command(
&[
&"find",
&format!("tests/{}", test_type),
&format!("tests/{test_type}"),
&"-type",
&"f",
&"-name",
@ -1152,15 +1211,12 @@ fn retain_files_callback<'a>(
}
// Putting back only the failing ones.
if let Ok(files) = std::fs::read_to_string(&file_path) {
if let Ok(files) = std::fs::read_to_string(file_path) {
for file in files.split('\n').map(|line| line.trim()).filter(|line| !line.is_empty()) {
run_command(&[&"git", &"checkout", &"--", &file], Some(&rust_path))?;
run_command(&[&"git", &"checkout", &"--", &file], Some(rust_path))?;
}
} else {
println!(
"Failed to read `{}`, not putting back failing {} tests",
file_path, test_type
);
println!("Failed to read `{file_path}`, not putting back failing {test_type} tests");
}
Ok(true)
@ -1188,8 +1244,7 @@ fn remove_files_callback<'a>(
}
} else {
println!(
"Failed to read `{}`, not putting back failing {} tests",
file_path, test_type
"Failed to read `{file_path}`, not putting back failing {test_type} tests"
);
}
} else {
@ -1202,7 +1257,7 @@ fn remove_files_callback<'a>(
remove_file(&path)?;
}
} else {
println!("Failed to read `{}`, not putting back failing ui tests", file_path);
println!("Failed to read `{file_path}`, not putting back failing ui tests");
}
}
Ok(true)
@ -1217,7 +1272,9 @@ fn run_all(env: &Env, args: &TestArg) -> Result<(), String> {
// asm_tests(env, args)?;
test_libcore(env, args)?;
extended_sysroot_tests(env, args)?;
cargo_tests(env, args)?;
test_rustc(env, args)?;
Ok(())
}

View File

@ -1,7 +1,5 @@
use std::collections::HashMap;
use std::ffi::OsStr;
#[cfg(unix)]
use std::ffi::c_int;
use std::fmt::Debug;
use std::fs;
#[cfg(unix)]
@ -9,11 +7,6 @@ use std::os::unix::process::ExitStatusExt;
use std::path::{Path, PathBuf};
use std::process::{Command, ExitStatus, Output};
#[cfg(unix)]
unsafe extern "C" {
fn raise(signal: c_int) -> c_int;
}
fn exec_command(
input: &[&dyn AsRef<OsStr>],
cwd: Option<&Path>,
@ -27,17 +20,14 @@ fn exec_command(
#[cfg(unix)]
{
if let Some(signal) = status.signal() {
unsafe {
raise(signal as _);
}
// In case the signal didn't kill the current process.
return Err(command_error(input, &cwd, format!("Process received signal {}", signal)));
return Err(command_error(input, &cwd, format!("Process received signal {signal}")));
}
}
Ok(status)
}
fn get_command_inner(
pub(crate) fn get_command_inner(
input: &[&dyn AsRef<OsStr>],
cwd: Option<&Path>,
env: Option<&HashMap<String, String>>,
@ -75,18 +65,18 @@ fn check_exit_status(
);
let input = input.iter().map(|i| i.as_ref()).collect::<Vec<&OsStr>>();
if show_err {
eprintln!("Command `{:?}` failed", input);
eprintln!("Command `{input:?}` failed");
}
if let Some(output) = output {
let stdout = String::from_utf8_lossy(&output.stdout);
if !stdout.is_empty() {
error.push_str("\n==== STDOUT ====\n");
error.push_str(&*stdout);
error.push_str(&stdout);
}
let stderr = String::from_utf8_lossy(&output.stderr);
if !stderr.is_empty() {
error.push_str("\n==== STDERR ====\n");
error.push_str(&*stderr);
error.push_str(&stderr);
}
}
Err(error)
@ -136,6 +126,7 @@ pub fn run_command_with_output_and_env(
Ok(())
}
#[cfg(not(unix))]
pub fn run_command_with_output_and_env_no_err(
input: &[&dyn AsRef<OsStr>],
cwd: Option<&Path>,
@ -242,7 +233,7 @@ pub fn get_toolchain() -> Result<String, String> {
if !line.starts_with("channel") {
return None;
}
line.split('"').skip(1).next()
line.split('"').nth(1)
})
.next()
{
@ -281,7 +272,7 @@ fn git_clone_inner(
}
fn get_repo_name(url: &str) -> String {
let repo_name = url.split('/').last().unwrap();
let repo_name = url.split('/').next_back().unwrap();
match repo_name.strip_suffix(".git") {
Some(n) => n.to_string(),
None => repo_name.to_string(),

View File

@ -77,18 +77,18 @@ fn main() {
assert_eq!(tmp as i128, -0x1234_5678_9ABC_DEF0i128);
// Check that all u/i128 <-> float casts work correctly.
let houndred_u128 = 100u128;
let houndred_i128 = 100i128;
let houndred_f32 = 100.0f32;
let houndred_f64 = 100.0f64;
assert_eq!(houndred_u128 as f32, 100.0);
assert_eq!(houndred_u128 as f64, 100.0);
assert_eq!(houndred_f32 as u128, 100);
assert_eq!(houndred_f64 as u128, 100);
assert_eq!(houndred_i128 as f32, 100.0);
assert_eq!(houndred_i128 as f64, 100.0);
assert_eq!(houndred_f32 as i128, 100);
assert_eq!(houndred_f64 as i128, 100);
let hundred_u128 = 100u128;
let hundred_i128 = 100i128;
let hundred_f32 = 100.0f32;
let hundred_f64 = 100.0f64;
assert_eq!(hundred_u128 as f32, 100.0);
assert_eq!(hundred_u128 as f64, 100.0);
assert_eq!(hundred_f32 as u128, 100);
assert_eq!(hundred_f64 as u128, 100);
assert_eq!(hundred_i128 as f32, 100.0);
assert_eq!(hundred_i128 as f64, 100.0);
assert_eq!(hundred_f32 as i128, 100);
assert_eq!(hundred_f64 as i128, 100);
let _a = 1u32 << 2u8;

View File

@ -0,0 +1,39 @@
From cdb3d407740e4f15c3746051f8ba89b8e74e99d3 Mon Sep 17 00:00:00 2001
From: None <none@example.com>
Date: Fri, 30 May 2025 13:46:22 -0400
Subject: [PATCH] Pin compiler_builtins to 0.1.160
---
library/alloc/Cargo.toml | 2 +-
library/std/Cargo.toml | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/library/alloc/Cargo.toml b/library/alloc/Cargo.toml
index 9d0d957..365c9dc 100644
--- a/library/alloc/Cargo.toml
+++ b/library/alloc/Cargo.toml
@@ -16,7 +16,7 @@ bench = false
[dependencies]
core = { path = "../core", public = true }
-compiler_builtins = { version = "=0.1.159", features = ['rustc-dep-of-std'] }
+compiler_builtins = { version = "=0.1.160", features = ['rustc-dep-of-std'] }
[features]
compiler-builtins-mem = ['compiler_builtins/mem']
diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml
index 4ff4895..31371f0 100644
--- a/library/std/Cargo.toml
+++ b/library/std/Cargo.toml
@@ -18,7 +18,7 @@ cfg-if = { version = "1.0", features = ['rustc-dep-of-std'] }
panic_unwind = { path = "../panic_unwind", optional = true }
panic_abort = { path = "../panic_abort" }
core = { path = "../core", public = true }
-compiler_builtins = { version = "=0.1.159" }
+compiler_builtins = { version = "=0.1.160" }
unwind = { path = "../unwind" }
hashbrown = { version = "0.15", default-features = false, features = [
'rustc-dep-of-std',
--
2.49.0

View File

@ -0,0 +1,25 @@
From a131c69e54b5c02fe3b517e8f3ad23d4f784ffc8 Mon Sep 17 00:00:00 2001
From: Antoni Boucher <bouanto@zoho.com>
Date: Fri, 13 Jun 2025 20:25:33 -0400
Subject: [PATCH] Workaround to make a run-make test pass
---
tests/run-make/linker-warning/rmake.rs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/run-make/linker-warning/rmake.rs b/tests/run-make/linker-warning/rmake.rs
index bc21739fefc..0946a7e2a48 100644
--- a/tests/run-make/linker-warning/rmake.rs
+++ b/tests/run-make/linker-warning/rmake.rs
@@ -55,7 +55,7 @@ fn main() {
diff()
.expected_file("short-error.txt")
.actual_text("(linker error)", out.stderr())
- .normalize(r#"/rustc[^/]*/"#, "/rustc/")
+ .normalize(r#"/tmp/rustc[^/]*/"#, "/tmp/rustc/")
.normalize(
regex::escape(run_make_support::build_root().to_str().unwrap()),
"/build-root",
--
2.49.0

View File

@ -1,3 +1,3 @@
[toolchain]
channel = "nightly-2025-05-12"
channel = "nightly-2025-05-21"
components = ["rust-src", "rustc-dev", "llvm-tools-preview"]

View File

@ -158,6 +158,7 @@ fn create_wrapper_function(
}
} else {
assert!(output.is_none());
block.add_eval(None, ret);
block.end_with_void_return(None);
}

View File

@ -1,3 +1,5 @@
// cSpell:ignoreRegExp [afkspqvwy]reg
use std::borrow::Cow;
use gccjit::{LValue, RValue, ToRValue, Type};
@ -138,7 +140,7 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
// `outputs.len() + inputs.len()`.
let mut labels = vec![];
// Clobbers collected from `out("explicit register") _` and `inout("expl_reg") var => _`
// Clobbers collected from `out("explicit register") _` and `inout("explicit_reg") var => _`
let mut clobbers = vec![];
// We're trying to preallocate space for the template
@ -203,7 +205,7 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
// is also used as an in register, do not add it to the clobbers list.
// it will be treated as a lateout register with `out_place: None`
if !late {
bug!("input registers can only be used as lateout regisers");
bug!("input registers can only be used as lateout registers");
}
("r", dummy_output_type(self.cx, reg.reg_class()))
} else {
@ -641,7 +643,8 @@ fn explicit_reg_to_gcc(reg: InlineAsmReg) -> &'static str {
},
}
}
InlineAsmReg::Arm(reg) => reg.name(),
InlineAsmReg::AArch64(reg) => reg.name(),
_ => unimplemented!(),
}
}

View File

@ -16,7 +16,7 @@ use crate::gcc_util::to_gcc_features;
/// Checks if the function `instance` is recursively inline.
/// Returns `false` if a functions is guaranteed to be non-recursive, and `true` if it *might* be recursive.
#[cfg(feature = "master")]
fn resursively_inline<'gcc, 'tcx>(
fn recursively_inline<'gcc, 'tcx>(
cx: &CodegenCx<'gcc, 'tcx>,
instance: ty::Instance<'tcx>,
) -> bool {
@ -61,7 +61,7 @@ fn inline_attr<'gcc, 'tcx>(
//
// That prevents issues steming from recursive `#[inline(always)]` at a *relatively* small cost.
// We *only* need to check all the terminators of a function marked with this attribute.
if resursively_inline(cx, instance) {
if recursively_inline(cx, instance) {
Some(FnAttribute::Inline)
} else {
Some(FnAttribute::AlwaysInline)

View File

@ -11,11 +11,12 @@
// does not remove it?
//
// TODO(antoyo): for performance, check which optimizations the C++ frontend enables.
//
// cSpell:disable
// Fix these warnings:
// /usr/bin/ld: warning: type of symbol `_RNvNvNvNtCs5JWOrf9uCus_5rayon11thread_pool19WORKER_THREAD_STATE7___getit5___KEY' changed from 1 to 6 in /tmp/ccKeUSiR.ltrans0.ltrans.o
// /usr/bin/ld: warning: type of symbol `_RNvNvNvNvNtNtNtCsAj5i4SGTR7_3std4sync4mpmc5waker17current_thread_id5DUMMY7___getit5___KEY' changed from 1 to 6 in /tmp/ccKeUSiR.ltrans0.ltrans.o
// /usr/bin/ld: warning: incremental linking of LTO and non-LTO objects; using -flinker-output=nolto-rel which will bypass whole program optimization
// cSpell:enable
use std::ffi::{CStr, CString};
use std::fs::{self, File};
use std::path::{Path, PathBuf};

View File

@ -186,6 +186,7 @@ pub(crate) fn codegen(
if fat_lto {
let lto_path = format!("{}.lto", path);
// cSpell:disable
// FIXME(antoyo): The LTO frontend generates the following warning:
// ../build_sysroot/sysroot_src/library/core/src/num/dec2flt/lemire.rs:150:15: warning: type of _ZN4core3num7dec2flt5table17POWER_OF_FIVE_12817ha449a68fb31379e4E does not match original declaration [-Wlto-type-mismatch]
// 150 | let (lo5, hi5) = POWER_OF_FIVE_128[index];
@ -193,6 +194,7 @@ pub(crate) fn codegen(
// lto1: note: _ZN4core3num7dec2flt5table17POWER_OF_FIVE_12817ha449a68fb31379e4E was previously declared here
//
// This option is to mute it to make the UI tests pass with LTO enabled.
// cSpell:enable
context.add_driver_option("-Wno-lto-type-mismatch");
// NOTE: this doesn't actually generate an executable. With the above
// flags, it combines the .o files together in another .o.

View File

@ -765,7 +765,15 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
#[cfg(feature = "master")]
match self.cx.type_kind(a_type) {
TypeKind::Half | TypeKind::Float => {
TypeKind::Half => {
let fmodf = self.context.get_builtin_function("fmodf");
let f32_type = self.type_f32();
let a = self.context.new_cast(self.location, a, f32_type);
let b = self.context.new_cast(self.location, b, f32_type);
let result = self.context.new_call(self.location, fmodf, &[a, b]);
return self.context.new_cast(self.location, result, a_type);
}
TypeKind::Float => {
let fmodf = self.context.get_builtin_function("fmodf");
return self.context.new_call(self.location, fmodf, &[a, b]);
}
@ -774,8 +782,19 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
return self.context.new_call(self.location, fmod, &[a, b]);
}
TypeKind::FP128 => {
let fmodl = self.context.get_builtin_function("fmodl");
return self.context.new_call(self.location, fmodl, &[a, b]);
let f128_type = self.type_f128();
let fmodf128 = self.context.new_function(
None,
gccjit::FunctionType::Extern,
f128_type,
&[
self.context.new_parameter(None, f128_type, "a"),
self.context.new_parameter(None, f128_type, "b"),
],
"fmodf128",
false,
);
return self.context.new_call(self.location, fmodf128, &[a, b]);
}
_ => (),
}
@ -924,7 +943,12 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
// dereference after a drop, for instance.
// FIXME(antoyo): this check that we don't call get_aligned() a second time on a type.
// Ideally, we shouldn't need to do this check.
let aligned_type = if pointee_ty == self.cx.u128_type || pointee_ty == self.cx.i128_type {
// FractalFir: the `align == self.int128_align` check ensures we *do* call `get_aligned` if
// the alignment of a `u128`/`i128` is not the one mandated by the ABI. This ensures we handle
// under-aligned loads correctly.
let aligned_type = if (pointee_ty == self.cx.u128_type || pointee_ty == self.cx.i128_type)
&& align == self.int128_align
{
pointee_ty
} else {
pointee_ty.get_aligned(align.bytes())
@ -1010,13 +1034,13 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
let b_offset = a.size(self).align_to(b.align(self).abi);
let mut load = |i, scalar: &abi::Scalar, align| {
let llptr = if i == 0 {
let ptr = if i == 0 {
place.val.llval
} else {
self.inbounds_ptradd(place.val.llval, self.const_usize(b_offset.bytes()))
};
let llty = place.layout.scalar_pair_element_gcc_type(self, i);
let load = self.load(llty, llptr, align);
let load = self.load(llty, ptr, align);
scalar_load_metadata(self, load, scalar);
if scalar.is_bool() { self.trunc(load, self.type_i1()) } else { load }
};

View File

@ -34,7 +34,7 @@ pub fn get_fn<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, instance: Instance<'tcx>)
unreachable!();
/*
// Create a fn pointer with the new signature.
let ptrty = fn_abi.ptr_to_gcc_type(cx);
let ptrtype = fn_abi.ptr_to_gcc_type(cx);
// This is subtle and surprising, but sometimes we have to bitcast
// the resulting fn pointer. The reason has to do with external
@ -59,7 +59,7 @@ pub fn get_fn<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, instance: Instance<'tcx>)
// This can occur on either a crate-local or crate-external
// reference. It also occurs when testing libcore and in some
// other weird situations. Annoying.
if cx.val_ty(func) != ptrty {
if cx.val_ty(func) != ptrtype {
// TODO(antoyo): cast the pointer.
func
}

View File

@ -9,7 +9,6 @@ use rustc_middle::mir::Mutability;
use rustc_middle::mir::interpret::{ConstAllocation, GlobalAlloc, Scalar};
use rustc_middle::ty::layout::LayoutOf;
use crate::consts::const_alloc_to_gcc;
use crate::context::CodegenCx;
use crate::type_of::LayoutGccExt;
@ -46,12 +45,65 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
}
pub fn bytes_in_context<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, bytes: &[u8]) -> RValue<'gcc> {
let context = &cx.context;
let byte_type = context.new_type::<u8>();
let typ = context.new_array_type(None, byte_type, bytes.len() as u64);
let elements: Vec<_> =
bytes.iter().map(|&byte| context.new_rvalue_from_int(byte_type, byte as i32)).collect();
context.new_array_constructor(None, typ, &elements)
// Instead of always using an array of bytes, use an array of larger integers of target endianness
// if possible. This reduces the amount of `rvalues` we use, which reduces memory usage significantly.
//
// FIXME(FractalFir): Consider using `global_set_initializer` instead. Before this is done, we need to confirm that
// `global_set_initializer` is more memory efficient than the current solution.
// `global_set_initializer` calls `global_set_initializer_rvalue` under the hood - does it generate an array of rvalues,
// or is it using a more efficient representation?
match bytes.len() % 8 {
0 => {
let context = &cx.context;
let byte_type = context.new_type::<u64>();
let typ = context.new_array_type(None, byte_type, bytes.len() as u64 / 8);
let elements: Vec<_> = bytes
.chunks_exact(8)
.map(|arr| {
let arr: [u8; 8] = arr.try_into().unwrap();
context.new_rvalue_from_long(
byte_type,
// Since we are representing arbitrary byte runs as integers, we need to follow the target
// endianness.
match cx.sess().target.options.endian {
rustc_abi::Endian::Little => u64::from_le_bytes(arr) as i64,
rustc_abi::Endian::Big => u64::from_be_bytes(arr) as i64,
},
)
})
.collect();
context.new_array_constructor(None, typ, &elements)
}
4 => {
let context = &cx.context;
let byte_type = context.new_type::<u32>();
let typ = context.new_array_type(None, byte_type, bytes.len() as u64 / 4);
let elements: Vec<_> = bytes
.chunks_exact(4)
.map(|arr| {
let arr: [u8; 4] = arr.try_into().unwrap();
context.new_rvalue_from_int(
byte_type,
match cx.sess().target.options.endian {
rustc_abi::Endian::Little => u32::from_le_bytes(arr) as i32,
rustc_abi::Endian::Big => u32::from_be_bytes(arr) as i32,
},
)
})
.collect();
context.new_array_constructor(None, typ, &elements)
}
_ => {
let context = cx.context;
let byte_type = context.new_type::<u8>();
let typ = context.new_array_type(None, byte_type, bytes.len() as u64);
let elements: Vec<_> = bytes
.iter()
.map(|&byte| context.new_rvalue_from_int(byte_type, byte as i32))
.collect();
context.new_array_constructor(None, typ, &elements)
}
}
}
pub fn type_is_pointer(typ: Type<'_>) -> bool {
@ -185,14 +237,15 @@ impl<'gcc, 'tcx> ConstCodegenMethods for CodegenCx<'gcc, 'tcx> {
// FIXME(antoyo): there's some issues with using the u128 code that follows, so hard-code
// the paths for floating-point values.
if ty == self.float_type {
// TODO: Remove this code?
/*if ty == self.float_type {
return self
.context
.new_rvalue_from_double(ty, f32::from_bits(data as u32) as f64);
}
if ty == self.double_type {
return self.context.new_rvalue_from_double(ty, f64::from_bits(data as u64));
}
}*/
let value = self.const_uint_big(self.type_ix(bitsize), data);
let bytesize = layout.size(self).bytes();
@ -212,7 +265,20 @@ impl<'gcc, 'tcx> ConstCodegenMethods for CodegenCx<'gcc, 'tcx> {
let alloc_id = prov.alloc_id();
let base_addr = match self.tcx.global_alloc(alloc_id) {
GlobalAlloc::Memory(alloc) => {
let init = const_alloc_to_gcc(self, alloc);
// For ZSTs directly codegen an aligned pointer.
// This avoids generating a zero-sized constant value and actually needing a
// real address at runtime.
if alloc.inner().len() == 0 {
assert_eq!(offset.bytes(), 0);
let val = self.const_usize(alloc.inner().align.bytes());
return if matches!(layout.primitive(), Pointer(_)) {
self.context.new_cast(None, val, ty)
} else {
self.const_bitcast(val, ty)
};
}
let init = self.const_data_from_alloc(alloc);
let alloc = alloc.inner();
let value = match alloc.mutability {
Mutability::Mut => self.static_addr_of_mut(init, alloc.align, None),
@ -234,7 +300,7 @@ impl<'gcc, 'tcx> ConstCodegenMethods for CodegenCx<'gcc, 'tcx> {
}),
)))
.unwrap_memory();
let init = const_alloc_to_gcc(self, alloc);
let init = self.const_data_from_alloc(alloc);
self.static_addr_of(init, alloc.inner().align, None)
}
GlobalAlloc::Static(def_id) => {
@ -257,7 +323,19 @@ impl<'gcc, 'tcx> ConstCodegenMethods for CodegenCx<'gcc, 'tcx> {
}
fn const_data_from_alloc(&self, alloc: ConstAllocation<'_>) -> Self::Value {
const_alloc_to_gcc(self, alloc)
// We ignore the alignment for the purpose of deduping RValues
// The alignment is not handled / used in any way by `const_alloc_to_gcc`,
// so it is OK to overwrite it here.
let mut mock_alloc = alloc.inner().clone();
mock_alloc.align = rustc_abi::Align::MAX;
// Check if the rvalue is already in the cache - if so, just return it directly.
if let Some(res) = self.const_cache.borrow().get(&mock_alloc) {
return *res;
}
// Rvalue not in the cache - convert and add it.
let res = crate::consts::const_alloc_to_gcc_uncached(self, alloc);
self.const_cache.borrow_mut().insert(mock_alloc, res);
res
}
fn const_ptr_byte_offset(&self, base_addr: Self::Value, offset: abi::Size) -> Self::Value {

View File

@ -36,18 +36,14 @@ fn set_global_alignment<'gcc, 'tcx>(
impl<'gcc, 'tcx> StaticCodegenMethods for CodegenCx<'gcc, 'tcx> {
fn static_addr_of(&self, cv: RValue<'gcc>, align: Align, kind: Option<&str>) -> RValue<'gcc> {
// TODO(antoyo): implement a proper rvalue comparison in libgccjit instead of doing the
// following:
for (value, variable) in &*self.const_globals.borrow() {
if format!("{:?}", value) == format!("{:?}", cv) {
if let Some(global_variable) = self.global_lvalues.borrow().get(variable) {
let alignment = align.bits() as i32;
if alignment > global_variable.get_alignment() {
global_variable.set_alignment(alignment);
}
if let Some(variable) = self.const_globals.borrow().get(&cv) {
if let Some(global_variable) = self.global_lvalues.borrow().get(variable) {
let alignment = align.bits() as i32;
if alignment > global_variable.get_alignment() {
global_variable.set_alignment(alignment);
}
return *variable;
}
return *variable;
}
let global_value = self.static_addr_of_mut(cv, align, kind);
#[cfg(feature = "master")]
@ -288,8 +284,10 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
global
}
}
pub fn const_alloc_to_gcc<'gcc>(
/// Converts a given const alloc to a gcc Rvalue, without any caching or deduplication.
/// YOU SHOULD NOT call this function directly - that may break the semantics of Rust.
/// Use `const_data_from_alloc` instead.
pub(crate) fn const_alloc_to_gcc_uncached<'gcc>(
cx: &CodegenCx<'gcc, '_>,
alloc: ConstAllocation<'_>,
) -> RValue<'gcc> {
@ -321,7 +319,7 @@ pub fn const_alloc_to_gcc<'gcc>(
// and we properly interpret the provenance as a relocation pointer offset.
alloc.inspect_with_uninit_and_ptr_outside_interpreter(offset..(offset + pointer_size)),
)
.expect("const_alloc_to_llvm: could not read relocation pointer")
.expect("const_alloc_to_gcc_uncached: could not read relocation pointer")
as u64;
let address_space = cx.tcx.global_alloc(alloc_id).address_space(cx);
@ -360,7 +358,7 @@ fn codegen_static_initializer<'gcc, 'tcx>(
def_id: DefId,
) -> Result<(RValue<'gcc>, ConstAllocation<'tcx>), ErrorHandled> {
let alloc = cx.tcx.eval_static_initializer(def_id)?;
Ok((const_alloc_to_gcc(cx, alloc), alloc))
Ok((cx.const_data_from_alloc(alloc), alloc))
}
fn check_and_apply_linkage<'gcc, 'tcx>(

View File

@ -1,14 +1,16 @@
use std::cell::{Cell, RefCell};
use std::collections::HashMap;
use gccjit::{
Block, CType, Context, Function, FunctionPtrType, FunctionType, LValue, Location, RValue, Type,
};
use rustc_abi::{HasDataLayout, PointeeInfo, Size, TargetDataLayout, VariantIdx};
use rustc_abi::{Align, HasDataLayout, PointeeInfo, Size, TargetDataLayout, VariantIdx};
use rustc_codegen_ssa::base::wants_msvc_seh;
use rustc_codegen_ssa::errors as ssa_errors;
use rustc_codegen_ssa::traits::{BackendTypes, BaseTypeCodegenMethods, MiscCodegenMethods};
use rustc_data_structures::base_n::{ALPHANUMERIC_ONLY, ToBaseN};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_middle::mir::interpret::Allocation;
use rustc_middle::mir::mono::CodegenUnit;
use rustc_middle::span_bug;
use rustc_middle::ty::layout::{
@ -28,6 +30,8 @@ use crate::common::SignType;
#[cfg_attr(not(feature = "master"), allow(dead_code))]
pub struct CodegenCx<'gcc, 'tcx> {
/// A cache of converted ConstAllocs
pub const_cache: RefCell<HashMap<Allocation, RValue<'gcc>>>,
pub codegen_unit: &'tcx CodegenUnit<'tcx>,
pub context: &'gcc Context<'gcc>,
@ -129,6 +133,9 @@ pub struct CodegenCx<'gcc, 'tcx> {
#[cfg(feature = "master")]
pub cleanup_blocks: RefCell<FxHashSet<Block<'gcc>>>,
/// The alignment of a u128/i128 type.
// We cache this, since it is needed for alignment checks during loads.
pub int128_align: Align,
}
impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
@ -220,6 +227,12 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
}
let mut cx = Self {
int128_align: tcx
.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(tcx.types.i128))
.expect("Can't get the layout of `i128`")
.align
.abi,
const_cache: Default::default(),
codegen_unit,
context,
current_func: RefCell::new(None),
@ -428,8 +441,8 @@ impl<'gcc, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
// `rust_eh_personality` function, but rather we wired it up to the
// CRT's custom personality function, which forces LLVM to consider
// landing pads as "landing pads for SEH".
if let Some(llpersonality) = self.eh_personality.get() {
return llpersonality;
if let Some(personality_func) = self.eh_personality.get() {
return personality_func;
}
let tcx = self.tcx;
let func = match tcx.lang_items().eh_personality() {

View File

@ -143,6 +143,7 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec<Stri
// To find a list of GCC's names, check https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html
pub fn to_gcc_features<'a>(sess: &Session, s: &'a str) -> SmallVec<[&'a str; 2]> {
let arch = if sess.target.arch == "x86_64" { "x86" } else { &*sess.target.arch };
// cSpell:disable
match (arch, s) {
// FIXME: seems like x87 does not exist?
("x86", "x87") => smallvec![],
@ -181,6 +182,7 @@ pub fn to_gcc_features<'a>(sess: &Session, s: &'a str) -> SmallVec<[&'a str; 2]>
("aarch64", "sve2-bitperm") => smallvec!["sve2-bitperm", "neon"],
(_, s) => smallvec![s],
}
// cSpell:enable
}
fn arch_to_gcc(name: &str) -> &str {

View File

@ -2,6 +2,8 @@
//! This module exists because some integer types are not supported on some gcc platforms, e.g.
//! 128-bit integers on 32-bit platforms and thus require to be handled manually.
// cSpell:words cmpti divti modti mulodi muloti udivti umodti
use gccjit::{BinaryOp, ComparisonOp, FunctionType, Location, RValue, ToRValue, Type, UnaryOp};
use rustc_abi::{CanonAbi, Endian, ExternAbi};
use rustc_codegen_ssa::common::{IntPredicate, TypeKind};
@ -913,9 +915,11 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
debug_assert!(value_type.dyncast_array().is_some());
let name_suffix = match self.type_kind(dest_typ) {
// cSpell:disable
TypeKind::Float => "tisf",
TypeKind::Double => "tidf",
TypeKind::FP128 => "tixf",
TypeKind::FP128 => "titf",
// cSpell:enable
kind => panic!("cannot cast a non-native integer to type {:?}", kind),
};
let sign = if signed { "" } else { "un" };
@ -957,8 +961,10 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
debug_assert!(dest_typ.dyncast_array().is_some());
let name_suffix = match self.type_kind(value_type) {
// cSpell:disable
TypeKind::Float => "sfti",
TypeKind::Double => "dfti",
// cSpell:enable
kind => panic!("cannot cast a {:?} to non-native integer", kind),
};
let sign = if signed { "" } else { "uns" };

File diff suppressed because it is too large Load Diff

View File

@ -1012,7 +1012,7 @@ pub fn intrinsic<'gcc, 'tcx>(name: &str, cx: &CodegenCx<'gcc, 'tcx>) -> Function
};
let func = cx.context.get_builtin_function(gcc_name);
cx.functions.borrow_mut().insert(gcc_name.to_string(), func);
return func;
func
}
#[cfg(feature = "master")]
@ -1548,10 +1548,13 @@ pub fn intrinsic<'gcc, 'tcx>(name: &str, cx: &CodegenCx<'gcc, 'tcx>) -> Function
"llvm.x86.tcmmrlfp16ps" => "__builtin_trap",
// NOTE: this file is generated by https://github.com/GuillaumeGomez/llvmint/blob/master/generate_list.py
_ => include!("archs.rs"),
_ => map_arch_intrinsic(name),
};
let func = cx.context.get_target_builtin_function(gcc_name);
cx.functions.borrow_mut().insert(gcc_name.to_string(), func);
func
}
#[cfg(feature = "master")]
include!("archs.rs");

View File

@ -196,6 +196,95 @@ fn get_simple_function<'gcc, 'tcx>(
))
}
fn get_simple_function_f128<'gcc, 'tcx>(
cx: &CodegenCx<'gcc, 'tcx>,
name: Symbol,
) -> Option<Function<'gcc>> {
if !cx.supports_f128_type {
return None;
}
let f128_type = cx.type_f128();
let func_name = match name {
sym::ceilf128 => "ceilf128",
sym::floorf128 => "floorf128",
sym::truncf128 => "truncf128",
sym::roundf128 => "roundf128",
sym::round_ties_even_f128 => "roundevenf128",
sym::sqrtf128 => "sqrtf128",
_ => return None,
};
Some(cx.context.new_function(
None,
FunctionType::Extern,
f128_type,
&[cx.context.new_parameter(None, f128_type, "a")],
func_name,
false,
))
}
fn get_simple_function_f128_2args<'gcc, 'tcx>(
cx: &CodegenCx<'gcc, 'tcx>,
name: Symbol,
) -> Option<Function<'gcc>> {
if !cx.supports_f128_type {
return None;
}
let f128_type = cx.type_f128();
let func_name = match name {
sym::maxnumf128 => "fmaxf128",
sym::minnumf128 => "fminf128",
_ => return None,
};
Some(cx.context.new_function(
None,
FunctionType::Extern,
f128_type,
&[
cx.context.new_parameter(None, f128_type, "a"),
cx.context.new_parameter(None, f128_type, "b"),
],
func_name,
false,
))
}
fn f16_builtin<'gcc, 'tcx>(
cx: &CodegenCx<'gcc, 'tcx>,
name: Symbol,
args: &[OperandRef<'tcx, RValue<'gcc>>],
) -> RValue<'gcc> {
let f32_type = cx.type_f32();
let builtin_name = match name {
sym::ceilf16 => "__builtin_ceilf",
sym::floorf16 => "__builtin_floorf",
sym::fmaf16 => "fmaf",
sym::maxnumf16 => "__builtin_fmaxf",
sym::minnumf16 => "__builtin_fminf",
sym::powf16 => "__builtin_powf",
sym::powif16 => {
let func = cx.context.get_builtin_function("__builtin_powif");
let arg0 = cx.context.new_cast(None, args[0].immediate(), f32_type);
let args = [arg0, args[1].immediate()];
let result = cx.context.new_call(None, func, &args);
return cx.context.new_cast(None, result, cx.type_f16());
}
sym::roundf16 => "__builtin_roundf",
sym::round_ties_even_f16 => "__builtin_rintf",
sym::sqrtf16 => "__builtin_sqrtf",
sym::truncf16 => "__builtin_truncf",
_ => unreachable!(),
};
let func = cx.context.get_builtin_function(builtin_name);
let args: Vec<_> =
args.iter().map(|arg| cx.context.new_cast(None, arg.immediate(), f32_type)).collect();
let result = cx.context.new_call(None, func, &args);
cx.context.new_cast(None, result, cx.type_f16())
}
impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
fn codegen_intrinsic_call(
&mut self,
@ -211,7 +300,9 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc
let fn_args = instance.args;
let simple = get_simple_intrinsic(self, name);
let simple_func = get_simple_function(self, name);
let simple_func = get_simple_function(self, name)
.or_else(|| get_simple_function_f128(self, name))
.or_else(|| get_simple_function_f128_2args(self, name));
// FIXME(tempdragon): Re-enable `clippy::suspicious_else_formatting` if the following issue is solved:
// https://github.com/rust-lang/rust-clippy/issues/12497
@ -234,17 +325,55 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc
&args.iter().map(|arg| arg.immediate()).collect::<Vec<_>>(),
)
}
sym::fmaf16 => {
// TODO(antoyo): use the correct builtin for f16.
let func = self.cx.context.get_builtin_function("fmaf");
let args: Vec<_> = args
.iter()
.map(|arg| {
self.cx.context.new_cast(self.location, arg.immediate(), self.cx.type_f32())
})
.collect();
let result = self.cx.context.new_call(self.location, func, &args);
self.cx.context.new_cast(self.location, result, self.cx.type_f16())
sym::ceilf16
| sym::floorf16
| sym::fmaf16
| sym::maxnumf16
| sym::minnumf16
| sym::powf16
| sym::powif16
| sym::roundf16
| sym::round_ties_even_f16
| sym::sqrtf16
| sym::truncf16 => f16_builtin(self, name, args),
sym::fmaf128 => {
let f128_type = self.cx.type_f128();
let func = self.cx.context.new_function(
None,
FunctionType::Extern,
f128_type,
&[
self.cx.context.new_parameter(None, f128_type, "a"),
self.cx.context.new_parameter(None, f128_type, "b"),
self.cx.context.new_parameter(None, f128_type, "c"),
],
"fmaf128",
false,
);
self.cx.context.new_call(
self.location,
func,
&args.iter().map(|arg| arg.immediate()).collect::<Vec<_>>(),
)
}
sym::powif128 => {
let f128_type = self.cx.type_f128();
let func = self.cx.context.new_function(
None,
FunctionType::Extern,
f128_type,
&[
self.cx.context.new_parameter(None, f128_type, "a"),
self.cx.context.new_parameter(None, self.int_type, "b"),
],
"__powitf2",
false,
);
self.cx.context.new_call(
self.location,
func,
&args.iter().map(|arg| arg.immediate()).collect::<Vec<_>>(),
)
}
sym::is_val_statically_known => {
let a = args[0].immediate();
@ -526,7 +655,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc
fn type_checked_load(
&mut self,
_llvtable: Self::Value,
_vtable: Self::Value,
_vtable_byte_offset: u64,
_typeid: Self::Value,
) -> Self::Value {
@ -622,23 +751,23 @@ impl<'gcc, 'tcx> ArgAbiExt<'gcc, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> {
// We instead thus allocate some scratch space...
let scratch_size = cast.size(bx);
let scratch_align = cast.align(bx);
let llscratch = bx.alloca(scratch_size, scratch_align);
bx.lifetime_start(llscratch, scratch_size);
let scratch = bx.alloca(scratch_size, scratch_align);
bx.lifetime_start(scratch, scratch_size);
// ... where we first store the value...
rustc_codegen_ssa::mir::store_cast(bx, cast, val, llscratch, scratch_align);
rustc_codegen_ssa::mir::store_cast(bx, cast, val, scratch, scratch_align);
// ... and then memcpy it to the intended destination.
bx.memcpy(
dst.val.llval,
self.layout.align.abi,
llscratch,
scratch,
scratch_align,
bx.const_usize(self.layout.size.bytes()),
MemFlags::empty(),
);
bx.lifetime_end(llscratch, scratch_size);
bx.lifetime_end(scratch, scratch_size);
}
} else {
OperandValue::Immediate(val).store(bx, dst);

View File

@ -1081,7 +1081,9 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
let (_, element_ty1) = args[1].layout.ty.simd_size_and_type(bx.tcx());
let (_, element_ty2) = args[2].layout.ty.simd_size_and_type(bx.tcx());
let (pointer_count, underlying_ty) = match *element_ty1.kind() {
ty::RawPtr(p_ty, mutbl) if p_ty == in_elem && mutbl == hir::Mutability::Mut => {
ty::RawPtr(p_ty, mutability)
if p_ty == in_elem && mutability == hir::Mutability::Mut =>
{
(ptr_count(element_ty1), non_ptr(element_ty1))
}
_ => {

View File

@ -3,10 +3,12 @@
* TODO(antoyo): support #[inline] attributes.
* TODO(antoyo): support LTO (gcc's equivalent to Full LTO is -flto -flto-partition=one https://documentation.suse.com/sbp/all/html/SBP-GCC-10/index.html).
* For Thin LTO, this might be helpful:
// cspell:disable-next-line
* In gcc 4.6 -fwhopr was removed and became default with -flto. The non-whopr path can still be executed via -flto-partition=none.
* Or the new incremental LTO (https://www.phoronix.com/news/GCC-Incremental-LTO-Patches)?
*
* Maybe some missing optizations enabled by rustc's LTO is in there: https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html
* Maybe some missing optimizations enabled by rustc's LTO is in there: https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html
// cspell:disable-next-line
* Like -fipa-icf (should be already enabled) and maybe -fdevirtualize-at-ltrans.
* TODO: disable debug info always being emitted. Perhaps this slows down things?
*
@ -206,7 +208,7 @@ impl CodegenBackend for GccCodegenBackend {
#[cfg(not(feature = "master"))]
{
let temp_dir = TempDir::new().expect("cannot create temporary directory");
let temp_file = temp_dir.into_path().join("result.asm");
let temp_file = temp_dir.keep().join("result.asm");
let check_context = Context::default();
check_context.set_print_errors_to_stderr(false);
let _int128_ty = check_context.new_c_type(CType::UInt128t);
@ -430,10 +432,11 @@ impl WriteBackendMethods for GccCodegenBackend {
) -> Result<ModuleCodegen<Self::Module>, FatalError> {
back::write::link(cgcx, dcx, modules)
}
fn autodiff(
_cgcx: &CodegenContext<Self>,
_module: &ModuleCodegen<Self::Module>,
_diff_fncs: Vec<AutoDiffItem>,
_diff_functions: Vec<AutoDiffItem>,
_config: &ModuleConfig,
) -> Result<(), FatalError> {
unimplemented!()
@ -494,12 +497,14 @@ fn target_config(sess: &Session, target_info: &LockedTargetInfo) -> TargetConfig
return false;
}
target_info.cpu_supports(feature)
// cSpell:disable
/*
adx, aes, avx, avx2, avx512bf16, avx512bitalg, avx512bw, avx512cd, avx512dq, avx512er, avx512f, avx512fp16, avx512ifma,
avx512pf, avx512vbmi, avx512vbmi2, avx512vl, avx512vnni, avx512vp2intersect, avx512vpopcntdq,
bmi1, bmi2, cmpxchg16b, ermsb, f16c, fma, fxsr, gfni, lzcnt, movbe, pclmulqdq, popcnt, rdrand, rdseed, rtm,
sha, sse, sse2, sse3, sse4.1, sse4.2, sse4a, ssse3, tbm, vaes, vpclmulqdq, xsave, xsavec, xsaveopt, xsaves
*/
// cSpell:enable
})
.map(Symbol::intern)
.collect()
@ -508,13 +513,16 @@ fn target_config(sess: &Session, target_info: &LockedTargetInfo) -> TargetConfig
let target_features = f(false);
let unstable_target_features = f(true);
let has_reliable_f16 = target_info.supports_target_dependent_type(CType::Float16);
let has_reliable_f128 = target_info.supports_target_dependent_type(CType::Float128);
TargetConfig {
target_features,
unstable_target_features,
// There are no known bugs with GCC support for f16 or f128
has_reliable_f16: true,
has_reliable_f16_math: true,
has_reliable_f128: true,
has_reliable_f128_math: true,
has_reliable_f16,
has_reliable_f16_math: has_reliable_f16,
has_reliable_f128,
has_reliable_f128_math: has_reliable_f128,
}
}

View File

@ -302,13 +302,13 @@ impl<'gcc, 'tcx> BaseTypeCodegenMethods for CodegenCx<'gcc, 'tcx> {
#[cfg_attr(feature = "master", allow(unused_mut))]
fn type_array(&self, ty: Type<'gcc>, mut len: u64) -> Type<'gcc> {
#[cfg(not(feature = "master"))]
if let Some(struct_type) = ty.is_struct() {
if struct_type.get_field_count() == 0 {
// NOTE: since gccjit only supports i32 for the array size and libcore's tests uses a
// size of usize::MAX in test_binary_search, we workaround this by setting the size to
// zero for ZSTs.
len = 0;
}
if let Some(struct_type) = ty.is_struct()
&& struct_type.get_field_count() == 0
{
// NOTE: since gccjit only supports i32 for the array size and libcore's tests uses a
// size of usize::MAX in test_binary_search, we workaround this by setting the size to
// zero for ZSTs.
len = 0;
}
self.context.new_array_type(None, ty, len)

View File

@ -217,7 +217,7 @@ impl<'tcx> LayoutGccExt<'tcx> for TyAndLayout<'tcx> {
let ty = match *self.ty.kind() {
// NOTE: we cannot remove this match like in the LLVM codegen because the call
// to fn_ptr_backend_type handle the on-stack attribute.
// TODO(antoyo): find a less hackish way to hande the on-stack attribute.
// TODO(antoyo): find a less hackish way to handle the on-stack attribute.
ty::FnPtr(sig_tys, hdr) => cx
.fn_ptr_backend_type(cx.fn_abi_of_fn_ptr(sig_tys.with(hdr), ty::List::empty())),
_ => self.scalar_gcc_type_at(cx, scalar, Size::ZERO),

View File

@ -1,26 +1,12 @@
tests/ui/allocator/no_std-alloc-error-handler-custom.rs
tests/ui/allocator/no_std-alloc-error-handler-default.rs
tests/ui/asm/may_unwind.rs
tests/ui/functions-closures/parallel-codegen-closures.rs
tests/ui/linkage-attr/linkage1.rs
tests/ui/lto/dylib-works.rs
tests/ui/sepcomp/sepcomp-cci.rs
tests/ui/sepcomp/sepcomp-extern.rs
tests/ui/sepcomp/sepcomp-fns-backwards.rs
tests/ui/sepcomp/sepcomp-fns.rs
tests/ui/sepcomp/sepcomp-statics.rs
tests/ui/asm/x86_64/may_unwind.rs
tests/ui/panics/catch-unwind-bang.rs
tests/ui/drop/dynamic-drop-async.rs
tests/ui/cfg/cfg-panic-abort.rs
tests/ui/drop/repeat-drop.rs
tests/ui/coroutine/panic-drops-resume.rs
tests/ui/fmt/format-args-capture.rs
tests/ui/coroutine/panic-drops.rs
tests/ui/intrinsics/panic-uninitialized-zeroed.rs
tests/ui/iterators/iter-sum-overflow-debug.rs
tests/ui/iterators/iter-sum-overflow-overflow-checks.rs
tests/ui/mir/mir_calls_to_shims.rs
tests/ui/mir/mir_drop_order.rs
tests/ui/mir/mir_let_chains_drop_order.rs
tests/ui/oom_unwind.rs
@ -31,27 +17,15 @@ tests/ui/unwind-no-uwtable.rs
tests/ui/parser/unclosed-delimiter-in-dep.rs
tests/ui/consts/missing_span_in_backtrace.rs
tests/ui/drop/dynamic-drop.rs
tests/ui/issues/issue-43853.rs
tests/ui/issues/issue-47364.rs
tests/ui/macros/rfc-2011-nicer-assert-messages/assert-without-captures-does-not-create-unnecessary-code.rs
tests/ui/rfcs/rfc-1857-stabilize-drop-order/drop-order.rs
tests/ui/rfcs/rfc-2091-track-caller/std-panic-locations.rs
tests/ui/simd/issue-17170.rs
tests/ui/simd/issue-39720.rs
tests/ui/alloc-error/default-alloc-error-hook.rs
tests/ui/coroutine/panic-safe.rs
tests/ui/issues/issue-14875.rs
tests/ui/issues/issue-29948.rs
tests/ui/panics/nested_panic_caught.rs
tests/ui/process/println-with-broken-pipe.rs
tests/ui/lto/thin-lto-inlines2.rs
tests/ui/lto/weak-works.rs
tests/ui/panic-runtime/lto-abort.rs
tests/ui/lto/thin-lto-inlines.rs
tests/ui/lto/thin-lto-global-allocator.rs
tests/ui/lto/msvc-imp-present.rs
tests/ui/panic-runtime/lto-abort.rs
tests/ui/lto/lto-thin-rustc-loads-linker-plugin.rs
tests/ui/lto/all-crates.rs
tests/ui/async-await/deep-futures-are-freeze.rs
tests/ui/coroutine/resume-after-return.rs
tests/ui/simd/masked-load-store.rs
@ -59,15 +33,11 @@ tests/ui/simd/repr_packed.rs
tests/ui/async-await/in-trait/dont-project-to-specializable-projection.rs
tests/ui/consts/try-operator.rs
tests/ui/coroutine/unwind-abort-mix.rs
tests/ui/type-alias-impl-trait/rpit_tait_equality_in_canonical_query.rs
tests/ui/impl-trait/equality-in-canonical-query.rs
tests/ui/consts/issue-miri-1910.rs
tests/ui/mir/mir_heavy_promoted.rs
tests/ui/consts/const_cmp_type_id.rs
tests/ui/consts/issue-73976-monomorphic.rs
tests/ui/consts/issue-94675.rs
tests/ui/traits/const-traits/const-drop-fail.rs
tests/ui/traits/const-traits/const-drop.rs
tests/ui/runtime/on-broken-pipe/child-processes.rs
tests/ui/sanitizer/cfi/assoc-ty-lifetime-issue-123053.rs
tests/ui/sanitizer/cfi/async-closures.rs
@ -85,14 +55,9 @@ tests/ui/sanitizer/cfi/can-reveal-opaques.rs
tests/ui/sanitizer/kcfi-mangling.rs
tests/ui/statics/const_generics.rs
tests/ui/backtrace/dylib-dep.rs
tests/ui/errors/pic-linker.rs
tests/ui/delegation/fn-header.rs
tests/ui/consts/zst_no_llvm_alloc.rs
tests/ui/consts/const-eval/parse_ints.rs
tests/ui/simd/intrinsic/generic-arithmetic-pass.rs
tests/ui/simd/intrinsic/generic-as.rs
tests/ui/backtrace/backtrace.rs
tests/ui/lifetimes/tail-expr-lock-poisoning.rs
tests/ui/runtime/rt-explody-panic-payloads.rs
tests/ui/codegen/equal-pointers-unequal/as-cast/inline1.rs
tests/ui/codegen/equal-pointers-unequal/as-cast/inline2.rs
@ -108,4 +73,9 @@ tests/ui/codegen/equal-pointers-unequal/strict-provenance/segfault.rs
tests/ui/codegen/equal-pointers-unequal/strict-provenance/zero.rs
tests/ui/simd/simd-bitmask-notpow2.rs
tests/ui/codegen/StackColoring-not-blowup-stack-issue-40883.rs
tests/ui/numbers-arithmetic/u128-as-f32.rs
tests/ui/lto/all-crates.rs
tests/ui/uninhabited/uninhabited-transparent-return-abi.rs
tests/ui/coroutine/panic-drops-resume.rs
tests/ui/coroutine/panic-drops.rs
tests/ui/coroutine/panic-safe.rs

View File

@ -57,10 +57,10 @@ pub fn main_inner(profile: Profile) {
#[cfg(not(feature = "master"))]
fn filter(filename: &Path) -> bool {
if let Some(filename) = filename.to_str() {
if filename.ends_with("gep.rs") {
return false;
}
if let Some(filename) = filename.to_str()
&& filename.ends_with("gep.rs")
{
return false;
}
rust_filter(filename)
}

31
tests/run/packed_u128.rs Normal file
View File

@ -0,0 +1,31 @@
// Compiler:
//
// Run-time:
// status: 0
#![feature(no_core)]
#![no_std]
#![no_core]
#![no_main]
extern crate mini_core;
use intrinsics::black_box;
use mini_core::*;
#[repr(packed(1))]
pub struct ScalarInt {
data: u128,
size: u8,
}
#[inline(never)]
#[no_mangle]
fn read_data(a: &ScalarInt) {
black_box(a.data);
}
#[no_mangle]
extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 {
let data =
[black_box(ScalarInt { data: 0, size: 1 }), black_box(ScalarInt { data: 0, size: 1 })];
read_data(&data[1]);
0
}

View File

@ -0,0 +1,2 @@
lateout
repr

View File

@ -0,0 +1,75 @@
aapcs
addo
archs
ashl
ashr
cgcx
clzll
cmse
codegened
csky
ctlz
ctpop
cttz
ctzll
flto
fmaximumf
fmuladd
fmuladdf
fminimumf
fmul
fptosi
fptosui
fptoui
fwrapv
gimple
hrtb
immediates
liblto
llbb
llcx
llextra
llfn
lgcc
llmod
llresult
llret
ltrans
llty
llval
llvals
loong
lshr
masm
maximumf
maxnumf
mavx
mcmodel
minimumf
minnumf
monomorphization
monomorphizations
monomorphized
monomorphizing
movnt
mulo
nvptx
pointee
powitf
reassoc
riscv
rlib
roundevenf
rustc
sitofp
sizet
spir
subo
sysv
tbaa
uitofp
unord
uninlined
utrunc
xabort
zext

View File

@ -168,25 +168,39 @@ def update_intrinsics(llvm_path, llvmint, llvmint2):
os.path.dirname(os.path.abspath(__file__)),
"../src/intrinsic/archs.rs",
)
# A hashmap of all architectures. This allows us to first match on the architecture, and then on the intrinsics.
# This speeds up the comparison, and makes our code considerably smaller.
# Since all intrinsic names start with "llvm.", we skip that prefix.
print("Updating content of `{}`...".format(output_file))
with open(output_file, "w", encoding="utf8") as out:
out.write("// File generated by `rustc_codegen_gcc/tools/generate_intrinsics.py`\n")
out.write("// DO NOT EDIT IT!\n")
out.write("match name {\n")
out.write("/// Translate a given LLVM intrinsic name to an equivalent GCC one.\n")
out.write("fn map_arch_intrinsic(name:&str)->&str{\n")
out.write('let Some(name) = name.strip_prefix("llvm.") else { unimplemented!("***** unsupported LLVM intrinsic {}", name) };\n')
out.write('let Some((arch, name)) = name.split_once(\'.\') else { unimplemented!("***** unsupported LLVM intrinsic {}", name) };\n')
out.write("match arch {\n")
for arch in archs:
if len(intrinsics[arch]) == 0:
continue
out.write("\"{}\" => {{ #[allow(non_snake_case)] fn {}(name: &str) -> &str {{ match name {{".format(arch,arch))
intrinsics[arch].sort(key=lambda x: (x[0], x[2]))
out.write(' // {}\n'.format(arch))
for entry in intrinsics[arch]:
llvm_name = entry[0].removeprefix("llvm.");
llvm_name = llvm_name.removeprefix(arch);
llvm_name = llvm_name.removeprefix(".");
if entry[2] is True: # if it is a duplicate
out.write(' // [DUPLICATE]: "{}" => "{}",\n'.format(entry[0], entry[1]))
out.write(' // [DUPLICATE]: "{}" => "{}",\n'.format(llvm_name, entry[1]))
elif "_round_mask" in entry[1]:
out.write(' // [INVALID CONVERSION]: "{}" => "{}",\n'.format(entry[0], entry[1]))
out.write(' // [INVALID CONVERSION]: "{}" => "{}",\n'.format(llvm_name, entry[1]))
else:
out.write(' "{}" => "{}",\n'.format(entry[0], entry[1]))
out.write(' _ => unimplemented!("***** unsupported LLVM intrinsic {}", name),\n')
out.write("}\n")
out.write(' "{}" => "{}",\n'.format(llvm_name, entry[1]))
out.write(' _ => unimplemented!("***** unsupported LLVM intrinsic {}", name),\n')
out.write("}} }} {}(name) }}\n,".format(arch))
out.write(' _ => unimplemented!("***** unsupported LLVM architecture {}", name),\n')
out.write("}\n}")
subprocess.call(["rustfmt", output_file])
print("Done!")