about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAntoni Boucher <bouanto@zoho.com>2025-06-02 14:12:36 -0400
committerAntoni Boucher <bouanto@zoho.com>2025-06-02 14:12:36 -0400
commitf69d8fc32421ea7a067b9f18226883884c6762e4 (patch)
tree029391c09e9cd259d8e19b86222b2e1c6443d4df
parentf383b17668e0f564aae978f01d8eef7b115aa8ca (diff)
parent8efeb495bc74c984c639e3992d595811a2e25292 (diff)
downloadrust-f69d8fc32421ea7a067b9f18226883884c6762e4.tar.gz
rust-f69d8fc32421ea7a067b9f18226883884c6762e4.zip
Merge branch 'master' into sync_from_rust_2025_06_02
-rw-r--r--.github/workflows/ci.yml11
-rw-r--r--.github/workflows/failures.yml4
-rw-r--r--.github/workflows/m68k.yml6
-rw-r--r--.github/workflows/release.yml7
-rw-r--r--.github/workflows/stdarch.yml2
-rw-r--r--.gitignore3
-rw-r--r--CONTRIBUTING.md2
-rw-r--r--_typos.toml9
-rw-r--r--build_system/src/fuzz.rs238
-rw-r--r--build_system/src/main.rs7
-rw-r--r--build_system/src/test.rs44
-rw-r--r--example/std_example.rs24
-rw-r--r--patches/0001-Pin-compiler_builtins-to-0.1.160.patch39
-rw-r--r--rust-toolchain2
-rw-r--r--src/allocator.rs1
-rw-r--r--src/archive.rs24
-rw-r--r--src/attributes.rs4
-rw-r--r--src/builder.rs36
-rw-r--r--src/common.rs89
-rw-r--r--src/consts.rs24
-rw-r--r--src/context.rs15
-rw-r--r--src/int.rs2
-rw-r--r--src/intrinsic/mod.rs189
-rw-r--r--src/lib.rs11
-rw-r--r--src/type_of.rs2
-rw-r--r--tests/run/packed_u128.rs31
26 files changed, 706 insertions, 120 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index ef024258ffc..d8675755187 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -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:
diff --git a/.github/workflows/failures.yml b/.github/workflows/failures.yml
index bc42eb1468e..67b7fbe4478 100644
--- a/.github/workflows/failures.yml
+++ b/.github/workflows/failures.yml
@@ -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: |
diff --git a/.github/workflows/m68k.yml b/.github/workflows/m68k.yml
index 21731f7087e..245bee7f2a3 100644
--- a/.github/workflows/m68k.yml
+++ b/.github/workflows/m68k.yml
@@ -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
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 47a40286554..b9c385b4231 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -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
diff --git a/.github/workflows/stdarch.yml b/.github/workflows/stdarch.yml
index f26ac3b755f..20d009f08a7 100644
--- a/.github/workflows/stdarch.yml
+++ b/.github/workflows/stdarch.yml
@@ -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 }}
diff --git a/.gitignore b/.gitignore
index c1e6631a281..8f73d3eb972 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,4 +19,5 @@ tools/llvmint-2
 llvm
 build_system/target
 config.toml
-build
\ No newline at end of file
+build
+rustlantis
\ No newline at end of file
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 8e313ab08b5..54cba0e6de3 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -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`:
 
diff --git a/_typos.toml b/_typos.toml
new file mode 100644
index 00000000000..4a6a506a981
--- /dev/null
+++ b/_typos.toml
@@ -0,0 +1,9 @@
+[default.extend-words]
+ba = "ba"
+hsa = "hsa"
+olt = "olt"
+seh = "seh"
+typ = "typ"
+
+[files]
+extend-exclude = ["src/intrinsic/archs.rs"]
diff --git a/build_system/src/fuzz.rs b/build_system/src/fuzz.rs
new file mode 100644
index 00000000000..05a87412b36
--- /dev/null
+++ b/build_system/src/fuzz.rs
@@ -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)
+}
diff --git a/build_system/src/main.rs b/build_system/src/main.rs
index c70b00e09ae..078a4726ba8 100644
--- a/build_system/src/main.rs
+++ b/build_system/src/main.rs
@@ -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);
diff --git a/build_system/src/test.rs b/build_system/src/test.rs
index df4ac85233b..a209cb4b580 100644
--- a/build_system/src/test.rs
+++ b/build_system/src/test.rs
@@ -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(())
 }
 
diff --git a/example/std_example.rs b/example/std_example.rs
index 5fa1e0afb06..7587b4827ca 100644
--- a/example/std_example.rs
+++ b/example/std_example.rs
@@ -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;
 
diff --git a/patches/0001-Pin-compiler_builtins-to-0.1.160.patch b/patches/0001-Pin-compiler_builtins-to-0.1.160.patch
new file mode 100644
index 00000000000..39266e081ed
--- /dev/null
+++ b/patches/0001-Pin-compiler_builtins-to-0.1.160.patch
@@ -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
+
diff --git a/rust-toolchain b/rust-toolchain
index a8cda28688c..bafe497a2a2 100644
--- a/rust-toolchain
+++ b/rust-toolchain
@@ -1,3 +1,3 @@
 [toolchain]
-channel = "nightly-2025-05-12"
+channel = "nightly-2025-05-21"
 components = ["rust-src", "rustc-dev", "llvm-tools-preview"]
diff --git a/src/allocator.rs b/src/allocator.rs
index f4ebd42ee2d..279b7dd2fb9 100644
--- a/src/allocator.rs
+++ b/src/allocator.rs
@@ -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);
     }
 
diff --git a/src/archive.rs b/src/archive.rs
new file mode 100644
index 00000000000..0cee05f1cea
--- /dev/null
+++ b/src/archive.rs
@@ -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");
+    }
+}
diff --git a/src/attributes.rs b/src/attributes.rs
index c853c88a6ea..bf0927dc590 100644
--- a/src/attributes.rs
+++ b/src/attributes.rs
@@ -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)
diff --git a/src/builder.rs b/src/builder.rs
index d1fb8d8f9d6..96e3773c7c4 100644
--- a/src/builder.rs
+++ b/src/builder.rs
@@ -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())
diff --git a/src/common.rs b/src/common.rs
index 918195364ff..65f4788d902 100644
--- a/src/common.rs
+++ b/src/common.rs
@@ -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 {
diff --git a/src/consts.rs b/src/consts.rs
index deb13ddf755..890e5f1e6c9 100644
--- a/src/consts.rs
+++ b/src/consts.rs
@@ -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>(
diff --git a/src/context.rs b/src/context.rs
index c6c43201f21..1d46d73cd26 100644
--- a/src/context.rs
+++ b/src/context.rs
@@ -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),
diff --git a/src/int.rs b/src/int.rs
index 9b5b0fde6e2..fed96e5eb8c 100644
--- a/src/int.rs
+++ b/src/int.rs
@@ -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" };
diff --git a/src/intrinsic/mod.rs b/src/intrinsic/mod.rs
index 73be25ba92b..a03b4a9c581 100644
--- a/src/intrinsic/mod.rs
+++ b/src/intrinsic/mod.rs
@@ -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();
diff --git a/src/lib.rs b/src/lib.rs
index f79ba2dcfc7..d2977ea44f7 100644
--- a/src/lib.rs
+++ b/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,
     }
 }
diff --git a/src/type_of.rs b/src/type_of.rs
index 5745acce6fe..093f902bc3d 100644
--- a/src/type_of.rs
+++ b/src/type_of.rs
@@ -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),
diff --git a/tests/run/packed_u128.rs b/tests/run/packed_u128.rs
new file mode 100644
index 00000000000..b7cc6e21023
--- /dev/null
+++ b/tests/run/packed_u128.rs
@@ -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
+}