Merge branch 'master' into sync_from_rust_2025_06_02
This commit is contained in:
commit
f69d8fc324
26 changed files with 706 additions and 120 deletions
11
.github/workflows/ci.yml
vendored
11
.github/workflows/ci.yml
vendored
|
|
@ -64,8 +64,6 @@ 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
|
||||
|
|
@ -80,8 +78,7 @@ jobs:
|
|||
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: |
|
||||
|
|
@ -115,6 +112,12 @@ jobs:
|
|||
- 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
|
||||
|
||||
build_system:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
|
|
|
|||
4
.github/workflows/failures.yml
vendored
4
.github/workflows/failures.yml
vendored
|
|
@ -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: |
|
||||
|
|
|
|||
6
.github/workflows/m68k.yml
vendored
6
.github/workflows/m68k.yml
vendored
|
|
@ -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
|
||||
|
|
|
|||
7
.github/workflows/release.yml
vendored
7
.github/workflows/release.yml
vendored
|
|
@ -49,15 +49,14 @@ 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: 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
|
||||
|
|
|
|||
2
.github/workflows/stdarch.yml
vendored
2
.github/workflows/stdarch.yml
vendored
|
|
@ -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
3
.gitignore
vendored
|
|
@ -19,4 +19,5 @@ tools/llvmint-2
|
|||
llvm
|
||||
build_system/target
|
||||
config.toml
|
||||
build
|
||||
build
|
||||
rustlantis
|
||||
|
|
@ -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`:
|
||||
|
||||
|
|
|
|||
9
_typos.toml
Normal file
9
_typos.toml
Normal 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"]
|
||||
238
build_system/src/fuzz.rs
Normal file
238
build_system/src/fuzz.rs
Normal file
|
|
@ -0,0 +1,238 @@
|
|||
use std::ffi::OsStr;
|
||||
use std::path::Path;
|
||||
|
||||
use crate::utils::run_command_with_output;
|
||||
|
||||
fn show_usage() {
|
||||
println!(
|
||||
r#"
|
||||
`fuzz` command help:
|
||||
--help : Show this help"#
|
||||
);
|
||||
}
|
||||
|
||||
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() {
|
||||
"--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();
|
||||
// Spawn `threads`..
|
||||
for _ in 0..threads {
|
||||
let start = start.clone();
|
||||
// .. which each will ..
|
||||
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) {
|
||||
Err(err) => {
|
||||
// If the test failed at compile-time...
|
||||
println!("test({}) failed because {err:?}", next);
|
||||
// ... 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:?}");
|
||||
// ... copy that file to the directory `target/fuzz/runtime_error`...
|
||||
let mut out_path: std::path::PathBuf = "target/fuzz/runtime_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();
|
||||
}
|
||||
// If the test passed, do nothing
|
||||
Ok(Ok(())) => (),
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
// The "manager" thread loop.
|
||||
while start.load(Ordering::Relaxed) < end {
|
||||
// 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) - 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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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)
|
||||
}
|
||||
|
||||
/// Generates a new rustlantis file, & compares the result of running it with GCC and LLVM.
|
||||
fn test(seed: u64) -> Result<Result<(), std::path::PathBuf>, String> {
|
||||
// Generate a Rust source...
|
||||
let source_file = generate(seed)?;
|
||||
// ... test it with debug LLVM ...
|
||||
let llvm_res = debug_llvm(&source_file)?;
|
||||
// ... test it with release GCC ...
|
||||
let gcc_res = release_gcc(&source_file)?;
|
||||
// ... compare the results ...
|
||||
if llvm_res != gcc_res {
|
||||
// .. if they don't match, report an error.
|
||||
Ok(Err(source_file))
|
||||
} else {
|
||||
std::fs::remove_file(source_file).map_err(|err| format!("{err:?}"))?;
|
||||
Ok(Ok(()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates a new rustlantis file for us to run tests on.
|
||||
fn generate(seed: u64) -> 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 out = std::process::Command::new("cargo")
|
||||
.args(["run", "--release", "--bin", "generate"])
|
||||
.arg(&format!("{seed}"))
|
||||
.current_dir("clones/rustlantis")
|
||||
.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)
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
@ -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,
|
||||
|
|
@ -144,6 +146,7 @@ impl TestArg {
|
|||
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) =>
|
||||
{
|
||||
|
|
@ -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");
|
||||
|
|
@ -680,7 +710,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(())
|
||||
}
|
||||
|
||||
|
|
@ -1217,7 +1255,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(())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
39
patches/0001-Pin-compiler_builtins-to-0.1.160.patch
Normal file
39
patches/0001-Pin-compiler_builtins-to-0.1.160.patch
Normal 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
|
||||
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
[toolchain]
|
||||
channel = "nightly-2025-05-12"
|
||||
channel = "nightly-2025-05-21"
|
||||
components = ["rust-src", "rustc-dev", "llvm-tools-preview"]
|
||||
|
|
|
|||
|
|
@ -152,6 +152,7 @@ fn create_wrapper_function(
|
|||
if output.is_some() {
|
||||
block.end_with_return(None, ret);
|
||||
} else {
|
||||
block.add_eval(None, ret);
|
||||
block.end_with_void_return(None);
|
||||
}
|
||||
|
||||
|
|
|
|||
24
src/archive.rs
Normal file
24
src/archive.rs
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
use std::path::Path;
|
||||
|
||||
use rustc_codegen_ssa::back::archive::{
|
||||
ArArchiveBuilder, ArchiveBuilder, ArchiveBuilderBuilder, DEFAULT_OBJECT_READER,
|
||||
};
|
||||
use rustc_session::Session;
|
||||
|
||||
pub(crate) struct ArArchiveBuilderBuilder;
|
||||
|
||||
impl ArchiveBuilderBuilder for ArArchiveBuilderBuilder {
|
||||
fn new_archive_builder<'a>(&self, sess: &'a Session) -> Box<dyn ArchiveBuilder + 'a> {
|
||||
Box::new(ArArchiveBuilder::new(sess, &DEFAULT_OBJECT_READER))
|
||||
}
|
||||
|
||||
fn create_dll_import_lib(
|
||||
&self,
|
||||
_sess: &Session,
|
||||
_lib_name: &str,
|
||||
_import_name_and_ordinal_vector: Vec<(String, Option<u16>)>,
|
||||
_output_path: &Path,
|
||||
) {
|
||||
unimplemented!("creating dll imports is not yet supported");
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ use std::convert::TryFrom;
|
|||
use std::ops::Deref;
|
||||
|
||||
use gccjit::{
|
||||
BinaryOp, Block, ComparisonOp, Context, Function, LValue, Location, RValue, ToRValue, Type,
|
||||
UnaryOp,
|
||||
BinaryOp, Block, ComparisonOp, Context, Function, FunctionType, LValue, Location, RValue,
|
||||
ToRValue, Type, UnaryOp,
|
||||
};
|
||||
use rustc_abi as abi;
|
||||
use rustc_abi::{Align, HasDataLayout, Size, TargetDataLayout, WrappingRange};
|
||||
|
|
@ -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,
|
||||
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())
|
||||
|
|
|
|||
|
|
@ -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,7 @@ 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);
|
||||
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 +287,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 +310,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 {
|
||||
|
|
|
|||
|
|
@ -42,18 +42,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")]
|
||||
|
|
@ -294,8 +290,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> {
|
||||
|
|
@ -366,7 +364,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>(
|
||||
|
|
|
|||
|
|
@ -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::{
|
||||
|
|
@ -30,6 +32,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>,
|
||||
|
||||
|
|
@ -131,6 +135,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> {
|
||||
|
|
@ -222,6 +229,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),
|
||||
|
|
|
|||
|
|
@ -915,7 +915,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
|
|||
let name_suffix = match self.type_kind(dest_typ) {
|
||||
TypeKind::Float => "tisf",
|
||||
TypeKind::Double => "tidf",
|
||||
TypeKind::FP128 => "tixf",
|
||||
TypeKind::FP128 => "titf",
|
||||
kind => panic!("cannot cast a non-native integer to type {:?}", kind),
|
||||
};
|
||||
let sign = if signed { "" } else { "un" };
|
||||
|
|
|
|||
|
|
@ -72,44 +72,8 @@ fn get_simple_intrinsic<'gcc, 'tcx>(
|
|||
sym::fabsf64 => "fabs",
|
||||
sym::minnumf32 => "fminf",
|
||||
sym::minnumf64 => "fmin",
|
||||
sym::minimumf32 => "fminimumf",
|
||||
sym::minimumf64 => "fminimum",
|
||||
sym::minimumf128 => {
|
||||
// GCC doesn't have the intrinsic we want so we use the compiler-builtins one
|
||||
// https://docs.rs/compiler_builtins/latest/compiler_builtins/math/full_availability/fn.fminimumf128.html
|
||||
let f128_type = cx.type_f128();
|
||||
return 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"),
|
||||
],
|
||||
"fminimumf128",
|
||||
false,
|
||||
));
|
||||
}
|
||||
sym::maxnumf32 => "fmaxf",
|
||||
sym::maxnumf64 => "fmax",
|
||||
sym::maximumf32 => "fmaximumf",
|
||||
sym::maximumf64 => "fmaximum",
|
||||
sym::maximumf128 => {
|
||||
// GCC doesn't have the intrinsic we want so we use the compiler-builtins one
|
||||
// https://docs.rs/compiler_builtins/latest/compiler_builtins/math/full_availability/fn.fmaximumf128.html
|
||||
let f128_type = cx.type_f128();
|
||||
return 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"),
|
||||
],
|
||||
"fmaximumf128",
|
||||
false,
|
||||
));
|
||||
}
|
||||
sym::copysignf32 => "copysignf",
|
||||
sym::copysignf64 => "copysign",
|
||||
sym::copysignf128 => "copysignl",
|
||||
|
|
@ -196,6 +160,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 +264,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 +289,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();
|
||||
|
|
|
|||
11
src/lib.rs
11
src/lib.rs
|
|
@ -521,13 +521,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,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
31
tests/run/packed_u128.rs
Normal file
31
tests/run/packed_u128.rs
Normal 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
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue