]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
aarch64: Fix ZA state transition [PR119210]
authorAlice Carlotti <alice.carlotti@arm.com>
Thu, 16 Apr 2026 12:02:35 +0000 (13:02 +0100)
committerAlice Carlotti <alice.carlotti@arm.com>
Wed, 22 Apr 2026 15:57:55 +0000 (16:57 +0100)
In the INACTIVE_CALLER -> INACTIVE LOCAL transition, ensure ZA is active
and zeroed before setting tpidr2_el0.

gcc/ChangeLog:

PR target/119210
* config/aarch64/aarch64.cc (aarch64_mode_emit_local_sme_state):
Add PSTATE.ZA enablement, and zero it if already enabled.

gcc/testsuite/ChangeLog:

PR target/119210
* gcc.target/aarch64/sme/za_state_8.c: New test.

gcc/config/aarch64/aarch64.cc
gcc/testsuite/gcc.target/aarch64/sme/za_state_8.c [new file with mode: 0644]

index 62194b96450840d3bae69636fb04f3d62e516c8d..693ce8b4d25cd8cef03e415ea1c033cb9311cb21 100644 (file)
@@ -31951,7 +31951,8 @@ aarch64_mode_emit_local_sme_state (aarch64_local_sme_state mode,
       emit_insn (gen_aarch64_tpidr2_save ());
       emit_insn (gen_aarch64_clear_tpidr2 ());
       if (mode == aarch64_local_sme_state::ACTIVE_LIVE
-         || mode == aarch64_local_sme_state::ACTIVE_DEAD)
+         || mode == aarch64_local_sme_state::ACTIVE_DEAD
+         || mode == aarch64_local_sme_state::INACTIVE_LOCAL)
        {
          if (aarch64_cfun_has_state ("za"))
            emit_insn (gen_aarch64_initial_zero_za ());
@@ -32033,6 +32034,16 @@ aarch64_mode_emit_local_sme_state (aarch64_local_sme_state mode,
 
   if (mode == aarch64_local_sme_state::INACTIVE_LOCAL)
     {
+      if (prev_mode == aarch64_local_sme_state::INACTIVE_CALLER)
+       /* Enable ZA (if it wasn't already enabled on entry).  Enabling ZA has
+          the side-effect of zeroing ZA.
+
+          A functionally correct alternative would be to leave TPIDR2_EL0 null
+          and zero the save buffer.  However, zeroing the save buffer would require
+          more code and would optimize for the case in which a callee also
+          initialises private ZA state (which should be a rare event).  */
+       emit_insn (gen_aarch64_smstart_za ());
+
       if (prev_mode == aarch64_local_sme_state::ACTIVE_LIVE
          || prev_mode == aarch64_local_sme_state::ACTIVE_DEAD
          || prev_mode == aarch64_local_sme_state::INACTIVE_CALLER)
diff --git a/gcc/testsuite/gcc.target/aarch64/sme/za_state_8.c b/gcc/testsuite/gcc.target/aarch64/sme/za_state_8.c
new file mode 100644 (file)
index 0000000..9b7a6ff
--- /dev/null
@@ -0,0 +1,25 @@
+// { dg-options "-O -fomit-frame-pointer -fno-optimize-sibling-calls" }
+// { dg-final { check-function-bodies "**" "" } }
+
+#include <arm_sme.h>
+
+void callee_ns();
+__arm_streaming __arm_inout("za") void callee_s();
+
+/*
+** foo:
+**     ...
+**     smstart za
+**     ...
+**     msr     tpidr2_el0, x\d+
+**     ...
+*/
+__arm_locally_streaming __arm_new("za") const float * foo(const float* x) {
+    callee_ns ();
+    const float32_t *x_f_in = x;
+    svzero_za();
+    callee_s ();
+    return x_f_in;
+}
+
+