[clang] [Clang][AArch64] Diagnose invalid FPM scale arguments (PR #227722)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Oct 2 03:44:05 PDT 2026
https://github.com/CarolineConcatto updated https://github.com/llvm/llvm-project/pull/227722
>From 60354a0ecab4cd3c2a8b8226e7fcaea6d434b76a Mon Sep 17 00:00:00 2001
From: CarolineConcatto <caroline.concatto at arm.com>
Date: Wed, 30 Sep 2026 08:44:34 +0000
Subject: [PATCH] [Clang][AArch64] Diagnose invalid FPM scale arguments
Add builtins for the FPM scale helpers and diagnose constant
arguments outside their valid ranges.
---
clang/include/clang/Basic/BuiltinHeaders.def | 1 +
clang/include/clang/Basic/BuiltinsAArch64.td | 6 ++
clang/lib/CodeGen/TargetBuiltins/ARM.cpp | 33 ++++++++++
clang/lib/Sema/SemaARM.cpp | 30 +++++++++-
clang/test/CodeGen/AArch64/fpm-helpers.c | 6 +-
clang/test/Sema/aarch64-fpm-helpers.c | 63 ++++++++++++++++++++
clang/utils/TableGen/NeonEmitter.cpp | 17 +-----
7 files changed, 138 insertions(+), 18 deletions(-)
create mode 100644 clang/test/Sema/aarch64-fpm-helpers.c
diff --git a/clang/include/clang/Basic/BuiltinHeaders.def b/clang/include/clang/Basic/BuiltinHeaders.def
index 73a8e387cc4cb..6b564f168f38b 100644
--- a/clang/include/clang/Basic/BuiltinHeaders.def
+++ b/clang/include/clang/Basic/BuiltinHeaders.def
@@ -13,6 +13,7 @@
HEADER(NO_HEADER, nullptr)
HEADER(ARM_ACLE_H, "arm_acle.h")
+HEADER(ARM_VECTOR_TYPES_H, "arm_vector_types.h")
HEADER(BLOCKS_H, "Blocks.h")
HEADER(COMPLEX_H, "complex.h")
HEADER(CTYPE_H, "ctype.h")
diff --git a/clang/include/clang/Basic/BuiltinsAArch64.td b/clang/include/clang/Basic/BuiltinsAArch64.td
index 30aa3d526cbbb..bd579d90b4646 100644
--- a/clang/include/clang/Basic/BuiltinsAArch64.td
+++ b/clang/include/clang/Basic/BuiltinsAArch64.td
@@ -48,6 +48,12 @@ def sev : AArch64Builtin<"void ()">;
def sevl : AArch64Builtin<"void ()">;
def chkfeat : AArch64Builtin<"uint64_t (uint64_t)">;
+let Attributes = [NoThrow, RequireDeclaration], Header = "arm_vector_types.h" in {
+ def __arm_set_fpm_lscale : AArch64NoPrefixTargetLibBuiltin<"uint64_t (uint64_t, uint64_t)">;
+ def __arm_set_fpm_nscale : AArch64NoPrefixTargetLibBuiltin<"uint64_t (uint64_t, int64_t)">;
+ def __arm_set_fpm_lscale2 : AArch64NoPrefixTargetLibBuiltin<"uint64_t (uint64_t, uint64_t)">;
+}
+
let Attributes = [RequireDeclaration], Languages = "ALL_LANGUAGES", Header = "arm_acle.h" in {
def __yield : AArch64NoPrefixTargetLibBuiltin<"void ()">;
def __wfe : AArch64NoPrefixTargetLibBuiltin<"void ()">;
diff --git a/clang/lib/CodeGen/TargetBuiltins/ARM.cpp b/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
index 52a7564789fb7..9ea1c85b871af 100644
--- a/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
+++ b/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
@@ -4922,6 +4922,39 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID,
MTEIntrinsicID = Intrinsic::aarch64_subp; break;
}
+ if (BuiltinID == clang::AArch64::BI__arm_set_fpm_lscale) {
+ Value *FPM = EmitScalarExpr(E->getArg(0));
+ Value *Scale = EmitScalarExpr(E->getArg(1));
+
+ Scale = Builder.CreateZExtOrTrunc(Scale, FPM->getType());
+ Value *MaskedFPM = Builder.CreateAnd(FPM, Builder.getInt64(~0x7f0000ULL));
+ Value *ShiftedScale = Builder.CreateShl(Scale, Builder.getInt64(16));
+
+ return Builder.CreateOr(MaskedFPM, ShiftedScale);
+ }
+ if (BuiltinID == clang::AArch64::BI__arm_set_fpm_nscale) {
+ Value *FPM = EmitScalarExpr(E->getArg(0));
+ Value *Scale = EmitScalarExpr(E->getArg(1));
+
+ Scale = Builder.CreateZExtOrTrunc(Scale, FPM->getType());
+ Scale = Builder.CreateAnd(Scale, Builder.getInt64(0xff));
+
+ Value *MaskedFPM = Builder.CreateAnd(FPM, Builder.getInt64(~0xff000000ULL));
+ Value *ShiftedScale = Builder.CreateShl(Scale, Builder.getInt64(24));
+
+ return Builder.CreateOr(MaskedFPM, ShiftedScale);
+ }
+ if (BuiltinID == clang::AArch64::BI__arm_set_fpm_lscale2) {
+ Value *FPM = EmitScalarExpr(E->getArg(0));
+ Value *LowFPM = Builder.CreateAnd(FPM, Builder.getInt64(0xffffffff));
+ Value *Scale = EmitScalarExpr(E->getArg(1));
+
+ Scale = Builder.CreateZExtOrTrunc(Scale, FPM->getType());
+ Value *ShiftedScale = Builder.CreateShl(Scale, Builder.getInt64(32));
+
+ return Builder.CreateOr(LowFPM, ShiftedScale);
+ }
+
if (MTEIntrinsicID != Intrinsic::not_intrinsic) {
if (MTEIntrinsicID == Intrinsic::aarch64_irg) {
Value *Pointer = EmitScalarExpr(E->getArg(0));
diff --git a/clang/lib/Sema/SemaARM.cpp b/clang/lib/Sema/SemaARM.cpp
index 2bf6901bcc07f..1310bb80908fe 100644
--- a/clang/lib/Sema/SemaARM.cpp
+++ b/clang/lib/Sema/SemaARM.cpp
@@ -1156,6 +1156,26 @@ bool SemaARM::CheckARMBuiltinExclusiveCall(const TargetInfo &TI,
return false;
}
+static bool checkFPMScaleIfConstant(Sema &S, CallExpr *Call, unsigned ArgNum,
+ int64_t Low, int64_t High) {
+ Expr *Arg = Call->getArg(ArgNum);
+
+ if (Arg->isTypeDependent() || Arg->isValueDependent())
+ return false;
+
+ std::optional<llvm::APSInt> Value = Arg->getIntegerConstantExpr(S.Context);
+
+ // Runtime value: accept it.
+ if (!Value)
+ return false;
+
+ if (*Value < Low || *Value > High)
+ return S.Diag(Call->getBeginLoc(), diag::warn_argument_invalid_range)
+ << toString(*Value, 10) << Low << High << Arg->getSourceRange();
+
+ return false;
+}
+
bool SemaARM::CheckARMBuiltinFunctionCall(const TargetInfo &TI,
unsigned BuiltinID,
CallExpr *TheCall) {
@@ -1189,7 +1209,6 @@ bool SemaARM::CheckARMBuiltinFunctionCall(const TargetInfo &TI,
return true;
if (CheckCDEBuiltinFunctionCall(TI, BuiltinID, TheCall))
return true;
-
// For intrinsics which take an immediate value as part of the instruction,
// range check them here.
// FIXME: VFP Intrinsics should error if VFP not present.
@@ -1345,6 +1364,15 @@ bool SemaARM::CheckAArch64BuiltinFunctionCall(const TargetInfo &TI,
if (CheckSMEBuiltinFunctionCall(BuiltinID, TheCall))
return true;
+ if (BuiltinID == AArch64::BI__arm_set_fpm_lscale)
+ return checkFPMScaleIfConstant(SemaRef, TheCall, 1, 0, 127);
+
+ if (BuiltinID == AArch64::BI__arm_set_fpm_nscale)
+ return checkFPMScaleIfConstant(SemaRef, TheCall, 1, -128, 127);
+
+ if (BuiltinID == AArch64::BI__arm_set_fpm_lscale2)
+ return checkFPMScaleIfConstant(SemaRef, TheCall, 1, 0, 63);
+
// For intrinsics which take an immediate value as part of the instruction,
// range check them here.
unsigned i = 0, l = 0, u = 0;
diff --git a/clang/test/CodeGen/AArch64/fpm-helpers.c b/clang/test/CodeGen/AArch64/fpm-helpers.c
index 6264b5caeb4f5..f2e84bcd378c5 100644
--- a/clang/test/CodeGen/AArch64/fpm-helpers.c
+++ b/clang/test/CodeGen/AArch64/fpm-helpers.c
@@ -139,21 +139,21 @@ fpm_t test_lscale() { return __arm_set_fpm_lscale(INIT_ZERO, 127); }
//
fpm_t test_lscale2() { return __arm_set_fpm_lscale2(INIT_ZERO, 63); }
-// CHECK-LABEL: define dso_local noundef range(i64 0, 4278190081) i64 @test_nscale_1(
+// CHECK-LABEL: define dso_local noundef i64 @test_nscale_1(
// CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: ret i64 2147483648
//
fpm_t test_nscale_1() { return __arm_set_fpm_nscale(INIT_ZERO, -128); }
-// CHECK-LABEL: define dso_local noundef range(i64 0, 4278190081) i64 @test_nscale_2(
+// CHECK-LABEL: define dso_local noundef i64 @test_nscale_2(
// CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: ret i64 2130706432
//
fpm_t test_nscale_2() { return __arm_set_fpm_nscale(INIT_ZERO, 127); }
-// CHECK-LABEL: define dso_local noundef range(i64 0, 4278190081) i64 @test_nscale_3(
+// CHECK-LABEL: define dso_local noundef i64 @test_nscale_3(
// CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: ret i64 4278190080
diff --git a/clang/test/Sema/aarch64-fpm-helpers.c b/clang/test/Sema/aarch64-fpm-helpers.c
new file mode 100644
index 0000000000000..d1f38efc7a174
--- /dev/null
+++ b/clang/test/Sema/aarch64-fpm-helpers.c
@@ -0,0 +1,63 @@
+// RUN: %clang_cc1 -triple aarch64 -fsyntax-only -verify -DUSE_NEON_H %s
+// RUN: %clang_cc1 -triple aarch64 -fsyntax-only -verify -DUSE_SVE_H %s
+// RUN: %clang_cc1 -triple aarch64 -fsyntax-only -verify -DUSE_SME_H %s
+// RUN: %clang_cc1 -triple aarch64 -x c++ -fsyntax-only -verify -DUSE_NEON_H %s
+// RUN: %clang_cc1 -triple aarch64 -x c++ -fsyntax-only -verify -DUSE_SVE_H %s
+// RUN: %clang_cc1 -triple aarch64 -x c++ -fsyntax-only -verify -DUSE_SME_H %s
+
+// REQUIRES: aarch64-registered-target
+
+#ifdef USE_NEON_H
+#include "arm_neon.h"
+#endif
+
+#ifdef USE_SVE_H
+#include "arm_sve.h"
+#endif
+
+#ifdef USE_SME_H
+#include "arm_sme.h"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void test_lscale(fpm_t fpm, uint64_t variable) {
+ __arm_set_fpm_lscale(fpm, 0);
+ __arm_set_fpm_lscale(fpm, 127);
+ __arm_set_fpm_lscale(fpm, variable);
+
+ __arm_set_fpm_lscale(fpm, 128);
+ // expected-error at -1 {{argument value 128 is outside the valid range [0, 127]}}
+
+ __arm_set_fpm_lscale(fpm, -1);
+ // expected-error at -1 {{argument value 18446744073709551615 is outside the valid range [0, 127]}}
+}
+
+void test_nscale(fpm_t fpm, int64_t variable) {
+ __arm_set_fpm_nscale(fpm, -128);
+ __arm_set_fpm_nscale(fpm, 127);
+ __arm_set_fpm_nscale(fpm, variable);
+
+ __arm_set_fpm_nscale(fpm, -129);
+ // expected-error at -1 {{argument value -129 is outside the valid range [-128, 127]}}
+
+ __arm_set_fpm_nscale(fpm, 128);
+ // expected-error at -1 {{argument value 128 is outside the valid range [-128, 127]}}
+}
+
+void test_lscale2(fpm_t fpm, uint64_t variable) {
+ __arm_set_fpm_lscale2(fpm, 0);
+ __arm_set_fpm_lscale2(fpm, 63);
+ __arm_set_fpm_lscale2(fpm, variable);
+
+ __arm_set_fpm_lscale2(fpm, 64);
+ // expected-error at -1 {{argument value 64 is outside the valid range [0, 63]}}
+
+ __arm_set_fpm_lscale2(fpm, -1);
+ // expected-error at -1 {{argument value 18446744073709551615 is outside the valid range [0, 63]}}
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/clang/utils/TableGen/NeonEmitter.cpp b/clang/utils/TableGen/NeonEmitter.cpp
index 6c90b55dbf648..e567a27032d12 100644
--- a/clang/utils/TableGen/NeonEmitter.cpp
+++ b/clang/utils/TableGen/NeonEmitter.cpp
@@ -2694,20 +2694,9 @@ __arm_set_fpm_overflow_cvt(fpm_t __fpm, enum __ARM_FPM_OVERFLOW __behaviour) {
return (__fpm & ~0x8000ull) | ((fpm_t)__behaviour << 15u);
}
-static __inline__ fpm_t __attribute__((__always_inline__, __nodebug__))
-__arm_set_fpm_lscale(fpm_t __fpm, uint64_t __scale) {
- return (__fpm & ~0x7f0000ull) | (__scale << 16u);
-}
-
-static __inline__ fpm_t __attribute__((__always_inline__, __nodebug__))
-__arm_set_fpm_nscale(fpm_t __fpm, int64_t __scale) {
- return (__fpm & ~0xff000000ull) | (((fpm_t)__scale & 0xffu) << 24u);
-}
-
-static __inline__ fpm_t __attribute__((__always_inline__, __nodebug__))
-__arm_set_fpm_lscale2(fpm_t __fpm, uint64_t __scale) {
- return (uint32_t)__fpm | (__scale << 32u);
-}
+__inline__ fpm_t __attribute__((__always_inline__, __nodebug__)) __arm_set_fpm_lscale(fpm_t __fpm, uint64_t __scale);
+__inline__ fpm_t __attribute__((__always_inline__, __nodebug__)) __arm_set_fpm_nscale(fpm_t __fpm, int64_t __scale);
+__inline__ fpm_t __attribute__((__always_inline__, __nodebug__)) __arm_set_fpm_lscale2(fpm_t __fpm, uint64_t __scale);
)";
More information about the cfe-commits
mailing list