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 <[email protected]> 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@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 +} + _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
