From dbc3af4fc620aeb6fbf088e961fe8658bbd959c7 Mon Sep 17 00:00:00 2001 From: Richard Sandiford Date: Sat, 13 Jan 2018 17:56:52 +0000 Subject: [PATCH] SVE unwinding 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 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 From-SVN: r256615 --- gcc/ChangeLog | 5 ++ gcc/doc/tm.texi | 7 ++ gcc/doc/tm.texi.in | 7 ++ gcc/testsuite/ChangeLog | 10 +++ .../g++.target/aarch64/sve/aarch64-sve.exp | 45 ++++++++++++ gcc/testsuite/g++.target/aarch64/sve/catch_1.C | 69 ++++++++++++++++++ gcc/testsuite/g++.target/aarch64/sve/catch_2.C | 4 ++ gcc/testsuite/g++.target/aarch64/sve/catch_3.C | 78 +++++++++++++++++++++ gcc/testsuite/g++.target/aarch64/sve/catch_4.C | 4 ++ gcc/testsuite/g++.target/aarch64/sve/catch_5.C | 81 ++++++++++++++++++++++ gcc/testsuite/g++.target/aarch64/sve/catch_6.C | 4 ++ libgcc/ChangeLog | 7 ++ libgcc/config/aarch64/value-unwind.h | 16 +++++ libgcc/unwind-dw2.c | 16 +++-- 14 files changed, 349 insertions(+), 4 deletions(-) create mode 100644 gcc/testsuite/g++.target/aarch64/sve/aarch64-sve.exp create mode 100644 gcc/testsuite/g++.target/aarch64/sve/catch_1.C create mode 100644 gcc/testsuite/g++.target/aarch64/sve/catch_2.C create mode 100644 gcc/testsuite/g++.target/aarch64/sve/catch_3.C create mode 100644 gcc/testsuite/g++.target/aarch64/sve/catch_4.C create mode 100644 gcc/testsuite/g++.target/aarch64/sve/catch_5.C create mode 100644 gcc/testsuite/g++.target/aarch64/sve/catch_6.C diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 40da1eb..64951e9 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,4 +1,9 @@ 2018-01-13 Richard Sandiford + + * doc/tm.texi.in (DWARF_LAZY_REGISTER_VALUE): Document. + * doc/tm.texi: Regenerate. + +2018-01-13 Richard Sandiford Alan Hayward David Sherwood diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi index 11b560b..4cd8dce 100644 --- a/gcc/doc/tm.texi +++ b/gcc/doc/tm.texi @@ -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 diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in index 0cd694a..3a2c2f2 100644 --- a/gcc/doc/tm.texi.in +++ b/gcc/doc/tm.texi.in @@ -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 diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index eb1b229..48f40da 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,4 +1,14 @@ 2018-01-13 Richard Sandiford + + * 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 Alan Hayward David Sherwood 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 index 0000000..7557aa6 --- /dev/null +++ b/gcc/testsuite/g++.target/aarch64/sve/aarch64-sve.exp @@ -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 +# . */ + +# 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 index 0000000..39759cb --- /dev/null +++ b/gcc/testsuite/g++.target/aarch64/sve/catch_1.C @@ -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 index 0000000..7722af1 --- /dev/null +++ b/gcc/testsuite/g++.target/aarch64/sve/catch_2.C @@ -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 index 0000000..7d17024 --- /dev/null +++ b/gcc/testsuite/g++.target/aarch64/sve/catch_3.C @@ -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 index 0000000..07841ac --- /dev/null +++ b/gcc/testsuite/g++.target/aarch64/sve/catch_4.C @@ -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 index 0000000..0eee6f0 --- /dev/null +++ b/gcc/testsuite/g++.target/aarch64/sve/catch_5.C @@ -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 index 0000000..45f204a --- /dev/null +++ b/gcc/testsuite/g++.target/aarch64/sve/catch_6.C @@ -0,0 +1,4 @@ +/* { dg-do run { target aarch64_sve_hw } } */ +/* { dg-options "-O3 -fopenmp-simd -fomit-frame-pointer" } */ + +#include "catch_5.C" diff --git a/libgcc/ChangeLog b/libgcc/ChangeLog index 25625ef..85738df 100644 --- a/libgcc/ChangeLog +++ b/libgcc/ChangeLog @@ -1,3 +1,10 @@ +2018-01-13 Richard Sandiford + + * 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 * config/rs6000/quad-float128.h (IBM128_TYPE): Explicitly use diff --git a/libgcc/config/aarch64/value-unwind.h b/libgcc/config/aarch64/value-unwind.h index 8458a3d..e1539625 100644 --- a/libgcc/config/aarch64/value-unwind.h +++ b/libgcc/config/aarch64/value-unwind.h @@ -23,3 +23,19 @@ #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)) diff --git a/libgcc/unwind-dw2.c b/libgcc/unwind-dw2.c index a83ca2f..de9310f 100644 --- a/libgcc/unwind-dw2.c +++ b/libgcc/unwind-dw2.c @@ -216,12 +216,12 @@ _Unwind_IsExtendedContext (struct _Unwind_Context *context) || (context->flags & EXTENDED_CONTEXT_BIT)); } -/* 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; -- 2.7.4