[clang] [llvm] [DAG] computeKnownFPClass - Add handling for AssertNoFPClass (PR #190185)
Joao Victor Amorim Vieira via llvm-commits
llvm-commits at lists.llvm.org
Sun Apr 5 12:31:51 PDT 2026
https://github.com/joaovam updated https://github.com/llvm/llvm-project/pull/190185
>From 73f7758c11889befb4ec52a9bbe49d1c0dc3cb47 Mon Sep 17 00:00:00 2001
From: Aditya Medhane <153186739+flash1729 at users.noreply.github.com>
Date: Thu, 2 Apr 2026 19:40:48 +0530
Subject: [PATCH] [APINotes][BoundsSafety] Upstream API notes format for
bounds-safety function parameters
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Related: #183340
Upstream from swiftlang/llvm-project at 721e6a3 with some modifications.
Since __counted_by and related attributes aren't supported upstream yet
in function signatures, this only upstreams the format part — YAML
parsing, binary serialization, and deserialization. Semantic application
is a follow-up.
Modifications from downstream:
- Renamed local YAML struct from `BoundsSafety` to `BoundsSafetyNotes`
to avoid name collision with `Param::BoundsSafety` field (downstream
fixed this in a subsequent commit)
- `Level` in `BoundsSafetyNotes` is now `std::optional<unsigned>` so we
can actually tell apart "user didn't write Level:" from "Level: 0"
- `asdf_sized` and `asdf_sized_n` use `void *buf` — makes more sense for
sized_by
- Added `asdf_counted_indirect` with `int **` and `Level: 1` to test the
indirection level path
- Removed a vacuous assert (`CountedBy == 0`, always true for unsigned)
- Added `operator!=` for `BoundsSafetyInfo` to match other types in
`Types.h`
- Added doc comments for `LevelAudited` and `Level`
---
clang/include/clang/APINotes/Types.h | 82 ++++++++++++++++++-
clang/lib/APINotes/APINotesFormat.h | 2 +-
clang/lib/APINotes/APINotesReader.cpp | 26 +++++-
clang/lib/APINotes/APINotesTypes.cpp | 28 +++++++
clang/lib/APINotes/APINotesWriter.cpp | 35 +++++++-
clang/lib/APINotes/APINotesYAMLCompiler.cpp | 43 ++++++++++
.../Inputs/Headers/BoundsUnsafe.apinotes | 39 +++++++++
.../APINotes/Inputs/Headers/BoundsUnsafe.h | 6 ++
.../APINotes/Inputs/Headers/module.modulemap | 4 +
clang/test/APINotes/bounds-safety.c | 28 +++++++
.../lib/CodeGen/SelectionDAG/SelectionDAG.cpp | 9 ++
llvm/test/CodeGen/RISCV/known-fpclass.ll | 52 ++++++++++++
12 files changed, 349 insertions(+), 5 deletions(-)
create mode 100644 clang/test/APINotes/Inputs/Headers/BoundsUnsafe.apinotes
create mode 100644 clang/test/APINotes/Inputs/Headers/BoundsUnsafe.h
create mode 100644 clang/test/APINotes/bounds-safety.c
create mode 100644 llvm/test/CodeGen/RISCV/known-fpclass.ll
diff --git a/clang/include/clang/APINotes/Types.h b/clang/include/clang/APINotes/Types.h
index fb2b91a3e1750..0f370953fca59 100644
--- a/clang/include/clang/APINotes/Types.h
+++ b/clang/include/clang/APINotes/Types.h
@@ -338,6 +338,78 @@ inline bool operator!=(const ContextInfo &LHS, const ContextInfo &RHS) {
return !(LHS == RHS);
}
+class BoundsSafetyInfo {
+public:
+ enum class BoundsSafetyKind {
+ CountedBy,
+ CountedByOrNull,
+ SizedBy,
+ SizedByOrNull,
+ EndedBy,
+ };
+
+private:
+ /// The kind of bounds safety for this property. Only valid if the bounds
+ /// safety has been audited.
+ LLVM_PREFERRED_TYPE(BoundsSafetyKind)
+ unsigned Kind : 3;
+
+ /// Whether the bounds safety kind has been audited.
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned KindAudited : 1;
+
+ /// The pointer indirection level at which the bounds annotation applies.
+ /// Only valid if LevelAudited is set.
+ unsigned Level : 3;
+
+ /// Whether the pointer indirection level has been specified.
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned LevelAudited : 1;
+
+public:
+ std::string ExternalBounds;
+
+ BoundsSafetyInfo()
+ : Kind(0), KindAudited(false), Level(0), LevelAudited(false),
+ ExternalBounds("") {}
+
+ std::optional<BoundsSafetyKind> getKind() const {
+ return KindAudited ? std::optional<BoundsSafetyKind>(
+ static_cast<BoundsSafetyKind>(Kind))
+ : std::nullopt;
+ }
+
+ void setKindAudited(BoundsSafetyKind kind) {
+ KindAudited = true;
+ Kind = static_cast<unsigned>(kind);
+ }
+
+ std::optional<unsigned> getLevel() const {
+ return LevelAudited ? std::optional<unsigned>(Level) : std::nullopt;
+ }
+
+ void setLevelAudited(unsigned level) {
+ LevelAudited = true;
+ Level = level;
+ }
+
+ friend bool operator==(const BoundsSafetyInfo &, const BoundsSafetyInfo &);
+
+ LLVM_DUMP_METHOD void dump(llvm::raw_ostream &OS) const;
+};
+
+inline bool operator==(const BoundsSafetyInfo &LHS,
+ const BoundsSafetyInfo &RHS) {
+ return LHS.KindAudited == RHS.KindAudited && LHS.Kind == RHS.Kind &&
+ LHS.LevelAudited == RHS.LevelAudited && LHS.Level == RHS.Level &&
+ LHS.ExternalBounds == RHS.ExternalBounds;
+}
+
+inline bool operator!=(const BoundsSafetyInfo &LHS,
+ const BoundsSafetyInfo &RHS) {
+ return !(LHS == RHS);
+}
+
/// API notes for a variable/property.
class VariableInfo : public CommonEntityInfo {
/// Whether this property has been audited for nullability.
@@ -477,10 +549,12 @@ class ParamInfo : public VariableInfo {
unsigned RawRetainCountConvention : 3;
public:
+ std::optional<BoundsSafetyInfo> BoundsSafety;
+
ParamInfo()
: NoEscapeSpecified(false), NoEscape(false),
LifetimeboundSpecified(false), Lifetimebound(false),
- RawRetainCountConvention() {}
+ RawRetainCountConvention(), BoundsSafety(std::nullopt) {}
std::optional<bool> isNoEscape() const {
return NoEscapeSpecified ? std::optional<bool>(NoEscape) : std::nullopt;
@@ -526,6 +600,9 @@ class ParamInfo : public VariableInfo {
if (!RawRetainCountConvention)
RawRetainCountConvention = RHS.RawRetainCountConvention;
+ if (!BoundsSafety)
+ BoundsSafety = RHS.BoundsSafety;
+
return *this;
}
@@ -540,7 +617,8 @@ inline bool operator==(const ParamInfo &LHS, const ParamInfo &RHS) {
LHS.NoEscape == RHS.NoEscape &&
LHS.LifetimeboundSpecified == RHS.LifetimeboundSpecified &&
LHS.Lifetimebound == RHS.Lifetimebound &&
- LHS.RawRetainCountConvention == RHS.RawRetainCountConvention;
+ LHS.RawRetainCountConvention == RHS.RawRetainCountConvention &&
+ LHS.BoundsSafety == RHS.BoundsSafety;
}
inline bool operator!=(const ParamInfo &LHS, const ParamInfo &RHS) {
diff --git a/clang/lib/APINotes/APINotesFormat.h b/clang/lib/APINotes/APINotesFormat.h
index 9d868a1b4da1f..6b1cc3128ca42 100644
--- a/clang/lib/APINotes/APINotesFormat.h
+++ b/clang/lib/APINotes/APINotesFormat.h
@@ -24,7 +24,7 @@ const uint16_t VERSION_MAJOR = 0;
/// API notes file minor version number.
///
/// When the format changes IN ANY WAY, this number should be incremented.
-const uint16_t VERSION_MINOR = 38; // SwiftSafety
+const uint16_t VERSION_MINOR = 39; // BoundsSafety
const uint8_t kSwiftConforms = 1;
const uint8_t kSwiftDoesNotConform = 2;
diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp
index f00c7ac1d9d9b..eef98450fef8b 100644
--- a/clang/lib/APINotes/APINotesReader.cpp
+++ b/clang/lib/APINotes/APINotesReader.cpp
@@ -330,6 +330,29 @@ class FieldTableInfo
}
};
+/// Read serialized BoundsSafetyInfo.
+void ReadBoundsSafetyInfo(const uint8_t *&Data, BoundsSafetyInfo &Info) {
+ uint8_t Payload = endian::readNext<uint8_t, llvm::endianness::little>(Data);
+
+ if (Payload & 0x01) {
+ uint8_t Level = (Payload >> 1) & 0x7;
+ Info.setLevelAudited(Level);
+ }
+ Payload >>= 4;
+
+ if (Payload & 0x01) {
+ uint8_t Kind = (Payload >> 1) & 0x7;
+ assert(Kind <=
+ static_cast<uint8_t>(BoundsSafetyInfo::BoundsSafetyKind::EndedBy));
+ Info.setKindAudited(static_cast<BoundsSafetyInfo::BoundsSafetyKind>(Kind));
+ }
+
+ uint16_t ExternalBoundsLen =
+ endian::readNext<uint16_t, llvm::endianness::little>(Data);
+ Info.ExternalBounds = std::string(Data, Data + ExternalBoundsLen);
+ Data += ExternalBoundsLen;
+}
+
/// Read serialized ParamInfo.
void ReadParamInfo(const uint8_t *&Data, ParamInfo &Info) {
ReadVariableInfo(Data, Info);
@@ -346,7 +369,8 @@ void ReadParamInfo(const uint8_t *&Data, ParamInfo &Info) {
if (Payload & 0x01)
Info.setNoEscape(Payload & 0x02);
Payload >>= 2;
- assert(Payload == 0 && "Bad API notes");
+ if (Payload & 0x01)
+ ReadBoundsSafetyInfo(Data, Info.BoundsSafety.emplace());
}
/// Read serialized FunctionInfo.
diff --git a/clang/lib/APINotes/APINotesTypes.cpp b/clang/lib/APINotes/APINotesTypes.cpp
index bff4be104c6c8..78b3b5a237e38 100644
--- a/clang/lib/APINotes/APINotesTypes.cpp
+++ b/clang/lib/APINotes/APINotesTypes.cpp
@@ -76,6 +76,32 @@ LLVM_DUMP_METHOD void ObjCPropertyInfo::dump(llvm::raw_ostream &OS) const {
OS << '\n';
}
+LLVM_DUMP_METHOD void BoundsSafetyInfo::dump(llvm::raw_ostream &OS) const {
+ if (KindAudited) {
+ switch (static_cast<BoundsSafetyKind>(Kind)) {
+ case BoundsSafetyKind::CountedBy:
+ OS << "[counted_by] ";
+ break;
+ case BoundsSafetyKind::CountedByOrNull:
+ OS << "[counted_by_or_null] ";
+ break;
+ case BoundsSafetyKind::SizedBy:
+ OS << "[sized_by] ";
+ break;
+ case BoundsSafetyKind::SizedByOrNull:
+ OS << "[sized_by_or_null] ";
+ break;
+ case BoundsSafetyKind::EndedBy:
+ OS << "[ended_by] ";
+ break;
+ }
+ }
+ if (LevelAudited)
+ OS << "Level: " << Level << " ";
+ OS << "ExternalBounds: "
+ << (ExternalBounds.empty() ? "<missing>" : ExternalBounds) << '\n';
+}
+
LLVM_DUMP_METHOD void ParamInfo::dump(llvm::raw_ostream &OS) const {
static_cast<const VariableInfo &>(*this).dump(OS);
if (NoEscapeSpecified)
@@ -84,6 +110,8 @@ LLVM_DUMP_METHOD void ParamInfo::dump(llvm::raw_ostream &OS) const {
OS << (Lifetimebound ? "[Lifetimebound] " : "");
OS << "RawRetainCountConvention: " << RawRetainCountConvention << ' ';
OS << '\n';
+ if (BoundsSafety)
+ BoundsSafety->dump(OS);
}
LLVM_DUMP_METHOD void FunctionInfo::dump(llvm::raw_ostream &OS) const {
diff --git a/clang/lib/APINotes/APINotesWriter.cpp b/clang/lib/APINotes/APINotesWriter.cpp
index 390aea57f3915..c7f2348523d6e 100644
--- a/clang/lib/APINotes/APINotesWriter.cpp
+++ b/clang/lib/APINotes/APINotesWriter.cpp
@@ -1074,14 +1074,45 @@ void APINotesWriter::Implementation::writeGlobalVariableBlock(
}
namespace {
+void emitBoundsSafetyInfo(raw_ostream &OS, const BoundsSafetyInfo &BSI) {
+ llvm::support::endian::Writer writer(OS, llvm::endianness::little);
+ uint8_t flags = 0;
+ if (auto kind = BSI.getKind()) {
+ assert(*kind <= BoundsSafetyInfo::BoundsSafetyKind::EndedBy);
+ flags |= 0x01; // 1 bit
+ flags |= (uint8_t)*kind << 1; // 3 bits
+ }
+ flags <<= 4;
+ if (auto level = BSI.getLevel()) {
+ assert(*level < (1u << 3));
+ flags |= 0x01; // 1 bit
+ flags |= (uint8_t)*level << 1; // 3 bits
+ }
+
+ writer.write<uint8_t>(flags);
+ writer.write<uint16_t>(BSI.ExternalBounds.size());
+ writer.write(
+ ArrayRef<char>{BSI.ExternalBounds.data(), BSI.ExternalBounds.size()});
+}
+
+unsigned getBoundsSafetyInfoSize(const BoundsSafetyInfo &BSI) {
+ return 1 + sizeof(uint16_t) + BSI.ExternalBounds.size();
+}
+
unsigned getParamInfoSize(const ParamInfo &PI) {
- return getVariableInfoSize(PI) + 1;
+ unsigned BSISize = 0;
+ if (auto BSI = PI.BoundsSafety)
+ BSISize = getBoundsSafetyInfoSize(*BSI);
+ return getVariableInfoSize(PI) + 1 + BSISize;
}
void emitParamInfo(raw_ostream &OS, const ParamInfo &PI) {
emitVariableInfo(OS, PI);
uint8_t flags = 0;
+ if (PI.BoundsSafety)
+ flags |= 0x01;
+ flags <<= 2;
if (auto noescape = PI.isNoEscape()) {
flags |= 0x01;
if (*noescape)
@@ -1099,6 +1130,8 @@ void emitParamInfo(raw_ostream &OS, const ParamInfo &PI) {
llvm::support::endian::Writer writer(OS, llvm::endianness::little);
writer.write<uint8_t>(flags);
+ if (auto BSI = PI.BoundsSafety)
+ emitBoundsSafetyInfo(OS, *BSI);
}
/// Retrieve the serialized size of the given FunctionInfo, for use in on-disk
diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp
index 3b82fdda68af1..16435b1f15769 100644
--- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp
+++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp
@@ -49,6 +49,31 @@ enum class APIAvailability {
};
} // namespace
+namespace {
+struct BoundsSafetyNotes {
+ BoundsSafetyInfo::BoundsSafetyKind Kind;
+ std::optional<unsigned> Level;
+ StringRef BoundsExpr = "";
+};
+} // namespace
+
+namespace llvm {
+namespace yaml {
+template <> struct ScalarEnumerationTraits<BoundsSafetyInfo::BoundsSafetyKind> {
+ static void enumeration(IO &IO, BoundsSafetyInfo::BoundsSafetyKind &AA) {
+ IO.enumCase(AA, "counted_by",
+ BoundsSafetyInfo::BoundsSafetyKind::CountedBy);
+ IO.enumCase(AA, "counted_by_or_null",
+ BoundsSafetyInfo::BoundsSafetyKind::CountedByOrNull);
+ IO.enumCase(AA, "sized_by", BoundsSafetyInfo::BoundsSafetyKind::SizedBy);
+ IO.enumCase(AA, "sized_by_or_null",
+ BoundsSafetyInfo::BoundsSafetyKind::SizedByOrNull);
+ IO.enumCase(AA, "ended_by", BoundsSafetyInfo::BoundsSafetyKind::EndedBy);
+ }
+};
+} // namespace yaml
+} // namespace llvm
+
namespace llvm {
namespace yaml {
template <> struct ScalarEnumerationTraits<APIAvailability> {
@@ -86,6 +111,7 @@ struct Param {
std::optional<bool> Lifetimebound = false;
std::optional<NullabilityKind> Nullability;
std::optional<RetainCountConventionKind> RetainCountConvention;
+ std::optional<BoundsSafetyNotes> BoundsSafety;
StringRef Type;
};
@@ -137,6 +163,15 @@ template <> struct MappingTraits<Param> {
IO.mapOptional("NoEscape", P.NoEscape);
IO.mapOptional("Lifetimebound", P.Lifetimebound);
IO.mapOptional("Type", P.Type, StringRef(""));
+ IO.mapOptional("BoundsSafety", P.BoundsSafety);
+ }
+};
+
+template <> struct MappingTraits<BoundsSafetyNotes> {
+ static void mapping(IO &IO, BoundsSafetyNotes &BS) {
+ IO.mapRequired("Kind", BS.Kind);
+ IO.mapRequired("BoundedBy", BS.BoundsExpr);
+ IO.mapOptional("Level", BS.Level);
}
};
} // namespace yaml
@@ -787,6 +822,14 @@ class YAMLConverter {
PI.setLifetimebound(P.Lifetimebound);
PI.setType(std::string(P.Type));
PI.setRetainCountConvention(P.RetainCountConvention);
+ if (P.BoundsSafety) {
+ BoundsSafetyInfo BSI;
+ BSI.setKindAudited(P.BoundsSafety->Kind);
+ if (P.BoundsSafety->Level)
+ BSI.setLevelAudited(*P.BoundsSafety->Level);
+ BSI.ExternalBounds = P.BoundsSafety->BoundsExpr.str();
+ PI.BoundsSafety = BSI;
+ }
if (static_cast<int>(OutInfo.Params.size()) <= P.Position)
OutInfo.Params.resize(P.Position + 1);
if (P.Position == -1)
diff --git a/clang/test/APINotes/Inputs/Headers/BoundsUnsafe.apinotes b/clang/test/APINotes/Inputs/Headers/BoundsUnsafe.apinotes
new file mode 100644
index 0000000000000..27b44dae0466d
--- /dev/null
+++ b/clang/test/APINotes/Inputs/Headers/BoundsUnsafe.apinotes
@@ -0,0 +1,39 @@
+Name: BoundsUnsafe
+Functions:
+ - Name: asdf_counted
+ Parameters:
+ - Position: 0
+ BoundsSafety:
+ Kind: counted_by
+ BoundedBy: len
+ - Name: asdf_sized
+ Parameters:
+ - Position: 0
+ BoundsSafety:
+ Kind: sized_by
+ BoundedBy: size
+ - Name: asdf_counted_n
+ Parameters:
+ - Position: 0
+ BoundsSafety:
+ Kind: counted_by_or_null
+ BoundedBy: len
+ - Name: asdf_sized_n
+ Parameters:
+ - Position: 0
+ BoundsSafety:
+ Kind: sized_by_or_null
+ BoundedBy: size
+ - Name: asdf_ended
+ Parameters:
+ - Position: 0
+ BoundsSafety:
+ Kind: ended_by
+ BoundedBy: end
+ - Name: asdf_counted_indirect
+ Parameters:
+ - Position: 0
+ BoundsSafety:
+ Kind: counted_by
+ BoundedBy: len
+ Level: 1
diff --git a/clang/test/APINotes/Inputs/Headers/BoundsUnsafe.h b/clang/test/APINotes/Inputs/Headers/BoundsUnsafe.h
new file mode 100644
index 0000000000000..2122b5c9757b2
--- /dev/null
+++ b/clang/test/APINotes/Inputs/Headers/BoundsUnsafe.h
@@ -0,0 +1,6 @@
+void asdf_counted(int *buf, int len);
+void asdf_sized(void *buf, int size);
+void asdf_counted_n(int *buf, int len);
+void asdf_sized_n(void *buf, int size);
+void asdf_ended(int *buf, int *end);
+void asdf_counted_indirect(int **buf, int len);
diff --git a/clang/test/APINotes/Inputs/Headers/module.modulemap b/clang/test/APINotes/Inputs/Headers/module.modulemap
index bedb7d505f794..a83844c117fcf 100644
--- a/clang/test/APINotes/Inputs/Headers/module.modulemap
+++ b/clang/test/APINotes/Inputs/Headers/module.modulemap
@@ -1,3 +1,7 @@
+module BoundsUnsafe {
+ header "BoundsUnsafe.h"
+}
+
module ExternCtx {
header "ExternCtx.h"
}
diff --git a/clang/test/APINotes/bounds-safety.c b/clang/test/APINotes/bounds-safety.c
new file mode 100644
index 0000000000000..d9c6032aeb568
--- /dev/null
+++ b/clang/test/APINotes/bounds-safety.c
@@ -0,0 +1,28 @@
+// RUN: rm -rf %t && mkdir -p %t
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fsyntax-only -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter asdf | FileCheck %s
+
+// FIXME: Bounds safety annotations are parsed and stored in the APINotes
+// format, but are not yet applied as attributes in the AST upstream since
+// __counted_by and related attributes are not yet supported in function
+// signatures. Once that support is added, update this test to verify the
+// annotations appear on the relevant declarations.
+
+#include "BoundsUnsafe.h"
+
+// CHECK: imported in BoundsUnsafe asdf_counted 'void (int *, int)'
+// CHECK: imported in BoundsUnsafe buf 'int *'
+
+// CHECK: imported in BoundsUnsafe asdf_sized 'void (void *, int)'
+// CHECK: imported in BoundsUnsafe buf 'void *'
+
+// CHECK: imported in BoundsUnsafe asdf_counted_n 'void (int *, int)'
+// CHECK: imported in BoundsUnsafe buf 'int *'
+
+// CHECK: imported in BoundsUnsafe asdf_sized_n 'void (void *, int)'
+// CHECK: imported in BoundsUnsafe buf 'void *'
+
+// CHECK: imported in BoundsUnsafe asdf_ended 'void (int *, int *)'
+// CHECK: imported in BoundsUnsafe buf 'int *'
+
+// CHECK: imported in BoundsUnsafe asdf_counted_indirect 'void (int **, int)'
+// CHECK: imported in BoundsUnsafe buf 'int **'
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
index 60b1fc6a5166b..626066c7cc41c 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
@@ -6094,6 +6094,15 @@ KnownFPClass SelectionDAG::computeKnownFPClass(SDValue Op,
Known = KnownFPClass::bitcast(VT.getFltSemantics(), Bits);
break;
}
+ case ISD::AssertNoFPClass: {
+ Known = computeKnownFPClass(Op.getOperand(0), DemandedElts,
+ InterestedClasses, Depth + 1);
+ FPClassTest AssertedClasses =
+ static_cast<FPClassTest>(Op->getConstantOperandVal(1));
+ Known.KnownFPClasses &= ~AssertedClasses;
+ break;
+ }
+
default:
if (Opcode >= ISD::BUILTIN_OP_END || Opcode == ISD::INTRINSIC_WO_CHAIN ||
Opcode == ISD::INTRINSIC_W_CHAIN || Opcode == ISD::INTRINSIC_VOID) {
diff --git a/llvm/test/CodeGen/RISCV/known-fpclass.ll b/llvm/test/CodeGen/RISCV/known-fpclass.ll
new file mode 100644
index 0000000000000..bc39817f6d5e1
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/known-fpclass.ll
@@ -0,0 +1,52 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc -mtriple=riscv64 -mattr=+v,+f,+d -target-abi=lp64d < %s | FileCheck %s
+
+define <vscale x 4 x i1> @test_fcmp_ord_no_nan(<vscale x 4 x float> nofpclass(nan) %a, <vscale x 4 x float> nofpclass(nan) %b) {
+; CHECK-LABEL: test_fcmp_ord_no_nan:
+; CHECK: # %bb.0:
+; CHECK-NEXT: vsetvli a0, zero, e32, m2, ta, ma
+; CHECK-NEXT: vmfeq.vv v12, v10, v10
+; CHECK-NEXT: vmfeq.vv v10, v8, v8
+; CHECK-NEXT: vmand.mm v0, v10, v12
+; CHECK-NEXT: ret
+ %cmp = fcmp ord <vscale x 4 x float> %a, %b
+ ret <vscale x 4 x i1> %cmp
+}
+
+define <vscale x 4 x i1> @test_fcmp_uno_no_nan(<vscale x 4 x float> nofpclass(nan) %a, <vscale x 4 x float> nofpclass(nan) %b) {
+; CHECK-LABEL: test_fcmp_uno_no_nan:
+; CHECK: # %bb.0:
+; CHECK-NEXT: vsetvli a0, zero, e32, m2, ta, ma
+; CHECK-NEXT: vmfne.vv v12, v10, v10
+; CHECK-NEXT: vmfne.vv v10, v8, v8
+; CHECK-NEXT: vmor.mm v0, v10, v12
+; CHECK-NEXT: ret
+ %cmp = fcmp uno <vscale x 4 x float> %a, %b
+ ret <vscale x 4 x i1> %cmp
+}
+
+declare <vscale x 4 x i1> @llvm.is.fpclass.nxv4f32(<vscale x 4 x float>, i32)
+
+define <vscale x 4 x i1> @test_is_fpclass_no_inf(<vscale x 4 x float> nofpclass(inf) %a) {
+ ; 512 = 0x200 = fcPosInf | fcNegInf
+; CHECK-LABEL: test_is_fpclass_no_inf:
+; CHECK: # %bb.0:
+; CHECK-NEXT: vsetvli a0, zero, e8, mf2, ta, ma
+; CHECK-NEXT: vmclr.m v0
+; CHECK-NEXT: ret
+ %class = call <vscale x 4 x i1> @llvm.is.fpclass.nxv4f32(<vscale x 4 x float> %a, i32 512)
+ ret <vscale x 4 x i1> %class
+}
+
+define <vscale x 4 x i1> @test_fabs_no_nan(<vscale x 4 x float> nofpclass(nan) %a) {
+; CHECK-LABEL: test_fabs_no_nan:
+; CHECK: # %bb.0:
+; CHECK-NEXT: vsetvli a0, zero, e32, m2, ta, ma
+; CHECK-NEXT: vfabs.v v8, v8
+; CHECK-NEXT: vmfne.vv v0, v8, v8
+; CHECK-NEXT: ret
+ %abs = call <vscale x 4 x float> @llvm.fabs.nxv4f32(<vscale x 4 x float> %a)
+ %cmp = fcmp uno <vscale x 4 x float> %abs, %abs
+ ret <vscale x 4 x i1> %cmp
+}
+
More information about the llvm-commits
mailing list