[clang] [aarch64] Add more MSVC intrinsics (PR #194632)

via cfe-commits cfe-commits at lists.llvm.org
Tue Apr 28 06:50:44 PDT 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clangir

Author: Adhemerval Zanella (zatrazz)

<details>
<summary>Changes</summary>

This patchset adds more intrinsics from MSVC build 2026 [1]. I focused on the one documented by Microsoft, although intrin.h and related headers do export more symbols.

This is split from https://github.com/llvm/llvm-project/pull/193221 , where I only added the non-controversial ones.

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


7 Files Affected:

- (modified) clang/include/clang/Basic/BuiltinsAArch64.td (+6) 
- (modified) clang/lib/CIR/CodeGen/CIRGenBuiltinAArch64.cpp (+3-1) 
- (modified) clang/lib/CodeGen/TargetBuiltins/ARM.cpp (+73-4) 
- (modified) clang/lib/Headers/arm64intr.h (+10) 
- (modified) clang/lib/Headers/intrin.h (+6) 
- (modified) clang/lib/Sema/SemaARM.cpp (+5-1) 
- (modified) clang/test/CodeGen/arm64-microsoft-intrinsics.c (+64) 


``````````diff
diff --git a/clang/include/clang/Basic/BuiltinsAArch64.td b/clang/include/clang/Basic/BuiltinsAArch64.td
index 8e5dbb5f4172d..90824e955c072 100644
--- a/clang/include/clang/Basic/BuiltinsAArch64.td
+++ b/clang/include/clang/Basic/BuiltinsAArch64.td
@@ -335,6 +335,9 @@ 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 __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)">;
 	def __sys                   : AArch64NoPrefixTargetLibBuiltin<"unsigned int (int, long long int)">;
@@ -394,10 +397,13 @@ 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 {
 	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/CIR/CodeGen/CIRGenBuiltinAArch64.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltinAArch64.cpp
index cac5f8eced8a7..d563ccbf1a38e 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltinAArch64.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltinAArch64.cpp
@@ -2064,7 +2064,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 f8d383b15313f..b2e090489c7d9 100644
--- a/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
+++ b/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
@@ -4810,7 +4810,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");
@@ -4823,9 +4824,45 @@ 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__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");
+
+    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::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) {
@@ -5235,6 +5272,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);
@@ -5244,6 +5293,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/arm64intr.h b/clang/lib/Headers/arm64intr.h
index 4943b2db69d02..53a3d57a6e9d1 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/lib/Headers/intrin.h b/clang/lib/Headers/intrin.h
index 210ed0c1f773b..d8b09748402a7 100644
--- a/clang/lib/Headers/intrin.h
+++ b/clang/lib/Headers/intrin.h
@@ -374,6 +374,9 @@ 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);
+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);
@@ -438,10 +441,13 @@ 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, ...);
 
 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 7871fd624a467..c45580902db94 100644
--- a/clang/lib/Sema/SemaARM.cpp
+++ b/clang/lib/Sema/SemaARM.cpp
@@ -1169,9 +1169,13 @@ 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 ||
+      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 2f5ab50d6c848..4289ab6758dfc 100644
--- a/clang/test/CodeGen/arm64-microsoft-intrinsics.c
+++ b/clang/test/CodeGen/arm64-microsoft-intrinsics.c
@@ -157,6 +157,36 @@ unsigned __int64 check__getReg(void) {
 // CHECK-MSCOMPAT: call i64 @llvm.read_register.i64(metadata ![[MD2:.*]])
 // CHECK-MSCOMPAT: call i64 @llvm.read_register.i64(metadata ![[MD3:.*]])
 
+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'
+
+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'
+
+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'
+
 #ifdef __LP64__
 #define LONG __int32
 #else
@@ -584,6 +614,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);
 }
@@ -594,6 +647,17 @@ 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'
+
 
 // CHECK-MSCOMPAT: ![[MD2]] = !{!"x18"}
 // CHECK-MSCOMPAT: ![[MD3]] = !{!"sp"}
+// CHECK-MSCOMPAT: ![[MD4]] = !{!"d5"}

``````````

</details>


https://github.com/llvm/llvm-project/pull/194632


More information about the cfe-commits mailing list