diff -ruN a/compiler/rustc_target/src/callconv/powerpc.rs b/compiler/rustc_target/src/callconv/powerpc.rs
--- a/compiler/rustc_target/src/callconv/powerpc.rs	2026-07-22 21:59:42.235692385 +0000
+++ b/compiler/rustc_target/src/callconv/powerpc.rs	2026-07-22 22:00:22.773722882 +0000
@@ -1,4 +1,4 @@
-use crate::callconv::{ArgAbi, FnAbi};
+use crate::callconv::{Align, ArgAbi, FnAbi};
 use crate::spec::HasTargetSpec;
 
 fn classify_ret<Ty>(ret: &mut ArgAbi<'_, Ty>) {
@@ -21,7 +21,15 @@
         return;
     }
     if arg.layout.is_aggregate() {
-        arg.make_indirect();
+        if cx.target_spec().is_like_darwin {
+            // The Darwin (PowerOpen-derived) C ABI passes aggregates by value,
+            // laid out in registers / the parameter save area with 4-byte
+            // alignment (everything except vectors has 4-byte on-stack
+            // alignment). Mirrors clang's PPC32_DarwinABIInfo byval handling.
+            arg.pass_by_stack_offset(Some(Align::from_bytes(4).unwrap()));
+        } else {
+            arg.make_indirect();
+        }
     } else {
         arg.extend_integer_width_to(32);
     }
diff -ruN a/compiler/rustc_target/src/spec/base/apple/mod.rs b/compiler/rustc_target/src/spec/base/apple/mod.rs
--- a/compiler/rustc_target/src/spec/base/apple/mod.rs	2026-07-22 21:59:42.232692309 +0000
+++ b/compiler/rustc_target/src/spec/base/apple/mod.rs	2026-07-22 22:00:22.772722856 +0000
@@ -25,6 +25,8 @@
     I686,
     X86_64,
     X86_64h,
+    PowerPc,
+    PowerPc64,
 }
 
 impl Arch {
@@ -39,6 +41,8 @@
             I686 => "i686",
             X86_64 => "x86_64",
             X86_64h => "x86_64h",
+            PowerPc => "ppc",
+            PowerPc64 => "ppc64",
         }
     }
 
@@ -48,6 +52,8 @@
             Arm64 | Arm64e | Arm64_32 => "aarch64",
             I386 | I686 => "x86",
             X86_64 | X86_64h => "x86_64",
+            PowerPc => "powerpc",
+            PowerPc64 => "powerpc64",
         })
     }
 
@@ -71,6 +77,11 @@
             // comments (and disabled features) in `x86_64h_apple_darwin` for
             // details. It is a higher baseline then `penryn` however.
             X86_64h => "core-avx2",
+            // The lowest CPU each OS release runs on: macOS 10.4 (Tiger)
+            // supports the G4 and up, the 64-bit userspace of 10.5 (Leopard)
+            // needs a G5.
+            PowerPc => "g4",
+            PowerPc64 => "g5",
         }
     }
 
@@ -78,6 +89,7 @@
         match self {
             Armv7k | Armv7s => StackProbeType::None,
             Arm64 | Arm64e | Arm64_32 | I386 | I686 | X86_64 | X86_64h => StackProbeType::Inline,
+            PowerPc | PowerPc64 => StackProbeType::Inline,
         }
     }
 }
@@ -130,6 +142,8 @@
             // clang supports omitting frame pointers for the rest, but... don't?
             Arm64 | Arm64e | Arm64_32 => FramePointer::NonLeaf,
             I386 | I686 | X86_64 | X86_64h => FramePointer::Always,
+            // Apple's PowerPC ABI reserves r30 as the frame pointer.
+            PowerPc | PowerPc64 => FramePointer::Always,
         },
         has_rpath: true,
         dll_suffix: ".dylib".into(),
@@ -311,6 +325,10 @@
     pub fn minimum_deployment_target(target: &Target) -> Self {
         let (major, minor, patch) = match (&*target.os, &*target.arch, &*target.abi) {
             ("macos", "aarch64", _) => (11, 0, 0),
+            // PowerPC macOS: 10.4 (Tiger) is the oldest supported release,
+            // 10.5 (Leopard) the only one with a 64-bit userspace.
+            ("macos", "powerpc", _) => (10, 4, 0),
+            ("macos", "powerpc64", _) => (10, 5, 0),
             ("ios", "aarch64", "macabi") => (14, 0, 0),
             ("ios", "aarch64", "sim") => (14, 0, 0),
             ("ios", _, _) if target.llvm_target.starts_with("arm64e") => (14, 0, 0),
diff -ruN a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs
--- a/compiler/rustc_target/src/spec/mod.rs	2026-07-22 21:59:42.224692106 +0000
+++ b/compiler/rustc_target/src/spec/mod.rs	2026-07-22 22:00:22.772722856 +0000
@@ -2007,6 +2007,8 @@
     ("x86_64-apple-darwin", x86_64_apple_darwin),
     ("x86_64h-apple-darwin", x86_64h_apple_darwin),
     ("i686-apple-darwin", i686_apple_darwin),
+    ("powerpc-apple-darwin", powerpc_apple_darwin),
+    ("powerpc64-apple-darwin", powerpc64_apple_darwin),
 
     ("aarch64-unknown-fuchsia", aarch64_unknown_fuchsia),
     ("riscv64gc-unknown-fuchsia", riscv64gc_unknown_fuchsia),
diff -ruN a/compiler/rustc_target/src/spec/targets/powerpc64_apple_darwin.rs b/compiler/rustc_target/src/spec/targets/powerpc64_apple_darwin.rs
--- a/compiler/rustc_target/src/spec/targets/powerpc64_apple_darwin.rs	1970-01-01 00:00:00.000000000 +0000
+++ b/compiler/rustc_target/src/spec/targets/powerpc64_apple_darwin.rs	2026-07-22 22:05:02.629860947 +0000
@@ -0,0 +1,37 @@
+use rustc_abi::Endian;
+
+use crate::spec::base::apple::{Arch, TargetAbi, base};
+use crate::spec::{Target, TargetMetadata, TargetOptions};
+
+pub(crate) fn target() -> Target {
+    let (mut opts, llvm_target, arch) = base("macos", Arch::PowerPc64, TargetAbi::Normal);
+    opts.max_atomic_width = Some(64);
+
+    Target {
+        llvm_target,
+        metadata: TargetMetadata {
+            description: Some("PowerPC 64-bit macOS (10.5 Leopard)".into()),
+            tier: Some(3),
+            host_tools: Some(false),
+            std: Some(true),
+        },
+        pointer_width: 64,
+        // Darwin ppc64 uses natural alignment throughout (GCC's
+        // TARGET_ALIGN_NATURAL on rs6000-darwin -m64; no "power" alignment
+        // quirks). `Fn32`: Darwin has no function descriptors, function
+        // pointers are plain 4-byte-aligned code addresses.
+        //
+        // NOTE: upstream LLVM is not an ABI reference for Darwin PPC (see
+        // powerpc_apple_darwin.rs); unpatched LLVM reports
+        // "E-m:o-Fi64-i64:64-i128:128-n32:64" for this triple (`Fi64` being
+        // a function-descriptor assumption Darwin never had), and the
+        // patched LLVM must be brought in line with the string below.
+        data_layout: "E-m:o-Fn32-i64:64-i128:128-n32:64".into(),
+        arch,
+        options: TargetOptions {
+            endian: Endian::Big,
+            mcount: "\u{1}mcount".into(),
+            ..opts
+        },
+    }
+}
diff -ruN a/compiler/rustc_target/src/spec/targets/powerpc_apple_darwin.rs b/compiler/rustc_target/src/spec/targets/powerpc_apple_darwin.rs
--- a/compiler/rustc_target/src/spec/targets/powerpc_apple_darwin.rs	1970-01-01 00:00:00.000000000 +0000
+++ b/compiler/rustc_target/src/spec/targets/powerpc_apple_darwin.rs	2026-07-22 22:05:02.629860947 +0000
@@ -0,0 +1,46 @@
+use rustc_abi::Endian;
+
+use crate::spec::base::apple::{Arch, TargetAbi, base};
+use crate::spec::{Target, TargetMetadata, TargetOptions};
+
+pub(crate) fn target() -> Target {
+    let (mut opts, llvm_target, arch) = base("macos", Arch::PowerPc, TargetAbi::Normal);
+    // lwarx/stwcx. only handle word-sized atomics.
+    opts.max_atomic_width = Some(32);
+
+    Target {
+        llvm_target,
+        metadata: TargetMetadata {
+            description: Some("PowerPC 32-bit macOS (10.4 Tiger - 10.6 Snow Leopard)".into()),
+            tier: Some(3),
+            host_tools: Some(false),
+            std: Some(true),
+        },
+        pointer_width: 32,
+        // Darwin's "power" alignment, per the GCC rs6000-darwin sources
+        // (`ADJUST_FIELD_ALIGN`/`darwin_rs6000_special_round_type_align`),
+        // which are the ABI authority for this platform: i64/f64 are
+        // naturally aligned as globals/locals ("preferred" alignment 8) but
+        // only 4-byte aligned inside aggregates unless they are the first
+        // field, so their ABI alignment is 4. Iain Sandoe's out-of-tree
+        // clang/LLVM Darwin PPC work encodes the same rule as `i64:32:64`.
+        //
+        // NOTE: upstream LLVM has *never* implemented the Darwin PowerPC ABI
+        // correctly (and later dropped Darwin PPC support altogether), so it
+        // is not a reference for any of this. rustc does however require this
+        // string to be byte-identical to the one the LLVM backend reports for
+        // the triple, so the (necessarily patched) LLVM must have
+        // PPCTargetMachine::getDataLayoutString emit exactly the string
+        // below; with unpatched LLVM (which reports
+        // "E-m:o-p:32:32-Fn32-i64:64-n32") rustc refuses to start, which is
+        // correct, since repr(C) layout would not match the platform C ABI.
+        data_layout: "E-m:o-p:32:32-Fn32-i64:32:64-f64:32:64-n32".into(),
+        arch,
+        options: TargetOptions {
+            endian: Endian::Big,
+            // Darwin ppc profiling calls `mcount` with no leading underscore.
+            mcount: "\u{1}mcount".into(),
+            ..opts
+        },
+    }
+}
