SVE unwinding
authorRichard Sandiford <richard.sandiford@linaro.org>
Sat, 13 Jan 2018 17:56:52 +0000 (17:56 +0000)
committerRichard Sandiford <rsandifo@gcc.gnu.org>
Sat, 13 Jan 2018 17:56:52 +0000 (17:56 +0000)
This patch adds support for unwinding frames that use the SVE
pseudo VG register.  We want this register to act like a normal
register if the CFI explicitly sets it, but want to provide a
default value otherwise.  Computing the default value requires
an SVE target, so we only want to compute it on demand.

aarch64_vg uses a hard-coded .inst in order to avoid a build
dependency on binutils 2.28 or later.

2018-01-13  Richard Sandiford  <richard.sandiford@linaro.org>

gcc/
* doc/tm.texi.in (DWARF_LAZY_REGISTER_VALUE): Document.
* doc/tm.texi: Regenerate.

libgcc/
* config/aarch64/value-unwind.h (aarch64_vg): New function.
(DWARF_LAZY_REGISTER_VALUE): Define.
* unwind-dw2.c (_Unwind_GetGR): Use DWARF_LAZY_REGISTER_VALUE
to provide a fallback register value.

gcc/testsuite/
* g++.target/aarch64/sve/aarch64-sve.exp: New harness.
* g++.target/aarch64/sve/catch_1.C: New test.
* g++.target/aarch64/sve/catch_2.C: Likewise.
* g++.target/aarch64/sve/catch_3.C: Likewise.
* g++.target/aarch64/sve/catch_4.C: Likewise.
* g++.target/aarch64/sve/catch_5.C: Likewise.
* g++.target/aarch64/sve/catch_6.C: Likewise.

Reviewed-by: James Greenhalgh <james.greenhalgh@arm.com>
From-SVN: r256615

14 files changed:
gcc/ChangeLog
gcc/doc/tm.texi
gcc/doc/tm.texi.in
gcc/testsuite/ChangeLog
gcc/testsuite/g++.target/aarch64/sve/aarch64-sve.exp [new file with mode: 0644]
gcc/testsuite/g++.target/aarch64/sve/catch_1.C [new file with mode: 0644]
gcc/testsuite/g++.target/aarch64/sve/catch_2.C [new file with mode: 0644]
gcc/testsuite/g++.target/aarch64/sve/catch_3.C [new file with mode: 0644]
gcc/testsuite/g++.target/aarch64/sve/catch_4.C [new file with mode: 0644]
gcc/testsuite/g++.target/aarch64/sve/catch_5.C [new file with mode: 0644]
gcc/testsuite/g++.target/aarch64/sve/catch_6.C [new file with mode: 0644]
libgcc/ChangeLog
libgcc/config/aarch64/value-unwind.h
libgcc/unwind-dw2.c

index 40da1eb477aecbe47c5fe94ab388ae0114e565da..64951e9d78c11fd6d7c9bac035b25100b2829c2e 100644 (file)
@@ -1,3 +1,8 @@
+2018-01-13  Richard Sandiford  <richard.sandiford@linaro.org>
+
+       * doc/tm.texi.in (DWARF_LAZY_REGISTER_VALUE): Document.
+       * doc/tm.texi: Regenerate.
+
 2018-01-13  Richard Sandiford  <richard.sandiford@linaro.org>
            Alan Hayward  <alan.hayward@arm.com>
            David Sherwood  <david.sherwood@arm.com>
index 11b560b9c0767edf7e702f240487fce06fce9212..4cd8dcea3951990289ee8efd7776f1abc12c923a 100644 (file)
@@ -3621,6 +3621,13 @@ defined and 0 otherwise.
 
 @end defmac
 
+@defmac DWARF_LAZY_REGISTER_VALUE (@var{regno}, @var{value})
+Define this macro if the target has pseudo DWARF registers whose
+values need to be computed lazily on demand by the unwinder (such as when
+referenced in a CFA expression).  The macro returns true if @var{regno}
+is such a register and stores its value in @samp{*@var{value}} if so.
+@end defmac
+
 @node Elimination
 @subsection Eliminating Frame Pointer and Arg Pointer
 
index 0cd694a5448def3f5217de514474dfcd9ae929f5..3a2c2f26141711c66b31d8704380d99b88d97073 100644 (file)
@@ -3002,6 +3002,13 @@ defined and 0 otherwise.
 
 @end defmac
 
+@defmac DWARF_LAZY_REGISTER_VALUE (@var{regno}, @var{value})
+Define this macro if the target has pseudo DWARF registers whose
+values need to be computed lazily on demand by the unwinder (such as when
+referenced in a CFA expression).  The macro returns true if @var{regno}
+is such a register and stores its value in @samp{*@var{value}} if so.
+@end defmac
+
 @node Elimination
 @subsection Eliminating Frame Pointer and Arg Pointer
 
index eb1b229530aab3197a061cc5cb1765d5786a8c97..48f40dad147b560e06062d8ca3a4dfcf69950e06 100644 (file)
@@ -1,3 +1,13 @@
+2018-01-13  Richard Sandiford  <richard.sandiford@linaro.org>
+
+       * g++.target/aarch64/sve/aarch64-sve.exp: New harness.
+       * g++.target/aarch64/sve/catch_1.C: New test.
+       * g++.target/aarch64/sve/catch_2.C: Likewise.
+       * g++.target/aarch64/sve/catch_3.C: Likewise.
+       * g++.target/aarch64/sve/catch_4.C: Likewise.
+       * g++.target/aarch64/sve/catch_5.C: Likewise.
+       * g++.target/aarch64/sve/catch_6.C: Likewise.
+
 2018-01-13  Richard Sandiford  <richard.sandiford@linaro.org>
            Alan Hayward  <alan.hayward@arm.com>
            David Sherwood  <david.sherwood@arm.com>
diff --git a/gcc/testsuite/g++.target/aarch64/sve/aarch64-sve.exp b/gcc/testsuite/g++.target/aarch64/sve/aarch64-sve.exp
new file mode 100644 (file)
index 0000000..7557aa6
--- /dev/null
@@ -0,0 +1,45 @@
+#  Specific regression driver for AArch64.
+#  Copyright (C) 2009-2017 Free Software Foundation, Inc.
+#  Contributed by ARM Ltd.
+#
+#  This file is part of GCC.
+#
+#  GCC is free software; you can redistribute it and/or modify it
+#  under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 3, or (at your option)
+#  any later version.
+#
+#  GCC is distributed in the hope that it will be useful, but
+#  WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#  General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with GCC; see the file COPYING3.  If not see
+#  <http://www.gnu.org/licenses/>.  */
+
+# GCC testsuite that uses the `dg.exp' driver.
+
+# Exit immediately if this isn't an AArch64 target.
+if {![istarget aarch64*-*-*] } then {
+  return
+}
+
+# Load support procs.
+load_lib g++-dg.exp
+
+# Initialize `dg'.
+dg-init
+
+# Force SVE if we're not testing it already.
+if { [check_effective_target_aarch64_sve] } {
+    set sve_flags ""
+} else {
+    set sve_flags "-march=armv8.2-a+sve"
+}
+
+# Main loop.
+dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.C]] $sve_flags ""
+
+# All done.
+dg-finish
diff --git a/gcc/testsuite/g++.target/aarch64/sve/catch_1.C b/gcc/testsuite/g++.target/aarch64/sve/catch_1.C
new file mode 100644 (file)
index 0000000..39759cb
--- /dev/null
@@ -0,0 +1,69 @@
+/* { dg-do run { target aarch64_sve_hw } } */
+/* { dg-options "-O3 -fopenmp-simd -fno-omit-frame-pointer" } */
+
+/* Invoke X (P##n) for n in [0, 7].  */
+#define REPEAT8(X, P) \
+  X (P##0) X (P##1) X (P##2) X (P##3) X (P##4) X (P##5) X (P##6) X (P##7)
+
+/* Invoke X (n) for all octal n in [0, 39].  */
+#define REPEAT40(X) \
+  REPEAT8 (X, 0) REPEAT8 (X, 1)  REPEAT8 (X, 2) REPEAT8 (X, 3) REPEAT8 (X, 4)
+
+volatile int testi;
+
+/* Throw to f3.  */
+void __attribute__ ((weak))
+f1 (int x[40][100], int *y)
+{
+  /* A wild write to x and y.  */
+  asm volatile ("" ::: "memory");
+  if (y[testi] == x[testi][testi])
+    throw 100;
+}
+
+/* Expect vector work to be done, with spilling of vector registers.  */
+void __attribute__ ((weak))
+f2 (int x[40][100], int *y)
+{
+  /* Try to force some spilling.  */
+#define DECLARE(N) int y##N = y[N];
+  REPEAT40 (DECLARE);
+  for (int j = 0; j < 20; ++j)
+    {
+      f1 (x, y);
+#pragma omp simd
+      for (int i = 0; i < 100; ++i)
+       {
+#define INC(N) x[N][i] += y##N;
+         REPEAT40 (INC);
+       }
+    }
+}
+
+/* Catch an exception thrown from f1, via f2.  */
+void __attribute__ ((weak))
+f3 (int x[40][100], int *y, int *z)
+{
+  volatile int extra = 111;
+  try
+    {
+      f2 (x, y);
+    }
+  catch (int val)
+    {
+      *z = val + extra;
+    }
+}
+
+static int x[40][100];
+static int y[40];
+static int z;
+
+int
+main (void)
+{
+  f3 (x, y, &z);
+  if (z != 211)
+    __builtin_abort ();
+  return 0;
+}
diff --git a/gcc/testsuite/g++.target/aarch64/sve/catch_2.C b/gcc/testsuite/g++.target/aarch64/sve/catch_2.C
new file mode 100644 (file)
index 0000000..7722af1
--- /dev/null
@@ -0,0 +1,4 @@
+/* { dg-do run { target aarch64_sve_hw } } */
+/* { dg-options "-O3 -fopenmp-simd -fomit-frame-pointer" } */
+
+#include "catch_1.C"
diff --git a/gcc/testsuite/g++.target/aarch64/sve/catch_3.C b/gcc/testsuite/g++.target/aarch64/sve/catch_3.C
new file mode 100644 (file)
index 0000000..7d17024
--- /dev/null
@@ -0,0 +1,78 @@
+/* { dg-do run { target aarch64_sve_hw } } */
+/* { dg-options "-O3 -fopenmp-simd -fno-omit-frame-pointer" } */
+
+/* Invoke X (P##n) for n in [0, 7].  */
+#define REPEAT8(X, P) \
+  X (P##0) X (P##1) X (P##2) X (P##3) X (P##4) X (P##5) X (P##6) X (P##7)
+
+/* Invoke X (n) for all octal n in [0, 39].  */
+#define REPEAT40(X) \
+  REPEAT8 (X, 0) REPEAT8 (X, 1)  REPEAT8 (X, 2) REPEAT8 (X, 3) REPEAT8 (X, 4)
+
+volatile int testi, sink;
+
+/* Take 2 stack arguments and throw to f3.  */
+void __attribute__ ((weak))
+f1 (int x[40][100], int *y, int z1, int z2, int z3, int z4,
+    int z5, int z6, int z7, int z8)
+{
+  /* A wild write to x and y.  */
+  sink = z1;
+  sink = z2;
+  sink = z3;
+  sink = z4;
+  sink = z5;
+  sink = z6;
+  sink = z7;
+  sink = z8;
+  asm volatile ("" ::: "memory");
+  if (y[testi] == x[testi][testi])
+    throw 100;
+}
+
+/* Expect vector work to be done, with spilling of vector registers.  */
+void __attribute__ ((weak))
+f2 (int x[40][100], int *y)
+{
+  /* Try to force some spilling.  */
+#define DECLARE(N) int y##N = y[N];
+  REPEAT40 (DECLARE);
+  for (int j = 0; j < 20; ++j)
+    {
+      f1 (x, y, 1, 2, 3, 4, 5, 6, 7, 8);
+#pragma omp simd
+      for (int i = 0; i < 100; ++i)
+       {
+#define INC(N) x[N][i] += y##N;
+         REPEAT40 (INC);
+       }
+    }
+}
+
+/* Catch an exception thrown from f1, via f2.  */
+void __attribute__ ((weak))
+f3 (int x[40][100], int *y, int *z)
+{
+  volatile int extra = 111;
+  try
+    {
+      f2 (x, y);
+    }
+  catch (int val)
+    {
+      *z = val + extra;
+    }
+}
+
+static int x[40][100];
+static int y[40];
+static int z;
+
+int
+main (void)
+{
+  f3 (x, y, &z);
+  if (z != 211)
+    __builtin_abort ();
+  return 0;
+}
diff --git a/gcc/testsuite/g++.target/aarch64/sve/catch_4.C b/gcc/testsuite/g++.target/aarch64/sve/catch_4.C
new file mode 100644 (file)
index 0000000..07841ac
--- /dev/null
@@ -0,0 +1,4 @@
+/* { dg-do run { target aarch64_sve_hw } } */
+/* { dg-options "-O3 -fopenmp-simd -fomit-frame-pointer" } */
+
+#include "catch_3.C"
diff --git a/gcc/testsuite/g++.target/aarch64/sve/catch_5.C b/gcc/testsuite/g++.target/aarch64/sve/catch_5.C
new file mode 100644 (file)
index 0000000..0eee6f0
--- /dev/null
@@ -0,0 +1,81 @@
+/* { dg-do run { target aarch64_sve_hw } } */
+/* { dg-options "-O3 -fopenmp-simd -fno-omit-frame-pointer" } */
+
+/* Invoke X (P##n) for n in [0, 7].  */
+#define REPEAT8(X, P) \
+  X (P##0) X (P##1) X (P##2) X (P##3) X (P##4) X (P##5) X (P##6) X (P##7)
+
+/* Invoke X (n) for all octal n in [0, 39].  */
+#define REPEAT40(X) \
+  REPEAT8 (X, 0) REPEAT8 (X, 1)  REPEAT8 (X, 2) REPEAT8 (X, 3) REPEAT8 (X, 4)
+
+volatile int testi, sink;
+volatile void *ptr;
+
+/* Take 2 stack arguments and throw to f3.  */
+void __attribute__ ((weak))
+f1 (int x[40][100], int *y, int z1, int z2, int z3, int z4,
+    int z5, int z6, int z7, int z8)
+{
+  /* A wild write to x and y.  */
+  sink = z1;
+  sink = z2;
+  sink = z3;
+  sink = z4;
+  sink = z5;
+  sink = z6;
+  sink = z7;
+  sink = z8;
+  asm volatile ("" ::: "memory");
+  if (y[testi] == x[testi][testi])
+    throw 100;
+}
+
+/* Expect vector work to be done, with spilling of vector registers.  */
+void __attribute__ ((weak))
+f2 (int x[40][100], int *y)
+{
+  /* Create a true variable-sized frame.  */
+  ptr = __builtin_alloca (testi + 40);
+  /* Try to force some spilling.  */
+#define DECLARE(N) int y##N = y[N];
+  REPEAT40 (DECLARE);
+  for (int j = 0; j < 20; ++j)
+    {
+      f1 (x, y, 1, 2, 3, 4, 5, 6, 7, 8);
+#pragma omp simd
+      for (int i = 0; i < 100; ++i)
+       {
+#define INC(N) x[N][i] += y##N;
+         REPEAT40 (INC);
+       }
+    }
+}
+
+/* Catch an exception thrown from f1, via f2.  */
+void __attribute__ ((weak))
+f3 (int x[40][100], int *y, int *z)
+{
+  volatile int extra = 111;
+  try
+    {
+      f2 (x, y);
+    }
+  catch (int val)
+    {
+      *z = val + extra;
+    }
+}
+
+static int x[40][100];
+static int y[40];
+static int z;
+
+int
+main (void)
+{
+  f3 (x, y, &z);
+  if (z != 211)
+    __builtin_abort ();
+  return 0;
+}
diff --git a/gcc/testsuite/g++.target/aarch64/sve/catch_6.C b/gcc/testsuite/g++.target/aarch64/sve/catch_6.C
new file mode 100644 (file)
index 0000000..45f204a
--- /dev/null
@@ -0,0 +1,4 @@
+/* { dg-do run { target aarch64_sve_hw } } */
+/* { dg-options "-O3 -fopenmp-simd -fomit-frame-pointer" } */
+
+#include "catch_5.C"
index 25625efaf330f7081e8aa334ac390d2412483770..85738df894f2565c17e61c37e9119ce882e306c5 100644 (file)
@@ -1,3 +1,10 @@
+2018-01-13  Richard Sandiford  <richard.sandiford@linaro.org>
+
+       * config/aarch64/value-unwind.h (aarch64_vg): New function.
+       (DWARF_LAZY_REGISTER_VALUE): Define.
+       * unwind-dw2.c (_Unwind_GetGR): Use DWARF_LAZY_REGISTER_VALUE
+       to provide a fallback register value.
+
 2018-01-08  Michael Meissner  <meissner@linux.vnet.ibm.com>
 
        * config/rs6000/quad-float128.h (IBM128_TYPE): Explicitly use
index 8458a3d7d248ea7a5864c84d7b8cfe6a3640dd66..e15396250b0ad04b6b58ff46bad23b9d51bb146d 100644 (file)
 #if defined __aarch64__ && !defined __LP64__
 # define REG_VALUE_IN_UNWIND_CONTEXT
 #endif
+
+/* Return the value of the pseudo VG register.  This should only be
+   called if we know this is an SVE host.  */
+static inline int
+aarch64_vg (void)
+{
+  register int vg asm ("x0");
+  /* CNTD X0.  */
+  asm (".inst 0x04e0e3e0" : "=r" (vg));
+  return vg;
+}
+
+/* Lazily provide a value for VG, so that we don't try to execute SVE
+   instructions unless we know they're needed.  */
+#define DWARF_LAZY_REGISTER_VALUE(REGNO, VALUE) \
+  ((REGNO) == AARCH64_DWARF_VG && ((*VALUE) = aarch64_vg (), 1))
index a83ca2fbcf484e6601f7b62a0764b237f903e177..de9310f524fc1f5103b6df2e024c50454a53a1c0 100644 (file)
@@ -216,12 +216,12 @@ _Unwind_IsExtendedContext (struct _Unwind_Context *context)
          || (context->flags & EXTENDED_CONTEXT_BIT));
 }
 \f
-/* Get the value of register INDEX as saved in CONTEXT.  */
+/* Get the value of register REGNO as saved in CONTEXT.  */
 
 inline _Unwind_Word
-_Unwind_GetGR (struct _Unwind_Context *context, int index)
+_Unwind_GetGR (struct _Unwind_Context *context, int regno)
 {
-  int size;
+  int size, index;
   _Unwind_Context_Reg_Val val;
 
 #ifdef DWARF_ZERO_REG
@@ -229,7 +229,7 @@ _Unwind_GetGR (struct _Unwind_Context *context, int index)
     return 0;
 #endif
 
-  index = DWARF_REG_TO_UNWIND_COLUMN (index);
+  index = DWARF_REG_TO_UNWIND_COLUMN (regno);
   gcc_assert (index < (int) sizeof(dwarf_reg_size_table));
   size = dwarf_reg_size_table[index];
   val = context->reg[index];
@@ -237,6 +237,14 @@ _Unwind_GetGR (struct _Unwind_Context *context, int index)
   if (_Unwind_IsExtendedContext (context) && context->by_value[index])
     return _Unwind_Get_Unwind_Word (val);
 
+#ifdef DWARF_LAZY_REGISTER_VALUE
+  {
+    _Unwind_Word value;
+    if (DWARF_LAZY_REGISTER_VALUE (regno, &value))
+      return value;
+  }
+#endif
+
   /* This will segfault if the register hasn't been saved.  */
   if (size == sizeof(_Unwind_Ptr))
     return * (_Unwind_Ptr *) (_Unwind_Internal_Ptr) val;