[clang] [Clang][AArch64] Fix checkArmStreamingBuiltin for 'sve-b16b16' (PR #109420)

Sander de Smalen via cfe-commits cfe-commits at lists.llvm.org
Tue Sep 24 00:27:21 PDT 2024


https://github.com/sdesmalen-arm updated https://github.com/llvm/llvm-project/pull/109420

>From af4cd0b3643e682fcb34042d209df03037743eb0 Mon Sep 17 00:00:00 2001
From: Sander de Smalen <sander.desmalen at arm.com>
Date: Fri, 20 Sep 2024 14:16:23 +0100
Subject: [PATCH 1/2] [Clang][AArch64] Fix checkArmStreamingBuiltin for
 'sve-b16b16'

The implementation made the assumption that any feature starting with
"sve" meant that this was an SVE feature. This is not the case for
"sve-b16b16", as this is a feature that applies to both SVE and SME.

This meant that:

  __attribute__((target("+sme2,+sve2,+sve-b16b16")))
  svbfloat16_t foo(svbfloat16_t a, svbfloat16_t b, svbfloat16_t c)
                                                      __arm_streaming {
      return svclamp_bf16(a, b, c);
  }

would result in an incorrect diagnostic saying that `svclamp_bf16`
could only be used in non-streaming functions.
---
 clang/lib/Sema/SemaARM.cpp                    | 21 ++++++++++++-------
 ...reaming-sme-or-nonstreaming-sve-builtins.c |  6 ++++++
 2 files changed, 19 insertions(+), 8 deletions(-)

diff --git a/clang/lib/Sema/SemaARM.cpp b/clang/lib/Sema/SemaARM.cpp
index efde354860de43..fba1453e5d38fc 100644
--- a/clang/lib/Sema/SemaARM.cpp
+++ b/clang/lib/Sema/SemaARM.cpp
@@ -567,15 +567,18 @@ static bool checkArmStreamingBuiltin(Sema &S, CallExpr *TheCall,
   // * When compiling for SVE only, the caller must be in non-streaming mode.
   // * When compiling for both SVE and SME, the caller can be in either mode.
   if (BuiltinType == SemaARM::VerifyRuntimeMode) {
-    auto DisableFeatures = [](llvm::StringMap<bool> &Map, StringRef S) {
-      for (StringRef K : Map.keys())
-        if (K.starts_with(S))
-          Map[K] = false;
-    };
-
     llvm::StringMap<bool> CallerFeatureMapWithoutSVE;
     S.Context.getFunctionFeatureMap(CallerFeatureMapWithoutSVE, FD);
-    DisableFeatures(CallerFeatureMapWithoutSVE, "sve");
+    CallerFeatureMapWithoutSVE["sve"] = false;
+    CallerFeatureMapWithoutSVE["sve2"] = false;
+    CallerFeatureMapWithoutSVE["sve2p1"] = false;
+    // FIXME: This list must be updated with future extensions, because when
+    // an intrinsic is enabled by (sve2p1|sme2p1), disabling just "sve" is
+    // not sufficient, as the feature dependences are not resolved.
+    // At the moment, it should be sufficient to test the 'base' architectural
+    // support for SVE and SME, which must always be provided in the
+    // target guard. e.g. TargetGuard = "sve-b16b16" without "sme" or "sve"
+    // is not sufficient.
 
     // Avoid emitting diagnostics for a function that can never compile.
     if (FnType == SemaARM::ArmStreaming && !CallerFeatureMapWithoutSVE["sme"])
@@ -583,7 +586,9 @@ static bool checkArmStreamingBuiltin(Sema &S, CallExpr *TheCall,
 
     llvm::StringMap<bool> CallerFeatureMapWithoutSME;
     S.Context.getFunctionFeatureMap(CallerFeatureMapWithoutSME, FD);
-    DisableFeatures(CallerFeatureMapWithoutSME, "sme");
+    CallerFeatureMapWithoutSME["sme"] = false;
+    CallerFeatureMapWithoutSME["sme2"] = false;
+    CallerFeatureMapWithoutSME["sme2p1"] = false;
 
     // We know the builtin requires either some combination of SVE flags, or
     // some combination of SME flags, but we need to figure out which part
diff --git a/clang/test/Sema/aarch64-streaming-sme-or-nonstreaming-sve-builtins.c b/clang/test/Sema/aarch64-streaming-sme-or-nonstreaming-sve-builtins.c
index 45776eb13e4fbc..792d79ee3e600d 100644
--- a/clang/test/Sema/aarch64-streaming-sme-or-nonstreaming-sve-builtins.c
+++ b/clang/test/Sema/aarch64-streaming-sme-or-nonstreaming-sve-builtins.c
@@ -38,6 +38,12 @@ svfloat32_t good6(svfloat32_t a, svfloat32_t b, svfloat32_t c) __arm_streaming_c
   return svclamp(a, b, c);
 }
 
+// Test that the +sve-b16b16 is not considered an SVE flag (it applies to both)
+__attribute__((target("+sme2,+sve2,+sve-b16b16")))
+svbfloat16_t good7(svbfloat16_t a, svbfloat16_t b, svbfloat16_t c) __arm_streaming {
+    return svclamp_bf16(a, b, c);
+}
+
 // Without '+sme2', the builtin is only valid in non-streaming mode.
 __attribute__((target("+sve2p1,+sme")))
 svfloat32_t bad1(svfloat32_t a, svfloat32_t b, svfloat32_t c) __arm_streaming {

>From bfa1348e06a78c2cc30f2cf7e64ae993191fcd2d Mon Sep 17 00:00:00 2001
From: Sander de Smalen <sander.desmalen at arm.com>
Date: Tue, 24 Sep 2024 07:23:37 +0000
Subject: [PATCH 2/2] Add TableGen checks

---
 clang/lib/Sema/SemaARM.cpp          | 20 +++-----
 clang/utils/TableGen/SveEmitter.cpp | 76 ++++++++++++++++++++++++++++-
 2 files changed, 81 insertions(+), 15 deletions(-)

diff --git a/clang/lib/Sema/SemaARM.cpp b/clang/lib/Sema/SemaARM.cpp
index fba1453e5d38fc..de2236207564bd 100644
--- a/clang/lib/Sema/SemaARM.cpp
+++ b/clang/lib/Sema/SemaARM.cpp
@@ -569,16 +569,9 @@ static bool checkArmStreamingBuiltin(Sema &S, CallExpr *TheCall,
   if (BuiltinType == SemaARM::VerifyRuntimeMode) {
     llvm::StringMap<bool> CallerFeatureMapWithoutSVE;
     S.Context.getFunctionFeatureMap(CallerFeatureMapWithoutSVE, FD);
-    CallerFeatureMapWithoutSVE["sve"] = false;
-    CallerFeatureMapWithoutSVE["sve2"] = false;
-    CallerFeatureMapWithoutSVE["sve2p1"] = false;
-    // FIXME: This list must be updated with future extensions, because when
-    // an intrinsic is enabled by (sve2p1|sme2p1), disabling just "sve" is
-    // not sufficient, as the feature dependences are not resolved.
-    // At the moment, it should be sufficient to test the 'base' architectural
-    // support for SVE and SME, which must always be provided in the
-    // target guard. e.g. TargetGuard = "sve-b16b16" without "sme" or "sve"
-    // is not sufficient.
+    for (StringRef Feat : {"sve", "sve2", "sve2p1", "sve2-aes", "sve2-sha3",
+                           "sve2-sm4", "sve2-bitperm"})
+      CallerFeatureMapWithoutSVE[Feat] = false;
 
     // Avoid emitting diagnostics for a function that can never compile.
     if (FnType == SemaARM::ArmStreaming && !CallerFeatureMapWithoutSVE["sme"])
@@ -586,9 +579,10 @@ static bool checkArmStreamingBuiltin(Sema &S, CallExpr *TheCall,
 
     llvm::StringMap<bool> CallerFeatureMapWithoutSME;
     S.Context.getFunctionFeatureMap(CallerFeatureMapWithoutSME, FD);
-    CallerFeatureMapWithoutSME["sme"] = false;
-    CallerFeatureMapWithoutSME["sme2"] = false;
-    CallerFeatureMapWithoutSME["sme2p1"] = false;
+    for (StringRef Feat :
+         {"sme", "sme2", "sme2p1", "sme-f64f64", "sme-i16i64", "sme-b16b16",
+          "sme-f16f16", "sme-f8f32", "sme-f8f16"})
+      CallerFeatureMapWithoutSME[Feat] = false;
 
     // We know the builtin requires either some combination of SVE flags, or
     // some combination of SME flags, but we need to figure out which part
diff --git a/clang/utils/TableGen/SveEmitter.cpp b/clang/utils/TableGen/SveEmitter.cpp
index 2f9747e7de3de2..8fdbd49655d4fe 100644
--- a/clang/utils/TableGen/SveEmitter.cpp
+++ b/clang/utils/TableGen/SveEmitter.cpp
@@ -1770,6 +1770,58 @@ void SVEEmitter::createBuiltinZAState(raw_ostream &OS) {
   OS << "#endif\n\n";
 }
 
+static StringRef parseGuardParenExpr(StringRef &S) {
+  unsigned N = 0;
+  assert(S[0] == '(' && "Expected lparen");
+  for (unsigned I = 0; I < S.size(); ++I) {
+    if (S[I] == '(')
+      ++N;
+    else if (S[I] == ')')
+      --N;
+    if (N == 0) {
+      StringRef Expr = S.substr(1, I - 1);
+      S = S.drop_front(I + 1);
+      return Expr;
+    }
+  }
+  llvm_unreachable("Unmatched parenthesi");
+}
+
+static StringRef parseGuardFeature(StringRef &S) {
+  assert(std::isalpha(S[0]) && "expected feature name");
+  unsigned I;
+  for (I = 0; I < S.size(); ++I) {
+    if (S[I] == ',' || S[I] == '|' || S[I] == ')')
+      break;
+  }
+  StringRef Expr = S.take_front(I);
+  S = S.drop_front(I);
+  return Expr;
+}
+
+static StringRef parseGuardExpr(StringRef &S) {
+  if (S[0] == '(')
+    return parseGuardParenExpr(S);
+  if (std::isalpha(S[0]))
+    return parseGuardFeature(S);
+  llvm_unreachable("Unexpected token in expression");
+}
+
+// Parse the TargetGuard and verify that it satisfies at least one of the
+// features from the Required list.
+static bool verifyGuard(StringRef S, ArrayRef<StringRef> Required) {
+  if (S.empty())
+    return false;
+  StringRef LHS = parseGuardExpr(S);
+  if (S.empty())
+    return llvm::any_of(Required, [LHS](StringRef R) { return R == LHS; });
+  if (S[0] == '|')
+    return verifyGuard(LHS, Required) && verifyGuard(S.drop_front(1), Required);
+  if (S[0] == ',')
+    return verifyGuard(LHS, Required) || verifyGuard(S.drop_front(1), Required);
+  llvm_unreachable("Unexpected token in expression");
+}
+
 void SVEEmitter::createStreamingAttrs(raw_ostream &OS, ACLEKind Kind) {
   std::vector<const Record *> RV = Records.getAllDerivedDefinitions("Inst");
   SmallVector<std::unique_ptr<Intrinsic>, 128> Defs;
@@ -1802,9 +1854,29 @@ void SVEEmitter::createStreamingAttrs(raw_ostream &OS, ACLEKind Kind) {
 
     if (Def->isFlagSet(IsStreamingFlag))
       StreamingMap["ArmStreaming"].insert(Def->getMangledName());
-    else if (Def->isFlagSet(VerifyRuntimeMode))
+    else if (Def->isFlagSet(VerifyRuntimeMode)) {
+      // Verify that the target guards contain at least one feature that
+      // actually enables SVE or SME (explicitly, or implicitly). This is needed
+      // for the code in SemaARM.cpp (checkArmStreamingBuiltin) that checks
+      // whether the required runtime mode for an intrinsic matches with the
+      // given set of target features and function attributes.
+      //
+      // The feature lists below must match the disabled features in
+      // 'checkArmStreamingBuiltin'!
+      if (!Def->getSVEGuard().empty() &&
+          !verifyGuard(Def->getSVEGuard(),
+                       {"sve", "sve2", "sve2p1", "sve2-aes", "sve2-sha3",
+                        "sve2-sm4", "sve2-bitperm"}))
+        llvm_unreachable(
+            "SVE guard must include at least one base SVE version");
+      if (!Def->getSMEGuard().empty() &&
+          !verifyGuard(Def->getSMEGuard(),
+                       {"sme", "sme2", "sme2p1", "sme-f64f64", "sme-i16i64",
+                        "sme-b16b16", "sme-f16f16", "sme-f8f32", "sme-f8f16"}))
+        llvm_unreachable(
+            "SME guard must include at least one base SME version");
       StreamingMap["VerifyRuntimeMode"].insert(Def->getMangledName());
-    else if (Def->isFlagSet(IsStreamingCompatibleFlag))
+    } else if (Def->isFlagSet(IsStreamingCompatibleFlag))
       StreamingMap["ArmStreamingCompatible"].insert(Def->getMangledName());
     else
       StreamingMap["ArmNonStreaming"].insert(Def->getMangledName());



More information about the cfe-commits mailing list