[clang] [llvm] Zatrazz/ms intrinsincs (PR #193221)

Adhemerval Zanella via cfe-commits cfe-commits at lists.llvm.org
Tue Apr 21 06:32:29 PDT 2026


https://github.com/zatrazz created https://github.com/llvm/llvm-project/pull/193221

None

>From 6c0175ccce52a90415e5769961f96408c42c3554 Mon Sep 17 00:00:00 2001
From: Adhemerval Zanella <zatrazz at gmail.com>
Date: Thu, 9 Apr 2026 16:24:34 -0300
Subject: [PATCH 01/17] [aarch64] Add support for the _CountTrailingZeros{64}
 MS intrinsics

Adds support for the following MSVC intrinsics:
* `_CountTrailingZeros`
* `_CountTrailingZeros64`

These are documented at:
<https://learn.microsoft.com/en-us/cpp/intrinsics/arm64-intrinsics?view=msvc-180>
---
 clang/include/clang/Basic/BuiltinsAArch64.td  |  2 ++
 .../lib/CIR/CodeGen/CIRGenBuiltinAArch64.cpp  |  4 +++-
 clang/lib/CodeGen/TargetBuiltins/ARM.cpp      | 12 ++++++++++
 clang/lib/Headers/intrin.h                    |  2 ++
 .../test/CodeGen/arm64-microsoft-intrinsics.c | 23 +++++++++++++++++++
 5 files changed, 42 insertions(+), 1 deletion(-)

diff --git a/clang/include/clang/Basic/BuiltinsAArch64.td b/clang/include/clang/Basic/BuiltinsAArch64.td
index 8e5dbb5f4172d..502652e208dff 100644
--- a/clang/include/clang/Basic/BuiltinsAArch64.td
+++ b/clang/include/clang/Basic/BuiltinsAArch64.td
@@ -394,6 +394,8 @@ let Attributes = [NoThrow, RequireDeclaration], Languages = "ALL_MS_LANGUAGES",
 	def _CountLeadingZeros64 : AArch64NoPrefixTargetLibBuiltin<"unsigned int (unsigned long long int)">;
 	def _CountOneBits        : AArch64NoPrefixTargetLibBuiltin<"unsigned int (msuint32_t)">;
 	def _CountOneBits64      : AArch64NoPrefixTargetLibBuiltin<"unsigned int (unsigned long long int)">;
+	def _CountTrailingZeros  : AArch64NoPrefixTargetLibBuiltin<"unsigned int (msuint32_t)">;
+	def _CountTrailingZeros64: AArch64NoPrefixTargetLibBuiltin<"unsigned int (unsigned long long int)">;
 }
 
 let Attributes = [NoThrow, RequireDeclaration], Languages = "ALL_MS_LANGUAGES", Header = "intrin.h" in {
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltinAArch64.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltinAArch64.cpp
index a24e3677ce782..2ba47f67c8df9 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltinAArch64.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltinAArch64.cpp
@@ -1990,7 +1990,9 @@ CIRGenFunction::emitAArch64BuiltinExpr(unsigned builtinID, const CallExpr *expr,
   }
 
   if (builtinID == AArch64::BI_CountOneBits ||
-      builtinID == AArch64::BI_CountOneBits64) {
+      builtinID == AArch64::BI_CountOneBits64 ||
+      builtinID == AArch64::BI_CountTrailingZeros ||
+      builtinID == AArch64::BI_CountTrailingZeros64) {
     cgm.errorNYI(expr->getSourceRange(),
                  std::string("unimplemented AArch64 builtin call: ") +
                      getContext().BuiltinInfo.getName(builtinID));
diff --git a/clang/lib/CodeGen/TargetBuiltins/ARM.cpp b/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
index c7dc15b8cadc8..66a5d0f8e03d0 100644
--- a/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
+++ b/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
@@ -5212,6 +5212,18 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID,
     return Result;
   }
 
+  if (BuiltinID == AArch64::BI_CountTrailingZeros ||
+      BuiltinID == AArch64::BI_CountTrailingZeros64) {
+    Value *ArgValue = EmitScalarExpr(E->getArg(0));
+    llvm::Type *ArgType = ArgValue->getType();
+    Function *F = CGM.getIntrinsic(Intrinsic::cttz, ArgType);
+
+    Value *Result = Builder.CreateCall(F, {ArgValue, Builder.getInt1(false)});
+    if (BuiltinID == AArch64::BI_CountTrailingZeros64)
+      Result = Builder.CreateTrunc(Result, Builder.getInt32Ty());
+    return Result;
+  }
+
   if (BuiltinID == AArch64::BI__prefetch) {
     Value *Address = EmitScalarExpr(E->getArg(0));
     Value *RW = llvm::ConstantInt::get(Int32Ty, 0);
diff --git a/clang/lib/Headers/intrin.h b/clang/lib/Headers/intrin.h
index 210ed0c1f773b..7b5698763d1ca 100644
--- a/clang/lib/Headers/intrin.h
+++ b/clang/lib/Headers/intrin.h
@@ -438,6 +438,8 @@ unsigned int _CountLeadingSigns(long);
 unsigned int _CountLeadingSigns64(__int64);
 unsigned int _CountOneBits(unsigned long);
 unsigned int _CountOneBits64(unsigned __int64);
+unsigned int _CountTrailingZeros(unsigned long);
+unsigned int _CountTrailingZeros64(unsigned __int64);
 
 unsigned int __hlt(unsigned int, ...);
 
diff --git a/clang/test/CodeGen/arm64-microsoft-intrinsics.c b/clang/test/CodeGen/arm64-microsoft-intrinsics.c
index 2f5ab50d6c848..c3efe232420e2 100644
--- a/clang/test/CodeGen/arm64-microsoft-intrinsics.c
+++ b/clang/test/CodeGen/arm64-microsoft-intrinsics.c
@@ -584,6 +584,29 @@ unsigned int check_CountOneBits64(unsigned __int64 arg1) {
 // CHECK-MSCOMPAT: ret i32 %[[VAR2]]
 // CHECK-LINUX: error: call to undeclared function '_CountOneBits64'
 
+unsigned int check_CountTrailingZeros(unsigned LONG arg1) {
+  return _CountTrailingZeros(arg1);
+}
+
+// CHECK-MSCOMPAT: %[[ARG1:.*]].addr = alloca i32, align 4
+// CHECK-MSCOMPAT: store i32 %[[ARG1]], ptr %[[ARG1]].addr, align 4
+// CHECK-MSCOMPAT: %[[VAR0:.*]] = load i32, ptr %[[ARG1]].addr, align 4
+// CHECK-MSCOMPAT: %[[VAR1:.*]] = call i32 @llvm.cttz.i32(i32 %0, i1 false)
+// CHECK-MSCOMPAT: ret i32 %[[VAR1]]
+// CHECK-LINUX: error: call to undeclared function '_CountTrailingZeros'
+
+unsigned int check_CountTrailingZeros64(unsigned __int64 arg1) {
+  return _CountTrailingZeros64(arg1);
+}
+
+// CHECK-MSCOMPAT: %[[ARG1:.*]].addr = alloca i64, align 8
+// CHECK-MSCOMPAT: store i64 %[[ARG1]], ptr %[[ARG1]].addr, align 8
+// CHECK-MSCOMPAT: %[[VAR0:.*]] = load i64, ptr %[[ARG1]].addr, align 8
+// CHECK-MSCOMPAT: %[[VAR1:.*]] = call i64 @llvm.cttz.i64(i64 %0, i1 false)
+// CHECK-MSCOMPAT: %[[VAR2:.*]] = trunc i64 %1 to i32
+// CHECK-MSCOMPAT: ret i32 %[[VAR2]]
+// CHECK-LINUX: error: call to undeclared function '_CountTrailingZeros64'
+
 void check__prefetch(void *arg1) {
   return __prefetch(arg1);
 }

>From af0d64639955aac09885cfacd943813e038d1ad4 Mon Sep 17 00:00:00 2001
From: Adhemerval Zanella <zatrazz at gmail.com>
Date: Mon, 13 Apr 2026 16:04:27 -0300
Subject: [PATCH 02/17] [aarch64] Add support for the __ldar{8|16|32|64} MS
 intrinsics (#121689)

Adds support for the following MSVC intrinsics:

* `__ldar8`  - maps to LDARB
* `__ldar16` - maps to LDARH
* `__ldar32` - maps to LDAR
* `__ldar64` - maps to LDAR

The emit is done using SeqCst atomic operations because wth RCPC enabled
the AArch64 backend lower acquire loads to LDARP* instead of LDAR*.

These are documented at:
<https://learn.microsoft.com/en-us/cpp/intrinsics/arm64-intrinsics?view=msvc-180>
---
 clang/include/clang/Basic/BuiltinsAArch64.td  |  7 +++++
 clang/lib/CodeGen/TargetBuiltins/ARM.cpp      | 17 ++++++++++
 clang/lib/Headers/arm64intr.h                 | 13 ++++++++
 .../test/CodeGen/arm64-microsoft-intrinsics.c | 31 +++++++++++++++++++
 4 files changed, 68 insertions(+)

diff --git a/clang/include/clang/Basic/BuiltinsAArch64.td b/clang/include/clang/Basic/BuiltinsAArch64.td
index 502652e208dff..371453419b41f 100644
--- a/clang/include/clang/Basic/BuiltinsAArch64.td
+++ b/clang/include/clang/Basic/BuiltinsAArch64.td
@@ -405,3 +405,10 @@ let Attributes = [NoThrow, RequireDeclaration], Languages = "ALL_MS_LANGUAGES",
 let Attributes = [NoThrow, RequireDeclaration], Languages = "ALL_MS_LANGUAGES", Header = "intrin.h" in {
 	def __hlt : AArch64NoPrefixTargetLibBuiltin<"unsigned int (unsigned int, ...)">;
 }
+
+let Attributes = [NoThrow, RequireDeclaration], Languages = "ALL_MS_LANGUAGES", Header = "intrin.h" in {
+	def __ldar8  : AArch64NoPrefixTargetLibBuiltin<"unsigned char (unsigned char const volatile *)">;
+	def __ldar16 : AArch64NoPrefixTargetLibBuiltin<"unsigned short (unsigned short const volatile *)">;
+	def __ldar32 : AArch64NoPrefixTargetLibBuiltin<"unsigned int (unsigned int const volatile *)">;
+	def __ldar64 : AArch64NoPrefixTargetLibBuiltin<"unsigned long long int (unsigned long long int const volatile*)">;
+}
diff --git a/clang/lib/CodeGen/TargetBuiltins/ARM.cpp b/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
index 66a5d0f8e03d0..fa6256c5927fc 100644
--- a/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
+++ b/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
@@ -5242,6 +5242,23 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID,
     return ConstantInt::get(Builder.getInt32Ty(), 0);
   }
 
+  if (BuiltinID == AArch64::BI__ldar8 || BuiltinID == AArch64::BI__ldar16 ||
+      BuiltinID == AArch64::BI__ldar32 || BuiltinID == AArch64::BI__ldar64) {
+    Value *Ptr = EmitScalarExpr(E->getArg(0));
+    QualType ElTy = E->getArg(0)->getType()->getPointeeType();
+    CharUnits LoadSize = CGM.getContext().getTypeSizeInChars(ElTy);
+    llvm::Type *ITy =
+        llvm::IntegerType::get(getLLVMContext(), LoadSize.getQuantity() * 8);
+    llvm::LoadInst *Load = Builder.CreateAlignedLoad(ITy, Ptr, LoadSize);
+    // We need SeqCst instead of Acquire because with RCPC enabled the AArch64
+    // lowers Acquire loads to LDARP* instead of LDAR*.  The SeqCst has not RCPC
+    // override and always maps to LDAR*.  This is the same apprach used by
+    // __iso_volatile_load (which uses Monotonic plus volatile for plain ldr).
+    Load->setAtomic(llvm::AtomicOrdering::SequentiallyConsistent);
+    Load->setVolatile(true);
+    return Load;
+  }
+
   if (BuiltinID == NEON::BI__builtin_neon_vcvth_bf16_f32)
     return Builder.CreateFPTrunc(
         Builder.CreateBitCast(EmitScalarExpr(E->getArg(0)),
diff --git a/clang/lib/Headers/arm64intr.h b/clang/lib/Headers/arm64intr.h
index 4943b2db69d02..58629d7b0f1b1 100644
--- a/clang/lib/Headers/arm64intr.h
+++ b/clang/lib/Headers/arm64intr.h
@@ -31,5 +31,18 @@ typedef enum
   _ARM64_BARRIER_OSHLD = 0x1
 } _ARM64INTR_BARRIER_TYPE;
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+unsigned __int8 __ldar8(const volatile unsigned __int8 *);
+unsigned __int16 __ldar16(const volatile unsigned __int16 *);
+unsigned __int32 __ldar32(const volatile unsigned __int32 *);
+unsigned __int64 __ldar64(const volatile unsigned __int64 *);
+
+#ifdef __cplusplus
+}
+#endif
+
 #endif /* __ARM64INTR_H */
 #endif /* _MSC_VER */
diff --git a/clang/test/CodeGen/arm64-microsoft-intrinsics.c b/clang/test/CodeGen/arm64-microsoft-intrinsics.c
index c3efe232420e2..2817f8c74a9df 100644
--- a/clang/test/CodeGen/arm64-microsoft-intrinsics.c
+++ b/clang/test/CodeGen/arm64-microsoft-intrinsics.c
@@ -617,6 +617,37 @@ void check__prefetch(void *arg1) {
 // CHECK-MSCOMPAT: call void @llvm.prefetch.p0(ptr %[[VAR0]], i32 0, i32 3, i32 1)
 // CHECK-MSCOMPAT: ret void
 
+unsigned char check__ldar8(unsigned char volatile *p) {
+  return __ldar8(p);
+}
+// CHECK-MSCOMPAT-LABEL: define{{.*}}i8 @check__ldar8(ptr{{.*}}%p){{.*}}{
+// CHECK-MSCOMPAT:       %[[RET:[0-9]+]] = load atomic volatile i8, ptr %{{.*}} seq_cst, align 1
+// CHECK-MSCOMPAT:       ret i8 %[[RET]]
+// CHECK-LINUX: error: call to undeclared function '__ldar8'
+
+unsigned short check__ldar16(unsigned short volatile *p) {
+  return __ldar16(p);
+}
+// CHECK-MSCOMPAT-LABEL: define{{.*}}i16 @check__ldar16(ptr{{.*}}%p){{.*}}{
+// CHECK-MSCOMPAT:       %[[RET:[0-9]+]] = load atomic volatile i16, ptr %{{.*}} seq_cst, align 2
+// CHECK-MSCOMPAT:       ret i16 %[[RET]]
+// CHECK-LINUX: error: call to undeclared function '__ldar16'
+
+unsigned int check__ldar32(unsigned int volatile *p) {
+  return __ldar32(p);
+}
+// CHECK-MSCOMPAT-LABEL: define{{.*}}i32 @check__ldar32(ptr{{.*}}%p){{.*}}{
+// CHECK-MSCOMPAT:       %[[RET:[0-9]+]] = load atomic volatile i32, ptr %{{.*}} seq_cst, align 4
+// CHECK-MSCOMPAT:       ret i32 %[[RET]]
+// CHECK-LINUX: error: call to undeclared function '__ldar32'
+
+unsigned long long int  check__ldar64(unsigned long long int volatile *p) {
+  return __ldar64(p);
+}
+// CHECK-MSCOMPAT-LABEL: define{{.*}}i64 @check__ldar64(ptr{{.*}}%p){{.*}}{
+// CHECK-MSCOMPAT:       %[[RET:[0-9]+]] = load atomic volatile i64, ptr %{{.*}} seq_cst, align 8
+// CHECK-MSCOMPAT:       ret i64 %[[RET]]
+// CHECK-LINUX: error: call to undeclared function '__ldar64'
 
 // CHECK-MSCOMPAT: ![[MD2]] = !{!"x18"}
 // CHECK-MSCOMPAT: ![[MD3]] = !{!"sp"}

>From 8674f7e37c618bdf8471226069859e278fd94acc Mon Sep 17 00:00:00 2001
From: Adhemerval Zanella <zatrazz at gmail.com>
Date: Wed, 15 Apr 2026 09:43:49 -0300
Subject: [PATCH 03/17] [aarch64] Add support for the __stlr{8|16|32|64} MS
 intrinsics

Adds support for the following MSVC intrinsics:
* `__stlr8`  - maps to STLRB.
* `__stlr16` - maps to STLRH.
* `__stlr32` - maps to STLR.
* `__stlr64` - maps to STLR.

These are documented at:
<https://learn.microsoft.com/en-us/cpp/intrinsics/arm64-intrinsics?view=msvc-180>
---
 clang/include/clang/Basic/BuiltinsAArch64.td  |  4 ++
 clang/lib/CodeGen/TargetBuiltins/ARM.cpp      | 12 +++++
 clang/lib/Headers/arm64intr.h                 |  5 +++
 .../test/CodeGen/arm64-microsoft-intrinsics.c | 44 +++++++++++++++++++
 4 files changed, 65 insertions(+)

diff --git a/clang/include/clang/Basic/BuiltinsAArch64.td b/clang/include/clang/Basic/BuiltinsAArch64.td
index 371453419b41f..e89a078f46a89 100644
--- a/clang/include/clang/Basic/BuiltinsAArch64.td
+++ b/clang/include/clang/Basic/BuiltinsAArch64.td
@@ -411,4 +411,8 @@ let Attributes = [NoThrow, RequireDeclaration], Languages = "ALL_MS_LANGUAGES",
 	def __ldar16 : AArch64NoPrefixTargetLibBuiltin<"unsigned short (unsigned short const volatile *)">;
 	def __ldar32 : AArch64NoPrefixTargetLibBuiltin<"unsigned int (unsigned int const volatile *)">;
 	def __ldar64 : AArch64NoPrefixTargetLibBuiltin<"unsigned long long int (unsigned long long int const volatile*)">;
+	def __stlr8  : AArch64NoPrefixTargetLibBuiltin<"void (unsigned char volatile *, unsigned char)">;
+	def __stlr16 : AArch64NoPrefixTargetLibBuiltin<"void (unsigned short volatile *, unsigned short)">;
+	def __stlr32 : AArch64NoPrefixTargetLibBuiltin<"void (unsigned int volatile *, unsigned int)">;
+	def __stlr64 : AArch64NoPrefixTargetLibBuiltin<"void (unsigned long long int volatile *, unsigned long long int)">;
 }
diff --git a/clang/lib/CodeGen/TargetBuiltins/ARM.cpp b/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
index fa6256c5927fc..b3c7cba725b6f 100644
--- a/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
+++ b/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
@@ -5259,6 +5259,18 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID,
     return Load;
   }
 
+  if (BuiltinID == AArch64::BI__stlr8 || BuiltinID == AArch64::BI__stlr16 ||
+      BuiltinID == AArch64::BI__stlr32 || BuiltinID == AArch64::BI__stlr64) {
+    Value *Ptr = EmitScalarExpr(E->getArg(0));
+    Value *Val = EmitScalarExpr(E->getArg(1));
+    QualType ElTy = E->getArg(0)->getType()->getPointeeType();
+    CharUnits StoreSize = CGM.getContext().getTypeSizeInChars(ElTy);
+    llvm::StoreInst *Store = Builder.CreateAlignedStore(Val, Ptr, StoreSize);
+    Store->setAtomic(llvm::AtomicOrdering::Release);
+    Store->setVolatile(true);
+    return Store;
+  }
+
   if (BuiltinID == NEON::BI__builtin_neon_vcvth_bf16_f32)
     return Builder.CreateFPTrunc(
         Builder.CreateBitCast(EmitScalarExpr(E->getArg(0)),
diff --git a/clang/lib/Headers/arm64intr.h b/clang/lib/Headers/arm64intr.h
index 58629d7b0f1b1..53dca5fcc14e6 100644
--- a/clang/lib/Headers/arm64intr.h
+++ b/clang/lib/Headers/arm64intr.h
@@ -40,6 +40,11 @@ unsigned __int16 __ldar16(const volatile unsigned __int16 *);
 unsigned __int32 __ldar32(const volatile unsigned __int32 *);
 unsigned __int64 __ldar64(const volatile unsigned __int64 *);
 
+void __stlr8(unsigned __int8 volatile *, unsigned __int8);
+void __stlr16(unsigned __int16 volatile *, unsigned __int16);
+void __stlr32(unsigned __int32 volatile *, unsigned __int32);
+void __stlr64(unsigned __int64 volatile *, unsigned __int64);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/clang/test/CodeGen/arm64-microsoft-intrinsics.c b/clang/test/CodeGen/arm64-microsoft-intrinsics.c
index 2817f8c74a9df..933fbfc074be0 100644
--- a/clang/test/CodeGen/arm64-microsoft-intrinsics.c
+++ b/clang/test/CodeGen/arm64-microsoft-intrinsics.c
@@ -649,5 +649,49 @@ unsigned long long int  check__ldar64(unsigned long long int volatile *p) {
 // CHECK-MSCOMPAT:       ret i64 %[[RET]]
 // CHECK-LINUX: error: call to undeclared function '__ldar64'
 
+void test__stlr8(unsigned __int8 volatile *p, unsigned __int8 v)
+{
+  __stlr8 (p, v);
+}
+// CHECK-MSCOMPAT-LABEL: define{{.*}}void @test__stlr8(ptr{{.*}}%p, i8{{.*}}%v){{.*}}{
+// CHECK-MSCOMPAT:       %[[DEST:[0-9]+]] = load ptr, ptr %p.addr, align 8
+// CHECK-MSCOMPAT:       %[[VALUE:[0-9]+]] = load i8, ptr %v.addr, align 1
+// CHECK-MSCOMPAT:       store atomic volatile i8 %[[VALUE]], ptr %[[DEST]] release, align 1
+// CHECK-MSCOMPAT:       ret void
+// CHECK-LINUX: error: call to undeclared function '__stlr8'
+
+void test__stlr16(unsigned __int16 volatile *p, unsigned __int16 v)
+{
+  __stlr16 (p, v);
+}
+// CHECK-MSCOMPAT-LABEL: define{{.*}}void @test__stlr16(ptr{{.*}}%p, i16{{.*}}%v){{.*}}{
+// CHECK-MSCOMPAT:       %[[DEST:[0-9]+]] = load ptr, ptr %p.addr, align 8
+// CHECK-MSCOMPAT:       %[[VALUE:[0-9]+]] = load i16, ptr %v.addr, align 2
+// CHECK-MSCOMPAT:       store atomic volatile i16 %[[VALUE]], ptr %[[DEST]] release, align 2
+// CHECK-MSCOMPAT:       ret void
+// CHECK-LINUX: error: call to undeclared function '__stlr16'
+
+void test__stlr32(unsigned __int32 volatile *p, unsigned __int32 v)
+{
+  __stlr32(p, v);
+}
+// CHECK-MSCOMPAT-LABEL: define{{.*}}void @test__stlr32(ptr{{.*}}%p, i32{{.*}}%v){{.*}}{
+// CHECK-MSCOMPAT:       %[[DEST:[0-9]+]] = load ptr, ptr %p.addr, align 8
+// CHECK-MSCOMPAT:       %[[VALUE:[0-9]+]] = load i32, ptr %v.addr, align 4
+// CHECK-MSCOMPAT:       store atomic volatile i32 %[[VALUE]], ptr %[[DEST]] release, align 4
+// CHECK-MSCOMPAT:       ret void
+// CHECK-LINUX: error: call to undeclared function '__stlr32'
+
+void test__stlr64(unsigned __int64 volatile *p, unsigned __int64 v)
+{
+  __stlr64(p, v);
+}
+// CHECK-MSCOMPAT-LABEL: define{{.*}}void @test__stlr64(ptr{{.*}}%p, i64{{.*}}%v){{.*}}{
+// CHECK-MSCOMPAT:       %[[DEST:[0-9]+]] = load ptr, ptr %p.addr, align 8
+// CHECK-MSCOMPAT:       %[[VALUE:[0-9]+]] = load i64, ptr %v.addr, align 8
+// CHECK-MSCOMPAT:       store atomic volatile i64 %[[VALUE]], ptr %[[DEST]] release, align 8
+// CHECK-MSCOMPAT:       ret void
+// CHECK-LINUX: error: call to undeclared function '__stlr64'
+
 // CHECK-MSCOMPAT: ![[MD2]] = !{!"x18"}
 // CHECK-MSCOMPAT: ![[MD3]] = !{!"sp"}

>From d17ba40c828d05d2bc5ce6ff200264bbb93af8cb Mon Sep 17 00:00:00 2001
From: Adhemerval Zanella <zatrazz at gmail.com>
Date: Tue, 14 Apr 2026 11:27:39 -0300
Subject: [PATCH 04/17] [aarch64] Add support for the __ldapr{8|16|32|64} MS
 intrinsics

Adds support for the following MSVC intrinsics:
* `__ldapr8`  - LDAPRB
* `__ldapr16` - LDAPRH
* `__ldapr32` - LDAPR
* `__ldapr64` - LDAPR

The emit is done using new intrisincs to issue LDAPR* instruction
directly, regardless of RCPC target features.  This mimics MSVC compiler
and the idea of the builtin to mimic an inline asm.

These are documented at:
<https://learn.microsoft.com/en-us/cpp/intrinsics/arm64-intrinsics?view=msvc-180>
---
 clang/include/clang/Basic/BuiltinsAArch64.td  |  4 ++
 clang/lib/CodeGen/TargetBuiltins/ARM.cpp      | 26 +++++++++++
 clang/lib/Headers/arm64intr.h                 |  5 ++
 .../test/CodeGen/arm64-microsoft-intrinsics.c | 38 +++++++++++++++
 llvm/include/llvm/IR/IntrinsicsAArch64.td     | 15 ++++++
 .../lib/Target/AArch64/AArch64InstrAtomics.td |  7 +++
 .../lib/Target/AArch64/AArch64InstrFormats.td | 15 ++++++
 llvm/lib/Target/AArch64/AArch64InstrInfo.td   |  9 ++++
 .../CodeGen/AArch64/ms-intrinsics-ldapr.ll    | 46 +++++++++++++++++++
 9 files changed, 165 insertions(+)
 create mode 100644 llvm/test/CodeGen/AArch64/ms-intrinsics-ldapr.ll

diff --git a/clang/include/clang/Basic/BuiltinsAArch64.td b/clang/include/clang/Basic/BuiltinsAArch64.td
index e89a078f46a89..2163cbd129980 100644
--- a/clang/include/clang/Basic/BuiltinsAArch64.td
+++ b/clang/include/clang/Basic/BuiltinsAArch64.td
@@ -415,4 +415,8 @@ let Attributes = [NoThrow, RequireDeclaration], Languages = "ALL_MS_LANGUAGES",
 	def __stlr16 : AArch64NoPrefixTargetLibBuiltin<"void (unsigned short volatile *, unsigned short)">;
 	def __stlr32 : AArch64NoPrefixTargetLibBuiltin<"void (unsigned int volatile *, unsigned int)">;
 	def __stlr64 : AArch64NoPrefixTargetLibBuiltin<"void (unsigned long long int volatile *, unsigned long long int)">;
+	def __ldapr8  : AArch64NoPrefixTargetLibBuiltin<"unsigned char (unsigned char const volatile *)">;
+	def __ldapr16 : AArch64NoPrefixTargetLibBuiltin<"unsigned short (unsigned short const volatile *)">;
+	def __ldapr32 : AArch64NoPrefixTargetLibBuiltin<"unsigned int (unsigned int const volatile *)">;
+	def __ldapr64 : AArch64NoPrefixTargetLibBuiltin<"unsigned long long int (unsigned long long int const volatile *)">;
 }
diff --git a/clang/lib/CodeGen/TargetBuiltins/ARM.cpp b/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
index b3c7cba725b6f..317336dfcb858 100644
--- a/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
+++ b/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
@@ -5271,6 +5271,32 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID,
     return Store;
   }
 
+  if (BuiltinID == AArch64::BI__ldapr8 || BuiltinID == AArch64::BI__ldapr16 ||
+      BuiltinID == AArch64::BI__ldapr32 || BuiltinID == AArch64::BI__ldapr64) {
+    unsigned IntrID;
+    switch (BuiltinID) {
+    case AArch64::BI__ldapr8:
+      IntrID = Intrinsic::aarch64_ldapr8;
+      break;
+    case AArch64::BI__ldapr16:
+      IntrID = Intrinsic::aarch64_ldapr16;
+      break;
+    case AArch64::BI__ldapr32:
+      IntrID = Intrinsic::aarch64_ldapr32;
+      break;
+    default:
+      IntrID = Intrinsic::aarch64_ldapr64;
+      break;
+    }
+    Value *Ptr = EmitScalarExpr(E->getArg(0));
+    Value *Result = Builder.CreateCall(CGM.getIntrinsic(IntrID), Ptr);
+    // LDAPRB/H return i32 (zero-extended); truncate to match the declared type.
+    llvm::Type *RetTy = ConvertType(E->getType());
+    if (Result->getType() != RetTy)
+      Result = Builder.CreateTrunc(Result, RetTy);
+    return Result;
+  }
+
   if (BuiltinID == NEON::BI__builtin_neon_vcvth_bf16_f32)
     return Builder.CreateFPTrunc(
         Builder.CreateBitCast(EmitScalarExpr(E->getArg(0)),
diff --git a/clang/lib/Headers/arm64intr.h b/clang/lib/Headers/arm64intr.h
index 53dca5fcc14e6..a0c62c2fc7775 100644
--- a/clang/lib/Headers/arm64intr.h
+++ b/clang/lib/Headers/arm64intr.h
@@ -40,6 +40,11 @@ unsigned __int16 __ldar16(const volatile unsigned __int16 *);
 unsigned __int32 __ldar32(const volatile unsigned __int32 *);
 unsigned __int64 __ldar64(const volatile unsigned __int64 *);
 
+unsigned __int8 __ldapr8(const volatile unsigned __int8 *);
+unsigned __int16 __ldapr16(const volatile unsigned __int16 *);
+unsigned __int32 __ldapr32(const volatile unsigned __int32 *);
+unsigned __int64 __ldapr64(const volatile unsigned __int64 *);
+
 void __stlr8(unsigned __int8 volatile *, unsigned __int8);
 void __stlr16(unsigned __int16 volatile *, unsigned __int16);
 void __stlr32(unsigned __int32 volatile *, unsigned __int32);
diff --git a/clang/test/CodeGen/arm64-microsoft-intrinsics.c b/clang/test/CodeGen/arm64-microsoft-intrinsics.c
index 933fbfc074be0..ce4a9abf24f50 100644
--- a/clang/test/CodeGen/arm64-microsoft-intrinsics.c
+++ b/clang/test/CodeGen/arm64-microsoft-intrinsics.c
@@ -693,5 +693,43 @@ void test__stlr64(unsigned __int64 volatile *p, unsigned __int64 v)
 // CHECK-MSCOMPAT:       ret void
 // CHECK-LINUX: error: call to undeclared function '__stlr64'
 
+unsigned char check__ldapr8(unsigned char volatile *p) {
+  return __ldapr8(p);
+}
+// CHECK-MSCOMPAT-LABEL: define{{.*}}i8 @check__ldapr8(ptr{{.*}}%p){{.*}}{
+// CHECK-MSCOMPAT:       %[[LOAD:[0-9]+]] = load ptr, ptr %p.addr, align 8
+// CHECK-MSCOMPAT:       %[[TMP:[0-9]+]] = call i32 @llvm.aarch64.ldapr8(ptr %[[LOAD]])
+// CHECK-MSCOMPAT:       %[[RET:[0-9]+]] = trunc i32 %[[TMP]] to i8
+// CHECK-MSCOMPAT:       ret i8 %[[RET]]
+// CHECK-LINUX: error: call to undeclared function '__ldapr8'
+
+unsigned short check__ldapr16(unsigned short volatile *p) {
+  return __ldapr16(p);
+}
+// CHECK-MSCOMPAT-LABEL: define{{.*}}i16 @check__ldapr16(ptr{{.*}}%p){{.*}}{
+// CHECK-MSCOMPAT:       %[[LOAD:[0-9]+]] = load ptr, ptr %p.addr, align 8
+// CHECK-MSCOMPAT:       %[[TMP:[0-9]+]] = call i32 @llvm.aarch64.ldapr16(ptr %[[LOAD]])
+// CHECK-MSCOMPAT:       %[[RET:[0-9]+]] = trunc i32 %[[TMP]] to i16
+// CHECK-MSCOMPAT:       ret i16 %[[RET]]
+// CHECK-LINUX: error: call to undeclared function '__ldapr16'
+
+unsigned int check__ldapr32(unsigned int volatile *p) {
+  return __ldapr32(p);
+}
+// CHECK-MSCOMPAT-LABEL: define{{.*}}i32 @check__ldapr32(ptr{{.*}}%p){{.*}}{
+// CHECK-MSCOMPAT:       %[[LOAD:[0-9]+]] = load ptr, ptr %p.addr, align 8
+// CHECK-MSCOMPAT:       %[[RET:[0-9]+]] = call i32 @llvm.aarch64.ldapr32(ptr %[[LOAD]])
+// CHECK-MSCOMPAT:       ret i32 %[[RET]]
+// CHECK-LINUX: error: call to undeclared function '__ldapr32'
+
+unsigned long long int check__ldapr64(unsigned long long int volatile *p) {
+  return __ldapr64(p);
+}
+// CHECK-MSCOMPAT-LABEL: define{{.*}}i64 @check__ldapr64(ptr{{.*}}%p){{.*}}{
+// CHECK-MSCOMPAT:       %[[LOAD:[0-9]+]] = load ptr, ptr %p.addr, align 8
+// CHECK-MSCOMPAT:       %[[RET:[0-9]+]] = call i64 @llvm.aarch64.ldapr64(ptr %[[LOAD]])
+// CHECK-MSCOMPAT:       ret i64 %[[RET]]
+// CHECK-LINUX: error: call to undeclared function '__ldapr64'
+
 // CHECK-MSCOMPAT: ![[MD2]] = !{!"x18"}
 // CHECK-MSCOMPAT: ![[MD3]] = !{!"sp"}
diff --git a/llvm/include/llvm/IR/IntrinsicsAArch64.td b/llvm/include/llvm/IR/IntrinsicsAArch64.td
index 578f54561910b..149c034b58b3f 100644
--- a/llvm/include/llvm/IR/IntrinsicsAArch64.td
+++ b/llvm/include/llvm/IR/IntrinsicsAArch64.td
@@ -34,6 +34,21 @@ def int_aarch64_stlxp : Intrinsic<[llvm_i32_ty],
 
 def int_aarch64_clrex : Intrinsic<[]>;
 
+// LDAPR intrinsics — emit LDAPR* directly, regardless of RCPC target feature.
+// LDAPRB/H/W return i32 (zero-extended into GPR32); LDAPRX returns i64.
+def int_aarch64_ldapr8  : Intrinsic<[llvm_i32_ty], [llvm_ptr_ty],
+                                    [IntrNoFree, IntrWillReturn,
+                                     IntrReadMem, IntrArgMemOnly]>;
+def int_aarch64_ldapr16 : Intrinsic<[llvm_i32_ty], [llvm_ptr_ty],
+                                    [IntrNoFree, IntrWillReturn,
+                                     IntrReadMem, IntrArgMemOnly]>;
+def int_aarch64_ldapr32 : Intrinsic<[llvm_i32_ty], [llvm_ptr_ty],
+                                    [IntrNoFree, IntrWillReturn,
+                                     IntrReadMem, IntrArgMemOnly]>;
+def int_aarch64_ldapr64 : Intrinsic<[llvm_i64_ty], [llvm_ptr_ty],
+                                    [IntrNoFree, IntrWillReturn,
+                                     IntrReadMem, IntrArgMemOnly]>;
+
 def int_aarch64_sdiv : DefaultAttrsIntrinsic<[llvm_anyint_ty], [LLVMMatchType<0>,
                                 LLVMMatchType<0>], [IntrNoMem]>;
 def int_aarch64_udiv : DefaultAttrsIntrinsic<[llvm_anyint_ty], [LLVMMatchType<0>,
diff --git a/llvm/lib/Target/AArch64/AArch64InstrAtomics.td b/llvm/lib/Target/AArch64/AArch64InstrAtomics.td
index 2187f21abb70f..be3a8cf819e90 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrAtomics.td
+++ b/llvm/lib/Target/AArch64/AArch64InstrAtomics.td
@@ -60,6 +60,13 @@ let Predicates = [HasRCPC] in {
   def : Pat<(acquiring_load<atomic_load_nonext_64> GPR64sp:$ptr), (LDAPRX GPR64sp:$ptr)>;
 }
 
+// Unconditional patterns for the __ldapr* MSVC builtins. These force LDAPR*
+// emission regardless of the RCPC subtarget feature.
+def : Pat<(i32 (int_aarch64_ldapr8  GPR64sp:$Rn)), (LDAPRB_cg GPR64sp:$Rn)>;
+def : Pat<(i32 (int_aarch64_ldapr16 GPR64sp:$Rn)), (LDAPRH_cg GPR64sp:$Rn)>;
+def : Pat<(i32 (int_aarch64_ldapr32 GPR64sp:$Rn)), (LDAPRW_cg GPR64sp:$Rn)>;
+def : Pat<(i64 (int_aarch64_ldapr64 GPR64sp:$Rn)), (LDAPRX_cg GPR64sp:$Rn)>;
+
 // 8-bit loads
 def : Pat<(seq_cst_load<atomic_load_azext_8>  GPR64sp:$ptr), (LDARB GPR64sp:$ptr)>;
 def : Pat<(acquiring_load<atomic_load_azext_8>  GPR64sp:$ptr), (LDARB GPR64sp:$ptr)>;
diff --git a/llvm/lib/Target/AArch64/AArch64InstrFormats.td b/llvm/lib/Target/AArch64/AArch64InstrFormats.td
index a1ad3196aae83..f7fc1c761703e 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrFormats.td
+++ b/llvm/lib/Target/AArch64/AArch64InstrFormats.td
@@ -2225,6 +2225,21 @@ class RCPCLoad<bits<2> sz, string asm, RegisterClass RC>
   let Inst{4-0} = Rt;
 }
 
+// Like RCPCLoad but without the HasRCPC predicate — for isCodeGenOnly use by
+// MS intrinsics that must emit LDAPR* regardless of the -march setting.
+class RCPCLoad_cg<bits<2> sz, string asm, RegisterClass RC>
+  : I<(outs RC:$Rt), (ins GPR64sp0:$Rn), asm, "\t$Rt, [$Rn]", "", []>,
+  Sched<[WriteLD]> {
+  bits<5> Rn;
+  bits<5> Rt;
+  let Inst{31-30} = sz;
+  let Inst{29-10} = 0b11100010111111110000;
+  let Unpredictable{20-16} = 0b11111;
+  let Inst{9-5} = Rn;
+  let Inst{4-0} = Rt;
+  let Predicates = [];
+}
+
 class AuthBase<bits<1> M, dag oops, dag iops, string asm, string operands,
                list<dag> pattern>
   : I<oops, iops, asm, operands, "", pattern>, Sched<[]> {
diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.td b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
index ca377de8deb78..9191e50df1c9a 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrInfo.td
+++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
@@ -1924,6 +1924,7 @@ def : SM4_pattern<SM4ENCKEY, int_aarch64_crypto_sm4ekey>;
 def : SM4_pattern<SM4E, int_aarch64_crypto_sm4e>;
 } // HasSM4
 
+// v8.3 RCPC instructions. Defs are unconditional...
 let Predicates = [HasRCPC] in {
   // v8.3 Release Consistent Processor Consistent support, optional in v8.2.
   def LDAPRB  : RCPCLoad<0b00, "ldaprb", GPR32>;
@@ -1932,6 +1933,14 @@ let Predicates = [HasRCPC] in {
   def LDAPRX  : RCPCLoad<0b11, "ldapr", GPR64>;
 }
 
+// Code-gen-only LDAPR variants for the __ldapr* MSVC builtins.
+let isCodeGenOnly = 1 in {
+  def LDAPRB_cg : RCPCLoad<0b00, "ldaprb", GPR32>;
+  def LDAPRH_cg : RCPCLoad<0b01, "ldaprh", GPR32>;
+  def LDAPRW_cg : RCPCLoad<0b10, "ldapr", GPR32>;
+  def LDAPRX_cg : RCPCLoad<0b11, "ldapr", GPR64>;
+}
+
 // v8.3a complex add and multiply-accumulate. No predicate here, that is done
 // inside the multiclass as the FP16 versions need different predicates.
 defm FCMLA : SIMDThreeSameVectorTiedComplexHSD<1, 0b110, complexrotateop,
diff --git a/llvm/test/CodeGen/AArch64/ms-intrinsics-ldapr.ll b/llvm/test/CodeGen/AArch64/ms-intrinsics-ldapr.ll
new file mode 100644
index 0000000000000..ebc2a0307f955
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/ms-intrinsics-ldapr.ll
@@ -0,0 +1,46 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=aarch64-none-linux-gnu -fast-isel=0 -global-isel=false \
+; RUN:     -verify-machineinstrs < %s | FileCheck %s
+; RUN: llc -mtriple=aarch64-none-linux-gnu -mattr=+rcpc -fast-isel=0 \
+; RUN:     -global-isel=false -verify-machineinstrs < %s | FileCheck %s
+
+; Tests for the __ldapr* MS builtins on AArch64. These lower to the
+; llvm.aarch64.ldaprN intrinsics and must select ldaprb/h/w/x regardless
+; of whether +rcpc is present in the target features (the _cg ISel variants
+; have no feature predicate).
+
+define i32 @test_ldapr8(ptr %p) {
+; CHECK-LABEL: test_ldapr8:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    ldaprb w0, [x0]
+; CHECK-NEXT:    ret
+  %r = call i32 @llvm.aarch64.ldapr8(ptr %p)
+  ret i32 %r
+}
+
+define i32 @test_ldapr16(ptr %p) {
+; CHECK-LABEL: test_ldapr16:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    ldaprh w0, [x0]
+; CHECK-NEXT:    ret
+  %r = call i32 @llvm.aarch64.ldapr16(ptr %p)
+  ret i32 %r
+}
+
+define i32 @test_ldapr32(ptr %p) {
+; CHECK-LABEL: test_ldapr32:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    ldapr w0, [x0]
+; CHECK-NEXT:    ret
+  %r = call i32 @llvm.aarch64.ldapr32(ptr %p)
+  ret i32 %r
+}
+
+define i64 @test_ldapr64(ptr %p) {
+; CHECK-LABEL: test_ldapr64:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    ldapr x0, [x0]
+; CHECK-NEXT:    ret
+  %r = call i64 @llvm.aarch64.ldapr64(ptr %p)
+  ret i64 %r
+}

>From 3156bdeeac0e2bf2100ab7618758c7e075114172 Mon Sep 17 00:00:00 2001
From: Adhemerval Zanella <zatrazz at gmail.com>
Date: Wed, 15 Apr 2026 16:53:14 -0300
Subject: [PATCH 05/17] [aarch64] Add support for the __cas{8|16|32|64} MS
 intrinsics

Adds support for the following MSVC intrinsics:
* `__cas8`  - maps to CASB.
* `__cas16` - maps to CASH.
* `__cas32` - maps to CAS.
* `__cas64` - maps to CAS.

The emit is done using new intrisincs to issue CAS* instruction
directly, regardless of LSE target feature.  This mimics MSVC compiler
and the idea of the builtin to mimic an inline asm.

These are documented at:
<https://learn.microsoft.com/en-us/cpp/intrinsics/arm64-intrinsics?view=msvc-180>
---
 clang/include/clang/Basic/BuiltinsAArch64.td  |  5 ++
 clang/lib/CodeGen/TargetBuiltins/ARM.cpp      | 41 ++++++++++++++
 clang/lib/Headers/arm64intr.h                 |  9 +++
 .../test/CodeGen/arm64-microsoft-intrinsics.c | 56 +++++++++++++++++++
 llvm/include/llvm/IR/IntrinsicsAArch64.td     | 15 +++++
 .../lib/Target/AArch64/AArch64InstrAtomics.td | 19 +++++++
 .../lib/Target/AArch64/AArch64InstrFormats.td | 18 ++++++
 llvm/lib/Target/AArch64/AArch64InstrInfo.td   |  6 ++
 .../test/CodeGen/AArch64/ms-intrinsics-cas.ll | 52 +++++++++++++++++
 9 files changed, 221 insertions(+)
 create mode 100644 llvm/test/CodeGen/AArch64/ms-intrinsics-cas.ll

diff --git a/clang/include/clang/Basic/BuiltinsAArch64.td b/clang/include/clang/Basic/BuiltinsAArch64.td
index 2163cbd129980..be529f3671bae 100644
--- a/clang/include/clang/Basic/BuiltinsAArch64.td
+++ b/clang/include/clang/Basic/BuiltinsAArch64.td
@@ -419,4 +419,9 @@ let Attributes = [NoThrow, RequireDeclaration], Languages = "ALL_MS_LANGUAGES",
 	def __ldapr16 : AArch64NoPrefixTargetLibBuiltin<"unsigned short (unsigned short const volatile *)">;
 	def __ldapr32 : AArch64NoPrefixTargetLibBuiltin<"unsigned int (unsigned int const volatile *)">;
 	def __ldapr64 : AArch64NoPrefixTargetLibBuiltin<"unsigned long long int (unsigned long long int const volatile *)">;
+	
+	def __cas8  : AArch64NoPrefixTargetLibBuiltin<"unsigned char (unsigned char volatile *, unsigned char, unsigned char)">;
+	def __cas16 : AArch64NoPrefixTargetLibBuiltin<"unsigned short (unsigned short volatile *, unsigned short, unsigned short)">;
+	def __cas32 : AArch64NoPrefixTargetLibBuiltin<"unsigned int (unsigned int volatile *, unsigned int, unsigned int)">;
+	def __cas64 : AArch64NoPrefixTargetLibBuiltin<"unsigned long long int (unsigned long long int volatile *, unsigned long long int, unsigned long long int)">;
 }
diff --git a/clang/lib/CodeGen/TargetBuiltins/ARM.cpp b/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
index 317336dfcb858..08e9377347ec2 100644
--- a/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
+++ b/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
@@ -5297,6 +5297,47 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID,
     return Result;
   }
 
+  if (BuiltinID == AArch64::BI__cas8 || BuiltinID == AArch64::BI__cas16 ||
+      BuiltinID == AArch64::BI__cas32 || BuiltinID == AArch64::BI__cas64) {
+    unsigned IntrID;
+    llvm::Type *IntrArgTy;
+    switch (BuiltinID) {
+    case AArch64::BI__cas8:
+      IntrID = Intrinsic::aarch64_cas8;
+      IntrArgTy = Builder.getInt32Ty();
+      break;
+    case AArch64::BI__cas16:
+      IntrID = Intrinsic::aarch64_cas16;
+      IntrArgTy = Builder.getInt32Ty();
+      break;
+    case AArch64::BI__cas32:
+      IntrID = Intrinsic::aarch64_cas32;
+      IntrArgTy = Builder.getInt32Ty();
+      break;
+    case AArch64::BI__cas64:
+      IntrID = Intrinsic::aarch64_cas64;
+      IntrArgTy = Builder.getInt64Ty();
+      break;
+    default:
+      llvm_unreachable("missing builtin ID in switch!");
+    }
+    Value *Ptr = EmitScalarExpr(E->getArg(0));
+    Value *Comp = EmitScalarExpr(E->getArg(1));
+    Value *Val = EmitScalarExpr(E->getArg(2));
+    // For 8/16-bit we need to zext to GRP size
+    if (Comp->getType() != IntrArgTy)
+      Comp = Builder.CreateZExt(Comp, IntrArgTy);
+    if (Val->getType() != IntrArgTy)
+      Val = Builder.CreateZExt(Val, IntrArgTy);
+    Value *Result =
+        Builder.CreateCall(CGM.getIntrinsic(IntrID), {Ptr, Comp, Val});
+    // CAS{B/H} return i32 (zero-extended); truncate to declared type.
+    llvm::Type *RetTy = ConvertType(E->getType());
+    if (Result->getType() != RetTy)
+      Result = Builder.CreateTrunc(Result, RetTy);
+    return Result;
+  }
+
   if (BuiltinID == NEON::BI__builtin_neon_vcvth_bf16_f32)
     return Builder.CreateFPTrunc(
         Builder.CreateBitCast(EmitScalarExpr(E->getArg(0)),
diff --git a/clang/lib/Headers/arm64intr.h b/clang/lib/Headers/arm64intr.h
index a0c62c2fc7775..1d8fe8c728f00 100644
--- a/clang/lib/Headers/arm64intr.h
+++ b/clang/lib/Headers/arm64intr.h
@@ -50,6 +50,15 @@ void __stlr16(unsigned __int16 volatile *, unsigned __int16);
 void __stlr32(unsigned __int32 volatile *, unsigned __int32);
 void __stlr64(unsigned __int64 volatile *, unsigned __int64);
 
+unsigned __int8 __cas8(unsigned __int8 volatile *, unsigned __int8,
+                       unsigned __int8);
+unsigned __int16 __cas16(unsigned __int16 volatile *, unsigned __int16,
+                         unsigned __int16);
+unsigned __int32 __cas32(unsigned __int32 volatile *, unsigned __int32,
+                         unsigned __int32);
+unsigned __int64 __cas64(unsigned __int64 volatile *, unsigned __int64,
+                         unsigned __int64);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/clang/test/CodeGen/arm64-microsoft-intrinsics.c b/clang/test/CodeGen/arm64-microsoft-intrinsics.c
index ce4a9abf24f50..7f9da0b628186 100644
--- a/clang/test/CodeGen/arm64-microsoft-intrinsics.c
+++ b/clang/test/CodeGen/arm64-microsoft-intrinsics.c
@@ -731,5 +731,61 @@ unsigned long long int check__ldapr64(unsigned long long int volatile *p) {
 // CHECK-MSCOMPAT:       ret i64 %[[RET]]
 // CHECK-LINUX: error: call to undeclared function '__ldapr64'
 
+unsigned char test__cas8(unsigned char volatile* t, unsigned char c, unsigned char v)
+{
+  return __cas8 (t, c, v);
+}
+// CHECK-MSCOMPAT-LABEL: define{{.*}}i8 @test__cas8(ptr{{.*}}%t, i8{{.*}}%c, i8{{.*}}%v){{.*}}{
+// CHECK-MSCOMPAT:       %[[TMPT:[0-9]+]] = load ptr, ptr %t.addr, align 8
+// CHECK-MSCOMPAT:       %[[TMPC:[0-9]+]] = load i8, ptr %c.addr, align 1
+// CHECK-MSCOMPAT:       %[[TMPV:[0-9]+]] = load i8, ptr %v.addr, align 1
+// CHECK-MSCOMPAT:       %[[ZEXTC:[0-9]+]] = zext i8 %[[TMPC]] to i32
+// CHECK-MSCOMPAT:       %[[ZEXTV:[0-9]+]]  = zext i8 %[[TMPV]] to i32 
+// CHECK-MSCOMPAT:       %[[RET:[0-9]+]] = call i32 @llvm.aarch64.cas8(ptr %[[TMPT]], i32 %[[ZEXTC]], i32 %[[ZEXTV]])
+// CHECK-MSCOMPAT:       %[[RETT:[0-9]+]]  = trunc i32 %[[RET]] to i8
+// CHECK-MSCOMPAT:       ret i8 %[[RETT]]
+// CHECK-LINUX: error: call to undeclared function '__cas8'
+
+unsigned short test__cas16(unsigned short volatile* t, unsigned short c, unsigned short v)
+{
+  return __cas16 (t, c, v);
+}
+// CHECK-MSCOMPAT-LABEL: define{{.*}}i16 @test__cas16(ptr{{.*}}%t, i16{{.*}}%c, i16{{.*}}%v){{.*}}{
+// CHECK-MSCOMPAT:       %[[TMPT:[0-9]+]] = load ptr, ptr %t.addr, align 8
+// CHECK-MSCOMPAT:       %[[TMPC:[0-9]+]] = load i16, ptr %c.addr, align 2
+// CHECK-MSCOMPAT:       %[[TMPV:[0-9]+]] = load i16, ptr %v.addr, align 2
+// CHECK-MSCOMPAT:       %[[ZEXTC:[0-9]+]] = zext i16 %[[TMPC]] to i32
+// CHECK-MSCOMPAT:       %[[ZEXTV:[0-9]+]]  = zext i16 %[[TMPV]] to i32 
+// CHECK-MSCOMPAT:       %[[RET:[0-9]+]] = call i32 @llvm.aarch64.cas16(ptr %[[TMPT]], i32 %[[ZEXTC]], i32 %[[ZEXTV]])
+// CHECK-MSCOMPAT:       %[[RETT:[0-9]+]]  = trunc i32 %[[RET]] to i16
+// CHECK-MSCOMPAT:       ret i16 %[[RETT]]
+// CHECK-LINUX: error: call to undeclared function '__cas16'
+
+unsigned int test__cas32(unsigned int volatile* t, unsigned int c, unsigned int v)
+{
+  return __cas32 (t, c, v);
+}
+// CHECK-MSCOMPAT-LABEL: define{{.*}}i32 @test__cas32(ptr{{.*}}%t, i32{{.*}}%c, i32{{.*}}%v){{.*}}{
+// CHECK-MSCOMPAT:       %[[TMPT:[0-9]+]] = load ptr, ptr %t.addr, align 8
+// CHECK-MSCOMPAT:       %[[TMPC:[0-9]+]] = load i32, ptr %c.addr, align 4
+// CHECK-MSCOMPAT:       %[[TMPV:[0-9]+]] = load i32, ptr %v.addr, align 4
+// CHECK-MSCOMPAT:       %[[RET:[0-9]+]] = call i32 @llvm.aarch64.cas32(ptr %[[TMPT]], i32 %[[TMPC]], i32 %[[TMPV]])
+// CHECK-MSCOMPAT:       ret i32 %[[RET]]
+// CHECK-LINUX: error: call to undeclared function '__cas32'
+
+unsigned long long int test__cas64(unsigned long long int volatile* t,
+                                   unsigned long long int c,
+                                   unsigned long long int v)
+{
+  return __cas64 (t, c, v);
+}
+// CHECK-MSCOMPAT-LABEL: define{{.*}}i64 @test__cas64(ptr{{.*}}%t, i64{{.*}}%c, i64{{.*}}%v){{.*}}{
+// CHECK-MSCOMPAT:       %[[TMPT:[0-9]+]] = load ptr, ptr %t.addr, align 8
+// CHECK-MSCOMPAT:       %[[TMPC:[0-9]+]] = load i64, ptr %c.addr, align 8
+// CHECK-MSCOMPAT:       %[[TMPV:[0-9]+]] = load i64, ptr %v.addr, align 8
+// CHECK-MSCOMPAT:       %[[RET:[0-9]+]] = call i64 @llvm.aarch64.cas64(ptr %[[TMPT]], i64 %[[TMPC]], i64 %[[TMPV]])
+// CHECK-MSCOMPAT:       ret i64 %[[RET]]
+// CHECK-LINUX: error: call to undeclared function '__cas64'
+
 // CHECK-MSCOMPAT: ![[MD2]] = !{!"x18"}
 // CHECK-MSCOMPAT: ![[MD3]] = !{!"sp"}
diff --git a/llvm/include/llvm/IR/IntrinsicsAArch64.td b/llvm/include/llvm/IR/IntrinsicsAArch64.td
index 149c034b58b3f..0a741fcb6e6f6 100644
--- a/llvm/include/llvm/IR/IntrinsicsAArch64.td
+++ b/llvm/include/llvm/IR/IntrinsicsAArch64.td
@@ -49,6 +49,21 @@ def int_aarch64_ldapr64 : Intrinsic<[llvm_i64_ty], [llvm_ptr_ty],
                                     [IntrNoFree, IntrWillReturn,
                                      IntrReadMem, IntrArgMemOnly]>;
 
+// CAS intrinsics — emit CAS* (no acquire/release) directly, regardless of the
+// LSE target feature. CAS{H,b} comparand/value use i32; CAS{X}uses i64.
+def int_aarch64_cas8  : Intrinsic<[llvm_i32_ty],
+                                  [llvm_ptr_ty, llvm_i32_ty, llvm_i32_ty],
+                                  [IntrNoFree, IntrWillReturn]>;
+def int_aarch64_cas16 : Intrinsic<[llvm_i32_ty],
+                                  [llvm_ptr_ty, llvm_i32_ty, llvm_i32_ty],
+                                  [IntrNoFree, IntrWillReturn]>;
+def int_aarch64_cas32 : Intrinsic<[llvm_i32_ty],
+                                  [llvm_ptr_ty, llvm_i32_ty, llvm_i32_ty],
+                                  [IntrNoFree, IntrWillReturn]>;
+def int_aarch64_cas64 : Intrinsic<[llvm_i64_ty],
+                                  [llvm_ptr_ty, llvm_i64_ty, llvm_i64_ty],
+                                  [IntrNoFree, IntrWillReturn]>;
+
 def int_aarch64_sdiv : DefaultAttrsIntrinsic<[llvm_anyint_ty], [LLVMMatchType<0>,
                                 LLVMMatchType<0>], [IntrNoMem]>;
 def int_aarch64_udiv : DefaultAttrsIntrinsic<[llvm_anyint_ty], [LLVMMatchType<0>,
diff --git a/llvm/lib/Target/AArch64/AArch64InstrAtomics.td b/llvm/lib/Target/AArch64/AArch64InstrAtomics.td
index be3a8cf819e90..1386ae74bded8 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrAtomics.td
+++ b/llvm/lib/Target/AArch64/AArch64InstrAtomics.td
@@ -163,6 +163,25 @@ def : Pat<(f64 (bitconvert (i64 (relaxed_load<atomic_load_nonext_64>
                (am_unscaled64 GPR64sp:$Rn, simm9:$offset))))),
           (LDURDi GPR64sp:$Rn, simm9:$offset)>;
 
+// Unconditional patterns for the __cas* MSVC builtins to emit CAS* regardless
+// of the LSE subtarget feature.  cas{b,h} use only the low bits of the 
+// comparison register, so the zero extension masking emitted by CodeGen 
+// (zext i8/i16 -> i32 -> 'and w,w,#mask') is redundant.
+def : Pat<(i32 (int_aarch64_cas8  GPR64sp:$Rn,
+               (and GPR32:$Rs, (i32 255)),   (and GPR32:$Rt, (i32 255)))),
+          (CASB_cg GPR32:$Rs, GPR32:$Rt, GPR64sp:$Rn)>;
+def : Pat<(i32 (int_aarch64_cas8  GPR64sp:$Rn, GPR32:$Rs, GPR32:$Rt)),
+          (CASB_cg GPR32:$Rs, GPR32:$Rt, GPR64sp:$Rn)>;
+def : Pat<(i32 (int_aarch64_cas16 GPR64sp:$Rn,
+               (and GPR32:$Rs, (i32 65535)), (and GPR32:$Rt, (i32 65535)))),
+          (CASH_cg GPR32:$Rs, GPR32:$Rt, GPR64sp:$Rn)>;
+def : Pat<(i32 (int_aarch64_cas16 GPR64sp:$Rn, GPR32:$Rs, GPR32:$Rt)),
+          (CASH_cg GPR32:$Rs, GPR32:$Rt, GPR64sp:$Rn)>;
+def : Pat<(i32 (int_aarch64_cas32 GPR64sp:$Rn, GPR32:$Rs, GPR32:$Rt)),
+          (CASW_cg GPR32:$Rs, GPR32:$Rt, GPR64sp:$Rn)>;
+def : Pat<(i64 (int_aarch64_cas64 GPR64sp:$Rn, GPR64:$Rs, GPR64:$Rt)),
+          (CASX_cg GPR64:$Rs, GPR64:$Rt, GPR64sp:$Rn)>;
+
 //===----------------------------------
 // Atomic stores
 //===----------------------------------
diff --git a/llvm/lib/Target/AArch64/AArch64InstrFormats.td b/llvm/lib/Target/AArch64/AArch64InstrFormats.td
index f7fc1c761703e..a782bff1dd6d5 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrFormats.td
+++ b/llvm/lib/Target/AArch64/AArch64InstrFormats.td
@@ -12427,6 +12427,17 @@ class BaseCAS<string order, string size, RegisterClass RC>
   let NP = 1;
 }
 
+// Like BaseCAS but without the HasLSE predicate — for isCodeGenOnly use by
+// MS intrinsics that must emit CAS* regardless of the -march setting.
+class BaseCAS_cg<string order, string size, RegisterClass RC>
+      : BaseCASEncoding<(outs RC:$out),(ins RC:$Rs, RC:$Rt, GPR64sp:$Rn),
+                        "cas" # order # size, "\t$Rs, $Rt, [$Rn]",
+                        "$out = $Rs",[]>,
+        Sched<[WriteAtomic]> {
+  let NP = 1;
+  let Predicates = [];
+}
+
 multiclass CompareAndSwap<bits<1> Acq, bits<1> Rel, string order> {
   let Sz = 0b00, Acq = Acq, Rel = Rel in def B : BaseCAS<order, "b", GPR32>;
   let Sz = 0b01, Acq = Acq, Rel = Rel in def H : BaseCAS<order, "h", GPR32>;
@@ -12434,6 +12445,13 @@ multiclass CompareAndSwap<bits<1> Acq, bits<1> Rel, string order> {
   let Sz = 0b11, Acq = Acq, Rel = Rel in def X : BaseCAS<order, "", GPR64>;
 }
 
+multiclass CompareAndSwap_cg<bits<1> Acq, bits<1> Rel, string order> {
+  let Sz = 0b00, Acq = Acq, Rel = Rel in def B_cg : BaseCAS_cg<order, "b", GPR32>;
+  let Sz = 0b01, Acq = Acq, Rel = Rel in def H_cg : BaseCAS_cg<order, "h", GPR32>;
+  let Sz = 0b10, Acq = Acq, Rel = Rel in def W_cg : BaseCAS_cg<order, "", GPR32>;
+  let Sz = 0b11, Acq = Acq, Rel = Rel in def X_cg : BaseCAS_cg<order, "", GPR64>;
+}
+
 class BaseCASP<string order, string size, RegisterOperand RC>
       : BaseCASEncoding<(outs RC:$out),(ins RC:$Rs, RC:$Rt, GPR64sp:$Rn),
                         "casp" # order # size, "\t$Rs, $Rt, [$Rn]",
diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.td b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
index 9191e50df1c9a..62fce7f508194 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrInfo.td
+++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
@@ -3034,6 +3034,12 @@ defm CASA  : CompareAndSwap<1, 0, "a">;
 defm CASL  : CompareAndSwap<0, 1, "l">;
 defm CASAL : CompareAndSwap<1, 1, "al">;
 
+// isCodeGenOnly=1: excluded from asm parser/disassembler, no HasLSE predicate.
+// Used by the __cas* MS intrinsics to force CAS* emission regardless of -march.
+let isCodeGenOnly = 1 in {
+  defm CAS   : CompareAndSwap_cg<0, 0, "">;
+}
+
 // v8.1 atomic CASP
 defm CASP   : CompareAndSwapPair<0, 0, "">;
 defm CASPA  : CompareAndSwapPair<1, 0, "a">;
diff --git a/llvm/test/CodeGen/AArch64/ms-intrinsics-cas.ll b/llvm/test/CodeGen/AArch64/ms-intrinsics-cas.ll
new file mode 100644
index 0000000000000..d213b4d973551
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/ms-intrinsics-cas.ll
@@ -0,0 +1,52 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=aarch64-none-linux-gnu -fast-isel=0 -global-isel=false \
+; RUN:     -verify-machineinstrs < %s | FileCheck %s
+; RUN: llc -mtriple=aarch64-none-linux-gnu -mattr=+lse -fast-isel=0 \
+; RUN:     -global-isel=false -verify-machineinstrs < %s | FileCheck %s
+
+; Tests for the __cas* MS builtins on AArch64.  These lower to
+; llvm.aarch64.cas* intrinsics and must select the corresponding CAS instruction
+; regardless of whether +lse is present in the target features (the _cg ISel 
+; variants have no feature predicate).
+
+; __cas* — no acquire, no release
+
+define i32 @test_cas8(ptr %p, i32 %rs, i32 %rt) {
+; CHECK-LABEL: test_cas8:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    casb w1, w2, [x0]
+; CHECK-NEXT:    mov w0, w1
+; CHECK-NEXT:    ret
+  %r = call i32 @llvm.aarch64.cas8(ptr %p, i32 %rs, i32 %rt)
+  ret i32 %r
+}
+
+define i32 @test_cas16(ptr %p, i32 %rs, i32 %rt) {
+; CHECK-LABEL: test_cas16:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    cash w1, w2, [x0]
+; CHECK-NEXT:    mov w0, w1
+; CHECK-NEXT:    ret
+  %r = call i32 @llvm.aarch64.cas16(ptr %p, i32 %rs, i32 %rt)
+  ret i32 %r
+}
+
+define i32 @test_cas32(ptr %p, i32 %rs, i32 %rt) {
+; CHECK-LABEL: test_cas32:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    cas w1, w2, [x0]
+; CHECK-NEXT:    mov w0, w1
+; CHECK-NEXT:    ret
+  %r = call i32 @llvm.aarch64.cas32(ptr %p, i32 %rs, i32 %rt)
+  ret i32 %r
+}
+
+define i64 @test_cas64(ptr %p, i64 %rs, i64 %rt) {
+; CHECK-LABEL: test_cas64:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    cas x1, x2, [x0]
+; CHECK-NEXT:    mov x0, x1
+; CHECK-NEXT:    ret
+  %r = call i64 @llvm.aarch64.cas64(ptr %p, i64 %rs, i64 %rt)
+  ret i64 %r
+}

>From f075032e2a5af03c183164e45937ecb10489b377 Mon Sep 17 00:00:00 2001
From: Adhemerval Zanella <zatrazz at gmail.com>
Date: Wed, 15 Apr 2026 17:39:55 -0300
Subject: [PATCH 06/17] [aarch64] Add support for the __casa{8|16|32|64} MS
 intrinsics

Adds support for the following MSVC intrinsics:
* `__casa8`  - maps to CASAB.
* `__casa16` - maps to CASAH.
* `__casa32` - maps to CASA.
* `__casa64` - maps to CASA.

The emit is done using new intrisincs to issue CASA* instruction
directly, regardless of LSE target feature.  This mimics MSVC compiler
and the idea of the builtin to mimic an inline asm.

These are documented at:
<https://learn.microsoft.com/en-us/cpp/intrinsics/arm64-intrinsics?view=msvc-180>
---
 clang/include/clang/Basic/BuiltinsAArch64.td  |  4 ++
 clang/lib/CodeGen/TargetBuiltins/ARM.cpp      | 20 ++++++-
 clang/lib/Headers/arm64intr.h                 |  9 +++
 .../test/CodeGen/arm64-microsoft-intrinsics.c | 56 +++++++++++++++++++
 llvm/include/llvm/IR/IntrinsicsAArch64.td     | 15 +++++
 .../lib/Target/AArch64/AArch64InstrAtomics.td | 16 ++++++
 llvm/lib/Target/AArch64/AArch64InstrInfo.td   |  1 +
 .../test/CodeGen/AArch64/ms-intrinsics-cas.ll | 40 +++++++++++++
 8 files changed, 160 insertions(+), 1 deletion(-)

diff --git a/clang/include/clang/Basic/BuiltinsAArch64.td b/clang/include/clang/Basic/BuiltinsAArch64.td
index be529f3671bae..d7500185f26c9 100644
--- a/clang/include/clang/Basic/BuiltinsAArch64.td
+++ b/clang/include/clang/Basic/BuiltinsAArch64.td
@@ -424,4 +424,8 @@ let Attributes = [NoThrow, RequireDeclaration], Languages = "ALL_MS_LANGUAGES",
 	def __cas16 : AArch64NoPrefixTargetLibBuiltin<"unsigned short (unsigned short volatile *, unsigned short, unsigned short)">;
 	def __cas32 : AArch64NoPrefixTargetLibBuiltin<"unsigned int (unsigned int volatile *, unsigned int, unsigned int)">;
 	def __cas64 : AArch64NoPrefixTargetLibBuiltin<"unsigned long long int (unsigned long long int volatile *, unsigned long long int, unsigned long long int)">;
+	def __casa8  : AArch64NoPrefixTargetLibBuiltin<"unsigned char (unsigned char volatile *, unsigned char, unsigned char)">;
+	def __casa16 : AArch64NoPrefixTargetLibBuiltin<"unsigned short (unsigned short volatile *, unsigned short, unsigned short)">;
+	def __casa32 : AArch64NoPrefixTargetLibBuiltin<"unsigned int (unsigned int volatile *, unsigned int, unsigned int)">;
+	def __casa64 : AArch64NoPrefixTargetLibBuiltin<"unsigned long long int (unsigned long long int volatile *, unsigned long long int, unsigned long long int)">;
 }
diff --git a/clang/lib/CodeGen/TargetBuiltins/ARM.cpp b/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
index 08e9377347ec2..42e9bea137ce4 100644
--- a/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
+++ b/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
@@ -5298,7 +5298,9 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID,
   }
 
   if (BuiltinID == AArch64::BI__cas8 || BuiltinID == AArch64::BI__cas16 ||
-      BuiltinID == AArch64::BI__cas32 || BuiltinID == AArch64::BI__cas64) {
+      BuiltinID == AArch64::BI__cas32 || BuiltinID == AArch64::BI__cas64 ||
+      BuiltinID == AArch64::BI__casa8 || BuiltinID == AArch64::BI__casa16 ||
+      BuiltinID == AArch64::BI__casa32 || BuiltinID == AArch64::BI__casa64) {
     unsigned IntrID;
     llvm::Type *IntrArgTy;
     switch (BuiltinID) {
@@ -5318,6 +5320,22 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID,
       IntrID = Intrinsic::aarch64_cas64;
       IntrArgTy = Builder.getInt64Ty();
       break;
+    case AArch64::BI__casa8:
+      IntrID = Intrinsic::aarch64_casa8;
+      IntrArgTy = Builder.getInt32Ty();
+      break;
+    case AArch64::BI__casa16:
+      IntrID = Intrinsic::aarch64_casa16;
+      IntrArgTy = Builder.getInt32Ty();
+      break;
+    case AArch64::BI__casa32:
+      IntrID = Intrinsic::aarch64_casa32;
+      IntrArgTy = Builder.getInt32Ty();
+      break;
+    case AArch64::BI__casa64:
+      IntrID = Intrinsic::aarch64_casa64;
+      IntrArgTy = Builder.getInt64Ty();
+      break;
     default:
       llvm_unreachable("missing builtin ID in switch!");
     }
diff --git a/clang/lib/Headers/arm64intr.h b/clang/lib/Headers/arm64intr.h
index 1d8fe8c728f00..1a133adf3d218 100644
--- a/clang/lib/Headers/arm64intr.h
+++ b/clang/lib/Headers/arm64intr.h
@@ -59,6 +59,15 @@ unsigned __int32 __cas32(unsigned __int32 volatile *, unsigned __int32,
 unsigned __int64 __cas64(unsigned __int64 volatile *, unsigned __int64,
                          unsigned __int64);
 
+unsigned __int8 __casa8(unsigned __int8 volatile *, unsigned __int8,
+                        unsigned __int8);
+unsigned __int16 __casa16(unsigned __int16 volatile *, unsigned __int16,
+                          unsigned __int16);
+unsigned __int32 __casa32(unsigned __int32 volatile *, unsigned __int32,
+                          unsigned __int32);
+unsigned __int64 __casa64(unsigned __int64 volatile *, unsigned __int64,
+                          unsigned __int64);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/clang/test/CodeGen/arm64-microsoft-intrinsics.c b/clang/test/CodeGen/arm64-microsoft-intrinsics.c
index 7f9da0b628186..4050aac1efa14 100644
--- a/clang/test/CodeGen/arm64-microsoft-intrinsics.c
+++ b/clang/test/CodeGen/arm64-microsoft-intrinsics.c
@@ -787,5 +787,61 @@ unsigned long long int test__cas64(unsigned long long int volatile* t,
 // CHECK-MSCOMPAT:       ret i64 %[[RET]]
 // CHECK-LINUX: error: call to undeclared function '__cas64'
 
+unsigned char test__casa8(unsigned char volatile* t, unsigned char c, unsigned char v)
+{
+  return __casa8 (t, c, v);
+}
+// CHECK-MSCOMPAT-LABEL: define{{.*}}i8 @test__casa8(ptr{{.*}}%t, i8{{.*}}%c, i8{{.*}}%v){{.*}}{
+// CHECK-MSCOMPAT:       %[[TMPT:[0-9]+]] = load ptr, ptr %t.addr, align 8
+// CHECK-MSCOMPAT:       %[[TMPC:[0-9]+]] = load i8, ptr %c.addr, align 1
+// CHECK-MSCOMPAT:       %[[TMPV:[0-9]+]] = load i8, ptr %v.addr, align 1
+// CHECK-MSCOMPAT:       %[[ZEXTC:[0-9]+]] = zext i8 %[[TMPC]] to i32
+// CHECK-MSCOMPAT:       %[[ZEXTV:[0-9]+]]  = zext i8 %[[TMPV]] to i32
+// CHECK-MSCOMPAT:       %[[RET:[0-9]+]] = call i32 @llvm.aarch64.casa8(ptr %[[TMPT]], i32 %[[ZEXTC]], i32 %[[ZEXTV]])
+// CHECK-MSCOMPAT:       %[[RETT:[0-9]+]]  = trunc i32 %[[RET]] to i8
+// CHECK-MSCOMPAT:       ret i8 %[[RETT]]
+// CHECK-LINUX: error: call to undeclared function '__casa8'
+
+unsigned short test__casa16(unsigned short volatile* t, unsigned short c, unsigned short v)
+{
+  return __casa16 (t, c, v);
+}
+// CHECK-MSCOMPAT-LABEL: define{{.*}}i16 @test__casa16(ptr{{.*}}%t, i16{{.*}}%c, i16{{.*}}%v){{.*}}{
+// CHECK-MSCOMPAT:       %[[TMPT:[0-9]+]] = load ptr, ptr %t.addr, align 8
+// CHECK-MSCOMPAT:       %[[TMPC:[0-9]+]] = load i16, ptr %c.addr, align 2
+// CHECK-MSCOMPAT:       %[[TMPV:[0-9]+]] = load i16, ptr %v.addr, align 2
+// CHECK-MSCOMPAT:       %[[ZEXTC:[0-9]+]] = zext i16 %[[TMPC]] to i32
+// CHECK-MSCOMPAT:       %[[ZEXTV:[0-9]+]]  = zext i16 %[[TMPV]] to i32
+// CHECK-MSCOMPAT:       %[[RET:[0-9]+]] = call i32 @llvm.aarch64.casa16(ptr %[[TMPT]], i32 %[[ZEXTC]], i32 %[[ZEXTV]])
+// CHECK-MSCOMPAT:       %[[RETT:[0-9]+]]  = trunc i32 %[[RET]] to i16
+// CHECK-MSCOMPAT:       ret i16 %[[RETT]]
+// CHECK-LINUX: error: call to undeclared function '__casa16'
+
+unsigned int test__casa32(unsigned int volatile* t, unsigned int c, unsigned int v)
+{
+  return __casa32 (t, c, v);
+}
+// CHECK-MSCOMPAT-LABEL: define{{.*}}i32 @test__casa32(ptr{{.*}}%t, i32{{.*}}%c, i32{{.*}}%v){{.*}}{
+// CHECK-MSCOMPAT:       %[[TMPT:[0-9]+]] = load ptr, ptr %t.addr, align 8
+// CHECK-MSCOMPAT:       %[[TMPC:[0-9]+]] = load i32, ptr %c.addr, align 4
+// CHECK-MSCOMPAT:       %[[TMPV:[0-9]+]] = load i32, ptr %v.addr, align 4
+// CHECK-MSCOMPAT:       %[[RET:[0-9]+]] = call i32 @llvm.aarch64.casa32(ptr %[[TMPT]], i32 %[[TMPC]], i32 %[[TMPV]])
+// CHECK-MSCOMPAT:       ret i32 %[[RET]]
+// CHECK-LINUX: error: call to undeclared function '__casa32'
+
+unsigned long long int test__casa64(unsigned long long int volatile* t,
+                                    unsigned long long int c,
+                                    unsigned long long int v)
+{
+  return __casa64 (t, c, v);
+}
+// CHECK-MSCOMPAT-LABEL: define{{.*}}i64 @test__casa64(ptr{{.*}}%t, i64{{.*}}%c, i64{{.*}}%v){{.*}}{
+// CHECK-MSCOMPAT:       %[[TMPT:[0-9]+]] = load ptr, ptr %t.addr, align 8
+// CHECK-MSCOMPAT:       %[[TMPC:[0-9]+]] = load i64, ptr %c.addr, align 8
+// CHECK-MSCOMPAT:       %[[TMPV:[0-9]+]] = load i64, ptr %v.addr, align 8
+// CHECK-MSCOMPAT:       %[[RET:[0-9]+]] = call i64 @llvm.aarch64.casa64(ptr %[[TMPT]], i64 %[[TMPC]], i64 %[[TMPV]])
+// CHECK-MSCOMPAT:       ret i64 %[[RET]]
+// CHECK-LINUX: error: call to undeclared function '__casa64'
+
 // CHECK-MSCOMPAT: ![[MD2]] = !{!"x18"}
 // CHECK-MSCOMPAT: ![[MD3]] = !{!"sp"}
diff --git a/llvm/include/llvm/IR/IntrinsicsAArch64.td b/llvm/include/llvm/IR/IntrinsicsAArch64.td
index 0a741fcb6e6f6..0eab6c65e43af 100644
--- a/llvm/include/llvm/IR/IntrinsicsAArch64.td
+++ b/llvm/include/llvm/IR/IntrinsicsAArch64.td
@@ -64,6 +64,21 @@ def int_aarch64_cas64 : Intrinsic<[llvm_i64_ty],
                                   [llvm_ptr_ty, llvm_i64_ty, llvm_i64_ty],
                                   [IntrNoFree, IntrWillReturn]>;
 
+// CASA intrinsics — emit CASA* (acquire, no release) directly, regardless of
+// the LSE target feature. Same type conventions as cas*.
+def int_aarch64_casa8  : Intrinsic<[llvm_i32_ty],
+                                   [llvm_ptr_ty, llvm_i32_ty, llvm_i32_ty],
+                                   [IntrNoFree, IntrWillReturn]>;
+def int_aarch64_casa16 : Intrinsic<[llvm_i32_ty],
+                                   [llvm_ptr_ty, llvm_i32_ty, llvm_i32_ty],
+                                   [IntrNoFree, IntrWillReturn]>;
+def int_aarch64_casa32 : Intrinsic<[llvm_i32_ty],
+                                   [llvm_ptr_ty, llvm_i32_ty, llvm_i32_ty],
+                                   [IntrNoFree, IntrWillReturn]>;
+def int_aarch64_casa64 : Intrinsic<[llvm_i64_ty],
+                                   [llvm_ptr_ty, llvm_i64_ty, llvm_i64_ty],
+                                   [IntrNoFree, IntrWillReturn]>;
+
 def int_aarch64_sdiv : DefaultAttrsIntrinsic<[llvm_anyint_ty], [LLVMMatchType<0>,
                                 LLVMMatchType<0>], [IntrNoMem]>;
 def int_aarch64_udiv : DefaultAttrsIntrinsic<[llvm_anyint_ty], [LLVMMatchType<0>,
diff --git a/llvm/lib/Target/AArch64/AArch64InstrAtomics.td b/llvm/lib/Target/AArch64/AArch64InstrAtomics.td
index 1386ae74bded8..be20a0060a939 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrAtomics.td
+++ b/llvm/lib/Target/AArch64/AArch64InstrAtomics.td
@@ -182,6 +182,22 @@ def : Pat<(i32 (int_aarch64_cas32 GPR64sp:$Rn, GPR32:$Rs, GPR32:$Rt)),
 def : Pat<(i64 (int_aarch64_cas64 GPR64sp:$Rn, GPR64:$Rs, GPR64:$Rt)),
           (CASX_cg GPR64:$Rs, GPR64:$Rt, GPR64sp:$Rn)>;
 
+// Unconditional patterns for the __casa* MSVC builtins (acquire ordering).
+def : Pat<(i32 (int_aarch64_casa8  GPR64sp:$Rn,
+               (and GPR32:$Rs, (i32 255)),   (and GPR32:$Rt, (i32 255)))),
+          (CASAB_cg GPR32:$Rs, GPR32:$Rt, GPR64sp:$Rn)>;
+def : Pat<(i32 (int_aarch64_casa8  GPR64sp:$Rn, GPR32:$Rs, GPR32:$Rt)),
+          (CASAB_cg GPR32:$Rs, GPR32:$Rt, GPR64sp:$Rn)>;
+def : Pat<(i32 (int_aarch64_casa16 GPR64sp:$Rn,
+               (and GPR32:$Rs, (i32 65535)), (and GPR32:$Rt, (i32 65535)))),
+          (CASAH_cg GPR32:$Rs, GPR32:$Rt, GPR64sp:$Rn)>;
+def : Pat<(i32 (int_aarch64_casa16 GPR64sp:$Rn, GPR32:$Rs, GPR32:$Rt)),
+          (CASAH_cg GPR32:$Rs, GPR32:$Rt, GPR64sp:$Rn)>;
+def : Pat<(i32 (int_aarch64_casa32 GPR64sp:$Rn, GPR32:$Rs, GPR32:$Rt)),
+          (CASAW_cg GPR32:$Rs, GPR32:$Rt, GPR64sp:$Rn)>;
+def : Pat<(i64 (int_aarch64_casa64 GPR64sp:$Rn, GPR64:$Rs, GPR64:$Rt)),
+          (CASAX_cg GPR64:$Rs, GPR64:$Rt, GPR64sp:$Rn)>;
+
 //===----------------------------------
 // Atomic stores
 //===----------------------------------
diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.td b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
index 62fce7f508194..f1fa483de809e 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrInfo.td
+++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
@@ -3038,6 +3038,7 @@ defm CASAL : CompareAndSwap<1, 1, "al">;
 // Used by the __cas* MS intrinsics to force CAS* emission regardless of -march.
 let isCodeGenOnly = 1 in {
   defm CAS   : CompareAndSwap_cg<0, 0, "">;
+  defm CASA  : CompareAndSwap_cg<1, 0, "a">;
 }
 
 // v8.1 atomic CASP
diff --git a/llvm/test/CodeGen/AArch64/ms-intrinsics-cas.ll b/llvm/test/CodeGen/AArch64/ms-intrinsics-cas.ll
index d213b4d973551..f5ea4977c102f 100644
--- a/llvm/test/CodeGen/AArch64/ms-intrinsics-cas.ll
+++ b/llvm/test/CodeGen/AArch64/ms-intrinsics-cas.ll
@@ -50,3 +50,43 @@ define i64 @test_cas64(ptr %p, i64 %rs, i64 %rt) {
   %r = call i64 @llvm.aarch64.cas64(ptr %p, i64 %rs, i64 %rt)
   ret i64 %r
 }
+
+define i32 @test_casa8(ptr %p, i32 %rs, i32 %rt) {
+; CHECK-LABEL: test_casa8:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    casab w1, w2, [x0]
+; CHECK-NEXT:    mov w0, w1
+; CHECK-NEXT:    ret
+  %r = call i32 @llvm.aarch64.casa8(ptr %p, i32 %rs, i32 %rt)
+  ret i32 %r
+}
+
+define i32 @test_casa16(ptr %p, i32 %rs, i32 %rt) {
+; CHECK-LABEL: test_casa16:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    casah w1, w2, [x0]
+; CHECK-NEXT:    mov w0, w1
+; CHECK-NEXT:    ret
+  %r = call i32 @llvm.aarch64.casa16(ptr %p, i32 %rs, i32 %rt)
+  ret i32 %r
+}
+
+define i32 @test_casa32(ptr %p, i32 %rs, i32 %rt) {
+; CHECK-LABEL: test_casa32:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    casa w1, w2, [x0]
+; CHECK-NEXT:    mov w0, w1
+; CHECK-NEXT:    ret
+  %r = call i32 @llvm.aarch64.casa32(ptr %p, i32 %rs, i32 %rt)
+  ret i32 %r
+}
+
+define i64 @test_casa64(ptr %p, i64 %rs, i64 %rt) {
+; CHECK-LABEL: test_casa64:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    casa x1, x2, [x0]
+; CHECK-NEXT:    mov x0, x1
+; CHECK-NEXT:    ret
+  %r = call i64 @llvm.aarch64.casa64(ptr %p, i64 %rs, i64 %rt)
+  ret i64 %r
+}

>From 3e94fad3e94349965c34689bfd74fe17a1fb55a8 Mon Sep 17 00:00:00 2001
From: Adhemerval Zanella <zatrazz at gmail.com>
Date: Thu, 16 Apr 2026 09:15:28 -0300
Subject: [PATCH 07/17] [aarch64] Add support for the __casl{8|16|32|64} MS
 intrinsics

Adds support for the following MSVC intrinsics:
* `__casl8`  - maps to CASLB.
* `__casl16` - maps to CASLH.
* `__casl32` - maps to CASL.
* `__casl64` - maps to CASL.

The emit is done using new intrisincs to issue CASL* instruction
directly, regardless of LSE target feature.  This mimics MSVC compiler
and the idea of the builtin to mimic an inline asm.

These are documented at:
<https://learn.microsoft.com/en-us/cpp/intrinsics/arm64-intrinsics?view=msvc-180>
---
 clang/include/clang/Basic/BuiltinsAArch64.td  |  4 ++
 clang/lib/CodeGen/TargetBuiltins/ARM.cpp      | 20 ++++++-
 clang/lib/Headers/arm64intr.h                 |  9 +++
 .../test/CodeGen/arm64-microsoft-intrinsics.c | 56 +++++++++++++++++++
 llvm/include/llvm/IR/IntrinsicsAArch64.td     | 15 +++++
 .../lib/Target/AArch64/AArch64InstrAtomics.td | 16 ++++++
 llvm/lib/Target/AArch64/AArch64InstrInfo.td   |  1 +
 .../test/CodeGen/AArch64/ms-intrinsics-cas.ll | 40 +++++++++++++
 8 files changed, 160 insertions(+), 1 deletion(-)

diff --git a/clang/include/clang/Basic/BuiltinsAArch64.td b/clang/include/clang/Basic/BuiltinsAArch64.td
index d7500185f26c9..d8b9a1803efcf 100644
--- a/clang/include/clang/Basic/BuiltinsAArch64.td
+++ b/clang/include/clang/Basic/BuiltinsAArch64.td
@@ -428,4 +428,8 @@ let Attributes = [NoThrow, RequireDeclaration], Languages = "ALL_MS_LANGUAGES",
 	def __casa16 : AArch64NoPrefixTargetLibBuiltin<"unsigned short (unsigned short volatile *, unsigned short, unsigned short)">;
 	def __casa32 : AArch64NoPrefixTargetLibBuiltin<"unsigned int (unsigned int volatile *, unsigned int, unsigned int)">;
 	def __casa64 : AArch64NoPrefixTargetLibBuiltin<"unsigned long long int (unsigned long long int volatile *, unsigned long long int, unsigned long long int)">;
+	def __casl8  : AArch64NoPrefixTargetLibBuiltin<"unsigned char (unsigned char volatile *, unsigned char, unsigned char)">;
+	def __casl16 : AArch64NoPrefixTargetLibBuiltin<"unsigned short (unsigned short volatile *, unsigned short, unsigned short)">;
+	def __casl32 : AArch64NoPrefixTargetLibBuiltin<"unsigned int (unsigned int volatile *, unsigned int, unsigned int)">;
+	def __casl64 : AArch64NoPrefixTargetLibBuiltin<"unsigned long long int (unsigned long long int volatile *, unsigned long long int, unsigned long long int)">;
 }
diff --git a/clang/lib/CodeGen/TargetBuiltins/ARM.cpp b/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
index 42e9bea137ce4..9eea803ededc1 100644
--- a/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
+++ b/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
@@ -5300,7 +5300,9 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID,
   if (BuiltinID == AArch64::BI__cas8 || BuiltinID == AArch64::BI__cas16 ||
       BuiltinID == AArch64::BI__cas32 || BuiltinID == AArch64::BI__cas64 ||
       BuiltinID == AArch64::BI__casa8 || BuiltinID == AArch64::BI__casa16 ||
-      BuiltinID == AArch64::BI__casa32 || BuiltinID == AArch64::BI__casa64) {
+      BuiltinID == AArch64::BI__casa32 || BuiltinID == AArch64::BI__casa64 ||
+      BuiltinID == AArch64::BI__casl8 || BuiltinID == AArch64::BI__casl16 ||
+      BuiltinID == AArch64::BI__casl32 || BuiltinID == AArch64::BI__casl64) {
     unsigned IntrID;
     llvm::Type *IntrArgTy;
     switch (BuiltinID) {
@@ -5336,6 +5338,22 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID,
       IntrID = Intrinsic::aarch64_casa64;
       IntrArgTy = Builder.getInt64Ty();
       break;
+    case AArch64::BI__casl8:
+      IntrID = Intrinsic::aarch64_casl8;
+      IntrArgTy = Builder.getInt32Ty();
+      break;
+    case AArch64::BI__casl16:
+      IntrID = Intrinsic::aarch64_casl16;
+      IntrArgTy = Builder.getInt32Ty();
+      break;
+    case AArch64::BI__casl32:
+      IntrID = Intrinsic::aarch64_casl32;
+      IntrArgTy = Builder.getInt32Ty();
+      break;
+    case AArch64::BI__casl64:
+      IntrID = Intrinsic::aarch64_casl64;
+      IntrArgTy = Builder.getInt64Ty();
+      break;
     default:
       llvm_unreachable("missing builtin ID in switch!");
     }
diff --git a/clang/lib/Headers/arm64intr.h b/clang/lib/Headers/arm64intr.h
index 1a133adf3d218..78da35fc3c26c 100644
--- a/clang/lib/Headers/arm64intr.h
+++ b/clang/lib/Headers/arm64intr.h
@@ -68,6 +68,15 @@ unsigned __int32 __casa32(unsigned __int32 volatile *, unsigned __int32,
 unsigned __int64 __casa64(unsigned __int64 volatile *, unsigned __int64,
                           unsigned __int64);
 
+unsigned __int8 __casl8(unsigned __int8 volatile *, unsigned __int8,
+                        unsigned __int8);
+unsigned __int16 __casl16(unsigned __int16 volatile *, unsigned __int16,
+                          unsigned __int16);
+unsigned __int32 __casl32(unsigned __int32 volatile *, unsigned __int32,
+                          unsigned __int32);
+unsigned __int64 __casl64(unsigned __int64 volatile *, unsigned __int64,
+                          unsigned __int64);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/clang/test/CodeGen/arm64-microsoft-intrinsics.c b/clang/test/CodeGen/arm64-microsoft-intrinsics.c
index 4050aac1efa14..a69ef417a318d 100644
--- a/clang/test/CodeGen/arm64-microsoft-intrinsics.c
+++ b/clang/test/CodeGen/arm64-microsoft-intrinsics.c
@@ -843,5 +843,61 @@ unsigned long long int test__casa64(unsigned long long int volatile* t,
 // CHECK-MSCOMPAT:       ret i64 %[[RET]]
 // CHECK-LINUX: error: call to undeclared function '__casa64'
 
+unsigned char test__casl8(unsigned char volatile* t, unsigned char c, unsigned char v)
+{
+  return __casl8 (t, c, v);
+}
+// CHECK-MSCOMPAT-LABEL: define{{.*}}i8 @test__casl8(ptr{{.*}}%t, i8{{.*}}%c, i8{{.*}}%v){{.*}}{
+// CHECK-MSCOMPAT:       %[[TMPT:[0-9]+]] = load ptr, ptr %t.addr, align 8
+// CHECK-MSCOMPAT:       %[[TMPC:[0-9]+]] = load i8, ptr %c.addr, align 1
+// CHECK-MSCOMPAT:       %[[TMPV:[0-9]+]] = load i8, ptr %v.addr, align 1
+// CHECK-MSCOMPAT:       %[[ZEXTC:[0-9]+]] = zext i8 %[[TMPC]] to i32
+// CHECK-MSCOMPAT:       %[[ZEXTV:[0-9]+]]  = zext i8 %[[TMPV]] to i32
+// CHECK-MSCOMPAT:       %[[RET:[0-9]+]] = call i32 @llvm.aarch64.casl8(ptr %[[TMPT]], i32 %[[ZEXTC]], i32 %[[ZEXTV]])
+// CHECK-MSCOMPAT:       %[[RETT:[0-9]+]]  = trunc i32 %[[RET]] to i8
+// CHECK-MSCOMPAT:       ret i8 %[[RETT]]
+// CHECK-LINUX: error: call to undeclared function '__casl8'
+
+unsigned short test__casl16(unsigned short volatile* t, unsigned short c, unsigned short v)
+{
+  return __casl16 (t, c, v);
+}
+// CHECK-MSCOMPAT-LABEL: define{{.*}}i16 @test__casl16(ptr{{.*}}%t, i16{{.*}}%c, i16{{.*}}%v){{.*}}{
+// CHECK-MSCOMPAT:       %[[TMPT:[0-9]+]] = load ptr, ptr %t.addr, align 8
+// CHECK-MSCOMPAT:       %[[TMPC:[0-9]+]] = load i16, ptr %c.addr, align 2
+// CHECK-MSCOMPAT:       %[[TMPV:[0-9]+]] = load i16, ptr %v.addr, align 2
+// CHECK-MSCOMPAT:       %[[ZEXTC:[0-9]+]] = zext i16 %[[TMPC]] to i32
+// CHECK-MSCOMPAT:       %[[ZEXTV:[0-9]+]]  = zext i16 %[[TMPV]] to i32
+// CHECK-MSCOMPAT:       %[[RET:[0-9]+]] = call i32 @llvm.aarch64.casl16(ptr %[[TMPT]], i32 %[[ZEXTC]], i32 %[[ZEXTV]])
+// CHECK-MSCOMPAT:       %[[RETT:[0-9]+]]  = trunc i32 %[[RET]] to i16
+// CHECK-MSCOMPAT:       ret i16 %[[RETT]]
+// CHECK-LINUX: error: call to undeclared function '__casl16'
+
+unsigned int test__casl32(unsigned int volatile* t, unsigned int c, unsigned int v)
+{
+  return __casl32 (t, c, v);
+}
+// CHECK-MSCOMPAT-LABEL: define{{.*}}i32 @test__casl32(ptr{{.*}}%t, i32{{.*}}%c, i32{{.*}}%v){{.*}}{
+// CHECK-MSCOMPAT:       %[[TMPT:[0-9]+]] = load ptr, ptr %t.addr, align 8
+// CHECK-MSCOMPAT:       %[[TMPC:[0-9]+]] = load i32, ptr %c.addr, align 4
+// CHECK-MSCOMPAT:       %[[TMPV:[0-9]+]] = load i32, ptr %v.addr, align 4
+// CHECK-MSCOMPAT:       %[[RET:[0-9]+]] = call i32 @llvm.aarch64.casl32(ptr %[[TMPT]], i32 %[[TMPC]], i32 %[[TMPV]])
+// CHECK-MSCOMPAT:       ret i32 %[[RET]]
+// CHECK-LINUX: error: call to undeclared function '__casl32'
+
+unsigned long long int test__casl64(unsigned long long int volatile* t,
+                                    unsigned long long int c,
+                                    unsigned long long int v)
+{
+  return __casl64 (t, c, v);
+}
+// CHECK-MSCOMPAT-LABEL: define{{.*}}i64 @test__casl64(ptr{{.*}}%t, i64{{.*}}%c, i64{{.*}}%v){{.*}}{
+// CHECK-MSCOMPAT:       %[[TMPT:[0-9]+]] = load ptr, ptr %t.addr, align 8
+// CHECK-MSCOMPAT:       %[[TMPC:[0-9]+]] = load i64, ptr %c.addr, align 8
+// CHECK-MSCOMPAT:       %[[TMPV:[0-9]+]] = load i64, ptr %v.addr, align 8
+// CHECK-MSCOMPAT:       %[[RET:[0-9]+]] = call i64 @llvm.aarch64.casl64(ptr %[[TMPT]], i64 %[[TMPC]], i64 %[[TMPV]])
+// CHECK-MSCOMPAT:       ret i64 %[[RET]]
+// CHECK-LINUX: error: call to undeclared function '__casl64'
+
 // CHECK-MSCOMPAT: ![[MD2]] = !{!"x18"}
 // CHECK-MSCOMPAT: ![[MD3]] = !{!"sp"}
diff --git a/llvm/include/llvm/IR/IntrinsicsAArch64.td b/llvm/include/llvm/IR/IntrinsicsAArch64.td
index 0eab6c65e43af..a7839295cfb34 100644
--- a/llvm/include/llvm/IR/IntrinsicsAArch64.td
+++ b/llvm/include/llvm/IR/IntrinsicsAArch64.td
@@ -79,6 +79,21 @@ def int_aarch64_casa64 : Intrinsic<[llvm_i64_ty],
                                    [llvm_ptr_ty, llvm_i64_ty, llvm_i64_ty],
                                    [IntrNoFree, IntrWillReturn]>;
 
+// CASAL intrinsics — emit CASAL* (release) directly, regardless of the LSE 
+//target feature. Same type conventions as cas*.
+def int_aarch64_casl8  : Intrinsic<[llvm_i32_ty],
+                                   [llvm_ptr_ty, llvm_i32_ty, llvm_i32_ty],
+                                   [IntrNoFree, IntrWillReturn]>;
+def int_aarch64_casl16 : Intrinsic<[llvm_i32_ty],
+                                   [llvm_ptr_ty, llvm_i32_ty, llvm_i32_ty],
+                                   [IntrNoFree, IntrWillReturn]>;
+def int_aarch64_casl32 : Intrinsic<[llvm_i32_ty],
+                                   [llvm_ptr_ty, llvm_i32_ty, llvm_i32_ty],
+                                   [IntrNoFree, IntrWillReturn]>;
+def int_aarch64_casl64 : Intrinsic<[llvm_i64_ty],
+                                   [llvm_ptr_ty, llvm_i64_ty, llvm_i64_ty],
+                                   [IntrNoFree, IntrWillReturn]>;
+
 def int_aarch64_sdiv : DefaultAttrsIntrinsic<[llvm_anyint_ty], [LLVMMatchType<0>,
                                 LLVMMatchType<0>], [IntrNoMem]>;
 def int_aarch64_udiv : DefaultAttrsIntrinsic<[llvm_anyint_ty], [LLVMMatchType<0>,
diff --git a/llvm/lib/Target/AArch64/AArch64InstrAtomics.td b/llvm/lib/Target/AArch64/AArch64InstrAtomics.td
index be20a0060a939..04b2f3bf92bd5 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrAtomics.td
+++ b/llvm/lib/Target/AArch64/AArch64InstrAtomics.td
@@ -198,6 +198,22 @@ def : Pat<(i32 (int_aarch64_casa32 GPR64sp:$Rn, GPR32:$Rs, GPR32:$Rt)),
 def : Pat<(i64 (int_aarch64_casa64 GPR64sp:$Rn, GPR64:$Rs, GPR64:$Rt)),
           (CASAX_cg GPR64:$Rs, GPR64:$Rt, GPR64sp:$Rn)>;
 
+// Unconditional patterns for the __casl* MSVC builtins (release ordering).
+def : Pat<(i32 (int_aarch64_casl8  GPR64sp:$Rn,
+               (and GPR32:$Rs, (i32 255)),   (and GPR32:$Rt, (i32 255)))),
+          (CASLB_cg GPR32:$Rs, GPR32:$Rt, GPR64sp:$Rn)>;
+def : Pat<(i32 (int_aarch64_casl8  GPR64sp:$Rn, GPR32:$Rs, GPR32:$Rt)),
+          (CASLB_cg GPR32:$Rs, GPR32:$Rt, GPR64sp:$Rn)>;
+def : Pat<(i32 (int_aarch64_casl16 GPR64sp:$Rn,
+               (and GPR32:$Rs, (i32 65535)), (and GPR32:$Rt, (i32 65535)))),
+          (CASLH_cg GPR32:$Rs, GPR32:$Rt, GPR64sp:$Rn)>;
+def : Pat<(i32 (int_aarch64_casl16 GPR64sp:$Rn, GPR32:$Rs, GPR32:$Rt)),
+          (CASLH_cg GPR32:$Rs, GPR32:$Rt, GPR64sp:$Rn)>;
+def : Pat<(i32 (int_aarch64_casl32 GPR64sp:$Rn, GPR32:$Rs, GPR32:$Rt)),
+          (CASLW_cg GPR32:$Rs, GPR32:$Rt, GPR64sp:$Rn)>;
+def : Pat<(i64 (int_aarch64_casl64 GPR64sp:$Rn, GPR64:$Rs, GPR64:$Rt)),
+          (CASLX_cg GPR64:$Rs, GPR64:$Rt, GPR64sp:$Rn)>;
+
 //===----------------------------------
 // Atomic stores
 //===----------------------------------
diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.td b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
index f1fa483de809e..b331d63de1794 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrInfo.td
+++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
@@ -3039,6 +3039,7 @@ defm CASAL : CompareAndSwap<1, 1, "al">;
 let isCodeGenOnly = 1 in {
   defm CAS   : CompareAndSwap_cg<0, 0, "">;
   defm CASA  : CompareAndSwap_cg<1, 0, "a">;
+  defm CASL  : CompareAndSwap_cg<0, 1, "l">;
 }
 
 // v8.1 atomic CASP
diff --git a/llvm/test/CodeGen/AArch64/ms-intrinsics-cas.ll b/llvm/test/CodeGen/AArch64/ms-intrinsics-cas.ll
index f5ea4977c102f..aef26339de87e 100644
--- a/llvm/test/CodeGen/AArch64/ms-intrinsics-cas.ll
+++ b/llvm/test/CodeGen/AArch64/ms-intrinsics-cas.ll
@@ -90,3 +90,43 @@ define i64 @test_casa64(ptr %p, i64 %rs, i64 %rt) {
   %r = call i64 @llvm.aarch64.casa64(ptr %p, i64 %rs, i64 %rt)
   ret i64 %r
 }
+
+define i32 @test_casl8(ptr %p, i32 %rs, i32 %rt) {
+; CHECK-LABEL: test_casl8:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    caslb w1, w2, [x0]
+; CHECK-NEXT:    mov w0, w1
+; CHECK-NEXT:    ret
+  %r = call i32 @llvm.aarch64.casl8(ptr %p, i32 %rs, i32 %rt)
+  ret i32 %r
+}
+
+define i32 @test_casl16(ptr %p, i32 %rs, i32 %rt) {
+; CHECK-LABEL: test_casl16:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    caslh w1, w2, [x0]
+; CHECK-NEXT:    mov w0, w1
+; CHECK-NEXT:    ret
+  %r = call i32 @llvm.aarch64.casl16(ptr %p, i32 %rs, i32 %rt)
+  ret i32 %r
+}
+
+define i32 @test_casl32(ptr %p, i32 %rs, i32 %rt) {
+; CHECK-LABEL: test_casl32:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    casl w1, w2, [x0]
+; CHECK-NEXT:    mov w0, w1
+; CHECK-NEXT:    ret
+  %r = call i32 @llvm.aarch64.casl32(ptr %p, i32 %rs, i32 %rt)
+  ret i32 %r
+}
+
+define i64 @test_casl64(ptr %p, i64 %rs, i64 %rt) {
+; CHECK-LABEL: test_casl64:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    casl x1, x2, [x0]
+; CHECK-NEXT:    mov x0, x1
+; CHECK-NEXT:    ret
+  %r = call i64 @llvm.aarch64.casl64(ptr %p, i64 %rs, i64 %rt)
+  ret i64 %r
+}

>From e27d5e34c7073c96cc478c0c3dd4b55781912978 Mon Sep 17 00:00:00 2001
From: Adhemerval Zanella <zatrazz at gmail.com>
Date: Thu, 16 Apr 2026 10:47:20 -0300
Subject: [PATCH 08/17] [aarch64] Add support for the __casal{8|16|32|64} MS
 intrinsics

Adds support for the following MSVC intrinsics:
* `__casal8`  - maps to CASALB.
* `__casal16` - maps to CASALH.
* `__casal32` - maps to CASAL.
* `__casal64` - maps to CASAL.

The emit is done using new intrisincs to issue CASAL* instruction
directly, regardless of LSE target feature.  This mimics MSVC compiler
and the idea of the builtin to mimic an inline asm.

These are documented at:
<https://learn.microsoft.com/en-us/cpp/intrinsics/arm64-intrinsics?view=msvc-180>
---
 clang/include/clang/Basic/BuiltinsAArch64.td  |  4 ++
 clang/lib/CodeGen/TargetBuiltins/ARM.cpp      | 20 ++++++-
 clang/lib/Headers/arm64intr.h                 |  9 +++
 .../test/CodeGen/arm64-microsoft-intrinsics.c | 56 +++++++++++++++++++
 llvm/include/llvm/IR/IntrinsicsAArch64.td     | 17 +++++-
 .../lib/Target/AArch64/AArch64InstrAtomics.td | 16 ++++++
 llvm/lib/Target/AArch64/AArch64InstrInfo.td   |  1 +
 .../test/CodeGen/AArch64/ms-intrinsics-cas.ll | 40 +++++++++++++
 8 files changed, 161 insertions(+), 2 deletions(-)

diff --git a/clang/include/clang/Basic/BuiltinsAArch64.td b/clang/include/clang/Basic/BuiltinsAArch64.td
index d8b9a1803efcf..d718ad7ef8110 100644
--- a/clang/include/clang/Basic/BuiltinsAArch64.td
+++ b/clang/include/clang/Basic/BuiltinsAArch64.td
@@ -432,4 +432,8 @@ let Attributes = [NoThrow, RequireDeclaration], Languages = "ALL_MS_LANGUAGES",
 	def __casl16 : AArch64NoPrefixTargetLibBuiltin<"unsigned short (unsigned short volatile *, unsigned short, unsigned short)">;
 	def __casl32 : AArch64NoPrefixTargetLibBuiltin<"unsigned int (unsigned int volatile *, unsigned int, unsigned int)">;
 	def __casl64 : AArch64NoPrefixTargetLibBuiltin<"unsigned long long int (unsigned long long int volatile *, unsigned long long int, unsigned long long int)">;
+	def __casal8  : AArch64NoPrefixTargetLibBuiltin<"unsigned char (unsigned char volatile *, unsigned char, unsigned char)">;
+	def __casal16 : AArch64NoPrefixTargetLibBuiltin<"unsigned short (unsigned short volatile *, unsigned short, unsigned short)">;
+	def __casal32 : AArch64NoPrefixTargetLibBuiltin<"unsigned int (unsigned int volatile *, unsigned int, unsigned int)">;
+	def __casal64 : AArch64NoPrefixTargetLibBuiltin<"unsigned long long int (unsigned long long int volatile *, unsigned long long int, unsigned long long int)">;
 }
diff --git a/clang/lib/CodeGen/TargetBuiltins/ARM.cpp b/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
index 9eea803ededc1..491fb469e7136 100644
--- a/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
+++ b/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
@@ -5302,7 +5302,9 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID,
       BuiltinID == AArch64::BI__casa8 || BuiltinID == AArch64::BI__casa16 ||
       BuiltinID == AArch64::BI__casa32 || BuiltinID == AArch64::BI__casa64 ||
       BuiltinID == AArch64::BI__casl8 || BuiltinID == AArch64::BI__casl16 ||
-      BuiltinID == AArch64::BI__casl32 || BuiltinID == AArch64::BI__casl64) {
+      BuiltinID == AArch64::BI__casl32 || BuiltinID == AArch64::BI__casl64 ||
+      BuiltinID == AArch64::BI__casal8 || BuiltinID == AArch64::BI__casal16 ||
+      BuiltinID == AArch64::BI__casal32 || BuiltinID == AArch64::BI__casal64) {
     unsigned IntrID;
     llvm::Type *IntrArgTy;
     switch (BuiltinID) {
@@ -5354,6 +5356,22 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID,
       IntrID = Intrinsic::aarch64_casl64;
       IntrArgTy = Builder.getInt64Ty();
       break;
+    case AArch64::BI__casal8:
+      IntrID = Intrinsic::aarch64_casal8;
+      IntrArgTy = Builder.getInt32Ty();
+      break;
+    case AArch64::BI__casal16:
+      IntrID = Intrinsic::aarch64_casal16;
+      IntrArgTy = Builder.getInt32Ty();
+      break;
+    case AArch64::BI__casal32:
+      IntrID = Intrinsic::aarch64_casal32;
+      IntrArgTy = Builder.getInt32Ty();
+      break;
+    case AArch64::BI__casal64:
+      IntrID = Intrinsic::aarch64_casal64;
+      IntrArgTy = Builder.getInt64Ty();
+      break;
     default:
       llvm_unreachable("missing builtin ID in switch!");
     }
diff --git a/clang/lib/Headers/arm64intr.h b/clang/lib/Headers/arm64intr.h
index 78da35fc3c26c..4f988f9de0a44 100644
--- a/clang/lib/Headers/arm64intr.h
+++ b/clang/lib/Headers/arm64intr.h
@@ -77,6 +77,15 @@ unsigned __int32 __casl32(unsigned __int32 volatile *, unsigned __int32,
 unsigned __int64 __casl64(unsigned __int64 volatile *, unsigned __int64,
                           unsigned __int64);
 
+unsigned __int8 __casal8(unsigned __int8 volatile *, unsigned __int8,
+                         unsigned __int8);
+unsigned __int16 __casal16(unsigned __int16 volatile *, unsigned __int16,
+                           unsigned __int16);
+unsigned __int32 __casal32(unsigned __int32 volatile *, unsigned __int32,
+                           unsigned __int32);
+unsigned __int64 __casal64(unsigned __int64 volatile *, unsigned __int64,
+                           unsigned __int64);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/clang/test/CodeGen/arm64-microsoft-intrinsics.c b/clang/test/CodeGen/arm64-microsoft-intrinsics.c
index a69ef417a318d..f752ec446d8bb 100644
--- a/clang/test/CodeGen/arm64-microsoft-intrinsics.c
+++ b/clang/test/CodeGen/arm64-microsoft-intrinsics.c
@@ -899,5 +899,61 @@ unsigned long long int test__casl64(unsigned long long int volatile* t,
 // CHECK-MSCOMPAT:       ret i64 %[[RET]]
 // CHECK-LINUX: error: call to undeclared function '__casl64'
 
+unsigned char test__casal8(unsigned char volatile* t, unsigned char c, unsigned char v)
+{
+  return __casal8 (t, c, v);
+}
+// CHECK-MSCOMPAT-LABEL: define{{.*}}i8 @test__casal8(ptr{{.*}}%t, i8{{.*}}%c, i8{{.*}}%v){{.*}}{
+// CHECK-MSCOMPAT:       %[[TMPT:[0-9]+]] = load ptr, ptr %t.addr, align 8
+// CHECK-MSCOMPAT:       %[[TMPC:[0-9]+]] = load i8, ptr %c.addr, align 1
+// CHECK-MSCOMPAT:       %[[TMPV:[0-9]+]] = load i8, ptr %v.addr, align 1
+// CHECK-MSCOMPAT:       %[[ZEXTC:[0-9]+]] = zext i8 %[[TMPC]] to i32
+// CHECK-MSCOMPAT:       %[[ZEXTV:[0-9]+]]  = zext i8 %[[TMPV]] to i32
+// CHECK-MSCOMPAT:       %[[RET:[0-9]+]] = call i32 @llvm.aarch64.casal8(ptr %[[TMPT]], i32 %[[ZEXTC]], i32 %[[ZEXTV]])
+// CHECK-MSCOMPAT:       %[[RETT:[0-9]+]]  = trunc i32 %[[RET]] to i8
+// CHECK-MSCOMPAT:       ret i8 %[[RETT]]
+// CHECK-LINUX: error: call to undeclared function '__casal8'
+
+unsigned short test__casal16(unsigned short volatile* t, unsigned short c, unsigned short v)
+{
+  return __casal16 (t, c, v);
+}
+// CHECK-MSCOMPAT-LABEL: define{{.*}}i16 @test__casal16(ptr{{.*}}%t, i16{{.*}}%c, i16{{.*}}%v){{.*}}{
+// CHECK-MSCOMPAT:       %[[TMPT:[0-9]+]] = load ptr, ptr %t.addr, align 8
+// CHECK-MSCOMPAT:       %[[TMPC:[0-9]+]] = load i16, ptr %c.addr, align 2
+// CHECK-MSCOMPAT:       %[[TMPV:[0-9]+]] = load i16, ptr %v.addr, align 2
+// CHECK-MSCOMPAT:       %[[ZEXTC:[0-9]+]] = zext i16 %[[TMPC]] to i32
+// CHECK-MSCOMPAT:       %[[ZEXTV:[0-9]+]]  = zext i16 %[[TMPV]] to i32
+// CHECK-MSCOMPAT:       %[[RET:[0-9]+]] = call i32 @llvm.aarch64.casal16(ptr %[[TMPT]], i32 %[[ZEXTC]], i32 %[[ZEXTV]])
+// CHECK-MSCOMPAT:       %[[RETT:[0-9]+]]  = trunc i32 %[[RET]] to i16
+// CHECK-MSCOMPAT:       ret i16 %[[RETT]]
+// CHECK-LINUX: error: call to undeclared function '__casal16'
+
+unsigned int test__casal32(unsigned int volatile* t, unsigned int c, unsigned int v)
+{
+  return __casal32 (t, c, v);
+}
+// CHECK-MSCOMPAT-LABEL: define{{.*}}i32 @test__casal32(ptr{{.*}}%t, i32{{.*}}%c, i32{{.*}}%v){{.*}}{
+// CHECK-MSCOMPAT:       %[[TMPT:[0-9]+]] = load ptr, ptr %t.addr, align 8
+// CHECK-MSCOMPAT:       %[[TMPC:[0-9]+]] = load i32, ptr %c.addr, align 4
+// CHECK-MSCOMPAT:       %[[TMPV:[0-9]+]] = load i32, ptr %v.addr, align 4
+// CHECK-MSCOMPAT:       %[[RET:[0-9]+]] = call i32 @llvm.aarch64.casal32(ptr %[[TMPT]], i32 %[[TMPC]], i32 %[[TMPV]])
+// CHECK-MSCOMPAT:       ret i32 %[[RET]]
+// CHECK-LINUX: error: call to undeclared function '__casal32'
+
+unsigned long long int test__casal64(unsigned long long int volatile* t,
+                                     unsigned long long int c,
+                                     unsigned long long int v)
+{
+  return __casal64 (t, c, v);
+}
+// CHECK-MSCOMPAT-LABEL: define{{.*}}i64 @test__casal64(ptr{{.*}}%t, i64{{.*}}%c, i64{{.*}}%v){{.*}}{
+// CHECK-MSCOMPAT:       %[[TMPT:[0-9]+]] = load ptr, ptr %t.addr, align 8
+// CHECK-MSCOMPAT:       %[[TMPC:[0-9]+]] = load i64, ptr %c.addr, align 8
+// CHECK-MSCOMPAT:       %[[TMPV:[0-9]+]] = load i64, ptr %v.addr, align 8
+// CHECK-MSCOMPAT:       %[[RET:[0-9]+]] = call i64 @llvm.aarch64.casal64(ptr %[[TMPT]], i64 %[[TMPC]], i64 %[[TMPV]])
+// CHECK-MSCOMPAT:       ret i64 %[[RET]]
+// CHECK-LINUX: error: call to undeclared function '__casal64'
+
 // CHECK-MSCOMPAT: ![[MD2]] = !{!"x18"}
 // CHECK-MSCOMPAT: ![[MD3]] = !{!"sp"}
diff --git a/llvm/include/llvm/IR/IntrinsicsAArch64.td b/llvm/include/llvm/IR/IntrinsicsAArch64.td
index a7839295cfb34..3f200b10d284a 100644
--- a/llvm/include/llvm/IR/IntrinsicsAArch64.td
+++ b/llvm/include/llvm/IR/IntrinsicsAArch64.td
@@ -80,7 +80,7 @@ def int_aarch64_casa64 : Intrinsic<[llvm_i64_ty],
                                    [IntrNoFree, IntrWillReturn]>;
 
 // CASAL intrinsics — emit CASAL* (release) directly, regardless of the LSE 
-//target feature. Same type conventions as cas*.
+// target feature. Same type conventions as cas*.
 def int_aarch64_casl8  : Intrinsic<[llvm_i32_ty],
                                    [llvm_ptr_ty, llvm_i32_ty, llvm_i32_ty],
                                    [IntrNoFree, IntrWillReturn]>;
@@ -94,6 +94,21 @@ def int_aarch64_casl64 : Intrinsic<[llvm_i64_ty],
                                    [llvm_ptr_ty, llvm_i64_ty, llvm_i64_ty],
                                    [IntrNoFree, IntrWillReturn]>;
 
+// CASAL intrinsics — emit CASAL* (acquire/release) directly, regardless of the LSE 
+// target feature. Same type conventions as cas*.
+def int_aarch64_casal8  : Intrinsic<[llvm_i32_ty],
+                                    [llvm_ptr_ty, llvm_i32_ty, llvm_i32_ty],
+                                    [IntrNoFree, IntrWillReturn]>;
+def int_aarch64_casal16 : Intrinsic<[llvm_i32_ty],
+                                    [llvm_ptr_ty, llvm_i32_ty, llvm_i32_ty],
+                                    [IntrNoFree, IntrWillReturn]>;
+def int_aarch64_casal32 : Intrinsic<[llvm_i32_ty],
+                                    [llvm_ptr_ty, llvm_i32_ty, llvm_i32_ty],
+                                    [IntrNoFree, IntrWillReturn]>;
+def int_aarch64_casal64 : Intrinsic<[llvm_i64_ty],
+                                    [llvm_ptr_ty, llvm_i64_ty, llvm_i64_ty],
+                                    [IntrNoFree, IntrWillReturn]>;
+
 def int_aarch64_sdiv : DefaultAttrsIntrinsic<[llvm_anyint_ty], [LLVMMatchType<0>,
                                 LLVMMatchType<0>], [IntrNoMem]>;
 def int_aarch64_udiv : DefaultAttrsIntrinsic<[llvm_anyint_ty], [LLVMMatchType<0>,
diff --git a/llvm/lib/Target/AArch64/AArch64InstrAtomics.td b/llvm/lib/Target/AArch64/AArch64InstrAtomics.td
index 04b2f3bf92bd5..11c8ca47c034b 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrAtomics.td
+++ b/llvm/lib/Target/AArch64/AArch64InstrAtomics.td
@@ -214,6 +214,22 @@ def : Pat<(i32 (int_aarch64_casl32 GPR64sp:$Rn, GPR32:$Rs, GPR32:$Rt)),
 def : Pat<(i64 (int_aarch64_casl64 GPR64sp:$Rn, GPR64:$Rs, GPR64:$Rt)),
           (CASLX_cg GPR64:$Rs, GPR64:$Rt, GPR64sp:$Rn)>;
 
+// Unconditional patterns for the __casal* MSVC builtins (acquire/release ordering).
+def : Pat<(i32 (int_aarch64_casal8  GPR64sp:$Rn,
+               (and GPR32:$Rs, (i32 255)),   (and GPR32:$Rt, (i32 255)))),
+          (CASALB_cg GPR32:$Rs, GPR32:$Rt, GPR64sp:$Rn)>;
+def : Pat<(i32 (int_aarch64_casal8  GPR64sp:$Rn, GPR32:$Rs, GPR32:$Rt)),
+          (CASALB_cg GPR32:$Rs, GPR32:$Rt, GPR64sp:$Rn)>;
+def : Pat<(i32 (int_aarch64_casal16 GPR64sp:$Rn,
+               (and GPR32:$Rs, (i32 65535)), (and GPR32:$Rt, (i32 65535)))),
+          (CASALH_cg GPR32:$Rs, GPR32:$Rt, GPR64sp:$Rn)>;
+def : Pat<(i32 (int_aarch64_casal16 GPR64sp:$Rn, GPR32:$Rs, GPR32:$Rt)),
+          (CASALH_cg GPR32:$Rs, GPR32:$Rt, GPR64sp:$Rn)>;
+def : Pat<(i32 (int_aarch64_casal32 GPR64sp:$Rn, GPR32:$Rs, GPR32:$Rt)),
+          (CASALW_cg GPR32:$Rs, GPR32:$Rt, GPR64sp:$Rn)>;
+def : Pat<(i64 (int_aarch64_casal64 GPR64sp:$Rn, GPR64:$Rs, GPR64:$Rt)),
+          (CASALX_cg GPR64:$Rs, GPR64:$Rt, GPR64sp:$Rn)>;
+
 //===----------------------------------
 // Atomic stores
 //===----------------------------------
diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.td b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
index b331d63de1794..48de7956f282a 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrInfo.td
+++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
@@ -3040,6 +3040,7 @@ let isCodeGenOnly = 1 in {
   defm CAS   : CompareAndSwap_cg<0, 0, "">;
   defm CASA  : CompareAndSwap_cg<1, 0, "a">;
   defm CASL  : CompareAndSwap_cg<0, 1, "l">;
+  defm CASAL : CompareAndSwap_cg<1, 1, "al">;
 }
 
 // v8.1 atomic CASP
diff --git a/llvm/test/CodeGen/AArch64/ms-intrinsics-cas.ll b/llvm/test/CodeGen/AArch64/ms-intrinsics-cas.ll
index aef26339de87e..7df4e9821f4ea 100644
--- a/llvm/test/CodeGen/AArch64/ms-intrinsics-cas.ll
+++ b/llvm/test/CodeGen/AArch64/ms-intrinsics-cas.ll
@@ -130,3 +130,43 @@ define i64 @test_casl64(ptr %p, i64 %rs, i64 %rt) {
   %r = call i64 @llvm.aarch64.casl64(ptr %p, i64 %rs, i64 %rt)
   ret i64 %r
 }
+
+define i32 @test_casal8(ptr %p, i32 %rs, i32 %rt) {
+; CHECK-LABEL: test_casal8:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    casalb w1, w2, [x0]
+; CHECK-NEXT:    mov w0, w1
+; CHECK-NEXT:    ret
+  %r = call i32 @llvm.aarch64.casal8(ptr %p, i32 %rs, i32 %rt)
+  ret i32 %r
+}
+
+define i32 @test_casal16(ptr %p, i32 %rs, i32 %rt) {
+; CHECK-LABEL: test_casal16:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    casalh w1, w2, [x0]
+; CHECK-NEXT:    mov w0, w1
+; CHECK-NEXT:    ret
+  %r = call i32 @llvm.aarch64.casal16(ptr %p, i32 %rs, i32 %rt)
+  ret i32 %r
+}
+
+define i32 @test_casal32(ptr %p, i32 %rs, i32 %rt) {
+; CHECK-LABEL: test_casal32:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    casal w1, w2, [x0]
+; CHECK-NEXT:    mov w0, w1
+; CHECK-NEXT:    ret
+  %r = call i32 @llvm.aarch64.casal32(ptr %p, i32 %rs, i32 %rt)
+  ret i32 %r
+}
+
+define i64 @test_casal64(ptr %p, i64 %rs, i64 %rt) {
+; CHECK-LABEL: test_casal64:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    casal x1, x2, [x0]
+; CHECK-NEXT:    mov x0, x1
+; CHECK-NEXT:    ret
+  %r = call i64 @llvm.aarch64.casal64(ptr %p, i64 %rs, i64 %rt)
+  ret i64 %r
+}

>From 2cd51c61e1cb0b58132137c3fc75317ef810210a Mon Sep 17 00:00:00 2001
From: Adhemerval Zanella <zatrazz at gmail.com>
Date: Fri, 17 Apr 2026 10:05:23 -0300
Subject: [PATCH 09/17] [aarch64] Add support for the __swp{8|16|32|64} MS
 intrinsics

Adds support for the following MSVC intrinsics:
* `__swp8`   - maps to SWPB.
* `__swp16`  - maps to SWPH.
* `__swp32`  - maps to SWP.
* `__swp64`  - maps to SWP.

The emit is done using new intrisincs to issue SWP* instruction
directly, regardless of LSE target feature.  This mimics MSVC compiler
and the idea of the builtin to mimic an inline asm.

These are documented at:
<https://learn.microsoft.com/en-us/cpp/intrinsics/arm64-intrinsics?view=msvc-180>
---
 clang/include/clang/Basic/BuiltinsAArch64.td  |  5 ++
 clang/lib/CodeGen/TargetBuiltins/ARM.cpp      | 36 ++++++++++++++
 clang/lib/Headers/arm64intr.h                 |  5 ++
 .../test/CodeGen/arm64-microsoft-intrinsics.c | 49 +++++++++++++++++++
 llvm/include/llvm/IR/IntrinsicsAArch64.td     | 15 ++++++
 .../lib/Target/AArch64/AArch64InstrAtomics.td | 16 ++++++
 .../lib/Target/AArch64/AArch64InstrFormats.td | 34 +++++++++++++
 llvm/lib/Target/AArch64/AArch64InstrInfo.td   |  6 +++
 .../test/CodeGen/AArch64/ms-intrinsics-swp.ll | 46 +++++++++++++++++
 9 files changed, 212 insertions(+)
 create mode 100644 llvm/test/CodeGen/AArch64/ms-intrinsics-swp.ll

diff --git a/clang/include/clang/Basic/BuiltinsAArch64.td b/clang/include/clang/Basic/BuiltinsAArch64.td
index d718ad7ef8110..0ded0aa4e5da1 100644
--- a/clang/include/clang/Basic/BuiltinsAArch64.td
+++ b/clang/include/clang/Basic/BuiltinsAArch64.td
@@ -436,4 +436,9 @@ let Attributes = [NoThrow, RequireDeclaration], Languages = "ALL_MS_LANGUAGES",
 	def __casal16 : AArch64NoPrefixTargetLibBuiltin<"unsigned short (unsigned short volatile *, unsigned short, unsigned short)">;
 	def __casal32 : AArch64NoPrefixTargetLibBuiltin<"unsigned int (unsigned int volatile *, unsigned int, unsigned int)">;
 	def __casal64 : AArch64NoPrefixTargetLibBuiltin<"unsigned long long int (unsigned long long int volatile *, unsigned long long int, unsigned long long int)">;
+
+	def __swp8 : AArch64NoPrefixTargetLibBuiltin<"unsigned char (unsigned char volatile *, unsigned char)">;
+	def __swp16 : AArch64NoPrefixTargetLibBuiltin<"unsigned short (unsigned short volatile *, unsigned short)">;
+	def __swp32 : AArch64NoPrefixTargetLibBuiltin<"unsigned int (unsigned int volatile *, unsigned int)">;
+	def __swp64 : AArch64NoPrefixTargetLibBuiltin<"unsigned long long int (unsigned long long int volatile *, unsigned long long int)">;
 }
diff --git a/clang/lib/CodeGen/TargetBuiltins/ARM.cpp b/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
index 491fb469e7136..615d751049b99 100644
--- a/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
+++ b/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
@@ -5392,6 +5392,42 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID,
     return Result;
   }
 
+  if (BuiltinID == AArch64::BI__swp8 || BuiltinID == AArch64::BI__swp16 ||
+      BuiltinID == AArch64::BI__swp32 || BuiltinID == AArch64::BI__swp64) {
+    unsigned IntrID;
+    llvm::Type *IntrArgTy;
+    switch (BuiltinID) {
+    case AArch64::BI__swp8:
+      IntrID = Intrinsic::aarch64_swp8;
+      IntrArgTy = Builder.getInt32Ty();
+      break;
+    case AArch64::BI__swp16:
+      IntrID = Intrinsic::aarch64_swp16;
+      IntrArgTy = Builder.getInt32Ty();
+      break;
+    case AArch64::BI__swp32:
+      IntrID = Intrinsic::aarch64_swp32;
+      IntrArgTy = Builder.getInt32Ty();
+      break;
+    case AArch64::BI__swp64:
+      IntrID = Intrinsic::aarch64_swp64;
+      IntrArgTy = Builder.getInt64Ty();
+      break;
+    default:
+      llvm_unreachable("missing builtin ID in switch!");
+    }
+    Value *Ptr = EmitScalarExpr(E->getArg(0));
+    Value *Val = EmitScalarExpr(E->getArg(1));
+    if (Val->getType() != IntrArgTy)
+      Val = Builder.CreateZExt(Val, IntrArgTy);
+    Value *Result = Builder.CreateCall(CGM.getIntrinsic(IntrID), {Ptr, Val});
+    // SWP{B/H} return i32 (zero-extended); truncate to declared type.
+    llvm::Type *RetTy = ConvertType(E->getType());
+    if (Result->getType() != RetTy)
+      Result = Builder.CreateTrunc(Result, RetTy);
+    return Result;
+  }
+
   if (BuiltinID == NEON::BI__builtin_neon_vcvth_bf16_f32)
     return Builder.CreateFPTrunc(
         Builder.CreateBitCast(EmitScalarExpr(E->getArg(0)),
diff --git a/clang/lib/Headers/arm64intr.h b/clang/lib/Headers/arm64intr.h
index 4f988f9de0a44..eb19f84a1f6d0 100644
--- a/clang/lib/Headers/arm64intr.h
+++ b/clang/lib/Headers/arm64intr.h
@@ -86,6 +86,11 @@ unsigned __int32 __casal32(unsigned __int32 volatile *, unsigned __int32,
 unsigned __int64 __casal64(unsigned __int64 volatile *, unsigned __int64,
                            unsigned __int64);
 
+unsigned __int8 __swp8(unsigned __int8 volatile *, unsigned __int8);
+unsigned __int16 __swp16(unsigned __int16 volatile *, unsigned __int16);
+unsigned __int32 __swp32(unsigned __int32 volatile *, unsigned __int32);
+unsigned __int64 __swp64(unsigned __int64 volatile *, unsigned __int64);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/clang/test/CodeGen/arm64-microsoft-intrinsics.c b/clang/test/CodeGen/arm64-microsoft-intrinsics.c
index f752ec446d8bb..abbcf897aafd9 100644
--- a/clang/test/CodeGen/arm64-microsoft-intrinsics.c
+++ b/clang/test/CodeGen/arm64-microsoft-intrinsics.c
@@ -955,5 +955,54 @@ unsigned long long int test__casal64(unsigned long long int volatile* t,
 // CHECK-MSCOMPAT:       ret i64 %[[RET]]
 // CHECK-LINUX: error: call to undeclared function '__casal64'
 
+unsigned char test__swp8(unsigned char volatile* t, unsigned char v)
+{
+  return __swp8(t, v);
+}
+// CHECK-MSCOMPAT-LABEL: define{{.*}}i8 @test__swp8(ptr{{.*}}%t, i8{{.*}}%v){{.*}}{
+// CHECK-MSCOMPAT:       %[[TMPT:[0-9]+]] = load ptr, ptr %t.addr, align 8
+// CHECK-MSCOMPAT:       %[[TMPV:[0-9]+]] = load i8, ptr %v.addr, align 1
+// CHECK-MSCOMPAT:       %[[ZEXTV:[0-9]+]] = zext i8 %[[TMPV]] to i32
+// CHECK-MSCOMPAT:       %[[RET:[0-9]+]] = call i32 @llvm.aarch64.swp8(ptr %[[TMPT]], i32 %[[ZEXTV]])
+// CHECK-MSCOMPAT:       %[[TRUNC:[0-9]+]] = trunc i32 %[[RET]] to i8
+// CHECK-MSCOMPAT:       ret i8 %[[TRUNC]]
+// CHECK-LINUX: error: call to undeclared function '__swp8'
+
+unsigned short test__swp16(unsigned short volatile* t, unsigned short v)
+{
+  return __swp16(t, v);
+}
+// CHECK-MSCOMPAT-LABEL: define{{.*}}i16 @test__swp16(ptr{{.*}}%t, i16{{.*}}%v){{.*}}{
+// CHECK-MSCOMPAT:       %[[TMPT:[0-9]+]] = load ptr, ptr %t.addr, align 8
+// CHECK-MSCOMPAT:       %[[TMPV:[0-9]+]] = load i16, ptr %v.addr, align 2
+// CHECK-MSCOMPAT:       %[[ZEXTV:[0-9]+]] = zext i16 %[[TMPV]] to i32
+// CHECK-MSCOMPAT:       %[[RET:[0-9]+]] = call i32 @llvm.aarch64.swp16(ptr %[[TMPT]], i32 %[[ZEXTV]])
+// CHECK-MSCOMPAT:       %[[TRUNC:[0-9]+]] = trunc i32 %[[RET]] to i16
+// CHECK-MSCOMPAT:       ret i16 %[[TRUNC]]
+// CHECK-LINUX: error: call to undeclared function '__swp16'
+
+unsigned int test__swp32(unsigned int volatile* t, unsigned int v)
+{
+  return __swp32(t, v);
+}
+// CHECK-MSCOMPAT-LABEL: define{{.*}}i32 @test__swp32(ptr{{.*}}%t, i32{{.*}}%v){{.*}}{
+// CHECK-MSCOMPAT:       %[[TMPT:[0-9]+]] = load ptr, ptr %t.addr, align 8
+// CHECK-MSCOMPAT:       %[[TMPV:[0-9]+]] = load i32, ptr %v.addr, align 4
+// CHECK-MSCOMPAT:       %[[RET:[0-9]+]] = call i32 @llvm.aarch64.swp32(ptr %[[TMPT]], i32 %[[TMPV]])
+// CHECK-MSCOMPAT:       ret i32 %[[RET]]
+// CHECK-LINUX: error: call to undeclared function '__swp32'
+
+unsigned long long int test__swp64(unsigned long long int volatile* t,
+                                   unsigned long long int v)
+{
+  return __swp64(t, v);
+}
+// CHECK-MSCOMPAT-LABEL: define{{.*}}i64 @test__swp64(ptr{{.*}}%t, i64{{.*}}%v){{.*}}{
+// CHECK-MSCOMPAT:       %[[TMPT:[0-9]+]] = load ptr, ptr %t.addr, align 8
+// CHECK-MSCOMPAT:       %[[TMPV:[0-9]+]] = load i64, ptr %v.addr, align 8
+// CHECK-MSCOMPAT:       %[[RET:[0-9]+]] = call i64 @llvm.aarch64.swp64(ptr %[[TMPT]], i64 %[[TMPV]])
+// CHECK-MSCOMPAT:       ret i64 %[[RET]]
+// CHECK-LINUX: error: call to undeclared function '__swp64'
+
 // CHECK-MSCOMPAT: ![[MD2]] = !{!"x18"}
 // CHECK-MSCOMPAT: ![[MD3]] = !{!"sp"}
diff --git a/llvm/include/llvm/IR/IntrinsicsAArch64.td b/llvm/include/llvm/IR/IntrinsicsAArch64.td
index 3f200b10d284a..c7958e52ea690 100644
--- a/llvm/include/llvm/IR/IntrinsicsAArch64.td
+++ b/llvm/include/llvm/IR/IntrinsicsAArch64.td
@@ -109,6 +109,21 @@ def int_aarch64_casal64 : Intrinsic<[llvm_i64_ty],
                                     [llvm_ptr_ty, llvm_i64_ty, llvm_i64_ty],
                                     [IntrNoFree, IntrWillReturn]>;
 
+// SWP intrinsics — emit SWP* (no acquire/release) directly, regardless of the
+// LSE target feature. swp{b,h} value uses i32; swp64 uses i64.
+def int_aarch64_swp8  : Intrinsic<[llvm_i32_ty],
+                                  [llvm_ptr_ty, llvm_i32_ty],
+                                  [IntrNoFree, IntrWillReturn]>;
+def int_aarch64_swp16 : Intrinsic<[llvm_i32_ty],
+                                  [llvm_ptr_ty, llvm_i32_ty],
+                                  [IntrNoFree, IntrWillReturn]>;
+def int_aarch64_swp32 : Intrinsic<[llvm_i32_ty],
+                                  [llvm_ptr_ty, llvm_i32_ty],
+                                  [IntrNoFree, IntrWillReturn]>;
+def int_aarch64_swp64 : Intrinsic<[llvm_i64_ty],
+                                  [llvm_ptr_ty, llvm_i64_ty],
+                                  [IntrNoFree, IntrWillReturn]>;
+
 def int_aarch64_sdiv : DefaultAttrsIntrinsic<[llvm_anyint_ty], [LLVMMatchType<0>,
                                 LLVMMatchType<0>], [IntrNoMem]>;
 def int_aarch64_udiv : DefaultAttrsIntrinsic<[llvm_anyint_ty], [LLVMMatchType<0>,
diff --git a/llvm/lib/Target/AArch64/AArch64InstrAtomics.td b/llvm/lib/Target/AArch64/AArch64InstrAtomics.td
index 11c8ca47c034b..187f73ce2049c 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrAtomics.td
+++ b/llvm/lib/Target/AArch64/AArch64InstrAtomics.td
@@ -230,6 +230,22 @@ def : Pat<(i32 (int_aarch64_casal32 GPR64sp:$Rn, GPR32:$Rs, GPR32:$Rt)),
 def : Pat<(i64 (int_aarch64_casal64 GPR64sp:$Rn, GPR64:$Rs, GPR64:$Rt)),
           (CASALX_cg GPR64:$Rs, GPR64:$Rt, GPR64sp:$Rn)>;
 
+// Unconditional patterns for the __swp* MSVC builtins (no ordering).
+// As with swp{b,h}, the zero-extension mask may survive or be eliminated by the
+// DAG combiner, so both masked and plain forms are needed.
+def : Pat<(i32 (int_aarch64_swp8  GPR64sp:$Rn, (and GPR32:$Rs, (i32 255)))),
+          (SWPB_cg GPR32:$Rs, GPR64sp:$Rn)>;
+def : Pat<(i32 (int_aarch64_swp8  GPR64sp:$Rn, GPR32:$Rs)),
+          (SWPB_cg GPR32:$Rs, GPR64sp:$Rn)>;
+def : Pat<(i32 (int_aarch64_swp16 GPR64sp:$Rn, (and GPR32:$Rs, (i32 65535)))),
+          (SWPH_cg GPR32:$Rs, GPR64sp:$Rn)>;
+def : Pat<(i32 (int_aarch64_swp16 GPR64sp:$Rn, GPR32:$Rs)),
+          (SWPH_cg GPR32:$Rs, GPR64sp:$Rn)>;
+def : Pat<(i32 (int_aarch64_swp32 GPR64sp:$Rn, GPR32:$Rs)),
+          (SWPW_cg GPR32:$Rs, GPR64sp:$Rn)>;
+def : Pat<(i64 (int_aarch64_swp64 GPR64sp:$Rn, GPR64:$Rs)),
+          (SWPX_cg GPR64:$Rs, GPR64sp:$Rn)>;
+
 //===----------------------------------
 // Atomic stores
 //===----------------------------------
diff --git a/llvm/lib/Target/AArch64/AArch64InstrFormats.td b/llvm/lib/Target/AArch64/AArch64InstrFormats.td
index a782bff1dd6d5..297450237a426 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrFormats.td
+++ b/llvm/lib/Target/AArch64/AArch64InstrFormats.td
@@ -12542,6 +12542,40 @@ multiclass Swap<bits<1> Acq, bits<1> Rel, string order> {
   let Sz = 0b11, Acq = Acq, Rel = Rel in def X : BaseSWP<order, "", GPR64>;
 }
 
+// Like BaseSWP but without the HasLSE predicate — for isCodeGenOnly use by
+// MS intrinsics that must emit SWP* regardless of the -march setting.
+class BaseSWP_cg<string order, string size, RegisterClass RC>
+      : I<(outs RC:$Rt),(ins RC:$Rs, GPR64sp:$Rn), "swp" # order # size,
+          "\t$Rs, $Rt, [$Rn]","",[]>,
+        Sched<[WriteAtomic]> {
+  bits<2> Sz;
+  bit Acq;
+  bit Rel;
+  bits<5> Rs;
+  bits<3> opc = 0b000;
+  bits<5> Rn;
+  bits<5> Rt;
+  let Inst{31-30} = Sz;
+  let Inst{29-24} = 0b111000;
+  let Inst{23} = Acq;
+  let Inst{22} = Rel;
+  let Inst{21} = 0b1;
+  let Inst{20-16} = Rs;
+  let Inst{15} = 0b1;
+  let Inst{14-12} = opc;
+  let Inst{11-10} = 0b00;
+  let Inst{9-5} = Rn;
+  let Inst{4-0} = Rt;
+  let Predicates = [];
+}
+
+multiclass Swap_cg<bits<1> Acq, bits<1> Rel, string order> {
+  let Sz = 0b00, Acq = Acq, Rel = Rel in def B_cg : BaseSWP_cg<order, "b", GPR32>;
+  let Sz = 0b01, Acq = Acq, Rel = Rel in def H_cg : BaseSWP_cg<order, "h", GPR32>;
+  let Sz = 0b10, Acq = Acq, Rel = Rel in def W_cg : BaseSWP_cg<order, "", GPR32>;
+  let Sz = 0b11, Acq = Acq, Rel = Rel in def X_cg : BaseSWP_cg<order, "", GPR64>;
+}
+
 // v9.6a swap operations
 class BaseSWPLSUI<string order, RegisterClass RC>
        : I<(outs RC:$Rt),(ins RC:$Rs, GPR64sp:$Rn), "swpt" # order,
diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.td b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
index 48de7956f282a..084f77879816a 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrInfo.td
+++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
@@ -3069,6 +3069,12 @@ defm SWPA  : Swap<1, 0, "a">;
 defm SWPL  : Swap<0, 1, "l">;
 defm SWPAL : Swap<1, 1, "al">;
 
+// isCodeGenOnly=1: excluded from asm parser/disassembler, no HasLSE predicate.
+// Used by the __swp* MS intrinsics to force SWP* emission regardless of -march.
+let isCodeGenOnly = 1 in {
+  defm SWP : Swap_cg<0, 0, "">;
+}
+
 // v9.6a atomic swap (FEAT_LSUI)
 let Predicates = [HasLSUI] in {
   defm SWPT   : SwapLSUI<0, 0, "">;
diff --git a/llvm/test/CodeGen/AArch64/ms-intrinsics-swp.ll b/llvm/test/CodeGen/AArch64/ms-intrinsics-swp.ll
new file mode 100644
index 0000000000000..42da0acb7d3e4
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/ms-intrinsics-swp.ll
@@ -0,0 +1,46 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=aarch64-none-linux-gnu -fast-isel=0 -global-isel=false \
+; RUN:     -verify-machineinstrs < %s | FileCheck %s
+; RUN: llc -mtriple=aarch64-none-linux-gnu -mattr=+lse -fast-isel=0 \
+; RUN:     -global-isel=false -verify-machineinstrs < %s | FileCheck %s
+
+; Tests for the __swp* MS builtins on AArch64. These lower to
+; llvm.aarch64.swp* intrinsics and must select SWP* regardless of whether
+; +lse is present in the target features (the _cg ISel variants have no
+; feature predicate).
+
+define i32 @test_swp8(ptr %p, i32 %v) {
+; CHECK-LABEL: test_swp8:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    swpb w1, w0, [x0]
+; CHECK-NEXT:    ret
+  %r = call i32 @llvm.aarch64.swp8(ptr %p, i32 %v)
+  ret i32 %r
+}
+
+define i32 @test_swp16(ptr %p, i32 %v) {
+; CHECK-LABEL: test_swp16:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    swph w1, w0, [x0]
+; CHECK-NEXT:    ret
+  %r = call i32 @llvm.aarch64.swp16(ptr %p, i32 %v)
+  ret i32 %r
+}
+
+define i32 @test_swp32(ptr %p, i32 %v) {
+; CHECK-LABEL: test_swp32:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    swp w1, w0, [x0]
+; CHECK-NEXT:    ret
+  %r = call i32 @llvm.aarch64.swp32(ptr %p, i32 %v)
+  ret i32 %r
+}
+
+define i64 @test_swp64(ptr %p, i64 %v) {
+; CHECK-LABEL: test_swp64:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    swp x1, x0, [x0]
+; CHECK-NEXT:    ret
+  %r = call i64 @llvm.aarch64.swp64(ptr %p, i64 %v)
+  ret i64 %r
+}

>From 3e3b716111b6977a4b05671a48b85783ba6f335a Mon Sep 17 00:00:00 2001
From: Adhemerval Zanella <zatrazz at gmail.com>
Date: Fri, 17 Apr 2026 12:41:03 -0300
Subject: [PATCH 10/17] [aarch64] Add support for the __swpa{8|16|32|64} MS
 intrinsics

Adds support for the following MSVC intrinsics:
* `__swpa8`  - maps to SWPAB.
* `__swpa16` - maps to SWPAH.
* `__swpa32` - maps to SWPA.
* `__swpa64` - maps to SWPA.

The emit is done using new intrisincs to issue SWPA* instruction
directly, regardless of LSE target feature.  This mimics MSVC compiler
and the idea of the builtin to mimic an inline asm.

These are documented at:
<https://learn.microsoft.com/en-us/cpp/intrinsics/arm64-intrinsics?view=msvc-180>
---
 clang/include/clang/Basic/BuiltinsAArch64.td  |  4 ++
 clang/lib/CodeGen/TargetBuiltins/ARM.cpp      | 20 +++++++-
 clang/lib/Headers/arm64intr.h                 |  5 ++
 .../test/CodeGen/arm64-microsoft-intrinsics.c | 49 +++++++++++++++++++
 llvm/include/llvm/IR/IntrinsicsAArch64.td     | 14 ++++++
 .../lib/Target/AArch64/AArch64InstrAtomics.td | 14 ++++++
 llvm/lib/Target/AArch64/AArch64InstrInfo.td   |  3 +-
 .../test/CodeGen/AArch64/ms-intrinsics-swp.ll | 36 ++++++++++++++
 8 files changed, 143 insertions(+), 2 deletions(-)

diff --git a/clang/include/clang/Basic/BuiltinsAArch64.td b/clang/include/clang/Basic/BuiltinsAArch64.td
index 0ded0aa4e5da1..34b11e43d65bf 100644
--- a/clang/include/clang/Basic/BuiltinsAArch64.td
+++ b/clang/include/clang/Basic/BuiltinsAArch64.td
@@ -441,4 +441,8 @@ let Attributes = [NoThrow, RequireDeclaration], Languages = "ALL_MS_LANGUAGES",
 	def __swp16 : AArch64NoPrefixTargetLibBuiltin<"unsigned short (unsigned short volatile *, unsigned short)">;
 	def __swp32 : AArch64NoPrefixTargetLibBuiltin<"unsigned int (unsigned int volatile *, unsigned int)">;
 	def __swp64 : AArch64NoPrefixTargetLibBuiltin<"unsigned long long int (unsigned long long int volatile *, unsigned long long int)">;
+	def __swpa8 : AArch64NoPrefixTargetLibBuiltin<"unsigned char (unsigned char volatile *, unsigned char)">;
+	def __swpa16 : AArch64NoPrefixTargetLibBuiltin<"unsigned short (unsigned short volatile *, unsigned short)">;
+	def __swpa32 : AArch64NoPrefixTargetLibBuiltin<"unsigned int (unsigned int volatile *, unsigned int)">;
+	def __swpa64 : AArch64NoPrefixTargetLibBuiltin<"unsigned long long int (unsigned long long int volatile *, unsigned long long int)">;
 }
diff --git a/clang/lib/CodeGen/TargetBuiltins/ARM.cpp b/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
index 615d751049b99..dc3bc31506f40 100644
--- a/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
+++ b/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
@@ -5393,7 +5393,9 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID,
   }
 
   if (BuiltinID == AArch64::BI__swp8 || BuiltinID == AArch64::BI__swp16 ||
-      BuiltinID == AArch64::BI__swp32 || BuiltinID == AArch64::BI__swp64) {
+      BuiltinID == AArch64::BI__swp32 || BuiltinID == AArch64::BI__swp64 ||
+      BuiltinID == AArch64::BI__swpa8 || BuiltinID == AArch64::BI__swpa16 ||
+      BuiltinID == AArch64::BI__swpa32 || BuiltinID == AArch64::BI__swpa64) {
     unsigned IntrID;
     llvm::Type *IntrArgTy;
     switch (BuiltinID) {
@@ -5413,6 +5415,22 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID,
       IntrID = Intrinsic::aarch64_swp64;
       IntrArgTy = Builder.getInt64Ty();
       break;
+    case AArch64::BI__swpa8:
+      IntrID = Intrinsic::aarch64_swpa8;
+      IntrArgTy = Builder.getInt32Ty();
+      break;
+    case AArch64::BI__swpa16:
+      IntrID = Intrinsic::aarch64_swpa16;
+      IntrArgTy = Builder.getInt32Ty();
+      break;
+    case AArch64::BI__swpa32:
+      IntrID = Intrinsic::aarch64_swpa32;
+      IntrArgTy = Builder.getInt32Ty();
+      break;
+    case AArch64::BI__swpa64:
+      IntrID = Intrinsic::aarch64_swpa64;
+      IntrArgTy = Builder.getInt64Ty();
+      break;
     default:
       llvm_unreachable("missing builtin ID in switch!");
     }
diff --git a/clang/lib/Headers/arm64intr.h b/clang/lib/Headers/arm64intr.h
index eb19f84a1f6d0..dd72fde0d6e89 100644
--- a/clang/lib/Headers/arm64intr.h
+++ b/clang/lib/Headers/arm64intr.h
@@ -91,6 +91,11 @@ unsigned __int16 __swp16(unsigned __int16 volatile *, unsigned __int16);
 unsigned __int32 __swp32(unsigned __int32 volatile *, unsigned __int32);
 unsigned __int64 __swp64(unsigned __int64 volatile *, unsigned __int64);
 
+unsigned __int8 __swpa8(unsigned __int8 volatile *, unsigned __int8);
+unsigned __int16 __swpa16(unsigned __int16 volatile *, unsigned __int16);
+unsigned __int32 __swpa32(unsigned __int32 volatile *, unsigned __int32);
+unsigned __int64 __swpa64(unsigned __int64 volatile *, unsigned __int64);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/clang/test/CodeGen/arm64-microsoft-intrinsics.c b/clang/test/CodeGen/arm64-microsoft-intrinsics.c
index abbcf897aafd9..6c4e54cf1f086 100644
--- a/clang/test/CodeGen/arm64-microsoft-intrinsics.c
+++ b/clang/test/CodeGen/arm64-microsoft-intrinsics.c
@@ -1004,5 +1004,54 @@ unsigned long long int test__swp64(unsigned long long int volatile* t,
 // CHECK-MSCOMPAT:       ret i64 %[[RET]]
 // CHECK-LINUX: error: call to undeclared function '__swp64'
 
+unsigned char test__swpa8(unsigned char volatile* t, unsigned char v)
+{
+  return __swpa8(t, v);
+}
+// CHECK-MSCOMPAT-LABEL: define{{.*}}i8 @test__swpa8(ptr{{.*}}%t, i8{{.*}}%v){{.*}}{
+// CHECK-MSCOMPAT:       %[[TMPT:[0-9]+]] = load ptr, ptr %t.addr, align 8
+// CHECK-MSCOMPAT:       %[[TMPV:[0-9]+]] = load i8, ptr %v.addr, align 1
+// CHECK-MSCOMPAT:       %[[ZEXTV:[0-9]+]] = zext i8 %[[TMPV]] to i32
+// CHECK-MSCOMPAT:       %[[RET:[0-9]+]] = call i32 @llvm.aarch64.swpa8(ptr %[[TMPT]], i32 %[[ZEXTV]])
+// CHECK-MSCOMPAT:       %[[TRUNC:[0-9]+]] = trunc i32 %[[RET]] to i8
+// CHECK-MSCOMPAT:       ret i8 %[[TRUNC]]
+// CHECK-LINUX: error: call to undeclared function '__swpa8'
+
+unsigned short test__swpa16(unsigned short volatile* t, unsigned short v)
+{
+  return __swpa16(t, v);
+}
+// CHECK-MSCOMPAT-LABEL: define{{.*}}i16 @test__swpa16(ptr{{.*}}%t, i16{{.*}}%v){{.*}}{
+// CHECK-MSCOMPAT:       %[[TMPT:[0-9]+]] = load ptr, ptr %t.addr, align 8
+// CHECK-MSCOMPAT:       %[[TMPV:[0-9]+]] = load i16, ptr %v.addr, align 2
+// CHECK-MSCOMPAT:       %[[ZEXTV:[0-9]+]] = zext i16 %[[TMPV]] to i32
+// CHECK-MSCOMPAT:       %[[RET:[0-9]+]] = call i32 @llvm.aarch64.swpa16(ptr %[[TMPT]], i32 %[[ZEXTV]])
+// CHECK-MSCOMPAT:       %[[TRUNC:[0-9]+]] = trunc i32 %[[RET]] to i16
+// CHECK-MSCOMPAT:       ret i16 %[[TRUNC]]
+// CHECK-LINUX: error: call to undeclared function '__swpa16'
+
+unsigned int test__swpa32(unsigned int volatile* t, unsigned int v)
+{
+  return __swpa32(t, v);
+}
+// CHECK-MSCOMPAT-LABEL: define{{.*}}i32 @test__swpa32(ptr{{.*}}%t, i32{{.*}}%v){{.*}}{
+// CHECK-MSCOMPAT:       %[[TMPT:[0-9]+]] = load ptr, ptr %t.addr, align 8
+// CHECK-MSCOMPAT:       %[[TMPV:[0-9]+]] = load i32, ptr %v.addr, align 4
+// CHECK-MSCOMPAT:       %[[RET:[0-9]+]] = call i32 @llvm.aarch64.swpa32(ptr %[[TMPT]], i32 %[[TMPV]])
+// CHECK-MSCOMPAT:       ret i32 %[[RET]]
+// CHECK-LINUX: error: call to undeclared function '__swpa32'
+
+unsigned long long int test__swpa64(unsigned long long int volatile* t,
+                                    unsigned long long int v)
+{
+  return __swpa64(t, v);
+}
+// CHECK-MSCOMPAT-LABEL: define{{.*}}i64 @test__swpa64(ptr{{.*}}%t, i64{{.*}}%v){{.*}}{
+// CHECK-MSCOMPAT:       %[[TMPT:[0-9]+]] = load ptr, ptr %t.addr, align 8
+// CHECK-MSCOMPAT:       %[[TMPV:[0-9]+]] = load i64, ptr %v.addr, align 8
+// CHECK-MSCOMPAT:       %[[RET:[0-9]+]] = call i64 @llvm.aarch64.swpa64(ptr %[[TMPT]], i64 %[[TMPV]])
+// CHECK-MSCOMPAT:       ret i64 %[[RET]]
+// CHECK-LINUX: error: call to undeclared function '__swpa64'
+
 // CHECK-MSCOMPAT: ![[MD2]] = !{!"x18"}
 // CHECK-MSCOMPAT: ![[MD3]] = !{!"sp"}
diff --git a/llvm/include/llvm/IR/IntrinsicsAArch64.td b/llvm/include/llvm/IR/IntrinsicsAArch64.td
index c7958e52ea690..0b4b0ec0c35a6 100644
--- a/llvm/include/llvm/IR/IntrinsicsAArch64.td
+++ b/llvm/include/llvm/IR/IntrinsicsAArch64.td
@@ -109,6 +109,20 @@ def int_aarch64_casal64 : Intrinsic<[llvm_i64_ty],
                                     [llvm_ptr_ty, llvm_i64_ty, llvm_i64_ty],
                                     [IntrNoFree, IntrWillReturn]>;
 
+// SWPA intrinsics — emit SWPA (acquire) directly, regardless of the LSE target
+def int_aarch64_swpa8  : Intrinsic<[llvm_i32_ty],
+                                   [llvm_ptr_ty, llvm_i32_ty],
+                                   [IntrNoFree, IntrWillReturn]>;
+def int_aarch64_swpa16 : Intrinsic<[llvm_i32_ty],
+                                   [llvm_ptr_ty, llvm_i32_ty],
+                                   [IntrNoFree, IntrWillReturn]>;
+def int_aarch64_swpa32 : Intrinsic<[llvm_i32_ty],
+                                   [llvm_ptr_ty, llvm_i32_ty],
+                                   [IntrNoFree, IntrWillReturn]>;
+def int_aarch64_swpa64 : Intrinsic<[llvm_i64_ty],
+                                   [llvm_ptr_ty, llvm_i64_ty],
+                                   [IntrNoFree, IntrWillReturn]>;
+
 // SWP intrinsics — emit SWP* (no acquire/release) directly, regardless of the
 // LSE target feature. swp{b,h} value uses i32; swp64 uses i64.
 def int_aarch64_swp8  : Intrinsic<[llvm_i32_ty],
diff --git a/llvm/lib/Target/AArch64/AArch64InstrAtomics.td b/llvm/lib/Target/AArch64/AArch64InstrAtomics.td
index 187f73ce2049c..2545d5707a532 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrAtomics.td
+++ b/llvm/lib/Target/AArch64/AArch64InstrAtomics.td
@@ -246,6 +246,20 @@ def : Pat<(i32 (int_aarch64_swp32 GPR64sp:$Rn, GPR32:$Rs)),
 def : Pat<(i64 (int_aarch64_swp64 GPR64sp:$Rn, GPR64:$Rs)),
           (SWPX_cg GPR64:$Rs, GPR64sp:$Rn)>;
 
+// Similar as __swp*, but for __swap*,
+def : Pat<(i32 (int_aarch64_swpa8  GPR64sp:$Rn, (and GPR32:$Rs, (i32 255)))),
+          (SWPAB_cg GPR32:$Rs, GPR64sp:$Rn)>;
+def : Pat<(i32 (int_aarch64_swpa8  GPR64sp:$Rn, GPR32:$Rs)),
+          (SWPAB_cg GPR32:$Rs, GPR64sp:$Rn)>;
+def : Pat<(i32 (int_aarch64_swpa16 GPR64sp:$Rn, (and GPR32:$Rs, (i32 65535)))),
+          (SWPAH_cg GPR32:$Rs, GPR64sp:$Rn)>;
+def : Pat<(i32 (int_aarch64_swpa16 GPR64sp:$Rn, GPR32:$Rs)),
+          (SWPAH_cg GPR32:$Rs, GPR64sp:$Rn)>;
+def : Pat<(i32 (int_aarch64_swpa32 GPR64sp:$Rn, GPR32:$Rs)),
+          (SWPAW_cg GPR32:$Rs, GPR64sp:$Rn)>;
+def : Pat<(i64 (int_aarch64_swpa64 GPR64sp:$Rn, GPR64:$Rs)),
+          (SWPAX_cg GPR64:$Rs, GPR64sp:$Rn)>;
+
 //===----------------------------------
 // Atomic stores
 //===----------------------------------
diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.td b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
index 084f77879816a..10eda2f472999 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrInfo.td
+++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
@@ -3072,7 +3072,8 @@ defm SWPAL : Swap<1, 1, "al">;
 // isCodeGenOnly=1: excluded from asm parser/disassembler, no HasLSE predicate.
 // Used by the __swp* MS intrinsics to force SWP* emission regardless of -march.
 let isCodeGenOnly = 1 in {
-  defm SWP : Swap_cg<0, 0, "">;
+  defm SWP  : Swap_cg<0, 0, "">;
+  defm SWPA : Swap_cg<1, 0, "a">;
 }
 
 // v9.6a atomic swap (FEAT_LSUI)
diff --git a/llvm/test/CodeGen/AArch64/ms-intrinsics-swp.ll b/llvm/test/CodeGen/AArch64/ms-intrinsics-swp.ll
index 42da0acb7d3e4..56ea0163244f4 100644
--- a/llvm/test/CodeGen/AArch64/ms-intrinsics-swp.ll
+++ b/llvm/test/CodeGen/AArch64/ms-intrinsics-swp.ll
@@ -44,3 +44,39 @@ define i64 @test_swp64(ptr %p, i64 %v) {
   %r = call i64 @llvm.aarch64.swp64(ptr %p, i64 %v)
   ret i64 %r
 }
+
+define i32 @test_swpa8(ptr %p, i32 %v) {
+; CHECK-LABEL: test_swpa8:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    swpab w1, w0, [x0]
+; CHECK-NEXT:    ret
+  %r = call i32 @llvm.aarch64.swpa8(ptr %p, i32 %v)
+  ret i32 %r
+}
+
+define i32 @test_swpa16(ptr %p, i32 %v) {
+; CHECK-LABEL: test_swpa16:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    swpah w1, w0, [x0]
+; CHECK-NEXT:    ret
+  %r = call i32 @llvm.aarch64.swpa16(ptr %p, i32 %v)
+  ret i32 %r
+}
+
+define i32 @test_swpa32(ptr %p, i32 %v) {
+; CHECK-LABEL: test_swpa32:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    swpa w1, w0, [x0]
+; CHECK-NEXT:    ret
+  %r = call i32 @llvm.aarch64.swpa32(ptr %p, i32 %v)
+  ret i32 %r
+}
+
+define i64 @test_swpa64(ptr %p, i64 %v) {
+; CHECK-LABEL: test_swpa64:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    swpa x1, x0, [x0]
+; CHECK-NEXT:    ret
+  %r = call i64 @llvm.aarch64.swpa64(ptr %p, i64 %v)
+  ret i64 %r
+}

>From 1b6e58d5b436fc96876b743bb91af21ab2efb798 Mon Sep 17 00:00:00 2001
From: Adhemerval Zanella <zatrazz at gmail.com>
Date: Fri, 17 Apr 2026 13:02:47 -0300
Subject: [PATCH 11/17] [aarch64] Add support for the __swpl{8|16|32|64} MS
 intrinsics

Adds support for the following MSVC intrinsics:
* `__swpl8`   - maps to SWPLB.
* `__swpl16`  - maps to SWPLH.
* `__swpl32`  - maps to SWPL.
* `__swpl64`  - maps to SWPL.

The emit is done using new intrisincs to issue SWPL* instruction
directly, regardless of LSE target feature.  This mimics MSVC compiler
and the idea of the builtin to mimic an inline asm.

These are documented at:
<https://learn.microsoft.com/en-us/cpp/intrinsics/arm64-intrinsics?view=msvc-180>
---
 clang/include/clang/Basic/BuiltinsAArch64.td  |  4 ++
 clang/lib/CodeGen/TargetBuiltins/ARM.cpp      | 20 +++++++-
 clang/lib/Headers/arm64intr.h                 |  5 ++
 .../test/CodeGen/arm64-microsoft-intrinsics.c | 49 +++++++++++++++++++
 llvm/include/llvm/IR/IntrinsicsAArch64.td     | 14 ++++++
 .../lib/Target/AArch64/AArch64InstrAtomics.td | 14 ++++++
 llvm/lib/Target/AArch64/AArch64InstrInfo.td   |  1 +
 .../test/CodeGen/AArch64/ms-intrinsics-swp.ll | 36 ++++++++++++++
 8 files changed, 142 insertions(+), 1 deletion(-)

diff --git a/clang/include/clang/Basic/BuiltinsAArch64.td b/clang/include/clang/Basic/BuiltinsAArch64.td
index 34b11e43d65bf..d27e34edf0355 100644
--- a/clang/include/clang/Basic/BuiltinsAArch64.td
+++ b/clang/include/clang/Basic/BuiltinsAArch64.td
@@ -445,4 +445,8 @@ let Attributes = [NoThrow, RequireDeclaration], Languages = "ALL_MS_LANGUAGES",
 	def __swpa16 : AArch64NoPrefixTargetLibBuiltin<"unsigned short (unsigned short volatile *, unsigned short)">;
 	def __swpa32 : AArch64NoPrefixTargetLibBuiltin<"unsigned int (unsigned int volatile *, unsigned int)">;
 	def __swpa64 : AArch64NoPrefixTargetLibBuiltin<"unsigned long long int (unsigned long long int volatile *, unsigned long long int)">;
+	def __swpl8 : AArch64NoPrefixTargetLibBuiltin<"unsigned char (unsigned char volatile *, unsigned char)">;
+	def __swpl16 : AArch64NoPrefixTargetLibBuiltin<"unsigned short (unsigned short volatile *, unsigned short)">;
+	def __swpl32 : AArch64NoPrefixTargetLibBuiltin<"unsigned int (unsigned int volatile *, unsigned int)">;
+	def __swpl64 : AArch64NoPrefixTargetLibBuiltin<"unsigned long long int (unsigned long long int volatile *, unsigned long long int)">;
 }
diff --git a/clang/lib/CodeGen/TargetBuiltins/ARM.cpp b/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
index dc3bc31506f40..18fe455d57734 100644
--- a/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
+++ b/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
@@ -5395,7 +5395,9 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID,
   if (BuiltinID == AArch64::BI__swp8 || BuiltinID == AArch64::BI__swp16 ||
       BuiltinID == AArch64::BI__swp32 || BuiltinID == AArch64::BI__swp64 ||
       BuiltinID == AArch64::BI__swpa8 || BuiltinID == AArch64::BI__swpa16 ||
-      BuiltinID == AArch64::BI__swpa32 || BuiltinID == AArch64::BI__swpa64) {
+      BuiltinID == AArch64::BI__swpa32 || BuiltinID == AArch64::BI__swpa64 ||
+      BuiltinID == AArch64::BI__swpl8 || BuiltinID == AArch64::BI__swpl16 ||
+      BuiltinID == AArch64::BI__swpl32 || BuiltinID == AArch64::BI__swpl64) {
     unsigned IntrID;
     llvm::Type *IntrArgTy;
     switch (BuiltinID) {
@@ -5431,6 +5433,22 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID,
       IntrID = Intrinsic::aarch64_swpa64;
       IntrArgTy = Builder.getInt64Ty();
       break;
+    case AArch64::BI__swpl8:
+      IntrID = Intrinsic::aarch64_swpl8;
+      IntrArgTy = Builder.getInt32Ty();
+      break;
+    case AArch64::BI__swpl16:
+      IntrID = Intrinsic::aarch64_swpl16;
+      IntrArgTy = Builder.getInt32Ty();
+      break;
+    case AArch64::BI__swpl32:
+      IntrID = Intrinsic::aarch64_swpl32;
+      IntrArgTy = Builder.getInt32Ty();
+      break;
+    case AArch64::BI__swpl64:
+      IntrID = Intrinsic::aarch64_swpl64;
+      IntrArgTy = Builder.getInt64Ty();
+      break;
     default:
       llvm_unreachable("missing builtin ID in switch!");
     }
diff --git a/clang/lib/Headers/arm64intr.h b/clang/lib/Headers/arm64intr.h
index dd72fde0d6e89..1c7ab0b544b9c 100644
--- a/clang/lib/Headers/arm64intr.h
+++ b/clang/lib/Headers/arm64intr.h
@@ -96,6 +96,11 @@ unsigned __int16 __swpa16(unsigned __int16 volatile *, unsigned __int16);
 unsigned __int32 __swpa32(unsigned __int32 volatile *, unsigned __int32);
 unsigned __int64 __swpa64(unsigned __int64 volatile *, unsigned __int64);
 
+unsigned __int8 __swpl8(unsigned __int8 volatile *, unsigned __int8);
+unsigned __int16 __swpl16(unsigned __int16 volatile *, unsigned __int16);
+unsigned __int32 __swpl32(unsigned __int32 volatile *, unsigned __int32);
+unsigned __int64 __swpl64(unsigned __int64 volatile *, unsigned __int64);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/clang/test/CodeGen/arm64-microsoft-intrinsics.c b/clang/test/CodeGen/arm64-microsoft-intrinsics.c
index 6c4e54cf1f086..08d96aa5db442 100644
--- a/clang/test/CodeGen/arm64-microsoft-intrinsics.c
+++ b/clang/test/CodeGen/arm64-microsoft-intrinsics.c
@@ -1053,5 +1053,54 @@ unsigned long long int test__swpa64(unsigned long long int volatile* t,
 // CHECK-MSCOMPAT:       ret i64 %[[RET]]
 // CHECK-LINUX: error: call to undeclared function '__swpa64'
 
+unsigned char test__swpl8(unsigned char volatile* t, unsigned char v)
+{
+  return __swpl8(t, v);
+}
+// CHECK-MSCOMPAT-LABEL: define{{.*}}i8 @test__swpl8(ptr{{.*}}%t, i8{{.*}}%v){{.*}}{
+// CHECK-MSCOMPAT:       %[[TMPT:[0-9]+]] = load ptr, ptr %t.addr, align 8
+// CHECK-MSCOMPAT:       %[[TMPV:[0-9]+]] = load i8, ptr %v.addr, align 1
+// CHECK-MSCOMPAT:       %[[ZEXTV:[0-9]+]] = zext i8 %[[TMPV]] to i32
+// CHECK-MSCOMPAT:       %[[RET:[0-9]+]] = call i32 @llvm.aarch64.swpl8(ptr %[[TMPT]], i32 %[[ZEXTV]])
+// CHECK-MSCOMPAT:       %[[TRUNC:[0-9]+]] = trunc i32 %[[RET]] to i8
+// CHECK-MSCOMPAT:       ret i8 %[[TRUNC]]
+// CHECK-LINUX: error: call to undeclared function '__swpl8'
+
+unsigned short test__swpl16(unsigned short volatile* t, unsigned short v)
+{
+  return __swpl16(t, v);
+}
+// CHECK-MSCOMPAT-LABEL: define{{.*}}i16 @test__swpl16(ptr{{.*}}%t, i16{{.*}}%v){{.*}}{
+// CHECK-MSCOMPAT:       %[[TMPT:[0-9]+]] = load ptr, ptr %t.addr, align 8
+// CHECK-MSCOMPAT:       %[[TMPV:[0-9]+]] = load i16, ptr %v.addr, align 2
+// CHECK-MSCOMPAT:       %[[ZEXTV:[0-9]+]] = zext i16 %[[TMPV]] to i32
+// CHECK-MSCOMPAT:       %[[RET:[0-9]+]] = call i32 @llvm.aarch64.swpl16(ptr %[[TMPT]], i32 %[[ZEXTV]])
+// CHECK-MSCOMPAT:       %[[TRUNC:[0-9]+]] = trunc i32 %[[RET]] to i16
+// CHECK-MSCOMPAT:       ret i16 %[[TRUNC]]
+// CHECK-LINUX: error: call to undeclared function '__swpl16'
+
+unsigned int test__swpl32(unsigned int volatile* t, unsigned int v)
+{
+  return __swpl32(t, v);
+}
+// CHECK-MSCOMPAT-LABEL: define{{.*}}i32 @test__swpl32(ptr{{.*}}%t, i32{{.*}}%v){{.*}}{
+// CHECK-MSCOMPAT:       %[[TMPT:[0-9]+]] = load ptr, ptr %t.addr, align 8
+// CHECK-MSCOMPAT:       %[[TMPV:[0-9]+]] = load i32, ptr %v.addr, align 4
+// CHECK-MSCOMPAT:       %[[RET:[0-9]+]] = call i32 @llvm.aarch64.swpl32(ptr %[[TMPT]], i32 %[[TMPV]])
+// CHECK-MSCOMPAT:       ret i32 %[[RET]]
+// CHECK-LINUX: error: call to undeclared function '__swpl32'
+
+unsigned long long int test__swpl64(unsigned long long int volatile* t,
+                                    unsigned long long int v)
+{
+  return __swpl64(t, v);
+}
+// CHECK-MSCOMPAT-LABEL: define{{.*}}i64 @test__swpl64(ptr{{.*}}%t, i64{{.*}}%v){{.*}}{
+// CHECK-MSCOMPAT:       %[[TMPT:[0-9]+]] = load ptr, ptr %t.addr, align 8
+// CHECK-MSCOMPAT:       %[[TMPV:[0-9]+]] = load i64, ptr %v.addr, align 8
+// CHECK-MSCOMPAT:       %[[RET:[0-9]+]] = call i64 @llvm.aarch64.swpl64(ptr %[[TMPT]], i64 %[[TMPV]])
+// CHECK-MSCOMPAT:       ret i64 %[[RET]]
+// CHECK-LINUX: error: call to undeclared function '__swpl64'
+
 // CHECK-MSCOMPAT: ![[MD2]] = !{!"x18"}
 // CHECK-MSCOMPAT: ![[MD3]] = !{!"sp"}
diff --git a/llvm/include/llvm/IR/IntrinsicsAArch64.td b/llvm/include/llvm/IR/IntrinsicsAArch64.td
index 0b4b0ec0c35a6..a17bad3b53570 100644
--- a/llvm/include/llvm/IR/IntrinsicsAArch64.td
+++ b/llvm/include/llvm/IR/IntrinsicsAArch64.td
@@ -123,6 +123,20 @@ def int_aarch64_swpa64 : Intrinsic<[llvm_i64_ty],
                                    [llvm_ptr_ty, llvm_i64_ty],
                                    [IntrNoFree, IntrWillReturn]>;
 
+// SWPL intrinsics — emit SWPL (release) directly, regardless of the LSE target
+def int_aarch64_swpl8  : Intrinsic<[llvm_i32_ty],
+                                   [llvm_ptr_ty, llvm_i32_ty],
+                                   [IntrNoFree, IntrWillReturn]>;
+def int_aarch64_swpl16 : Intrinsic<[llvm_i32_ty],
+                                   [llvm_ptr_ty, llvm_i32_ty],
+                                   [IntrNoFree, IntrWillReturn]>;
+def int_aarch64_swpl32 : Intrinsic<[llvm_i32_ty],
+                                   [llvm_ptr_ty, llvm_i32_ty],
+                                   [IntrNoFree, IntrWillReturn]>;
+def int_aarch64_swpl64 : Intrinsic<[llvm_i64_ty],
+                                   [llvm_ptr_ty, llvm_i64_ty],
+                                   [IntrNoFree, IntrWillReturn]>;
+
 // SWP intrinsics — emit SWP* (no acquire/release) directly, regardless of the
 // LSE target feature. swp{b,h} value uses i32; swp64 uses i64.
 def int_aarch64_swp8  : Intrinsic<[llvm_i32_ty],
diff --git a/llvm/lib/Target/AArch64/AArch64InstrAtomics.td b/llvm/lib/Target/AArch64/AArch64InstrAtomics.td
index 2545d5707a532..6dd48878500cd 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrAtomics.td
+++ b/llvm/lib/Target/AArch64/AArch64InstrAtomics.td
@@ -260,6 +260,20 @@ def : Pat<(i32 (int_aarch64_swpa32 GPR64sp:$Rn, GPR32:$Rs)),
 def : Pat<(i64 (int_aarch64_swpa64 GPR64sp:$Rn, GPR64:$Rs)),
           (SWPAX_cg GPR64:$Rs, GPR64sp:$Rn)>;
 
+// Similar as __swp*, but for __swal*,
+def : Pat<(i32 (int_aarch64_swpl8  GPR64sp:$Rn, (and GPR32:$Rs, (i32 255)))),
+          (SWPLB_cg GPR32:$Rs, GPR64sp:$Rn)>;
+def : Pat<(i32 (int_aarch64_swpl8  GPR64sp:$Rn, GPR32:$Rs)),
+          (SWPLB_cg GPR32:$Rs, GPR64sp:$Rn)>;
+def : Pat<(i32 (int_aarch64_swpl16 GPR64sp:$Rn, (and GPR32:$Rs, (i32 65535)))),
+          (SWPLH_cg GPR32:$Rs, GPR64sp:$Rn)>;
+def : Pat<(i32 (int_aarch64_swpl16 GPR64sp:$Rn, GPR32:$Rs)),
+          (SWPLH_cg GPR32:$Rs, GPR64sp:$Rn)>;
+def : Pat<(i32 (int_aarch64_swpl32 GPR64sp:$Rn, GPR32:$Rs)),
+          (SWPLW_cg GPR32:$Rs, GPR64sp:$Rn)>;
+def : Pat<(i64 (int_aarch64_swpl64 GPR64sp:$Rn, GPR64:$Rs)),
+          (SWPLX_cg GPR64:$Rs, GPR64sp:$Rn)>;
+
 //===----------------------------------
 // Atomic stores
 //===----------------------------------
diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.td b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
index 10eda2f472999..f5dcd4dd9389b 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrInfo.td
+++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
@@ -3074,6 +3074,7 @@ defm SWPAL : Swap<1, 1, "al">;
 let isCodeGenOnly = 1 in {
   defm SWP  : Swap_cg<0, 0, "">;
   defm SWPA : Swap_cg<1, 0, "a">;
+  defm SWPL : Swap_cg<0, 1, "l">;
 }
 
 // v9.6a atomic swap (FEAT_LSUI)
diff --git a/llvm/test/CodeGen/AArch64/ms-intrinsics-swp.ll b/llvm/test/CodeGen/AArch64/ms-intrinsics-swp.ll
index 56ea0163244f4..b8f613ae1a00f 100644
--- a/llvm/test/CodeGen/AArch64/ms-intrinsics-swp.ll
+++ b/llvm/test/CodeGen/AArch64/ms-intrinsics-swp.ll
@@ -80,3 +80,39 @@ define i64 @test_swpa64(ptr %p, i64 %v) {
   %r = call i64 @llvm.aarch64.swpa64(ptr %p, i64 %v)
   ret i64 %r
 }
+
+define i32 @test_swpl8(ptr %p, i32 %v) {
+; CHECK-LABEL: test_swpl8:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    swplb w1, w0, [x0]
+; CHECK-NEXT:    ret
+  %r = call i32 @llvm.aarch64.swpl8(ptr %p, i32 %v)
+  ret i32 %r
+}
+
+define i32 @test_swpl16(ptr %p, i32 %v) {
+; CHECK-LABEL: test_swpl16:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    swplh w1, w0, [x0]
+; CHECK-NEXT:    ret
+  %r = call i32 @llvm.aarch64.swpl16(ptr %p, i32 %v)
+  ret i32 %r
+}
+
+define i32 @test_swpl32(ptr %p, i32 %v) {
+; CHECK-LABEL: test_swpl32:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    swpl w1, w0, [x0]
+; CHECK-NEXT:    ret
+  %r = call i32 @llvm.aarch64.swpl32(ptr %p, i32 %v)
+  ret i32 %r
+}
+
+define i64 @test_swpl64(ptr %p, i64 %v) {
+; CHECK-LABEL: test_swpl64:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    swpl x1, x0, [x0]
+; CHECK-NEXT:    ret
+  %r = call i64 @llvm.aarch64.swpl64(ptr %p, i64 %v)
+  ret i64 %r
+}

>From afd13118eb40dcec9cc3bb3c078ec02724fe9ac9 Mon Sep 17 00:00:00 2001
From: Adhemerval Zanella <zatrazz at gmail.com>
Date: Fri, 17 Apr 2026 13:20:15 -0300
Subject: [PATCH 12/17] [aarch64] Add support for the __swpal{8|16|32|64} MS
 intrinsics

Adds support for the following MSVC intrinsics:
* `__swpal8`  - maps to SWPALB.
* `__swpal16` - maps to SWPALH.
* `__swpal32` - maps to SWPAL.
* `__swpal64` - maps to SWPAL.

The emit is done using new intrisincs to issue SWPAL* instruction
directly, regardless of LSE target feature.  This mimics MSVC compiler
and the idea of the builtin to mimic an inline asm.

These are documented at:
<https://learn.microsoft.com/en-us/cpp/intrinsics/arm64-intrinsics?view=msvc-180>
---
 clang/include/clang/Basic/BuiltinsAArch64.td  |  4 ++
 clang/lib/CodeGen/TargetBuiltins/ARM.cpp      | 20 +++++++-
 clang/lib/Headers/arm64intr.h                 |  5 ++
 .../test/CodeGen/arm64-microsoft-intrinsics.c | 50 +++++++++++++++++++
 llvm/include/llvm/IR/IntrinsicsAArch64.td     | 15 ++++++
 .../lib/Target/AArch64/AArch64InstrAtomics.td | 14 ++++++
 llvm/lib/Target/AArch64/AArch64InstrInfo.td   |  1 +
 .../test/CodeGen/AArch64/ms-intrinsics-swp.ll | 36 +++++++++++++
 8 files changed, 144 insertions(+), 1 deletion(-)

diff --git a/clang/include/clang/Basic/BuiltinsAArch64.td b/clang/include/clang/Basic/BuiltinsAArch64.td
index d27e34edf0355..791505085c6d3 100644
--- a/clang/include/clang/Basic/BuiltinsAArch64.td
+++ b/clang/include/clang/Basic/BuiltinsAArch64.td
@@ -449,4 +449,8 @@ let Attributes = [NoThrow, RequireDeclaration], Languages = "ALL_MS_LANGUAGES",
 	def __swpl16 : AArch64NoPrefixTargetLibBuiltin<"unsigned short (unsigned short volatile *, unsigned short)">;
 	def __swpl32 : AArch64NoPrefixTargetLibBuiltin<"unsigned int (unsigned int volatile *, unsigned int)">;
 	def __swpl64 : AArch64NoPrefixTargetLibBuiltin<"unsigned long long int (unsigned long long int volatile *, unsigned long long int)">;
+	def __swpal8 : AArch64NoPrefixTargetLibBuiltin<"unsigned char (unsigned char volatile *, unsigned char)">;
+	def __swpal16 : AArch64NoPrefixTargetLibBuiltin<"unsigned short (unsigned short volatile *, unsigned short)">;
+	def __swpal32 : AArch64NoPrefixTargetLibBuiltin<"unsigned int (unsigned int volatile *, unsigned int)">;
+	def __swpal64 : AArch64NoPrefixTargetLibBuiltin<"unsigned long long int (unsigned long long int volatile *, unsigned long long int)">;
 }
diff --git a/clang/lib/CodeGen/TargetBuiltins/ARM.cpp b/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
index 18fe455d57734..6c0345e71ba11 100644
--- a/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
+++ b/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
@@ -5397,7 +5397,9 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID,
       BuiltinID == AArch64::BI__swpa8 || BuiltinID == AArch64::BI__swpa16 ||
       BuiltinID == AArch64::BI__swpa32 || BuiltinID == AArch64::BI__swpa64 ||
       BuiltinID == AArch64::BI__swpl8 || BuiltinID == AArch64::BI__swpl16 ||
-      BuiltinID == AArch64::BI__swpl32 || BuiltinID == AArch64::BI__swpl64) {
+      BuiltinID == AArch64::BI__swpl32 || BuiltinID == AArch64::BI__swpl64 ||
+      BuiltinID == AArch64::BI__swpal8 || BuiltinID == AArch64::BI__swpal16 ||
+      BuiltinID == AArch64::BI__swpal32 || BuiltinID == AArch64::BI__swpal64) {
     unsigned IntrID;
     llvm::Type *IntrArgTy;
     switch (BuiltinID) {
@@ -5449,6 +5451,22 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID,
       IntrID = Intrinsic::aarch64_swpl64;
       IntrArgTy = Builder.getInt64Ty();
       break;
+    case AArch64::BI__swpal8:
+      IntrID = Intrinsic::aarch64_swpal8;
+      IntrArgTy = Builder.getInt32Ty();
+      break;
+    case AArch64::BI__swpal16:
+      IntrID = Intrinsic::aarch64_swpal16;
+      IntrArgTy = Builder.getInt32Ty();
+      break;
+    case AArch64::BI__swpal32:
+      IntrID = Intrinsic::aarch64_swpal32;
+      IntrArgTy = Builder.getInt32Ty();
+      break;
+    case AArch64::BI__swpal64:
+      IntrID = Intrinsic::aarch64_swpal64;
+      IntrArgTy = Builder.getInt64Ty();
+      break;
     default:
       llvm_unreachable("missing builtin ID in switch!");
     }
diff --git a/clang/lib/Headers/arm64intr.h b/clang/lib/Headers/arm64intr.h
index 1c7ab0b544b9c..58945148bea7b 100644
--- a/clang/lib/Headers/arm64intr.h
+++ b/clang/lib/Headers/arm64intr.h
@@ -101,6 +101,11 @@ unsigned __int16 __swpl16(unsigned __int16 volatile *, unsigned __int16);
 unsigned __int32 __swpl32(unsigned __int32 volatile *, unsigned __int32);
 unsigned __int64 __swpl64(unsigned __int64 volatile *, unsigned __int64);
 
+unsigned __int8 __swpal8(unsigned __int8 volatile *, unsigned __int8);
+unsigned __int16 __swpal16(unsigned __int16 volatile *, unsigned __int16);
+unsigned __int32 __swpal32(unsigned __int32 volatile *, unsigned __int32);
+unsigned __int64 __swpal64(unsigned __int64 volatile *, unsigned __int64);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/clang/test/CodeGen/arm64-microsoft-intrinsics.c b/clang/test/CodeGen/arm64-microsoft-intrinsics.c
index 08d96aa5db442..56fdf5f2b185c 100644
--- a/clang/test/CodeGen/arm64-microsoft-intrinsics.c
+++ b/clang/test/CodeGen/arm64-microsoft-intrinsics.c
@@ -1102,5 +1102,55 @@ unsigned long long int test__swpl64(unsigned long long int volatile* t,
 // CHECK-MSCOMPAT:       ret i64 %[[RET]]
 // CHECK-LINUX: error: call to undeclared function '__swpl64'
 
+unsigned char test__swpal8(unsigned char volatile* t, unsigned char v)
+{
+  return __swpal8(t, v);
+}
+// CHECK-MSCOMPAT-LABEL: define{{.*}}i8 @test__swpal8(ptr{{.*}}%t, i8{{.*}}%v){{.*}}{
+// CHECK-MSCOMPAT:       %[[TMPT:[0-9]+]] = load ptr, ptr %t.addr, align 8
+// CHECK-MSCOMPAT:       %[[TMPV:[0-9]+]] = load i8, ptr %v.addr, align 1
+// CHECK-MSCOMPAT:       %[[ZEXTV:[0-9]+]] = zext i8 %[[TMPV]] to i32
+// CHECK-MSCOMPAT:       %[[RET:[0-9]+]] = call i32 @llvm.aarch64.swpal8(ptr %[[TMPT]], i32 %[[ZEXTV]])
+// CHECK-MSCOMPAT:       %[[TRUNC:[0-9]+]] = trunc i32 %[[RET]] to i8
+// CHECK-MSCOMPAT:       ret i8 %[[TRUNC]]
+// CHECK-LINUX: error: call to undeclared function '__swpal8'
+
+unsigned short test__swpal16(unsigned short volatile* t, unsigned short v)
+{
+  return __swpal16(t, v);
+}
+// CHECK-MSCOMPAT-LABEL: define{{.*}}i16 @test__swpal16(ptr{{.*}}%t, i16{{.*}}%v){{.*}}{
+// CHECK-MSCOMPAT:       %[[TMPT:[0-9]+]] = load ptr, ptr %t.addr, align 8
+// CHECK-MSCOMPAT:       %[[TMPV:[0-9]+]] = load i16, ptr %v.addr, align 2
+// CHECK-MSCOMPAT:       %[[ZEXTV:[0-9]+]] = zext i16 %[[TMPV]] to i32
+// CHECK-MSCOMPAT:       %[[RET:[0-9]+]] = call i32 @llvm.aarch64.swpal16(ptr %[[TMPT]], i32 %[[ZEXTV]])
+// CHECK-MSCOMPAT:       %[[TRUNC:[0-9]+]] = trunc i32 %[[RET]] to i16
+// CHECK-MSCOMPAT:       ret i16 %[[TRUNC]]
+// CHECK-LINUX: error: call to undeclared function '__swpal16'
+
+unsigned int test__swpal32(unsigned int volatile* t, unsigned int v)
+{
+  return __swpal32(t, v);
+}
+// CHECK-MSCOMPAT-LABEL: define{{.*}}i32 @test__swpal32(ptr{{.*}}%t, i32{{.*}}%v){{.*}}{
+// CHECK-MSCOMPAT:       %[[TMPT:[0-9]+]] = load ptr, ptr %t.addr, align 8
+// CHECK-MSCOMPAT:       %[[TMPV:[0-9]+]] = load i32, ptr %v.addr, align 4
+// CHECK-MSCOMPAT:       %[[RET:[0-9]+]] = call i32 @llvm.aarch64.swpal32(ptr %[[TMPT]], i32 %[[TMPV]])
+// CHECK-MSCOMPAT:       ret i32 %[[RET]]
+// CHECK-LINUX: error: call to undeclared function '__swpal32'
+
+unsigned long long int test__swpal64(unsigned long long int volatile* t,
+                                     unsigned long long int v)
+{
+  return __swpal64(t, v);
+}
+// CHECK-MSCOMPAT-LABEL: define{{.*}}i64 @test__swpal64(ptr{{.*}}%t, i64{{.*}}%v){{.*}}{
+// CHECK-MSCOMPAT:       %[[TMPT:[0-9]+]] = load ptr, ptr %t.addr, align 8
+// CHECK-MSCOMPAT:       %[[TMPV:[0-9]+]] = load i64, ptr %v.addr, align 8
+// CHECK-MSCOMPAT:       %[[RET:[0-9]+]] = call i64 @llvm.aarch64.swpal64(ptr %[[TMPT]], i64 %[[TMPV]])
+// CHECK-MSCOMPAT:       ret i64 %[[RET]]
+// CHECK-LINUX: error: call to undeclared function '__swpal64'
+
+
 // CHECK-MSCOMPAT: ![[MD2]] = !{!"x18"}
 // CHECK-MSCOMPAT: ![[MD3]] = !{!"sp"}
diff --git a/llvm/include/llvm/IR/IntrinsicsAArch64.td b/llvm/include/llvm/IR/IntrinsicsAArch64.td
index a17bad3b53570..57b983799a822 100644
--- a/llvm/include/llvm/IR/IntrinsicsAArch64.td
+++ b/llvm/include/llvm/IR/IntrinsicsAArch64.td
@@ -152,6 +152,21 @@ def int_aarch64_swp64 : Intrinsic<[llvm_i64_ty],
                                   [llvm_ptr_ty, llvm_i64_ty],
                                   [IntrNoFree, IntrWillReturn]>;
 
+// SWP intrinsics — emit SWP* (no acquire/release) directly, regardless of the
+// LSE target feature. swp{b,h} value uses i32; swp64 uses i64.
+def int_aarch64_swpal8  : Intrinsic<[llvm_i32_ty],
+                                    [llvm_ptr_ty, llvm_i32_ty],
+                                    [IntrNoFree, IntrWillReturn]>;
+def int_aarch64_swpal16 : Intrinsic<[llvm_i32_ty],
+                                    [llvm_ptr_ty, llvm_i32_ty],
+                                    [IntrNoFree, IntrWillReturn]>;
+def int_aarch64_swpal32 : Intrinsic<[llvm_i32_ty],
+                                    [llvm_ptr_ty, llvm_i32_ty],
+                                    [IntrNoFree, IntrWillReturn]>;
+def int_aarch64_swpal64 : Intrinsic<[llvm_i64_ty],
+                                    [llvm_ptr_ty, llvm_i64_ty],
+                                    [IntrNoFree, IntrWillReturn]>;
+
 def int_aarch64_sdiv : DefaultAttrsIntrinsic<[llvm_anyint_ty], [LLVMMatchType<0>,
                                 LLVMMatchType<0>], [IntrNoMem]>;
 def int_aarch64_udiv : DefaultAttrsIntrinsic<[llvm_anyint_ty], [LLVMMatchType<0>,
diff --git a/llvm/lib/Target/AArch64/AArch64InstrAtomics.td b/llvm/lib/Target/AArch64/AArch64InstrAtomics.td
index 6dd48878500cd..6b13d426dc032 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrAtomics.td
+++ b/llvm/lib/Target/AArch64/AArch64InstrAtomics.td
@@ -274,6 +274,20 @@ def : Pat<(i32 (int_aarch64_swpl32 GPR64sp:$Rn, GPR32:$Rs)),
 def : Pat<(i64 (int_aarch64_swpl64 GPR64sp:$Rn, GPR64:$Rs)),
           (SWPLX_cg GPR64:$Rs, GPR64sp:$Rn)>;
 
+// Similar as __swp*, but for __swaal*,
+def : Pat<(i32 (int_aarch64_swpal8  GPR64sp:$Rn, (and GPR32:$Rs, (i32 255)))),
+          (SWPALB_cg GPR32:$Rs, GPR64sp:$Rn)>;
+def : Pat<(i32 (int_aarch64_swpal8  GPR64sp:$Rn, GPR32:$Rs)),
+          (SWPALB_cg GPR32:$Rs, GPR64sp:$Rn)>;
+def : Pat<(i32 (int_aarch64_swpal16 GPR64sp:$Rn, (and GPR32:$Rs, (i32 65535)))),
+          (SWPALH_cg GPR32:$Rs, GPR64sp:$Rn)>;
+def : Pat<(i32 (int_aarch64_swpal16 GPR64sp:$Rn, GPR32:$Rs)),
+          (SWPALH_cg GPR32:$Rs, GPR64sp:$Rn)>;
+def : Pat<(i32 (int_aarch64_swpal32 GPR64sp:$Rn, GPR32:$Rs)),
+          (SWPALW_cg GPR32:$Rs, GPR64sp:$Rn)>;
+def : Pat<(i64 (int_aarch64_swpal64 GPR64sp:$Rn, GPR64:$Rs)),
+          (SWPALX_cg GPR64:$Rs, GPR64sp:$Rn)>;
+
 //===----------------------------------
 // Atomic stores
 //===----------------------------------
diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.td b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
index f5dcd4dd9389b..18ba9825f7aa1 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrInfo.td
+++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
@@ -3075,6 +3075,7 @@ let isCodeGenOnly = 1 in {
   defm SWP  : Swap_cg<0, 0, "">;
   defm SWPA : Swap_cg<1, 0, "a">;
   defm SWPL : Swap_cg<0, 1, "l">;
+  defm SWPAL : Swap_cg<1, 1, "al">;
 }
 
 // v9.6a atomic swap (FEAT_LSUI)
diff --git a/llvm/test/CodeGen/AArch64/ms-intrinsics-swp.ll b/llvm/test/CodeGen/AArch64/ms-intrinsics-swp.ll
index b8f613ae1a00f..7dee3955f33ca 100644
--- a/llvm/test/CodeGen/AArch64/ms-intrinsics-swp.ll
+++ b/llvm/test/CodeGen/AArch64/ms-intrinsics-swp.ll
@@ -116,3 +116,39 @@ define i64 @test_swpl64(ptr %p, i64 %v) {
   %r = call i64 @llvm.aarch64.swpl64(ptr %p, i64 %v)
   ret i64 %r
 }
+
+define i32 @test_swpal8(ptr %p, i32 %v) {
+; CHECK-LABEL: test_swpal8:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    swpalb w1, w0, [x0]
+; CHECK-NEXT:    ret
+  %r = call i32 @llvm.aarch64.swpal8(ptr %p, i32 %v)
+  ret i32 %r
+}
+
+define i32 @test_swpal16(ptr %p, i32 %v) {
+; CHECK-LABEL: test_swpal16:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    swpalh w1, w0, [x0]
+; CHECK-NEXT:    ret
+  %r = call i32 @llvm.aarch64.swpal16(ptr %p, i32 %v)
+  ret i32 %r
+}
+
+define i32 @test_swpal32(ptr %p, i32 %v) {
+; CHECK-LABEL: test_swpal32:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    swpal w1, w0, [x0]
+; CHECK-NEXT:    ret
+  %r = call i32 @llvm.aarch64.swpal32(ptr %p, i32 %v)
+  ret i32 %r
+}
+
+define i64 @test_swpal64(ptr %p, i64 %v) {
+; CHECK-LABEL: test_swpal64:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    swpal x1, x0, [x0]
+; CHECK-NEXT:    ret
+  %r = call i64 @llvm.aarch64.swpal64(ptr %p, i64 %v)
+  ret i64 %r
+}

>From a76963cd8ea3c4cf7670e88c5d8fdfeff2f3a3ab Mon Sep 17 00:00:00 2001
From: Adhemerval Zanella <zatrazz at gmail.com>
Date: Sat, 18 Apr 2026 08:37:49 -0300
Subject: [PATCH 13/17] [aarch64] Add missing _ReadStatusReg/_WriteStatusReg on
 intrin.h

Add ARM64_SYSREG, ARM64_FPCR, and ARM64_FPSR definitions.  These
are provided by MSVC as a way to encode a register to be used
along the _ReadStatusReg/_WriteStatusReg intrisincs.

The ARM64_SYSREG works with other ARM64_* registers defined in other
MSVC headers (like windows.h).
---
 clang/lib/Headers/arm64intr.h                  | 10 ++++++++++
 .../test/CodeGen/arm64-microsoft-intrinsics.c  | 18 ++++++++++++++++++
 2 files changed, 28 insertions(+)

diff --git a/clang/lib/Headers/arm64intr.h b/clang/lib/Headers/arm64intr.h
index 58945148bea7b..8dba77f3b428f 100644
--- a/clang/lib/Headers/arm64intr.h
+++ b/clang/lib/Headers/arm64intr.h
@@ -15,6 +15,16 @@
 #ifndef __ARM64INTR_H
 #define __ARM64INTR_H
 
+/* Encode an AArch64 system register for use with
+   _ReadStatusReg/_WriteStatusReg. op0 must be 2 or 3; only the low bit is
+   stored. */
+#define ARM64_SYSREG(op0, op1, CRn, CRm, op2)                                  \
+  ((((op0) & 0x1) << 14) | (((op1) & 0x7) << 11) | (((CRn) & 0xF) << 7) |      \
+   (((CRm) & 0xF) << 3) | ((op2) & 0x7))
+
+#define ARM64_FPCR ARM64_SYSREG(3, 3, 4, 4, 0)
+#define ARM64_FPSR ARM64_SYSREG(3, 3, 4, 4, 1)
+
 typedef enum
 {
   _ARM64_BARRIER_SY    = 0xF,
diff --git a/clang/test/CodeGen/arm64-microsoft-intrinsics.c b/clang/test/CodeGen/arm64-microsoft-intrinsics.c
index 56fdf5f2b185c..8bbcd6e77e556 100644
--- a/clang/test/CodeGen/arm64-microsoft-intrinsics.c
+++ b/clang/test/CodeGen/arm64-microsoft-intrinsics.c
@@ -1151,6 +1151,24 @@ unsigned long long int test__swpal64(unsigned long long int volatile* t,
 // CHECK-MSCOMPAT:       ret i64 %[[RET]]
 // CHECK-LINUX: error: call to undeclared function '__swpal64'
 
+__int64 test__ReadStatusReg(void)
+{
+  return _ReadStatusReg(0x5A10);
+}
+// CHECK-MSCOMPAT-LABEL: define{{.*}}i64 @test__ReadStatusReg(){{.*}}{
+// CHECK-MSCOMPAT:       %[[RET:[0-9]+]] = call i64 @llvm.read_register.i64(metadata ![[NZCV:.*]])
+// CHECK-MSCOMPAT:       ret i64 %[[RET]]
+// CHECK-LINUX: error: call to undeclared function '_ReadStatusReg'
+
+void test__WriteStatusReg(__int64 v)
+{
+  _WriteStatusReg(0x5A10, v);
+}
+// CHECK-MSCOMPAT-LABEL: define{{.*}}void @test__WriteStatusReg(i64{{.*}}%v){{.*}}{
+// CHECK-MSCOMPAT:       %[[TMPV:[0-9]+]] = load i64, ptr %v.addr, align 8
+// CHECK-MSCOMPAT:       call void @llvm.write_register.i64(metadata ![[NZCV]], i64 %[[TMPV]])
+// CHECK-LINUX: error: call to undeclared function '_WriteStatusReg'
 
 // CHECK-MSCOMPAT: ![[MD2]] = !{!"x18"}
 // CHECK-MSCOMPAT: ![[MD3]] = !{!"sp"}
+// CHECK-MSCOMPAT: ![[NZCV]] = !{!"3:3:4:2:0"}

>From c7fc4beb19d1cd25ae38104a215ae2a22cfb0a4d Mon Sep 17 00:00:00 2001
From: Adhemerval Zanella <zatrazz at gmail.com>
Date: Sat, 18 Apr 2026 09:13:27 -0300
Subject: [PATCH 14/17] [aarch64] Add support for the __setReg MS intrinsics

The builtin write a 64-bit value directly into a specific hardware
processor register, identified by an integer index.

It is ARM64 specific and it is documented at:
<https://learn.microsoft.com/en-us/cpp/intrinsics/arm64-intrinsics?view=msvc-180>
---
 clang/include/clang/Basic/BuiltinsAArch64.td    |  1 +
 clang/lib/CodeGen/TargetBuiltins/ARM.cpp        | 16 ++++++++++++----
 clang/lib/Headers/intrin.h                      |  1 +
 clang/lib/Sema/SemaARM.cpp                      |  2 +-
 clang/test/CodeGen/arm64-microsoft-intrinsics.c | 10 ++++++++++
 5 files changed, 25 insertions(+), 5 deletions(-)

diff --git a/clang/include/clang/Basic/BuiltinsAArch64.td b/clang/include/clang/Basic/BuiltinsAArch64.td
index 791505085c6d3..3a4add71011dd 100644
--- a/clang/include/clang/Basic/BuiltinsAArch64.td
+++ b/clang/include/clang/Basic/BuiltinsAArch64.td
@@ -335,6 +335,7 @@ let Attributes = [NoThrow, RequireDeclaration], Languages = "ALL_MS_LANGUAGES",
 let Attributes = [NoThrow, RequireDeclaration], Languages = "ALL_MS_LANGUAGES", Header = "intrin.h" in {
 	def _ReadWriteBarrier       : AArch64NoPrefixTargetLibBuiltin<"void ()">;
 	def __getReg                : AArch64NoPrefixTargetLibBuiltin<"unsigned long long int (int)">;
+	def __setReg                : AArch64NoPrefixTargetLibBuiltin<"void (int, unsigned long long int)">;
 	def _ReadStatusReg          : AArch64NoPrefixTargetLibBuiltin<"long long int (int)">;
 	def _WriteStatusReg         : AArch64NoPrefixTargetLibBuiltin<"void (int, long long int)">;
 	def __sys                   : AArch64NoPrefixTargetLibBuiltin<"unsigned int (int, long long int)">;
diff --git a/clang/lib/CodeGen/TargetBuiltins/ARM.cpp b/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
index 6c0345e71ba11..d3f1cf37191dc 100644
--- a/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
+++ b/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
@@ -4787,7 +4787,8 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID,
     return CI;
   }
 
-  if (BuiltinID == clang::AArch64::BI__getReg) {
+  if (BuiltinID == clang::AArch64::BI__getReg ||
+      BuiltinID == clang::AArch64::BI__setReg) {
     Expr::EvalResult Result;
     if (!E->getArg(0)->EvaluateAsInt(Result, CGM.getContext()))
       llvm_unreachable("Sema will ensure that the parameter is constant");
@@ -4800,9 +4801,16 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID,
     llvm::MDNode *RegName = llvm::MDNode::get(Context, Ops);
     llvm::Value *Metadata = llvm::MetadataAsValue::get(Context, RegName);
 
-    llvm::Function *F =
-        CGM.getIntrinsic(Intrinsic::read_register, {Int64Ty});
-    return Builder.CreateCall(F, Metadata);
+    CallInst *CI;
+    if (BuiltinID == clang::AArch64::BI__getReg) {
+      llvm::Function *F = CGM.getIntrinsic(Intrinsic::read_register, {Int64Ty});
+      CI = Builder.CreateCall(F, Metadata);
+    } else {
+      llvm::Function *F =
+          CGM.getIntrinsic(Intrinsic::write_register, {Int64Ty});
+      CI = Builder.CreateCall(F, {Metadata, EmitScalarExpr(E->getArg(1))});
+    }
+    return CI;
   }
 
   if (BuiltinID == clang::AArch64::BI__break) {
diff --git a/clang/lib/Headers/intrin.h b/clang/lib/Headers/intrin.h
index 7b5698763d1ca..b0c82acf927fb 100644
--- a/clang/lib/Headers/intrin.h
+++ b/clang/lib/Headers/intrin.h
@@ -374,6 +374,7 @@ static __inline__ void __DEFAULT_FN_ATTRS __nop(void) {
 \*----------------------------------------------------------------------------*/
 #if defined(__aarch64__) || defined(__arm64ec__)
 unsigned __int64 __getReg(int);
+void __setReg(int, unsigned __int64);
 unsigned char _interlockedbittestandreset_acq(long volatile *, long);
 unsigned char _interlockedbittestandreset_nf(long volatile *, long);
 unsigned char _interlockedbittestandreset_rel(long volatile *, long);
diff --git a/clang/lib/Sema/SemaARM.cpp b/clang/lib/Sema/SemaARM.cpp
index 7871fd624a467..f69375f3b3edd 100644
--- a/clang/lib/Sema/SemaARM.cpp
+++ b/clang/lib/Sema/SemaARM.cpp
@@ -1169,7 +1169,7 @@ bool SemaARM::CheckAArch64BuiltinFunctionCall(const TargetInfo &TI,
   if (BuiltinID == AArch64::BI__sys)
     return SemaRef.BuiltinConstantArgRange(TheCall, 0, 0, 0x3fff);
 
-  if (BuiltinID == AArch64::BI__getReg)
+  if (BuiltinID == AArch64::BI__getReg || BuiltinID == AArch64::BI__setReg)
     return SemaRef.BuiltinConstantArgRange(TheCall, 0, 0, 31);
 
   if (BuiltinID == AArch64::BI__break)
diff --git a/clang/test/CodeGen/arm64-microsoft-intrinsics.c b/clang/test/CodeGen/arm64-microsoft-intrinsics.c
index 8bbcd6e77e556..9158ad466d34b 100644
--- a/clang/test/CodeGen/arm64-microsoft-intrinsics.c
+++ b/clang/test/CodeGen/arm64-microsoft-intrinsics.c
@@ -1169,6 +1169,16 @@ void test__WriteStatusReg(__int64 v)
 // CHECK-MSCOMPAT:       call void @llvm.write_register.i64(metadata ![[NZCV]], i64 %[[TMPV]])
 // CHECK-LINUX: error: call to undeclared function '_WriteStatusReg'
 
+void test__setReg(unsigned __int64 v)
+{
+  __setReg(18, v);
+  __setReg(31, v);
+}
+// CHECK-MSCOMPAT-LABEL: define{{.*}}void @test__setReg(i64{{.*}}%v){{.*}}{
+// CHECK-MSCOMPAT:       call void @llvm.write_register.i64(metadata ![[MD2]], i64
+// CHECK-MSCOMPAT:       call void @llvm.write_register.i64(metadata ![[MD3]], i64
+// CHECK-LINUX: error: call to undeclared function '__setReg'
+
 // CHECK-MSCOMPAT: ![[MD2]] = !{!"x18"}
 // CHECK-MSCOMPAT: ![[MD3]] = !{!"sp"}
 // CHECK-MSCOMPAT: ![[NZCV]] = !{!"3:3:4:2:0"}

>From 4b6f139a90bd8b74c191acaf3083b7ee02346612 Mon Sep 17 00:00:00 2001
From: Adhemerval Zanella <zatrazz at gmail.com>
Date: Sun, 19 Apr 2026 09:41:12 -0300
Subject: [PATCH 15/17] [aarch64] Add support for the __getRegFp MS intrinsicsA

The builtin reads from a hardware floating-point register using an integer index.

It is ARM64 specific and it is documented at:
<https://learn.microsoft.com/en-us/cpp/intrinsics/arm64-intrinsics?view=msvc-180>
---
 clang/include/clang/Basic/BuiltinsAArch64.td   |  1 +
 clang/lib/CodeGen/TargetBuiltins/ARM.cpp       | 18 ++++++++++++++++++
 clang/lib/Headers/intrin.h                     |  1 +
 clang/lib/Sema/SemaARM.cpp                     |  3 ++-
 .../test/CodeGen/arm64-microsoft-intrinsics.c  | 10 ++++++++++
 5 files changed, 32 insertions(+), 1 deletion(-)

diff --git a/clang/include/clang/Basic/BuiltinsAArch64.td b/clang/include/clang/Basic/BuiltinsAArch64.td
index 3a4add71011dd..880f8384200c7 100644
--- a/clang/include/clang/Basic/BuiltinsAArch64.td
+++ b/clang/include/clang/Basic/BuiltinsAArch64.td
@@ -335,6 +335,7 @@ let Attributes = [NoThrow, RequireDeclaration], Languages = "ALL_MS_LANGUAGES",
 let Attributes = [NoThrow, RequireDeclaration], Languages = "ALL_MS_LANGUAGES", Header = "intrin.h" in {
 	def _ReadWriteBarrier       : AArch64NoPrefixTargetLibBuiltin<"void ()">;
 	def __getReg                : AArch64NoPrefixTargetLibBuiltin<"unsigned long long int (int)">;
+	def __getRegFp              : AArch64NoPrefixTargetLibBuiltin<"double (int)">;
 	def __setReg                : AArch64NoPrefixTargetLibBuiltin<"void (int, unsigned long long int)">;
 	def _ReadStatusReg          : AArch64NoPrefixTargetLibBuiltin<"long long int (int)">;
 	def _WriteStatusReg         : AArch64NoPrefixTargetLibBuiltin<"void (int, long long int)">;
diff --git a/clang/lib/CodeGen/TargetBuiltins/ARM.cpp b/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
index d3f1cf37191dc..0dc9a1490fd26 100644
--- a/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
+++ b/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
@@ -4813,6 +4813,24 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID,
     return CI;
   }
 
+  if (BuiltinID == clang::AArch64::BI__getRegFp) {
+    Expr::EvalResult Result;
+    if (!E->getArg(0)->EvaluateAsInt(Result, CGM.getContext()))
+      llvm_unreachable("Sema will ensure that the parameter is constant");
+
+    llvm::APSInt Value = Result.Val.getInt();
+    LLVMContext &Context = CGM.getLLVMContext();
+    std::string Reg = "d" + toString(Value, 10);
+
+    llvm::Metadata *Ops[] = {llvm::MDString::get(Context, Reg)};
+    llvm::MDNode *RegName = llvm::MDNode::get(Context, Ops);
+    llvm::Value *Metadata = llvm::MetadataAsValue::get(Context, RegName);
+
+    llvm::Function *F = CGM.getIntrinsic(Intrinsic::read_register, {Int64Ty});
+    llvm::Value *Bits = Builder.CreateCall(F, Metadata);
+    return Builder.CreateBitCast(Bits, llvm::Type::getDoubleTy(Context));
+  }
+
   if (BuiltinID == clang::AArch64::BI__break) {
     Expr::EvalResult Result;
     if (!E->getArg(0)->EvaluateAsInt(Result, CGM.getContext()))
diff --git a/clang/lib/Headers/intrin.h b/clang/lib/Headers/intrin.h
index b0c82acf927fb..040dd0b734a18 100644
--- a/clang/lib/Headers/intrin.h
+++ b/clang/lib/Headers/intrin.h
@@ -374,6 +374,7 @@ static __inline__ void __DEFAULT_FN_ATTRS __nop(void) {
 \*----------------------------------------------------------------------------*/
 #if defined(__aarch64__) || defined(__arm64ec__)
 unsigned __int64 __getReg(int);
+double __getRegFp(int _Reg);
 void __setReg(int, unsigned __int64);
 unsigned char _interlockedbittestandreset_acq(long volatile *, long);
 unsigned char _interlockedbittestandreset_nf(long volatile *, long);
diff --git a/clang/lib/Sema/SemaARM.cpp b/clang/lib/Sema/SemaARM.cpp
index f69375f3b3edd..3a8a17c11bfb2 100644
--- a/clang/lib/Sema/SemaARM.cpp
+++ b/clang/lib/Sema/SemaARM.cpp
@@ -1169,7 +1169,8 @@ bool SemaARM::CheckAArch64BuiltinFunctionCall(const TargetInfo &TI,
   if (BuiltinID == AArch64::BI__sys)
     return SemaRef.BuiltinConstantArgRange(TheCall, 0, 0, 0x3fff);
 
-  if (BuiltinID == AArch64::BI__getReg || BuiltinID == AArch64::BI__setReg)
+  if (BuiltinID == AArch64::BI__getReg || BuiltinID == AArch64::BI__setReg ||
+      BuiltinID == AArch64::BI__getRegFp)
     return SemaRef.BuiltinConstantArgRange(TheCall, 0, 0, 31);
 
   if (BuiltinID == AArch64::BI__break)
diff --git a/clang/test/CodeGen/arm64-microsoft-intrinsics.c b/clang/test/CodeGen/arm64-microsoft-intrinsics.c
index 9158ad466d34b..f449b393a18b2 100644
--- a/clang/test/CodeGen/arm64-microsoft-intrinsics.c
+++ b/clang/test/CodeGen/arm64-microsoft-intrinsics.c
@@ -1179,6 +1179,16 @@ void test__setReg(unsigned __int64 v)
 // CHECK-MSCOMPAT:       call void @llvm.write_register.i64(metadata ![[MD3]], i64
 // CHECK-LINUX: error: call to undeclared function '__setReg'
 
+double test__getRegFp(void)
+{
+  return __getRegFp(5);
+}
+// CHECK-MSCOMPAT-LABEL: define{{.*}}double @test__getRegFp(){{.*}}{
+// CHECK-MSCOMPAT:       [[BITS:%.*]] = call i64 @llvm.read_register.i64(metadata ![[MD4:.*]])
+// CHECK-MSCOMPAT:       bitcast i64 [[BITS]] to double
+// CHECK-LINUX: error: call to undeclared function '__getRegFp'
+
 // CHECK-MSCOMPAT: ![[MD2]] = !{!"x18"}
 // CHECK-MSCOMPAT: ![[MD3]] = !{!"sp"}
 // CHECK-MSCOMPAT: ![[NZCV]] = !{!"3:3:4:2:0"}
+// CHECK-MSCOMPAT: ![[MD4]] = !{!"d5"}

>From b299c0c3740d2b0e758c96f732877efeffc8c2b4 Mon Sep 17 00:00:00 2001
From: Adhemerval Zanella <zatrazz at gmail.com>
Date: Sun, 19 Apr 2026 10:03:37 -0300
Subject: [PATCH 16/17] [aarch64] Add support for the __setRegFp MS intrinsics

The builtin writes to a hardware floating-point register using an integer index.

It is ARM64 specific and it is documented at:
<https://learn.microsoft.com/en-us/cpp/intrinsics/arm64-intrinsics?view=msvc-180>
---
 clang/include/clang/Basic/BuiltinsAArch64.td  |  1 +
 clang/lib/CodeGen/TargetBuiltins/ARM.cpp      | 19 +++++++++++++++----
 clang/lib/Headers/intrin.h                    |  1 +
 clang/lib/Sema/SemaARM.cpp                    |  2 +-
 .../test/CodeGen/arm64-microsoft-intrinsics.c |  9 +++++++++
 5 files changed, 27 insertions(+), 5 deletions(-)

diff --git a/clang/include/clang/Basic/BuiltinsAArch64.td b/clang/include/clang/Basic/BuiltinsAArch64.td
index 880f8384200c7..1cb57060fdeb4 100644
--- a/clang/include/clang/Basic/BuiltinsAArch64.td
+++ b/clang/include/clang/Basic/BuiltinsAArch64.td
@@ -336,6 +336,7 @@ let Attributes = [NoThrow, RequireDeclaration], Languages = "ALL_MS_LANGUAGES",
 	def _ReadWriteBarrier       : AArch64NoPrefixTargetLibBuiltin<"void ()">;
 	def __getReg                : AArch64NoPrefixTargetLibBuiltin<"unsigned long long int (int)">;
 	def __getRegFp              : AArch64NoPrefixTargetLibBuiltin<"double (int)">;
+	def __setRegFp              : AArch64NoPrefixTargetLibBuiltin<"void (int, double)">;
 	def __setReg                : AArch64NoPrefixTargetLibBuiltin<"void (int, unsigned long long int)">;
 	def _ReadStatusReg          : AArch64NoPrefixTargetLibBuiltin<"long long int (int)">;
 	def _WriteStatusReg         : AArch64NoPrefixTargetLibBuiltin<"void (int, long long int)">;
diff --git a/clang/lib/CodeGen/TargetBuiltins/ARM.cpp b/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
index 0dc9a1490fd26..14ded05172fd7 100644
--- a/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
+++ b/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
@@ -4813,7 +4813,8 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID,
     return CI;
   }
 
-  if (BuiltinID == clang::AArch64::BI__getRegFp) {
+  if (BuiltinID == clang::AArch64::BI__getRegFp ||
+      BuiltinID == clang::AArch64::BI__setRegFp) {
     Expr::EvalResult Result;
     if (!E->getArg(0)->EvaluateAsInt(Result, CGM.getContext()))
       llvm_unreachable("Sema will ensure that the parameter is constant");
@@ -4826,9 +4827,19 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID,
     llvm::MDNode *RegName = llvm::MDNode::get(Context, Ops);
     llvm::Value *Metadata = llvm::MetadataAsValue::get(Context, RegName);
 
-    llvm::Function *F = CGM.getIntrinsic(Intrinsic::read_register, {Int64Ty});
-    llvm::Value *Bits = Builder.CreateCall(F, Metadata);
-    return Builder.CreateBitCast(Bits, llvm::Type::getDoubleTy(Context));
+    llvm::Value *Ret;
+    if (BuiltinID == clang::AArch64::BI__getRegFp) {
+      llvm::Function *F = CGM.getIntrinsic(Intrinsic::read_register, {Int64Ty});
+      llvm::Value *Bits = Builder.CreateCall(F, Metadata);
+      Ret = Builder.CreateBitCast(Bits, llvm::Type::getDoubleTy(Context));
+    } else {
+      llvm::Value *Val = EmitScalarExpr(E->getArg(1));
+      llvm::Value *Bits = Builder.CreateBitCast(Val, Int64Ty);
+      llvm::Function *F =
+          CGM.getIntrinsic(Intrinsic::write_register, {Int64Ty});
+      Ret = Builder.CreateCall(F, {Metadata, Bits});
+    }
+    return Ret;
   }
 
   if (BuiltinID == clang::AArch64::BI__break) {
diff --git a/clang/lib/Headers/intrin.h b/clang/lib/Headers/intrin.h
index 040dd0b734a18..039846bafacf5 100644
--- a/clang/lib/Headers/intrin.h
+++ b/clang/lib/Headers/intrin.h
@@ -376,6 +376,7 @@ static __inline__ void __DEFAULT_FN_ATTRS __nop(void) {
 unsigned __int64 __getReg(int);
 double __getRegFp(int _Reg);
 void __setReg(int, unsigned __int64);
+void __setRegFp(int, double);
 unsigned char _interlockedbittestandreset_acq(long volatile *, long);
 unsigned char _interlockedbittestandreset_nf(long volatile *, long);
 unsigned char _interlockedbittestandreset_rel(long volatile *, long);
diff --git a/clang/lib/Sema/SemaARM.cpp b/clang/lib/Sema/SemaARM.cpp
index 3a8a17c11bfb2..d3bfd75223220 100644
--- a/clang/lib/Sema/SemaARM.cpp
+++ b/clang/lib/Sema/SemaARM.cpp
@@ -1170,7 +1170,7 @@ bool SemaARM::CheckAArch64BuiltinFunctionCall(const TargetInfo &TI,
     return SemaRef.BuiltinConstantArgRange(TheCall, 0, 0, 0x3fff);
 
   if (BuiltinID == AArch64::BI__getReg || BuiltinID == AArch64::BI__setReg ||
-      BuiltinID == AArch64::BI__getRegFp)
+      BuiltinID == AArch64::BI__getRegFp || BuiltinID == AArch64::BI__setRegFp)
     return SemaRef.BuiltinConstantArgRange(TheCall, 0, 0, 31);
 
   if (BuiltinID == AArch64::BI__break)
diff --git a/clang/test/CodeGen/arm64-microsoft-intrinsics.c b/clang/test/CodeGen/arm64-microsoft-intrinsics.c
index f449b393a18b2..bb2629dee9748 100644
--- a/clang/test/CodeGen/arm64-microsoft-intrinsics.c
+++ b/clang/test/CodeGen/arm64-microsoft-intrinsics.c
@@ -1188,6 +1188,15 @@ double test__getRegFp(void)
 // CHECK-MSCOMPAT:       bitcast i64 [[BITS]] to double
 // CHECK-LINUX: error: call to undeclared function '__getRegFp'
 
+void test__setRegFp(double v)
+{
+  __setRegFp(5, v);
+}
+// CHECK-MSCOMPAT-LABEL: define{{.*}}void @test__setRegFp(double{{.*}}%v){{.*}}{
+// CHECK-MSCOMPAT:       [[BITS:%.*]] = bitcast double {{.*}} to i64
+// CHECK-MSCOMPAT:       call void @llvm.write_register.i64(metadata ![[MD4:.*]], i64 [[BITS]])
+// CHECK-LINUX: error: call to undeclared function '__setRegFp'
+
 // CHECK-MSCOMPAT: ![[MD2]] = !{!"x18"}
 // CHECK-MSCOMPAT: ![[MD3]] = !{!"sp"}
 // CHECK-MSCOMPAT: ![[NZCV]] = !{!"3:3:4:2:0"}

>From 1a5e5edd13cd81ec5cde107883c1ca8f3479a077 Mon Sep 17 00:00:00 2001
From: Adhemerval Zanella <zatrazz at gmail.com>
Date: Mon, 20 Apr 2026 13:11:40 -0300
Subject: [PATCH 17/17] [aarch64] Add support for the __prefetch2 MS intrinsic

This is similar to already supported __prefetch builtin, with an extra 8-bit
argument that defines the prefetch operation hint.  The MSVC headers, nor
the documentation, seems to add any extra definitions on how to
construct the hint.

This s documented at:
<https://learn.microsoft.com/en-us/cpp/intrinsics/arm64-intrinsics?view=msvc-180>
---
 clang/include/clang/Basic/BuiltinsAArch64.td  |  1 +
 clang/lib/CodeGen/TargetBuiltins/ARM.cpp      | 20 +++++++++++++++++++
 clang/lib/Headers/intrin.h                    |  1 +
 clang/lib/Sema/SemaARM.cpp                    |  3 +++
 .../test/CodeGen/arm64-microsoft-intrinsics.c | 10 ++++++++++
 5 files changed, 35 insertions(+)

diff --git a/clang/include/clang/Basic/BuiltinsAArch64.td b/clang/include/clang/Basic/BuiltinsAArch64.td
index 1cb57060fdeb4..1ad0087702493 100644
--- a/clang/include/clang/Basic/BuiltinsAArch64.td
+++ b/clang/include/clang/Basic/BuiltinsAArch64.td
@@ -403,6 +403,7 @@ let Attributes = [NoThrow, RequireDeclaration], Languages = "ALL_MS_LANGUAGES",
 
 let Attributes = [NoThrow, RequireDeclaration], Languages = "ALL_MS_LANGUAGES", Header = "intrin.h" in {
 	def __prefetch : AArch64NoPrefixTargetLibBuiltin<"void (void const *)">;
+	def __prefetch2 : AArch64NoPrefixTargetLibBuiltin<"void (void const *, unsigned char)">;
 }
 
 let Attributes = [NoThrow, RequireDeclaration], Languages = "ALL_MS_LANGUAGES", Header = "intrin.h" in {
diff --git a/clang/lib/CodeGen/TargetBuiltins/ARM.cpp b/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
index 14ded05172fd7..2e412544e67c4 100644
--- a/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
+++ b/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
@@ -5270,6 +5270,26 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID,
     return Builder.CreateCall(F, {Address, RW, Locality, Data});
   }
 
+  if (BuiltinID == AArch64::BI__prefetch2) {
+    Value *Address = EmitScalarExpr(E->getArg(0));
+    llvm::APSInt PrfOp = E->getArg(1)->EvaluateKnownConstInt(CGM.getContext());
+    // Decode 5-bit PRFM encoding: bits[4:3]=type, bits[2:1]=target,
+    // bit[0]=policy
+    //   type: PLD=0(load), PLI=1(instr), PST=2(store)
+    //   target: L1=0, L2=1, L3=2
+    //   policy: KEEP=0, STRM=1
+    uint64_t Op = PrfOp.getZExtValue();
+    uint64_t Type = (Op >> 3) & 0x3;
+    uint64_t Target = (Op >> 1) & 0x3;
+    uint64_t Policy = Op & 0x1;
+    Value *RW = ConstantInt::get(Int32Ty, Type == 2 ? 1 : 0);
+    Value *Local = ConstantInt::get(Int32Ty, Target);
+    Value *Retain = ConstantInt::get(Int32Ty, Policy == 0 ? 1 : 0);
+    Value *IsData = ConstantInt::get(Int32Ty, Type == 1 ? 0 : 1);
+    Function *F = CGM.getIntrinsic(Intrinsic::aarch64_prefetch);
+    return Builder.CreateCall(F, {Address, RW, Local, Retain, IsData});
+  }
+
   if (BuiltinID == AArch64::BI__hlt) {
     Function *F = CGM.getIntrinsic(Intrinsic::aarch64_hlt);
     Builder.CreateCall(F, {EmitScalarExpr(E->getArg(0))});
diff --git a/clang/lib/Headers/intrin.h b/clang/lib/Headers/intrin.h
index 039846bafacf5..d8b09748402a7 100644
--- a/clang/lib/Headers/intrin.h
+++ b/clang/lib/Headers/intrin.h
@@ -447,6 +447,7 @@ unsigned int _CountTrailingZeros64(unsigned __int64);
 unsigned int __hlt(unsigned int, ...);
 
 void __cdecl __prefetch(const void *);
+void __cdecl __prefetch2(const void *, unsigned char);
 
 #endif
 
diff --git a/clang/lib/Sema/SemaARM.cpp b/clang/lib/Sema/SemaARM.cpp
index d3bfd75223220..c45580902db94 100644
--- a/clang/lib/Sema/SemaARM.cpp
+++ b/clang/lib/Sema/SemaARM.cpp
@@ -1173,6 +1173,9 @@ bool SemaARM::CheckAArch64BuiltinFunctionCall(const TargetInfo &TI,
       BuiltinID == AArch64::BI__getRegFp || BuiltinID == AArch64::BI__setRegFp)
     return SemaRef.BuiltinConstantArgRange(TheCall, 0, 0, 31);
 
+  if (BuiltinID == AArch64::BI__prefetch2)
+    return SemaRef.BuiltinConstantArgRange(TheCall, 1, 0, 31);
+
   if (BuiltinID == AArch64::BI__break)
     return SemaRef.BuiltinConstantArgRange(TheCall, 0, 0, 0xffff);
 
diff --git a/clang/test/CodeGen/arm64-microsoft-intrinsics.c b/clang/test/CodeGen/arm64-microsoft-intrinsics.c
index bb2629dee9748..2c15e8e75779c 100644
--- a/clang/test/CodeGen/arm64-microsoft-intrinsics.c
+++ b/clang/test/CodeGen/arm64-microsoft-intrinsics.c
@@ -617,6 +617,16 @@ void check__prefetch(void *arg1) {
 // CHECK-MSCOMPAT: call void @llvm.prefetch.p0(ptr %[[VAR0]], i32 0, i32 3, i32 1)
 // CHECK-MSCOMPAT: ret void
 
+void check__prefetch2(void *arg1) {
+  __prefetch2(arg1, 0x00);
+  __prefetch2(arg1, 0x13);
+}
+
+// CHECK-MSCOMPAT-LABEL: define{{.*}}void @check__prefetch2(ptr{{.*}}%arg1){{.*}}{
+// CHECK-MSCOMPAT: call void @llvm.aarch64.prefetch(ptr %{{.*}}, i32 0, i32 0, i32 1, i32 1)
+// CHECK-MSCOMPAT: call void @llvm.aarch64.prefetch(ptr %{{.*}}, i32 1, i32 1, i32 0, i32 1)
+// CHECK-LINUX: error: call to undeclared function '__prefetch2'
+
 unsigned char check__ldar8(unsigned char volatile *p) {
   return __ldar8(p);
 }



More information about the cfe-commits mailing list