Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[AArch64][SME2] Add ZT0 attributes to SMEAttrs #77607

Merged
merged 6 commits into from
Jan 16, 2024

Conversation

kmclaughlin-arm
Copy link
Contributor

This patch extends SMEAttrs to interpret the following new attributes,
which are mutually exclusive and apply to SME2 only:

  • aarch64_sme_zt0_in (ZT0_In)
  • aarch64_sme_zt0_out (ZT0_Out)
  • aarch64_sme_zt0_inout (ZT0_InOut)
  • aarch64_sme_zt0_new (ZT0_New)
  • aarch64_sme_zt0_preserved (ZT0_Preserved)

ZT0_In, ZT0_Out, ZT0_InOut & ZT0_Preserved are all considered to share
ZT0. These attributes will be required by later patches to determine
if ZT0 should be preserved around function calls, or cleared on entry
to the function.

This patch extends SMEAttrs to interpret the following new attributes,
which are mutually exclusive and apply to SME2 only:
  - aarch64_sme_zt0_in (ZT0_In)
  - aarch64_sme_zt0_out (ZT0_Out)
  - aarch64_sme_zt0_inout (ZT0_InOut)
  - aarch64_sme_zt0_new (ZT0_New)
  - aarch64_sme_zt0_preserved (ZT0_Preserved)

ZT0_In, ZT0_Out, ZT0_InOut & ZT0_Preserved are all considered to share
ZT0. These attributes will be required by later patches to determine
if ZT0 should be preserved around function calls, or cleared on entry
to the function.
@llvmbot
Copy link
Collaborator

llvmbot commented Jan 10, 2024

@llvm/pr-subscribers-llvm-ir

@llvm/pr-subscribers-backend-aarch64

Author: Kerry McLaughlin (kmclaughlin-arm)

Changes

This patch extends SMEAttrs to interpret the following new attributes,
which are mutually exclusive and apply to SME2 only:

  • aarch64_sme_zt0_in (ZT0_In)
  • aarch64_sme_zt0_out (ZT0_Out)
  • aarch64_sme_zt0_inout (ZT0_InOut)
  • aarch64_sme_zt0_new (ZT0_New)
  • aarch64_sme_zt0_preserved (ZT0_Preserved)

ZT0_In, ZT0_Out, ZT0_InOut & ZT0_Preserved are all considered to share
ZT0. These attributes will be required by later patches to determine
if ZT0 should be preserved around function calls, or cleared on entry
to the function.


Full diff: https://github.com/llvm/llvm-project/pull/77607.diff

3 Files Affected:

  • (modified) llvm/lib/Target/AArch64/Utils/AArch64SMEAttributes.cpp (+18)
  • (modified) llvm/lib/Target/AArch64/Utils/AArch64SMEAttributes.h (+24-8)
  • (modified) llvm/unittests/Target/AArch64/SMEAttributesTest.cpp (+103)
diff --git a/llvm/lib/Target/AArch64/Utils/AArch64SMEAttributes.cpp b/llvm/lib/Target/AArch64/Utils/AArch64SMEAttributes.cpp
index 0082b4017986c6..82b07419a244a0 100644
--- a/llvm/lib/Target/AArch64/Utils/AArch64SMEAttributes.cpp
+++ b/llvm/lib/Target/AArch64/Utils/AArch64SMEAttributes.cpp
@@ -18,8 +18,11 @@ void SMEAttrs::set(unsigned M, bool Enable) {
   else
     Bitmask &= ~M;
 
+  // Streaming Mode Attrs
   assert(!(hasStreamingInterface() && hasStreamingCompatibleInterface()) &&
          "SM_Enabled and SM_Compatible are mutually exclusive");
+
+  // ZA Attrs
   assert(!(hasNewZABody() && hasSharedZAInterface()) &&
          "ZA_New and ZA_Shared are mutually exclusive");
   assert(!(hasNewZABody() && preservesZA()) &&
@@ -28,6 +31,11 @@ void SMEAttrs::set(unsigned M, bool Enable) {
          "ZA_New and ZA_NoLazySave are mutually exclusive");
   assert(!(hasSharedZAInterface() && (Bitmask & ZA_NoLazySave)) &&
          "ZA_Shared and ZA_NoLazySave are mutually exclusive");
+
+  // ZT0 Attrs
+  assert((!sharesZT0() || (hasNewZT0Body() ^ isZT0In() ^ isZT0InOut() ^
+                           isZT0Out() ^ preservesZT0())) &&
+         "ZT0_New,In,Out,InOut,Preserved are mutually exclusive");
 }
 
 SMEAttrs::SMEAttrs(const CallBase &CB) {
@@ -60,6 +68,16 @@ SMEAttrs::SMEAttrs(const AttributeList &Attrs) {
     Bitmask |= ZA_New;
   if (Attrs.hasFnAttr("aarch64_pstate_za_preserved"))
     Bitmask |= ZA_Preserved;
+  if (Attrs.hasFnAttr("aarch64_sme_zt0_in"))
+    Bitmask |= ZT0_In;
+  if (Attrs.hasFnAttr("aarch64_sme_zt0_out"))
+    Bitmask |= ZT0_Out;
+  if (Attrs.hasFnAttr("aarch64_sme_zt0_inout"))
+    Bitmask |= ZT0_InOut;
+  if (Attrs.hasFnAttr("aarch64_sme_zt0_preserved"))
+    Bitmask |= ZT0_Preserved;
+  if (Attrs.hasFnAttr("aarch64_sme_zt0_new"))
+    Bitmask |= ZT0_New;
 }
 
 std::optional<bool>
diff --git a/llvm/lib/Target/AArch64/Utils/AArch64SMEAttributes.h b/llvm/lib/Target/AArch64/Utils/AArch64SMEAttributes.h
index e766b778b54102..88d67ad4b51f53 100644
--- a/llvm/lib/Target/AArch64/Utils/AArch64SMEAttributes.h
+++ b/llvm/lib/Target/AArch64/Utils/AArch64SMEAttributes.h
@@ -29,14 +29,19 @@ class SMEAttrs {
   // Enum with bitmasks for each individual SME feature.
   enum Mask {
     Normal = 0,
-    SM_Enabled = 1 << 0,    // aarch64_pstate_sm_enabled
-    SM_Compatible = 1 << 1, // aarch64_pstate_sm_compatible
-    SM_Body = 1 << 2,       // aarch64_pstate_sm_body
-    ZA_Shared = 1 << 3,     // aarch64_pstate_sm_shared
-    ZA_New = 1 << 4,        // aarch64_pstate_sm_new
-    ZA_Preserved = 1 << 5,  // aarch64_pstate_sm_preserved
-    ZA_NoLazySave = 1 << 6, // Used for SME ABI routines to avoid lazy saves
-    All = ZA_Preserved - 1
+    SM_Enabled = 1 << 0,     // aarch64_pstate_sm_enabled
+    SM_Compatible = 1 << 1,  // aarch64_pstate_sm_compatible
+    SM_Body = 1 << 2,        // aarch64_pstate_sm_body
+    ZA_Shared = 1 << 3,      // aarch64_pstate_sm_shared
+    ZA_New = 1 << 4,         // aarch64_pstate_sm_new
+    ZA_Preserved = 1 << 5,   // aarch64_pstate_sm_preserved
+    ZA_NoLazySave = 1 << 6,  // Used for SME ABI routines to avoid lazy saves
+    ZT0_New = 1 << 7,        // aarch64_sme_zt0_new
+    ZT0_In = 1 << 8,         // aarch64_sme_zt0_in
+    ZT0_Out = 1 << 9,        // aarch64_sme_zt0_out
+    ZT0_InOut = 1 << 10,     // aarch64_sme_zt0_inout
+    ZT0_Preserved = 1 << 11, // aarch64_sme_zt0_preserved
+    All = ZT0_Preserved - 1
   };
 
   SMEAttrs(unsigned Mask = Normal) : Bitmask(0) { set(Mask); }
@@ -86,6 +91,17 @@ class SMEAttrs {
     return hasZAState() && Callee.hasPrivateZAInterface() &&
            !(Callee.Bitmask & ZA_NoLazySave);
   }
+
+  // Interfaces to query ZT0 State
+  bool hasNewZT0Body() const { return Bitmask & ZT0_New; }
+  bool isZT0In() const { return Bitmask & ZT0_In; }
+  bool isZT0Out() const { return Bitmask & ZT0_Out; }
+  bool isZT0InOut() const { return Bitmask & ZT0_InOut; }
+  bool preservesZT0() const { return Bitmask & ZT0_Preserved; }
+  bool sharesZT0() const {
+    return Bitmask & (ZT0_In | ZT0_Out | ZT0_InOut | ZT0_Preserved);
+  }
+  bool hasZT0State() const { return hasNewZT0Body() || sharesZT0(); }
 };
 
 } // namespace llvm
diff --git a/llvm/unittests/Target/AArch64/SMEAttributesTest.cpp b/llvm/unittests/Target/AArch64/SMEAttributesTest.cpp
index 7780c71bbc00e0..9c88ec53589a73 100644
--- a/llvm/unittests/Target/AArch64/SMEAttributesTest.cpp
+++ b/llvm/unittests/Target/AArch64/SMEAttributesTest.cpp
@@ -50,6 +50,37 @@ TEST(SMEAttributes, Constructors) {
                       ->getFunction("foo"))
                   .preservesZA());
 
+  ASSERT_TRUE(SA(*parseIR("declare void @foo() \"aarch64_sme_zt0_in\"")
+                      ->getFunction("foo"))
+                  .isZT0In());
+  ASSERT_TRUE(SA(*parseIR("declare void @foo() \"aarch64_sme_zt0_out\"")
+                      ->getFunction("foo"))
+                  .isZT0Out());
+  ASSERT_TRUE(SA(*parseIR("declare void @foo() \"aarch64_sme_zt0_inout\"")
+                      ->getFunction("foo"))
+                  .isZT0InOut());
+
+  ASSERT_TRUE(SA(*parseIR("declare void @foo() \"aarch64_sme_zt0_in\"")
+                      ->getFunction("foo"))
+                  .sharesZT0());
+  ASSERT_TRUE(SA(*parseIR("declare void @foo() \"aarch64_sme_zt0_out\"")
+                      ->getFunction("foo"))
+                  .sharesZT0());
+  ASSERT_TRUE(SA(*parseIR("declare void @foo() \"aarch64_sme_zt0_inout\"")
+                      ->getFunction("foo"))
+                  .sharesZT0());
+  ASSERT_TRUE(SA(*parseIR("declare void @foo() \"aarch64_sme_zt0_preserved\"")
+                      ->getFunction("foo"))
+                  .sharesZT0());
+
+  ASSERT_TRUE(SA(*parseIR("declare void @foo() \"aarch64_sme_zt0_preserved\"")
+                      ->getFunction("foo"))
+                  .preservesZT0());
+
+  ASSERT_TRUE(SA(*parseIR("declare void @foo() \"aarch64_sme_zt0_new\"")
+                      ->getFunction("foo"))
+                  .hasNewZT0Body());
+
   // Invalid combinations.
   EXPECT_DEBUG_DEATH(SA(SA::SM_Enabled | SA::SM_Compatible),
                      "SM_Enabled and SM_Compatible are mutually exclusive");
@@ -58,6 +89,29 @@ TEST(SMEAttributes, Constructors) {
   EXPECT_DEBUG_DEATH(SA(SA::ZA_New | SA::ZA_Preserved),
                      "ZA_New and ZA_Preserved are mutually exclusive");
 
+  EXPECT_DEBUG_DEATH(SA(SA::ZT0_New | SA::ZT0_In),
+                     "ZT0_New,In,Out,InOut,Preserved are mutually exclusive");
+  EXPECT_DEBUG_DEATH(SA(SA::ZT0_New | SA::ZT0_Out),
+                     "ZT0_New,In,Out,InOut,Preserved are mutually exclusive");
+  EXPECT_DEBUG_DEATH(SA(SA::ZT0_New | SA::ZT0_InOut),
+                     "ZT0_New,In,Out,InOut,Preserved are mutually exclusive");
+  EXPECT_DEBUG_DEATH(SA(SA::ZT0_New | SA::ZT0_Preserved),
+                     "ZT0_New,In,Out,InOut,Preserved are mutually exclusive");
+
+  EXPECT_DEBUG_DEATH(SA(SA::ZT0_In | SA::ZT0_Out),
+                     "ZT0_New,In,Out,InOut,Preserved are mutually exclusive");
+  EXPECT_DEBUG_DEATH(SA(SA::ZT0_In | SA::ZT0_InOut),
+                     "ZT0_New,In,Out,InOut,Preserved are mutually exclusive");
+  EXPECT_DEBUG_DEATH(SA(SA::ZT0_Out | SA::ZT0_InOut),
+                     "ZT0_New,In,Out,InOut,Preserved are mutually exclusive");
+
+  EXPECT_DEBUG_DEATH(SA(SA::ZT0_Preserved | SA::ZT0_In),
+                     "ZT0_New,In,Out,InOut,Preserved are mutually exclusive");
+  EXPECT_DEBUG_DEATH(SA(SA::ZT0_Preserved | SA::ZT0_Out),
+                     "ZT0_New,In,Out,InOut,Preserved are mutually exclusive");
+  EXPECT_DEBUG_DEATH(SA(SA::ZT0_Preserved | SA::ZT0_InOut),
+                     "ZT0_New,In,Out,InOut,Preserved are mutually exclusive");
+
   // Test that the set() methods equally check validity.
   EXPECT_DEBUG_DEATH(SA(SA::SM_Enabled).set(SA::SM_Compatible),
                      "SM_Enabled and SM_Compatible are mutually exclusive");
@@ -95,6 +149,55 @@ TEST(SMEAttributes, Basics) {
   ASSERT_FALSE(SA(SA::Normal).hasNewZABody());
   ASSERT_FALSE(SA(SA::Normal).hasZAState());
   ASSERT_FALSE(SA(SA::Normal).preservesZA());
+
+  // Test ZT0 State interfaces
+  ASSERT_TRUE(SA(SA::ZT0_In).isZT0In());
+  ASSERT_FALSE(SA(SA::ZT0_In).isZT0Out());
+  ASSERT_FALSE(SA(SA::ZT0_In).isZT0InOut());
+  ASSERT_FALSE(SA(SA::ZT0_In).preservesZT0());
+  ASSERT_FALSE(SA(SA::ZT0_In).hasNewZT0Body());
+  ASSERT_TRUE(SA(SA::ZT0_In).sharesZT0());
+  ASSERT_TRUE(SA(SA::ZT0_In).hasZT0State());
+
+  ASSERT_TRUE(SA(SA::ZT0_Out).isZT0Out());
+  ASSERT_FALSE(SA(SA::ZT0_Out).isZT0In());
+  ASSERT_FALSE(SA(SA::ZT0_Out).isZT0InOut());
+  ASSERT_FALSE(SA(SA::ZT0_Out).preservesZT0());
+  ASSERT_FALSE(SA(SA::ZT0_Out).hasNewZT0Body());
+  ASSERT_TRUE(SA(SA::ZT0_Out).sharesZT0());
+  ASSERT_TRUE(SA(SA::ZT0_Out).hasZT0State());
+
+  ASSERT_TRUE(SA(SA::ZT0_InOut).isZT0InOut());
+  ASSERT_FALSE(SA(SA::ZT0_InOut).isZT0In());
+  ASSERT_FALSE(SA(SA::ZT0_InOut).isZT0Out());
+  ASSERT_FALSE(SA(SA::ZT0_InOut).preservesZT0());
+  ASSERT_FALSE(SA(SA::ZT0_InOut).hasNewZT0Body());
+  ASSERT_TRUE(SA(SA::ZT0_InOut).sharesZT0());
+  ASSERT_TRUE(SA(SA::ZT0_InOut).hasZT0State());
+
+  ASSERT_TRUE(SA(SA::ZT0_Preserved).preservesZT0());
+  ASSERT_FALSE(SA(SA::ZT0_Preserved).isZT0In());
+  ASSERT_FALSE(SA(SA::ZT0_Preserved).isZT0Out());
+  ASSERT_FALSE(SA(SA::ZT0_Preserved).isZT0InOut());
+  ASSERT_FALSE(SA(SA::ZT0_Preserved).hasNewZT0Body());
+  ASSERT_TRUE(SA(SA::ZT0_Preserved).sharesZT0());
+  ASSERT_TRUE(SA(SA::ZT0_Preserved).hasZT0State());
+
+  ASSERT_TRUE(SA(SA::ZT0_New).hasNewZT0Body());
+  ASSERT_FALSE(SA(SA::ZT0_New).isZT0In());
+  ASSERT_FALSE(SA(SA::ZT0_New).isZT0Out());
+  ASSERT_FALSE(SA(SA::ZT0_New).isZT0InOut());
+  ASSERT_FALSE(SA(SA::ZT0_New).preservesZT0());
+  ASSERT_FALSE(SA(SA::ZT0_New).sharesZT0());
+  ASSERT_TRUE(SA(SA::ZT0_New).hasZT0State());
+
+  ASSERT_FALSE(SA(SA::Normal).isZT0In());
+  ASSERT_FALSE(SA(SA::Normal).isZT0Out());
+  ASSERT_FALSE(SA(SA::Normal).isZT0InOut());
+  ASSERT_FALSE(SA(SA::Normal).preservesZT0());
+  ASSERT_FALSE(SA(SA::Normal).hasNewZT0Body());
+  ASSERT_FALSE(SA(SA::Normal).sharesZT0());
+  ASSERT_FALSE(SA(SA::Normal).hasZT0State());
 }
 
 TEST(SMEAttributes, Transitions) {

…_shared is set

- Changed hasSharedZAInterface() to return true if either sharedZA() or sharedZT0() is true
- Changed wording of the assert checking for mutually exclusive ZT0 attributes
Comment on lines 67 to 78
ASSERT_TRUE(SA(*parseIR("declare void @foo() \"aarch64_sme_zt0_in\"")
->getFunction("foo"))
.sharesZT0());
ASSERT_TRUE(SA(*parseIR("declare void @foo() \"aarch64_sme_zt0_out\"")
->getFunction("foo"))
.sharesZT0());
ASSERT_TRUE(SA(*parseIR("declare void @foo() \"aarch64_sme_zt0_inout\"")
->getFunction("foo"))
.sharesZT0());
ASSERT_TRUE(SA(*parseIR("declare void @foo() \"aarch64_sme_zt0_preserved\"")
->getFunction("foo"))
.sharesZT0());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is already tested below by e.g. ASSERT_TRUE(SA(SA::ZT0_In).hasZT0State());. There is no point in testing that here again (same for most cases below)

ASSERT_TRUE(SA(*parseIR("declare void @foo() \"aarch64_sme_zt0_new\"")
->getFunction("foo"))
.hasNewZT0Body());
ASSERT_TRUE(SA(*parseIR("declare void @foo() \"aarch64_sme_zt0_new\"")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you test this case below straight with ASSERT_TRUE(SA(SA::ZT0_New).hasPrivateZAInterface()), rather than doing it through some LLVM IR indirection?

Comment on lines 57 to 65
ASSERT_TRUE(SA(*parseIR("declare void @foo() \"aarch64_sme_zt0_in\"")
->getFunction("foo"))
.isZT0In());
ASSERT_TRUE(SA(*parseIR("declare void @foo() \"aarch64_sme_zt0_out\"")
->getFunction("foo"))
.isZT0Out());
ASSERT_TRUE(SA(*parseIR("declare void @foo() \"aarch64_sme_zt0_inout\"")
->getFunction("foo"))
.isZT0InOut());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is missing cases for aarch64_sme_zt0_preserved and aarch64_sme_zt0_new.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are tested a bit further down, on line 93:

ASSERT_TRUE(SA(*parseIR("declare void @foo() \"aarch64_sme_zt0_preserved\"")
                    ->getFunction("foo"))
                .preservesZT0());

ASSERT_TRUE(SA(*parseIR("declare void @foo() \"aarch64_sme_zt0_new\"")
                    ->getFunction("foo"))
                .hasNewZT0Body());

@@ -111,36 +80,36 @@ TEST(SMEAttributes, Constructors) {

EXPECT_DEBUG_DEATH(SA(SA::ZT0_New | SA::ZT0_In),
"ZT0_New, ZT0_In, ZT0_Out, ZT0_InOut and ZT0_Preserved "
"are all mutually exclusive");
"are all \" \"mutually exclusive");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did something go wrong here with the escapes/formatting? :)

Comment on lines 39 to 43
ZT0_New = 1 << 7, // aarch64_sme_zt0_new
ZT0_In = 1 << 8, // aarch64_sme_zt0_in
ZT0_Out = 1 << 9, // aarch64_sme_zt0_out
ZT0_InOut = 1 << 10, // aarch64_sme_zt0_inout
ZT0_Preserved = 1 << 11, // aarch64_sme_zt0_preserved
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that they are all mutually exclusive, you could reserve a few bits to represent that state, i.e.

enum class StateValue {
  None = 0,
  In = 1,
  Out = 2,
  InOut = 3,
  Preserved = 4,
  New = 5
};

with the bits in enum Mask being:

  ZT0_Shift = 7,
  ZT0_Mask = 0b111 << ZT0_Shift,

To get/set the SMEState value, you'd do something like this:

  StateValue getZT0State() const { return BitMask & ZT0_Mask >> ZT0_Shift; }
  void setZT0State(StateValue S) { BitMask |= S << ZT0_Shift; }

(possibly with some added casts)

EXPECT_DEBUG_DEATH(SA(SA::ZT0_New | SA::ZT0_In),
"ZT0_New, ZT0_In, ZT0_Out, ZT0_InOut and ZT0_Preserved "
"are all \" \"mutually exclusive");
EXPECT_DEBUG_DEATH(SA(SA::ZT0_New | SA::ZT0_Out),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you'd implement my suggestion above, there is no longer a need for these tests here.
But I would argue that we'd need some checks in Verifier.cpp for these attributes, which would be more user-facing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added some checks in the Verifier pass, for now these only check that incompatible attributes are not added to the same function.

… ZT0 attributes

- Added restrictions on the ZT0 attributes in the IR Verifier pass
Comment on lines 103 to 105
void setZT0State(StateValue S) {
Bitmask |= (static_cast<unsigned>(S) << ZT0_Shift);
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To enable the use of the SMEAttrs constructor using e.g. SMEAttrs(<mask>), perhaps it makes more sense to change setZT0State to something that encodes the value given a bitmask, i.e.

  static unsigned decodeZT0State(unsigned Bitmask) {
    return static_cast<StateValue>((Bitmask & ZT0_Mask) >> ZT0_Shift);
  }
  static unsigned encodeZT0State(StateValue S) {
    return static_cast<unsigned>(S) << ZT0_Shift;
  }

That avoids having to do:

   SA ZT0_In = SA(SA::Normal);
   ZT0_In.setZT0State(SA::StateValue::In);

and instead allows:

SA ZT0_In = SA(SA::encodeZT0State(SA::StateValue::In));

without the extra level of indirection.

bool isInZT0() const { return getZT0State() == StateValue::In; }
bool isOutZT0() const { return getZT0State() == StateValue::Out; }
bool isInOutZT0() const { return getZT0State() == StateValue::InOut; }
bool preservesZT0() const { return getZT0State() == StateValue::Preserved; }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
bool preservesZT0() const { return getZT0State() == StateValue::Preserved; }
bool isPreservesZT0() const { return getZT0State() == StateValue::Preserved; }

@@ -50,6 +54,22 @@ TEST(SMEAttributes, Constructors) {
->getFunction("foo"))
.preservesZA());

ASSERT_TRUE(SA(*parseIR("declare void @foo() \"aarch64_sme_zt0_in\"")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we change the names of the attributes from aarch64_sme_zt0_in to the aarch64_sme_in_zt0 format as well? (and similar below)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've changed the attributes to use this format, I think this makes sense given the Clang attribute will be __arm_in("zt0")

Bitmask |= (static_cast<unsigned>(S) << ZT0_Shift);
}

bool hasNewZT0Body() const { return getZT0State() == StateValue::New; }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
bool hasNewZT0Body() const { return getZT0State() == StateValue::New; }
bool isNewZT0() const { return getZT0State() == StateValue::New; }

@@ -2160,6 +2160,64 @@ void Verifier::verifyFunctionAttrs(FunctionType *FT, AttributeList Attrs,
V);
}

if (Attrs.hasFnAttr("aarch64_sme_zt0_new")) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than having to spell out all possible combinations here, my preference would be to do the same thing as you did for the assert, where the error is more generic, but where the check itself is simpler by using the exclusive-or operator (and therefore less error prone to missing a case).

…ttrs constructor with ZT0 attributes

- Changed preservesZT0() to isPreservesZT0()
- Changed hasNewZT0Body() to isNewZT0()
- Renamed attributes, for example aarch64_sme_zt0_new -> aarch64_sme_new_zt0
- Combined the warnings added to the IR Verifier
Comment on lines 2163 to 2172
if (Attrs.hasFnAttr("aarch64_sme_new_zt0") ||
Attrs.hasFnAttr("aarch64_sme_in_zt0") ||
Attrs.hasFnAttr("aarch64_sme_inout_zt0") ||
Attrs.hasFnAttr("aarch64_sme_out_zt0") ||
Attrs.hasFnAttr("aarch64_sme_preserved_zt0")) {
Check((Attrs.hasFnAttr("aarch64_sme_new_zt0") ^
Attrs.hasFnAttr("aarch64_sme_in_zt0") ^
Attrs.hasFnAttr("aarch64_sme_inout_zt0") ^
Attrs.hasFnAttr("aarch64_sme_out_zt0") ^
Attrs.hasFnAttr("aarch64_sme_preserved_zt0")),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about:

Check((Attrs.hasFnAttr("aarch64_sme_new_zt0") + 
       Attrs.hasFnAttr("aarch64_sme_in_zt0") + 
       Attrs.hasFnAttr("aarch64_sme_inout_zt0") + 
       Attrs.hasFnAttr("aarch64_sme_out_zt0") + 
       Attrs.hasFnAttr("aarch64_sme_preserved_zt0")) <= 1,
      "<message>", V);

?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is much better, thanks :)

Attrs.hasFnAttr("aarch64_sme_inout_zt0") ^
Attrs.hasFnAttr("aarch64_sme_out_zt0") ^
Attrs.hasFnAttr("aarch64_sme_preserved_zt0")),
"ZT0 state attributes 'aarch64_sme_[in|inout|out|new|preserved]_zt' "
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Just write out in full:

"Attributes 'aarch64_new_zt0', 'aarch64_in_zt0', 'aarch64_out_zt0', 'aarch64_inout_zt0' and 'aarch64_preserves_zt0' are mutually exclusive"

Comment on lines 38 to 39
"ZT0_New, ZT0_In, ZT0_Out, ZT0_InOut and ZT0_Preserved are all "
"mutually exclusive");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"ZT0_New, ZT0_In, ZT0_Out, ZT0_InOut and ZT0_Preserved are all "
"mutually exclusive");
"Attributes 'aarch64_new_zt0', 'aarch64_in_zt0', 'aarch64_out_zt0', "
"'aarch64_inout_zt0' and 'aarch64_preserves_zt0' are mutually exclusive");

@@ -60,6 +69,16 @@ SMEAttrs::SMEAttrs(const AttributeList &Attrs) {
Bitmask |= ZA_New;
if (Attrs.hasFnAttr("aarch64_pstate_za_preserved"))
Bitmask |= ZA_Preserved;
if (Attrs.hasFnAttr("aarch64_sme_in_zt0"))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's remove the _sme part from this, so that it's aarch64_in_zt0.

Bitmask |= encodeZT0State(StateValue::Out);
if (Attrs.hasFnAttr("aarch64_sme_inout_zt0"))
Bitmask |= encodeZT0State(StateValue::InOut);
if (Attrs.hasFnAttr("aarch64_sme_preserved_zt0"))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: preserves (not preserved)

ASSERT_TRUE(SA(SA::ZA_Shared).hasZAState());
ASSERT_FALSE(SA(SA::ZA_Shared).preservesZA());
ASSERT_TRUE(SA(SA::ZA_Shared | SA::ZA_Preserved).preservesZA());

ASSERT_TRUE(SA(SA::ZA_New).hasPrivateZAInterface());
ASSERT_FALSE(SA(SA::ZA_New).hasSharedZAInterface());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: add this case too?

ASSERT_FALSE(SA(SA::ZA_Shared).sharesZA());

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is already a sharesZA() test for SA::ZA_Shared on line 105, did you mean:
ASSERT_FALSE(SA(SA::ZA_Shared).sharesZT0())?

I've added sharesZT0() cases for SA::ZA_Shared & SA::ZA_New in the latest commit.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure why I asked for this yesterday, perhaps my eyes just didn't spot this case.

- Changed warning to use full names of all mutually exclusive attributes
- Removed 'sme' from attribute names, e.g. aarch64_sme_in_zt0 -> aarch64_in_zt0
ASSERT_TRUE(SA(SA::ZA_Shared).hasZAState());
ASSERT_FALSE(SA(SA::ZA_Shared).preservesZA());
ASSERT_TRUE(SA(SA::ZA_Shared | SA::ZA_Preserved).preservesZA());

ASSERT_TRUE(SA(SA::ZA_New).hasPrivateZAInterface());
ASSERT_FALSE(SA(SA::ZA_New).hasSharedZAInterface());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure why I asked for this yesterday, perhaps my eyes just didn't spot this case.

@kmclaughlin-arm kmclaughlin-arm merged commit a4ec04e into llvm:main Jan 16, 2024
4 checks passed
@kmclaughlin-arm kmclaughlin-arm deleted the sme2-new-zt0-attrs branch January 20, 2024 15:00
justinfargnoli pushed a commit to justinfargnoli/llvm-project that referenced this pull request Jan 28, 2024
This patch extends SMEAttrs to interpret the following new attributes,
which are mutually exclusive and apply to SME2 only:
  - aarch64_sme_zt0_in (ZT0_In)
  - aarch64_sme_zt0_out (ZT0_Out)
  - aarch64_sme_zt0_inout (ZT0_InOut)
  - aarch64_sme_zt0_new (ZT0_New)
  - aarch64_sme_zt0_preserved (ZT0_Preserved)

ZT0_In, ZT0_Out, ZT0_InOut & ZT0_Preserved are all considered to share
ZT0. These attributes will be required by later patches to determine
if ZT0 should be preserved around function calls, or cleared on entry
to the function.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants