[clang] [llvm] [AArch64][clang][llvm] Add ACLE `stshh` atomic store builtin (PR #181386)
Jonathan Thackray via cfe-commits
cfe-commits at lists.llvm.org
Fri Feb 13 14:30:55 PST 2026
https://github.com/jthackray updated https://github.com/llvm/llvm-project/pull/181386
>From ebdd55bb87825eecfa96cf8ca41d634804b1efff Mon Sep 17 00:00:00 2001
From: Jonathan Thackray <jonathan.thackray at arm.com>
Date: Fri, 13 Feb 2026 14:42:05 +0000
Subject: [PATCH 1/4] [AArch64][clang][llvm] Add ACLE `stshh` atomic store
builtin
Add `__arm_atomic_store_with_stshh` implementation as defined
in the ACLE. Validate that the arguments passed are correct, and
lower it to the stshh intrinsic plus an atomic store with the
allowed orderings.
Gate this on FEAT_PCDPHINT so that availability matches
hardware support for the `STSHH` instruction. Use an i64
immediate and side-effect modeling to satisfy tablegen and decoding.
---
clang/include/clang/Basic/BuiltinsAArch64.def | 3 +
.../clang/Basic/DiagnosticSemaKinds.td | 9 ++
.../lib/CIR/CodeGen/CIRGenBuiltinAArch64.cpp | 7 +
clang/lib/CodeGen/TargetBuiltins/ARM.cpp | 48 ++++++
clang/lib/Headers/arm_acle.h | 6 +
clang/lib/Sema/SemaARM.cpp | 140 ++++++++++++++++++
.../CodeGen/AArch64/pcdphint-atomic-store.c | 31 ++++
.../test/Sema/AArch64/pcdphint-atomic-store.c | 29 ++++
llvm/include/llvm/IR/IntrinsicsAArch64.td | 2 +
.../lib/Target/AArch64/AArch64InstrFormats.td | 12 +-
.../Disassembler/AArch64Disassembler.cpp | 13 ++
11 files changed, 298 insertions(+), 2 deletions(-)
create mode 100644 clang/test/CodeGen/AArch64/pcdphint-atomic-store.c
create mode 100644 clang/test/Sema/AArch64/pcdphint-atomic-store.c
diff --git a/clang/include/clang/Basic/BuiltinsAArch64.def b/clang/include/clang/Basic/BuiltinsAArch64.def
index 5d7e956b73b87..5d747f4d9c4b2 100644
--- a/clang/include/clang/Basic/BuiltinsAArch64.def
+++ b/clang/include/clang/Basic/BuiltinsAArch64.def
@@ -135,6 +135,9 @@ TARGET_BUILTIN(__builtin_arm_st64b, "vv*WUiC*", "n", "ls64")
TARGET_BUILTIN(__builtin_arm_st64bv, "WUiv*WUiC*", "n", "ls64")
TARGET_BUILTIN(__builtin_arm_st64bv0, "WUiv*WUiC*", "n", "ls64")
+// Atomic store with PCDPHINT
+TARGET_BUILTIN(__builtin_arm_atomic_store_with_stshh, "v.", "t", "pcdphint")
+
// Armv9.3-A Guarded Control Stack
TARGET_BUILTIN(__builtin_arm_gcspopm, "WUiWUi", "n", "gcs")
TARGET_BUILTIN(__builtin_arm_gcsss, "v*v*", "n", "gcs")
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 85a023435ba23..b17544a4a3daf 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -9475,6 +9475,15 @@ def err_atomic_builtin_must_be_pointer_intfltptr : Error<
def err_atomic_builtin_pointer_size : Error<
"address argument to atomic builtin must be a pointer to 1,2,4,8 or 16 byte "
"type (%0 invalid)">;
+def err_arm_atomic_store_with_stshh_bad_type : Error<
+ "address argument to '__arm_atomic_store_with_stshh' must be a pointer to an "
+ "8,16,32, or 64-bit integer type (%0 invalid)">;
+def err_arm_atomic_store_with_stshh_bad_value_type : Error<
+ "value argument to '__arm_atomic_store_with_stshh' must be an integer of the "
+ "same size as the pointed-to type (%0 invalid)">;
+def err_arm_atomic_store_with_stshh_bad_order : Error<
+ "memory order argument to '__arm_atomic_store_with_stshh' must be one of "
+ "__ATOMIC_RELAXED, __ATOMIC_RELEASE, or __ATOMIC_SEQ_CST">;
def err_atomic_exclusive_builtin_pointer_size : Error<
"address argument to load or store exclusive builtin must be a pointer to "
// Because the range of legal sizes for load/store exclusive varies with the
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltinAArch64.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltinAArch64.cpp
index 71cf896aede10..b2311e54415b8 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltinAArch64.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltinAArch64.cpp
@@ -1044,6 +1044,13 @@ CIRGenFunction::emitAArch64BuiltinExpr(unsigned builtinID, const CallExpr *expr,
return mlir::Value{};
}
+ if (builtinID == clang::AArch64::BI__builtin_arm_atomic_store_with_stshh) {
+ cgm.errorNYI(expr->getSourceRange(),
+ std::string("unimplemented AArch64 builtin call: ") +
+ getContext().BuiltinInfo.getName(builtinID));
+ return mlir::Value{};
+ }
+
if (builtinID == clang::AArch64::BI__builtin_arm_rndr ||
builtinID == clang::AArch64::BI__builtin_arm_rndrrs) {
cgm.errorNYI(expr->getSourceRange(),
diff --git a/clang/lib/CodeGen/TargetBuiltins/ARM.cpp b/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
index cb6bbfe07538e..eb3844707fd62 100644
--- a/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
+++ b/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
@@ -5290,6 +5290,54 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID,
return Builder.CreateCall(F, Args);
}
+ if (BuiltinID == clang::AArch64::BI__builtin_arm_atomic_store_with_stshh) {
+ const Expr *Arg0 = E->getArg(0);
+ const Expr *Arg1 = E->getArg(1);
+ const Expr *Arg2 = E->getArg(2);
+ const Expr *Arg3 = E->getArg(3);
+
+ Value *StoreAddr = EmitScalarExpr(Arg0);
+ Value *StoreValue = EmitScalarExpr(Arg1);
+
+ llvm::APSInt OrderVal = Arg2->EvaluateKnownConstInt(getContext());
+ llvm::APSInt RetVal = Arg3->EvaluateKnownConstInt(getContext());
+
+ llvm::AtomicOrdering Ordering;
+ switch (OrderVal.getZExtValue()) {
+ case 0: // __ATOMIC_RELAXED
+ Ordering = llvm::AtomicOrdering::Monotonic;
+ break;
+ case 3: // __ATOMIC_RELEASE
+ Ordering = llvm::AtomicOrdering::Release;
+ break;
+ case 5: // __ATOMIC_SEQ_CST
+ Ordering = llvm::AtomicOrdering::SequentiallyConsistent;
+ break;
+ default:
+ llvm_unreachable(
+ "unexpected memory order for __arm_atomic_store_with_stshh");
+ }
+
+ Function *F = CGM.getIntrinsic(Intrinsic::aarch64_stshh);
+ llvm::Value *Arg = llvm::ConstantInt::get(Int64Ty, RetVal.getZExtValue());
+ CallInst *HintCall = Builder.CreateCall(F, Arg);
+
+ QualType ValQT = Arg0->IgnoreParenImpCasts()
+ ->getType()
+ ->castAs<PointerType>()
+ ->getPointeeType();
+ llvm::Type *ValTy = ConvertType(ValQT);
+
+ CharUnits ValAlign = getContext().getTypeAlignInChars(ValQT);
+ Address Addr = Address(StoreAddr, ValTy, ValAlign);
+ LValue LVal = MakeAddrLValue(Addr, ValQT);
+
+ EmitAtomicStore(RValue::get(StoreValue), LVal, Ordering,
+ /* isVolatile= */ false,
+ /* isInit= */ false);
+ return HintCall;
+ }
+
if (BuiltinID == clang::AArch64::BI__builtin_arm_rndr ||
BuiltinID == clang::AArch64::BI__builtin_arm_rndrrs) {
diff --git a/clang/lib/Headers/arm_acle.h b/clang/lib/Headers/arm_acle.h
index 9a6b6a837fa5a..ec06072bcc4bf 100644
--- a/clang/lib/Headers/arm_acle.h
+++ b/clang/lib/Headers/arm_acle.h
@@ -840,6 +840,12 @@ __rndrrs(uint64_t *__p) {
}
#endif
+/* Atomic store with PCDPHINT */
+#if defined(__ARM_FEATURE_PCDPHINT)
+#define __arm_atomic_store_with_stshh(ptr, data, memory_order, ret) \
+ __builtin_arm_atomic_store_with_stshh((ptr), (data), (memory_order), (ret))
+#endif
+
/* 11.2 Guarded Control Stack intrinsics */
#if defined(__ARM_64BIT_STATE) && __ARM_64BIT_STATE
static __inline__ void * __attribute__((__always_inline__, __nodebug__))
diff --git a/clang/lib/Sema/SemaARM.cpp b/clang/lib/Sema/SemaARM.cpp
index 53e8c002a1962..08cf84452d7ee 100644
--- a/clang/lib/Sema/SemaARM.cpp
+++ b/clang/lib/Sema/SemaARM.cpp
@@ -1105,6 +1105,143 @@ bool SemaARM::CheckARMBuiltinFunctionCall(const TargetInfo &TI,
}
}
+static bool CheckAArch64AtomicStoreWithStshhCall(SemaARM &S,
+ CallExpr *TheCall) {
+ Sema &SemaRef = S.SemaRef;
+ ASTContext &Context = S.getASTContext();
+ DeclRefExpr *DRE =
+ cast<DeclRefExpr>(TheCall->getCallee()->IgnoreParenCasts());
+ SourceLocation Loc = DRE->getBeginLoc();
+
+ // Ensure we have the proper number of arguments.
+ if (SemaRef.checkArgCount(TheCall, 4))
+ return true;
+
+ ExprResult PtrRes =
+ SemaRef.DefaultFunctionArrayLvalueConversion(TheCall->getArg(0));
+
+ // Bail if conversion failed.
+ if (PtrRes.isInvalid())
+ return true;
+
+ TheCall->setArg(0, PtrRes.get());
+ Expr *PointerArg = PtrRes.get();
+
+ // Check arg 0 is a pointer type, err out if not
+ const PointerType *PointerTy = PointerArg->getType()->getAs<PointerType>();
+ if (!PointerTy) {
+ SemaRef.Diag(Loc, diag::err_atomic_builtin_must_be_pointer)
+ << PointerArg->getType() << 0 << PointerArg->getSourceRange();
+ return true;
+ }
+
+ // Reject const-qualified pointee types, with an error
+ QualType ValType = PointerTy->getPointeeType();
+ if (ValType.isConstQualified()) {
+ SemaRef.Diag(Loc, diag::err_atomic_builtin_cannot_be_const)
+ << PointerArg->getType() << PointerArg->getSourceRange();
+ return true;
+ }
+
+ // Only integer element types are supported.
+ ValType = ValType.getUnqualifiedType();
+ if (!ValType->isIntegerType()) {
+ SemaRef.Diag(Loc, diag::err_arm_atomic_store_with_stshh_bad_type)
+ << PointerArg->getType() << PointerArg->getSourceRange();
+ return true;
+ }
+
+ // Only 8/16/32/64-bit integers are supported.
+ unsigned Bits = Context.getTypeSize(ValType);
+ switch (Bits) {
+ case 8:
+ case 16:
+ case 32:
+ case 64:
+ break;
+ default:
+ SemaRef.Diag(Loc, diag::err_arm_atomic_store_with_stshh_bad_type)
+ << PointerArg->getType() << PointerArg->getSourceRange();
+ return true;
+ }
+
+ ExprResult ValRes =
+ SemaRef.DefaultFunctionArrayLvalueConversion(TheCall->getArg(1));
+
+ // Bail if conversion failed.
+ if (ValRes.isInvalid())
+ return true;
+
+ // Check if value is an integer type.
+ Expr *ValArg = ValRes.get();
+ if (!ValArg->getType()->isIntegerType()) {
+ SemaRef.Diag(Loc, diag::err_arm_atomic_store_with_stshh_bad_value_type)
+ << ValArg->getType() << ValArg->getSourceRange();
+ return true;
+ }
+
+ // Value width must match the pointee width.
+ if (Context.getTypeSize(ValArg->getType()) != Bits) {
+ SemaRef.Diag(Loc, diag::err_arm_atomic_store_with_stshh_bad_value_type)
+ << ValArg->getType() << ValArg->getSourceRange();
+ return true;
+ }
+
+ // Prepare a cast if the value type differs
+ ExprResult ValArgRes;
+ CastKind CK =
+ ValArg->getType().getCanonicalType() == ValType.getCanonicalType()
+ ? CK_NoOp
+ : CK_IntegralCast;
+
+ // Apply cast to the pointee type.
+ ValArgRes = SemaRef.ImpCastExprToType(ValArg, ValType, CK);
+
+ // Bail if cast failed.
+ if (ValArgRes.isInvalid())
+ return true;
+
+ TheCall->setArg(1, ValArgRes.get());
+ Expr *OrderArg = TheCall->getArg(2);
+
+ // Defer validation for dependent memory_order arguments.
+ if (OrderArg->isValueDependent())
+ return false;
+
+ // Require an order value.
+ std::optional<llvm::APSInt> OrderValOpt =
+ OrderArg->getIntegerConstantExpr(Context);
+ if (!OrderValOpt) {
+ SemaRef.Diag(Loc, diag::err_arm_atomic_store_with_stshh_bad_order)
+ << OrderArg->getSourceRange();
+ return true;
+ }
+
+ // Validate order; not used here; used later in codegen.
+ llvm::APSInt OrderVal = *OrderValOpt;
+ int64_t Order = OrderVal.getSExtValue();
+ // __ATOMIC_RELAXED=0, __ATOMIC_RELEASE=3, __ATOMIC_SEQ_CST=5.
+ constexpr int64_t AtomicRelaxed = 0;
+ constexpr int64_t AtomicRelease = 3;
+ constexpr int64_t AtomicSeqCst = 5;
+ switch (Order) {
+ case AtomicRelaxed:
+ case AtomicRelease:
+ case AtomicSeqCst:
+ break;
+ default:
+ SemaRef.Diag(Loc, diag::err_arm_atomic_store_with_stshh_bad_order)
+ << OrderArg->getSourceRange();
+ return true;
+ }
+
+ // Arg 3 (retention policy) must be between KEEP(0) and STRM(1).
+ if (SemaRef.BuiltinConstantArgRange(TheCall, 3, 0, 1))
+ return true;
+
+ return false;
+}
+
bool SemaARM::CheckAArch64BuiltinFunctionCall(const TargetInfo &TI,
unsigned BuiltinID,
CallExpr *TheCall) {
@@ -1115,6 +1252,9 @@ bool SemaARM::CheckAArch64BuiltinFunctionCall(const TargetInfo &TI,
return CheckARMBuiltinExclusiveCall(TI, BuiltinID, TheCall);
}
+ if (BuiltinID == AArch64::BI__builtin_arm_atomic_store_with_stshh)
+ return CheckAArch64AtomicStoreWithStshhCall(*this, TheCall);
+
if (BuiltinID == AArch64::BI__builtin_arm_prefetch) {
return SemaRef.BuiltinConstantArgRange(TheCall, 1, 0, 1) ||
SemaRef.BuiltinConstantArgRange(TheCall, 2, 0, 3) ||
diff --git a/clang/test/CodeGen/AArch64/pcdphint-atomic-store.c b/clang/test/CodeGen/AArch64/pcdphint-atomic-store.c
new file mode 100644
index 0000000000000..79510be522b6a
--- /dev/null
+++ b/clang/test/CodeGen/AArch64/pcdphint-atomic-store.c
@@ -0,0 +1,31 @@
+// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -target-feature +pcdphint -D__ARM_FEATURE_PCDPHINT -emit-llvm -o - %s | FileCheck %s
+
+#include <arm_acle.h>
+
+void test_u8(unsigned char *p, unsigned char v) {
+ __arm_atomic_store_with_stshh(p, v, __ATOMIC_RELAXED, 0);
+}
+// CHECK-LABEL: @test_u8
+// CHECK: call void @llvm.aarch64.stshh(i64 0)
+// CHECK-NEXT: store atomic i8 %{{.*}}, ptr %{{.*}} monotonic
+
+void test_u16(unsigned short *p, unsigned short v) {
+ __arm_atomic_store_with_stshh(p, v, __ATOMIC_RELEASE, 1);
+}
+// CHECK-LABEL: @test_u16
+// CHECK: call void @llvm.aarch64.stshh(i64 1)
+// CHECK-NEXT: store atomic i16 %{{.*}}, ptr %{{.*}} release
+
+void test_u32(unsigned int *p, unsigned int v) {
+ __arm_atomic_store_with_stshh(p, v, __ATOMIC_SEQ_CST, 0);
+}
+// CHECK-LABEL: @test_u32
+// CHECK: call void @llvm.aarch64.stshh(i64 0)
+// CHECK-NEXT: store atomic i32 %{{.*}}, ptr %{{.*}} seq_cst
+
+void test_u64(unsigned long long *p, unsigned long long v) {
+ __arm_atomic_store_with_stshh(p, v, __ATOMIC_RELAXED, 1);
+}
+// CHECK-LABEL: @test_u64
+// CHECK: call void @llvm.aarch64.stshh(i64 1)
+// CHECK-NEXT: store atomic i64 %{{.*}}, ptr %{{.*}} monotonic
diff --git a/clang/test/Sema/AArch64/pcdphint-atomic-store.c b/clang/test/Sema/AArch64/pcdphint-atomic-store.c
new file mode 100644
index 0000000000000..091f1c25c2880
--- /dev/null
+++ b/clang/test/Sema/AArch64/pcdphint-atomic-store.c
@@ -0,0 +1,29 @@
+// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -target-feature +pcdphint \
+// RUN: -D__ARM_FEATURE_PCDPHINT -fsyntax-only -verify %s
+
+#include <arm_acle.h>
+
+void test_const_pointer(const unsigned int *p, unsigned int v) {
+ __arm_atomic_store_with_stshh(p, v, __ATOMIC_RELAXED, 0);
+ // expected-error at -1 {{address argument to atomic builtin cannot be const-qualified}}
+}
+
+void test_non_integer_pointer(float *p, float v) {
+ __arm_atomic_store_with_stshh(p, v, __ATOMIC_RELAXED, 0);
+ // expected-error at -1 {{address argument to '__arm_atomic_store_with_stshh' must be a pointer to an 8,16,32, or 64-bit integer type}}
+}
+
+void test_invalid_bit_width(__int128 *p, __int128 v) {
+ __arm_atomic_store_with_stshh(p, v, __ATOMIC_RELAXED, 0);
+ // expected-error at -1 {{address argument to '__arm_atomic_store_with_stshh' must be a pointer to an 8,16,32, or 64-bit integer type}}
+}
+
+void test_invalid_memory_order(unsigned int *p, unsigned int v) {
+ __arm_atomic_store_with_stshh(p, v, __ATOMIC_ACQUIRE, 0);
+ // expected-error at -1 {{memory order argument to '__arm_atomic_store_with_stshh' must be one of __ATOMIC_RELAXED, __ATOMIC_RELEASE, or __ATOMIC_SEQ_CST}}
+}
+
+void test_invalid_retention_policy(unsigned int *p, unsigned int v) {
+ __arm_atomic_store_with_stshh(p, v, __ATOMIC_RELAXED, 2);
+ // expected-error at -1 {{argument value 2 is outside the valid range [0, 1]}}
+}
diff --git a/llvm/include/llvm/IR/IntrinsicsAArch64.td b/llvm/include/llvm/IR/IntrinsicsAArch64.td
index 7f4b7383415c1..19ba3a5a740c5 100644
--- a/llvm/include/llvm/IR/IntrinsicsAArch64.td
+++ b/llvm/include/llvm/IR/IntrinsicsAArch64.td
@@ -62,6 +62,8 @@ def int_aarch64_frint64x
// HINT
def int_aarch64_hint : DefaultAttrsIntrinsic<[], [llvm_i32_ty]>;
+def int_aarch64_stshh
+ : DefaultAttrsIntrinsic<[], [llvm_i64_ty], [IntrHasSideEffects]>;
def int_aarch64_break : Intrinsic<[], [llvm_i32_ty],
[IntrNoMem, IntrHasSideEffects, IntrNoReturn, IntrCold, ImmArg<ArgIndex<0>>]>;
diff --git a/llvm/lib/Target/AArch64/AArch64InstrFormats.td b/llvm/lib/Target/AArch64/AArch64InstrFormats.td
index 7d4e034ca16c8..69fb01ada0b40 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrFormats.td
+++ b/llvm/lib/Target/AArch64/AArch64InstrFormats.td
@@ -1840,16 +1840,24 @@ def PHintInstOperand : AsmOperandClass {
let ParserMethod = "tryParsePHintInstOperand";
}
-def phint_op : Operand<i32> {
+def phint_op : Operand<i64> {
let ParserMatchClass = PHintInstOperand;
let PrintMethod = "printPHintOp";
let OperandType = "OPERAND_IMMEDIATE";
+ let MIOperandInfo = (ops i64imm);
+ let DecoderMethod = "DecodeUImm<3>";
}
class STSHHI
- : SimpleSystemI<0, (ins phint_op:$policy), "stshh", "\t$policy", []>,
+ : SimpleSystemI<0, (ins phint_op:$policy), "stshh", "\t$policy",
+ [(int_aarch64_stshh (i64 imm0_7:$policy))]>,
Sched<[WriteHint]> {
bits<3> policy;
+ // NOTE: ideally, this would have mayLoad = 0, mayStore = 0, but we cannot
+ // model patterns with sufficiently fine granularity.
+ let mayLoad = 1;
+ let mayStore = 1;
+ let hasSideEffects = 1;
let Inst{20-12} = 0b000011001;
let Inst{11-8} = 0b0110;
let Inst{7-5} = policy;
diff --git a/llvm/lib/Target/AArch64/Disassembler/AArch64Disassembler.cpp b/llvm/lib/Target/AArch64/Disassembler/AArch64Disassembler.cpp
index 4eb762a00d477..8fa1913ce24e5 100644
--- a/llvm/lib/Target/AArch64/Disassembler/AArch64Disassembler.cpp
+++ b/llvm/lib/Target/AArch64/Disassembler/AArch64Disassembler.cpp
@@ -38,6 +38,9 @@ using DecodeStatus = MCDisassembler::DecodeStatus;
template <int Bits>
static DecodeStatus DecodeSImm(MCInst &Inst, uint64_t Imm, uint64_t Address,
const MCDisassembler *Decoder);
+template <int Bits>
+static DecodeStatus DecodeUImm(MCInst &Inst, uint64_t Imm, uint64_t Address,
+ const MCDisassembler *Decoder);
#define Success MCDisassembler::Success
#define Fail MCDisassembler::Fail
@@ -1442,6 +1445,16 @@ static DecodeStatus DecodeSImm(MCInst &Inst, uint64_t Imm, uint64_t Address,
return Success;
}
+template <int Bits>
+static DecodeStatus DecodeUImm(MCInst &Inst, uint64_t Imm, uint64_t Address,
+ const MCDisassembler *Decoder) {
+ if (Imm & ~((1ULL << Bits) - 1))
+ return Fail;
+
+ Inst.addOperand(MCOperand::createImm(Imm));
+ return Success;
+}
+
// Decode 8-bit signed/unsigned immediate for a given element width.
template <int ElementWidth>
static DecodeStatus DecodeImm8OptLsl(MCInst &Inst, unsigned Imm, uint64_t Addr,
>From bc632a607a2ff38d06f75ddebfa042e138e6ddc2 Mon Sep 17 00:00:00 2001
From: Jonathan Thackray <jonathan.thackray at arm.com>
Date: Fri, 13 Feb 2026 17:19:25 +0000
Subject: [PATCH 2/4] fixup!
A few small tidyups
---
clang/lib/CodeGen/TargetBuiltins/ARM.cpp | 13 +++++++------
clang/test/Sema/AArch64/pcdphint-atomic-store.c | 4 ++++
llvm/lib/Target/AArch64/AArch64InstrFormats.td | 8 ++++----
3 files changed, 15 insertions(+), 10 deletions(-)
diff --git a/clang/lib/CodeGen/TargetBuiltins/ARM.cpp b/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
index eb3844707fd62..f71737331ae4c 100644
--- a/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
+++ b/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
@@ -5300,7 +5300,7 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID,
Value *StoreValue = EmitScalarExpr(Arg1);
llvm::APSInt OrderVal = Arg2->EvaluateKnownConstInt(getContext());
- llvm::APSInt RetVal = Arg3->EvaluateKnownConstInt(getContext());
+ llvm::APSInt RetentionPolicy = Arg3->EvaluateKnownConstInt(getContext());
llvm::AtomicOrdering Ordering;
switch (OrderVal.getZExtValue()) {
@@ -5318,10 +5318,6 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID,
"unexpected memory order for __arm_atomic_store_with_stshh");
}
- Function *F = CGM.getIntrinsic(Intrinsic::aarch64_stshh);
- llvm::Value *Arg = llvm::ConstantInt::get(Int64Ty, RetVal.getZExtValue());
- CallInst *HintCall = Builder.CreateCall(F, Arg);
-
QualType ValQT = Arg0->IgnoreParenImpCasts()
->getType()
->castAs<PointerType>()
@@ -5332,10 +5328,15 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID,
Address Addr = Address(StoreAddr, ValTy, ValAlign);
LValue LVal = MakeAddrLValue(Addr, ValQT);
+ Function *F = CGM.getIntrinsic(Intrinsic::aarch64_stshh);
+ llvm::Value *Arg =
+ llvm::ConstantInt::get(Int64Ty, RetentionPolicy.getZExtValue());
+ Builder.CreateCall(F, Arg);
+
EmitAtomicStore(RValue::get(StoreValue), LVal, Ordering,
/* isVolatile= */ false,
/* isInit= */ false);
- return HintCall;
+ return nullptr;
}
if (BuiltinID == clang::AArch64::BI__builtin_arm_rndr ||
diff --git a/clang/test/Sema/AArch64/pcdphint-atomic-store.c b/clang/test/Sema/AArch64/pcdphint-atomic-store.c
index 091f1c25c2880..d9784656d486d 100644
--- a/clang/test/Sema/AArch64/pcdphint-atomic-store.c
+++ b/clang/test/Sema/AArch64/pcdphint-atomic-store.c
@@ -27,3 +27,7 @@ void test_invalid_retention_policy(unsigned int *p, unsigned int v) {
__arm_atomic_store_with_stshh(p, v, __ATOMIC_RELAXED, 2);
// expected-error at -1 {{argument value 2 is outside the valid range [0, 1]}}
}
+
+void test_signed_ok(int *p, int v) {
+ __arm_atomic_store_with_stshh(p, v, __ATOMIC_RELAXED, 0);
+}
diff --git a/llvm/lib/Target/AArch64/AArch64InstrFormats.td b/llvm/lib/Target/AArch64/AArch64InstrFormats.td
index 69fb01ada0b40..1390600488bf2 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrFormats.td
+++ b/llvm/lib/Target/AArch64/AArch64InstrFormats.td
@@ -1842,10 +1842,10 @@ def PHintInstOperand : AsmOperandClass {
def phint_op : Operand<i64> {
let ParserMatchClass = PHintInstOperand;
- let PrintMethod = "printPHintOp";
- let OperandType = "OPERAND_IMMEDIATE";
- let MIOperandInfo = (ops i64imm);
- let DecoderMethod = "DecodeUImm<3>";
+ let PrintMethod = "printPHintOp";
+ let OperandType = "OPERAND_IMMEDIATE";
+ let MIOperandInfo = (ops i64imm);
+ let DecoderMethod = "DecodeUImm<3>";
}
class STSHHI
>From e9034b6da0116b6e6a7a1c41239e2cb51d66d63a Mon Sep 17 00:00:00 2001
From: Jonathan Thackray <jonathan.thackray at arm.com>
Date: Fri, 13 Feb 2026 17:26:06 +0000
Subject: [PATCH 3/4] fixup!
More small issues tidied, and remove gating.
---
clang/include/clang/Basic/BuiltinsAArch64.def | 2 +-
clang/lib/CodeGen/TargetBuiltins/ARM.cpp | 4 ++--
clang/lib/Headers/arm_acle.h | 2 --
clang/lib/Sema/SemaARM.cpp | 2 +-
clang/test/CodeGen/AArch64/pcdphint-atomic-store.c | 2 +-
clang/test/Sema/AArch64/pcdphint-atomic-store.c | 8 ++++++--
6 files changed, 11 insertions(+), 9 deletions(-)
diff --git a/clang/include/clang/Basic/BuiltinsAArch64.def b/clang/include/clang/Basic/BuiltinsAArch64.def
index 5d747f4d9c4b2..5722b045f1ed1 100644
--- a/clang/include/clang/Basic/BuiltinsAArch64.def
+++ b/clang/include/clang/Basic/BuiltinsAArch64.def
@@ -136,7 +136,7 @@ TARGET_BUILTIN(__builtin_arm_st64bv, "WUiv*WUiC*", "n", "ls64")
TARGET_BUILTIN(__builtin_arm_st64bv0, "WUiv*WUiC*", "n", "ls64")
// Atomic store with PCDPHINT
-TARGET_BUILTIN(__builtin_arm_atomic_store_with_stshh, "v.", "t", "pcdphint")
+TARGET_BUILTIN(__builtin_arm_atomic_store_with_stshh, "v.", "t", "")
// Armv9.3-A Guarded Control Stack
TARGET_BUILTIN(__builtin_arm_gcspopm, "WUiWUi", "n", "gcs")
diff --git a/clang/lib/CodeGen/TargetBuiltins/ARM.cpp b/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
index f71737331ae4c..f7d405281caeb 100644
--- a/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
+++ b/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
@@ -5331,12 +5331,12 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID,
Function *F = CGM.getIntrinsic(Intrinsic::aarch64_stshh);
llvm::Value *Arg =
llvm::ConstantInt::get(Int64Ty, RetentionPolicy.getZExtValue());
- Builder.CreateCall(F, Arg);
+ CallInst *HintCall = Builder.CreateCall(F, Arg);
EmitAtomicStore(RValue::get(StoreValue), LVal, Ordering,
/* isVolatile= */ false,
/* isInit= */ false);
- return nullptr;
+ return HintCall;
}
if (BuiltinID == clang::AArch64::BI__builtin_arm_rndr ||
diff --git a/clang/lib/Headers/arm_acle.h b/clang/lib/Headers/arm_acle.h
index ec06072bcc4bf..88ffd82912df1 100644
--- a/clang/lib/Headers/arm_acle.h
+++ b/clang/lib/Headers/arm_acle.h
@@ -841,10 +841,8 @@ __rndrrs(uint64_t *__p) {
#endif
/* Atomic store with PCDPHINT */
-#if defined(__ARM_FEATURE_PCDPHINT)
#define __arm_atomic_store_with_stshh(ptr, data, memory_order, ret) \
__builtin_arm_atomic_store_with_stshh((ptr), (data), (memory_order), (ret))
-#endif
/* 11.2 Guarded Control Stack intrinsics */
#if defined(__ARM_64BIT_STATE) && __ARM_64BIT_STATE
diff --git a/clang/lib/Sema/SemaARM.cpp b/clang/lib/Sema/SemaARM.cpp
index 08cf84452d7ee..bb6999acf9c52 100644
--- a/clang/lib/Sema/SemaARM.cpp
+++ b/clang/lib/Sema/SemaARM.cpp
@@ -1217,7 +1217,7 @@ static bool CheckAArch64AtomicStoreWithStshhCall(SemaARM &S,
return true;
}
- // Validate order; not used here; used later in codegen.
+ // Validate order here; the value is mapped to LLVM ordering in codegen.
llvm::APSInt OrderVal = *OrderValOpt;
int64_t Order = OrderVal.getSExtValue();
// __ATOMIC_RELAXED=0, __ATOMIC_RELEASE=3, __ATOMIC_SEQ_CST=5.
diff --git a/clang/test/CodeGen/AArch64/pcdphint-atomic-store.c b/clang/test/CodeGen/AArch64/pcdphint-atomic-store.c
index 79510be522b6a..fceb739782641 100644
--- a/clang/test/CodeGen/AArch64/pcdphint-atomic-store.c
+++ b/clang/test/CodeGen/AArch64/pcdphint-atomic-store.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -target-feature +pcdphint -D__ARM_FEATURE_PCDPHINT -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -emit-llvm -o - %s | FileCheck %s
#include <arm_acle.h>
diff --git a/clang/test/Sema/AArch64/pcdphint-atomic-store.c b/clang/test/Sema/AArch64/pcdphint-atomic-store.c
index d9784656d486d..bd69ca859f15e 100644
--- a/clang/test/Sema/AArch64/pcdphint-atomic-store.c
+++ b/clang/test/Sema/AArch64/pcdphint-atomic-store.c
@@ -1,5 +1,4 @@
-// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -target-feature +pcdphint \
-// RUN: -D__ARM_FEATURE_PCDPHINT -fsyntax-only -verify %s
+// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -fsyntax-only -verify %s
#include <arm_acle.h>
@@ -31,3 +30,8 @@ void test_invalid_retention_policy(unsigned int *p, unsigned int v) {
void test_signed_ok(int *p, int v) {
__arm_atomic_store_with_stshh(p, v, __ATOMIC_RELAXED, 0);
}
+
+void test_value_size_mismatch(int *p, short v) {
+ __arm_atomic_store_with_stshh(p, v, __ATOMIC_RELAXED, 0);
+ // expected-error at -1 {{value argument to '__arm_atomic_store_with_stshh' must be an integer of the same size as the pointed-to type}}
+}
>From d14cc9737c0fbb120a100586696a710486f67ea4 Mon Sep 17 00:00:00 2001
From: Jonathan Thackray <jonathan.thackray at arm.com>
Date: Fri, 13 Feb 2026 21:38:30 +0000
Subject: [PATCH 4/4] fixup! Improve error diagnostics, and other cleanups
---
clang/include/clang/Basic/DiagnosticSemaKinds.td | 2 +-
clang/lib/CodeGen/TargetBuiltins/ARM.cpp | 3 ++-
clang/lib/Headers/arm_acle.h | 2 ++
clang/lib/Sema/SemaARM.cpp | 6 ++++--
clang/test/Sema/AArch64/pcdphint-atomic-store.c | 2 +-
llvm/test/CodeGen/AArch64/pcdphint-atomic-store.ll | 12 ++++++++++++
6 files changed, 22 insertions(+), 5 deletions(-)
create mode 100644 llvm/test/CodeGen/AArch64/pcdphint-atomic-store.ll
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index b17544a4a3daf..4a56f7e49a026 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -9480,7 +9480,7 @@ def err_arm_atomic_store_with_stshh_bad_type : Error<
"8,16,32, or 64-bit integer type (%0 invalid)">;
def err_arm_atomic_store_with_stshh_bad_value_type : Error<
"value argument to '__arm_atomic_store_with_stshh' must be an integer of the "
- "same size as the pointed-to type (%0 invalid)">;
+ "same size as the pointed-to type; expected %0 bits, got %1 bits">;
def err_arm_atomic_store_with_stshh_bad_order : Error<
"memory order argument to '__arm_atomic_store_with_stshh' must be one of "
"__ATOMIC_RELAXED, __ATOMIC_RELEASE, or __ATOMIC_SEQ_CST">;
diff --git a/clang/lib/CodeGen/TargetBuiltins/ARM.cpp b/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
index f7d405281caeb..fc707072592b2 100644
--- a/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
+++ b/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
@@ -5331,10 +5331,11 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID,
Function *F = CGM.getIntrinsic(Intrinsic::aarch64_stshh);
llvm::Value *Arg =
llvm::ConstantInt::get(Int64Ty, RetentionPolicy.getZExtValue());
+ // Execute hint before store to provide cache prefetch guidance.
CallInst *HintCall = Builder.CreateCall(F, Arg);
EmitAtomicStore(RValue::get(StoreValue), LVal, Ordering,
- /* isVolatile= */ false,
+ /* isVolatile= */ LVal.isVolatile(),
/* isInit= */ false);
return HintCall;
}
diff --git a/clang/lib/Headers/arm_acle.h b/clang/lib/Headers/arm_acle.h
index 88ffd82912df1..19a534d320790 100644
--- a/clang/lib/Headers/arm_acle.h
+++ b/clang/lib/Headers/arm_acle.h
@@ -841,8 +841,10 @@ __rndrrs(uint64_t *__p) {
#endif
/* Atomic store with PCDPHINT */
+#if defined(__ARM_64BIT_STATE) && __ARM_64BIT_STATE
#define __arm_atomic_store_with_stshh(ptr, data, memory_order, ret) \
__builtin_arm_atomic_store_with_stshh((ptr), (data), (memory_order), (ret))
+#endif
/* 11.2 Guarded Control Stack intrinsics */
#if defined(__ARM_64BIT_STATE) && __ARM_64BIT_STATE
diff --git a/clang/lib/Sema/SemaARM.cpp b/clang/lib/Sema/SemaARM.cpp
index bb6999acf9c52..7e7f2f1843b73 100644
--- a/clang/lib/Sema/SemaARM.cpp
+++ b/clang/lib/Sema/SemaARM.cpp
@@ -1176,14 +1176,16 @@ static bool CheckAArch64AtomicStoreWithStshhCall(SemaARM &S,
Expr *ValArg = ValRes.get();
if (!ValArg->getType()->isIntegerType()) {
SemaRef.Diag(Loc, diag::err_arm_atomic_store_with_stshh_bad_value_type)
- << ValArg->getType() << ValArg->getSourceRange();
+ << Bits << Context.getTypeSize(ValArg->getType())
+ << ValArg->getSourceRange();
return true;
}
// Value width must match the pointee width.
if (Context.getTypeSize(ValArg->getType()) != Bits) {
SemaRef.Diag(Loc, diag::err_arm_atomic_store_with_stshh_bad_value_type)
- << ValArg->getType() << ValArg->getSourceRange();
+ << Bits << Context.getTypeSize(ValArg->getType())
+ << ValArg->getSourceRange();
return true;
}
diff --git a/clang/test/Sema/AArch64/pcdphint-atomic-store.c b/clang/test/Sema/AArch64/pcdphint-atomic-store.c
index bd69ca859f15e..96efaff847b0c 100644
--- a/clang/test/Sema/AArch64/pcdphint-atomic-store.c
+++ b/clang/test/Sema/AArch64/pcdphint-atomic-store.c
@@ -33,5 +33,5 @@ void test_signed_ok(int *p, int v) {
void test_value_size_mismatch(int *p, short v) {
__arm_atomic_store_with_stshh(p, v, __ATOMIC_RELAXED, 0);
- // expected-error at -1 {{value argument to '__arm_atomic_store_with_stshh' must be an integer of the same size as the pointed-to type}}
+ // expected-error at -1 {{value argument to '__arm_atomic_store_with_stshh' must be an integer of the same size as the pointed-to type; expected 32 bits, got 16 bits}}
}
diff --git a/llvm/test/CodeGen/AArch64/pcdphint-atomic-store.ll b/llvm/test/CodeGen/AArch64/pcdphint-atomic-store.ll
new file mode 100644
index 0000000000000..c424c0db6525f
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/pcdphint-atomic-store.ll
@@ -0,0 +1,12 @@
+; RUN: llc -mtriple=aarch64 -mattr=+pcdphint < %s | FileCheck %s
+
+declare void @llvm.aarch64.stshh(i64)
+
+define void @test_stshh_atomic_store(ptr %p, i32 %v) {
+; CHECK-LABEL: test_stshh_atomic_store
+; CHECK: stshh
+; CHECK: str
+ call void @llvm.aarch64.stshh(i64 0)
+ store atomic i32 %v, ptr %p monotonic, align 4
+ ret void
+}
More information about the cfe-commits
mailing list