Skip to content

Conversation

ahmedbougacha
Copy link
Member

iOS 26/watchOS 26 deprecate some devices, allowing the default CPU to be raised based on the deployment target.

watchOS 26 also introduces arm64 (vs. arm64_32/arm64e).

The defaults are now:

- arm64-apple-ios26         apple-a12
- arm64-apple-watchos26     apple-s6
- arm64_32-apple-watchos26  apple-s6
- arm64e-apple-watchos26    apple-s6

Left unchanged are:

- arm64-apple-tvos26        apple-a7
- arm64e-apple-tvos26       apple-a12
- arm64e-apple-ios26        apple-a12
- arm64_32-apple-watchos11  apple-s4

While there, rewrite an outdated comment in a related Mac test.

iOS 26/watchOS 26 deprecate some devices, allowing the default CPU to be
raised based on the deployment target.

watchOS 26 also introduces arm64 (vs. arm64_32/arm64e).

The defaults are now:
- arm64-apple-ios26         apple-a12
- arm64-apple-watchos26     apple-s6
- arm64_32-apple-watchos26  apple-s6
- arm64e-apple-watchos26    apple-s6

Left unchanged are:
- arm64-apple-tvos26        apple-a7
- arm64e-apple-tvos26       apple-a12
- arm64e-apple-ios26        apple-a12
- arm64_32-apple-watchos11  apple-s4

While there, rewrite an outdated comment in a related Mac test.
@ahmedbougacha ahmedbougacha requested a review from jroelofs August 6, 2025 01:15
@llvmbot llvmbot added clang Clang issues not falling into any other category backend:AArch64 clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' labels Aug 6, 2025
@llvmbot
Copy link
Member

llvmbot commented Aug 6, 2025

@llvm/pr-subscribers-backend-aarch64

@llvm/pr-subscribers-clang

Author: Ahmed Bougacha (ahmedbougacha)

Changes

iOS 26/watchOS 26 deprecate some devices, allowing the default CPU to be raised based on the deployment target.

watchOS 26 also introduces arm64 (vs. arm64_32/arm64e).

The defaults are now:

- arm64-apple-ios26         apple-a12
- arm64-apple-watchos26     apple-s6
- arm64_32-apple-watchos26  apple-s6
- arm64e-apple-watchos26    apple-s6

Left unchanged are:

- arm64-apple-tvos26        apple-a7
- arm64e-apple-tvos26       apple-a12
- arm64e-apple-ios26        apple-a12
- arm64_32-apple-watchos11  apple-s4

While there, rewrite an outdated comment in a related Mac test.


Full diff: https://github.com/llvm/llvm-project/pull/152235.diff

3 Files Affected:

  • (modified) clang/lib/Driver/ToolChains/Arch/AArch64.cpp (+16)
  • (added) clang/test/Driver/aarch64-cpu-defaults-appleos26.c (+22)
  • (modified) clang/test/Driver/aarch64-mac-cpus.c (+1-1)
diff --git a/clang/lib/Driver/ToolChains/Arch/AArch64.cpp b/clang/lib/Driver/ToolChains/Arch/AArch64.cpp
index 418f9fd9ca4c4..98f5efbe5652f 100644
--- a/clang/lib/Driver/ToolChains/Arch/AArch64.cpp
+++ b/clang/lib/Driver/ToolChains/Arch/AArch64.cpp
@@ -52,6 +52,22 @@ std::string aarch64::getAArch64TargetCPU(const ArgList &Args,
     return "apple-m1";
   }
 
+  if (Triple.getOS() == llvm::Triple::IOS) {
+    assert(!Triple.isSimulatorEnvironment() && "iossim should be mac-like");
+    // iOS 26 only runs on apple-a12 and later CPUs.
+    if (!Triple.isOSVersionLT(26))
+      return "apple-a12";
+  }
+
+  if (Triple.isWatchOS()) {
+    assert(!Triple.isSimulatorEnvironment() && "watchossim should be mac-like");
+    // arm64_32/arm64e watchOS requires S4 before watchOS 26, S6 after.
+    if (Triple.getArch() == llvm::Triple::aarch64_32 || Triple.isArm64e())
+      return Triple.isOSVersionLT(26) ? "apple-s4" : "apple-s6";
+    // arm64 (non-e, non-32) watchOS comes later, and requires S6 anyway.
+    return "apple-s6";
+  }
+
   if (Triple.isXROS()) {
     // The xrOS simulator runs on M1 as well, it should have been covered above.
     assert(!Triple.isSimulatorEnvironment() && "xrossim should be mac-like");
diff --git a/clang/test/Driver/aarch64-cpu-defaults-appleos26.c b/clang/test/Driver/aarch64-cpu-defaults-appleos26.c
new file mode 100644
index 0000000000000..7ad460ee86fcf
--- /dev/null
+++ b/clang/test/Driver/aarch64-cpu-defaults-appleos26.c
@@ -0,0 +1,22 @@
+/// iOS 26 and watchOS 26 bump the default arm64 CPU targets.
+
+/// arm64 iOS 26 defaults to apple-a12.  arm64e already did.
+// RUN: %clang -target arm64-apple-ios26  -### -c %s 2>&1 | FileCheck %s --check-prefix=A12
+// RUN: %clang -target arm64e-apple-ios26 -### -c %s 2>&1 | FileCheck %s --check-prefix=A12
+
+// arm64e/arm64_32 watchOS 26 default to apple-s6.
+// RUN: %clang -target arm64e-apple-watchos26   -### -c %s 2>&1 | FileCheck %s --check-prefix=S6
+// RUN: %clang -target arm64_32-apple-watchos26 -### -c %s 2>&1 | FileCheck %s --check-prefix=S6
+
+// arm64 is new in watchOS 26, and defaults to apple-s6.
+// RUN: %clang -target arm64-apple-watchos26  -### -c %s 2>&1 | FileCheck %s --check-prefix=S6
+
+/// llvm usually treats tvOS like iOS, but it runs on different hardware.
+// RUN: %clang -target arm64-apple-tvos26  -### -c %s 2>&1 | FileCheck %s --check-prefix=A7
+// RUN: %clang -target arm64e-apple-tvos26 -### -c %s 2>&1 | FileCheck %s --check-prefix=A12
+
+/// Simulators are tested with other Mac-like targets in aarch64-mac-cpus.c.
+
+// A12: "-target-cpu" "apple-a12"
+// S6:  "-target-cpu" "apple-s6"
+// A7:  "-target-cpu" "apple-a7"
diff --git a/clang/test/Driver/aarch64-mac-cpus.c b/clang/test/Driver/aarch64-mac-cpus.c
index 8d23ad8c956fd..1aef1ae285e86 100644
--- a/clang/test/Driver/aarch64-mac-cpus.c
+++ b/clang/test/Driver/aarch64-mac-cpus.c
@@ -1,4 +1,4 @@
-// arm64 Mac-based targets default to Apple A13.
+// arm64/arm64e Mac-based targets default to Apple M1.
 
 // RUN: %clang --target=arm64-apple-macos             -### -c %s 2>&1 | FileCheck %s
 // RUN: %clang --target=arm64-apple-ios-macabi        -### -c %s 2>&1 | FileCheck %s

@llvmbot
Copy link
Member

llvmbot commented Aug 6, 2025

@llvm/pr-subscribers-clang-driver

Author: Ahmed Bougacha (ahmedbougacha)

Changes

iOS 26/watchOS 26 deprecate some devices, allowing the default CPU to be raised based on the deployment target.

watchOS 26 also introduces arm64 (vs. arm64_32/arm64e).

The defaults are now:

- arm64-apple-ios26         apple-a12
- arm64-apple-watchos26     apple-s6
- arm64_32-apple-watchos26  apple-s6
- arm64e-apple-watchos26    apple-s6

Left unchanged are:

- arm64-apple-tvos26        apple-a7
- arm64e-apple-tvos26       apple-a12
- arm64e-apple-ios26        apple-a12
- arm64_32-apple-watchos11  apple-s4

While there, rewrite an outdated comment in a related Mac test.


Full diff: https://github.com/llvm/llvm-project/pull/152235.diff

3 Files Affected:

  • (modified) clang/lib/Driver/ToolChains/Arch/AArch64.cpp (+16)
  • (added) clang/test/Driver/aarch64-cpu-defaults-appleos26.c (+22)
  • (modified) clang/test/Driver/aarch64-mac-cpus.c (+1-1)
diff --git a/clang/lib/Driver/ToolChains/Arch/AArch64.cpp b/clang/lib/Driver/ToolChains/Arch/AArch64.cpp
index 418f9fd9ca4c4..98f5efbe5652f 100644
--- a/clang/lib/Driver/ToolChains/Arch/AArch64.cpp
+++ b/clang/lib/Driver/ToolChains/Arch/AArch64.cpp
@@ -52,6 +52,22 @@ std::string aarch64::getAArch64TargetCPU(const ArgList &Args,
     return "apple-m1";
   }
 
+  if (Triple.getOS() == llvm::Triple::IOS) {
+    assert(!Triple.isSimulatorEnvironment() && "iossim should be mac-like");
+    // iOS 26 only runs on apple-a12 and later CPUs.
+    if (!Triple.isOSVersionLT(26))
+      return "apple-a12";
+  }
+
+  if (Triple.isWatchOS()) {
+    assert(!Triple.isSimulatorEnvironment() && "watchossim should be mac-like");
+    // arm64_32/arm64e watchOS requires S4 before watchOS 26, S6 after.
+    if (Triple.getArch() == llvm::Triple::aarch64_32 || Triple.isArm64e())
+      return Triple.isOSVersionLT(26) ? "apple-s4" : "apple-s6";
+    // arm64 (non-e, non-32) watchOS comes later, and requires S6 anyway.
+    return "apple-s6";
+  }
+
   if (Triple.isXROS()) {
     // The xrOS simulator runs on M1 as well, it should have been covered above.
     assert(!Triple.isSimulatorEnvironment() && "xrossim should be mac-like");
diff --git a/clang/test/Driver/aarch64-cpu-defaults-appleos26.c b/clang/test/Driver/aarch64-cpu-defaults-appleos26.c
new file mode 100644
index 0000000000000..7ad460ee86fcf
--- /dev/null
+++ b/clang/test/Driver/aarch64-cpu-defaults-appleos26.c
@@ -0,0 +1,22 @@
+/// iOS 26 and watchOS 26 bump the default arm64 CPU targets.
+
+/// arm64 iOS 26 defaults to apple-a12.  arm64e already did.
+// RUN: %clang -target arm64-apple-ios26  -### -c %s 2>&1 | FileCheck %s --check-prefix=A12
+// RUN: %clang -target arm64e-apple-ios26 -### -c %s 2>&1 | FileCheck %s --check-prefix=A12
+
+// arm64e/arm64_32 watchOS 26 default to apple-s6.
+// RUN: %clang -target arm64e-apple-watchos26   -### -c %s 2>&1 | FileCheck %s --check-prefix=S6
+// RUN: %clang -target arm64_32-apple-watchos26 -### -c %s 2>&1 | FileCheck %s --check-prefix=S6
+
+// arm64 is new in watchOS 26, and defaults to apple-s6.
+// RUN: %clang -target arm64-apple-watchos26  -### -c %s 2>&1 | FileCheck %s --check-prefix=S6
+
+/// llvm usually treats tvOS like iOS, but it runs on different hardware.
+// RUN: %clang -target arm64-apple-tvos26  -### -c %s 2>&1 | FileCheck %s --check-prefix=A7
+// RUN: %clang -target arm64e-apple-tvos26 -### -c %s 2>&1 | FileCheck %s --check-prefix=A12
+
+/// Simulators are tested with other Mac-like targets in aarch64-mac-cpus.c.
+
+// A12: "-target-cpu" "apple-a12"
+// S6:  "-target-cpu" "apple-s6"
+// A7:  "-target-cpu" "apple-a7"
diff --git a/clang/test/Driver/aarch64-mac-cpus.c b/clang/test/Driver/aarch64-mac-cpus.c
index 8d23ad8c956fd..1aef1ae285e86 100644
--- a/clang/test/Driver/aarch64-mac-cpus.c
+++ b/clang/test/Driver/aarch64-mac-cpus.c
@@ -1,4 +1,4 @@
-// arm64 Mac-based targets default to Apple A13.
+// arm64/arm64e Mac-based targets default to Apple M1.
 
 // RUN: %clang --target=arm64-apple-macos             -### -c %s 2>&1 | FileCheck %s
 // RUN: %clang --target=arm64-apple-ios-macabi        -### -c %s 2>&1 | FileCheck %s

// iOS 26 only runs on apple-a12 and later CPUs.
if (!Triple.isOSVersionLT(26))
return "apple-a12";
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about the two other places in the LTO backend where we do this sort of platform default thing?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah right, I think of those as a remnant of early LTO support, a default fallback for cases where the frontend doesn't set up the target IR attributes.
The last addition was arm64e/a12, when we still had unattributed functions, in obscure corners of clang IRGen that synthesize thunks and wrappers. I'd expect those to be thoroughly eliminated now, after the waves of ptrauth attribute variations that will break if missing, and can't rely on this sort of late fallback.

So I'd say we could probably try to remove the LTO logic altogether, at the risk of breaking some library users without a clang-equivalent driver.
There's a case to be made this logic should be an llvm default backing a driver override, which would help other users. I don't think that's a bad idea, and it seems it'd belong in TargetParser? But overall I think it's just another on the long list of low-level choices that clang has to do and are hard to separate; using clang seems unavoidable (also see Jordan and John's old llvm devmtg talk "Skip the FFI" ;)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Related: at some point we discussed having a way of describing default function attributes in a module (e.g., using module flags), for cases where functions are generated later that IRGen, in the IR pipeline. Of course I can't find any links anymore, but I'd expect those to have another (frontend-IRGen'd) function to use as a template. Of course one can imagine some hypothetical transform where that isn't true ;)

Comment on lines +65 to +66
if (Triple.getArch() == llvm::Triple::aarch64_32 || Triple.isArm64e())
return Triple.isOSVersionLT(26) ? "apple-s4" : "apple-s6";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (Triple.getArch() == llvm::Triple::aarch64_32 || Triple.isArm64e())
return Triple.isOSVersionLT(26) ? "apple-s4" : "apple-s6";
if ((Triple.getArch() == llvm::Triple::aarch64_32 || Triple.isArm64e()) &&
Triple.isOSVersionLT(26))
return "apple-s4";

Maybe? Seems nicer to avoid nested conditionals, but not a strong opinion.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that could be a sensible alternative, but I tried to keep it structured in a way that keeps each policy clearly delineated: in this case there are distinct reasons we end up with S6; the same applies later for the various ways we end up with A12.

@ahmedbougacha ahmedbougacha merged commit 69d1417 into llvm:main Aug 11, 2025
9 checks passed
@ahmedbougacha ahmedbougacha deleted the users/ahmedbougacha/aarch64-appleos26-cpu-defaults branch August 11, 2025 18:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backend:AArch64 clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants