[PATCH 1/2] fortran: add ALIGN attribute for variables
Magnus Weinmueller <[email protected]>
| Newsgroups | gmane.comp.gcc.patches,gmane.comp.gcc.fortran |
|---|---|
| Message-ID | <[email protected]> |
Add the GNU extension !GCC$ ATTRIBUTES ALIGN(n), which requests that a
variable be aligned to at least n bytes. n must be a positive power of
two and may not be smaller than the natural alignment of the type. For
variables with automatic or static storage duration the alignment is
applied to the decl itself; allocatables and pointers are not handled
here, as their data lives on the heap.
The spelling matches the align modifier of the OpenMP allocate
directive and C's __attribute__((aligned(n))).
The alignment value is stored in module files as a named optional
attribute bit (AB_ALIGN) alongside the existing boolean flags in
mio_symbol_attribute, so that module files without it (written by an
older compiler) still parse correctly; this avoids the need to bump
MOD_VERSION for this feature.
gcc/fortran/ChangeLog:
* gfortran.h (ext_attr_id_t): Add EXT_ATTR_ALIGN.
(struct symbol_attribute): Add ext_align field.
* decl.cc (ext_attr_list): Add align.
(gfc_match_gcc_attributes): Parse the mandatory ALIGN(n)
argument and store it in ext_align.
* trans-decl.cc (gfc_finish_var_decl): Apply ext_align to the
decl; diagnose a value below the natural alignment.
* module.cc (ab_attribute): Add AB_ALIGN.
(attr_bits): Add "ALIGN" entry.
(mio_symbol_attribute): Read and write ext_align as an optional
named attribute bit, defaulting to 0 when absent.
gcc/testsuite/ChangeLog:
* gfortran.dg/align_1.f90: New test.
* gfortran.dg/align_2.f90: New test.
* gfortran.dg/align_2b.f90: New test.
---
gcc/fortran/decl.cc | 49 ++++++++++++++++++++++++++
gcc/fortran/gfortran.h | 4 +++
gcc/fortran/module.cc | 17 ++++++++-
gcc/fortran/trans-decl.cc | 19 ++++++++++
gcc/testsuite/gfortran.dg/align_1.f90 | 38 ++++++++++++++++++++
gcc/testsuite/gfortran.dg/align_2.f90 | 19 ++++++++++
gcc/testsuite/gfortran.dg/align_2b.f90 | 12 +++++++
7 files changed, 157 insertions(+), 1 deletion(-)
create mode 100644 gcc/testsuite/gfortran.dg/align_1.f90
create mode 100644 gcc/testsuite/gfortran.dg/align_2.f90
create mode 100644 gcc/testsuite/gfortran.dg/align_2b.f90
diff --git a/gcc/fortran/decl.cc b/gcc/fortran/decl.cc
index 5d194635a..c6472cc14 100644
--- a/gcc/fortran/decl.cc
+++ b/gcc/fortran/decl.cc
@@ -12849,6 +12849,7 @@ const ext_attr_t ext_attr_list[] = {
{ "noinline", EXT_ATTR_NOINLINE, NULL },
{ "noreturn", EXT_ATTR_NORETURN, NULL },
{ "weak", EXT_ATTR_WEAK, NULL },
+ { "align", EXT_ATTR_ALIGN, NULL },
{ NULL, EXT_ATTR_LAST, NULL }
};
@@ -12896,6 +12897,44 @@ gfc_match_gcc_attributes (void)
if (!gfc_add_ext_attribute (&attr, (ext_attr_id_t)id, &gfc_current_locus))
return MATCH_ERROR;
+ /* ALIGN(n) takes a mandatory value. */
+ if (id == EXT_ATTR_ALIGN)
+ {
+ int align;
+
+ gfc_gobble_whitespace ();
+ ch = gfc_peek_ascii_char ();
+ if (ch != '(')
+ {
+ gfc_error ("ALIGN attribute at %C requires a value, e.g. "
+ "%<ALIGN(16)%>");
+ return MATCH_ERROR;
+ }
+
+ gfc_next_ascii_char ();
+
+ if (gfc_match_small_int (&align) != MATCH_YES)
+ {
+ gfc_error ("Expected a constant integer after ALIGN( at %C");
+ return MATCH_ERROR;
+ }
+
+ if (gfc_match_char (')') != MATCH_YES)
+ {
+ gfc_error ("Missing %<)%> in ALIGN attribute at %C");
+ return MATCH_ERROR;
+ }
+
+ if (align < 1 || (align & (align - 1)) != 0)
+ {
+ gfc_error ("ALIGN value (%d) at %C must be a positive power "
+ "of two", align);
+ return MATCH_ERROR;
+ }
+
+ attr.ext_align = (unsigned) align;
+ }
+
gfc_gobble_whitespace ();
ch = gfc_next_ascii_char ();
if (ch == ':')
@@ -12924,6 +12963,16 @@ gfc_match_gcc_attributes (void)
return MATCH_ERROR;
sym->attr.ext_attr |= attr.ext_attr;
+ if (attr.ext_attr & (1 << EXT_ATTR_ALIGN))
+ {
+ if (sym->attr.ext_align && sym->attr.ext_align != attr.ext_align)
+ {
+ gfc_error ("Symbol %qs at %C already has ALIGN(%u)",
+ sym->name, sym->attr.ext_align);
+ return MATCH_ERROR;
+ }
+ sym->attr.ext_align = attr.ext_align;
+ }
if (gfc_match_eos () == MATCH_YES)
break;
diff --git a/gcc/fortran/gfortran.h b/gcc/fortran/gfortran.h
index b0ce54e1c..561d73e04 100644
--- a/gcc/fortran/gfortran.h
+++ b/gcc/fortran/gfortran.h
@@ -888,6 +888,7 @@ typedef enum
EXT_ATTR_NOINLINE,
EXT_ATTR_NORETURN,
EXT_ATTR_WEAK,
+ EXT_ATTR_ALIGN,
EXT_ATTR_LAST, EXT_ATTR_NUM = EXT_ATTR_LAST
}
ext_attr_id_t;
@@ -1064,6 +1065,9 @@ typedef struct
/* Attributes set by compiler extensions (!GCC$ ATTRIBUTES). */
unsigned ext_attr:EXT_ATTR_NUM;
+ /* Value of !GCC$ ATTRIBUTES ALIGN(n), in bytes. */
+ unsigned ext_align;
+
/* The namespace where the attribute has been set. */
struct gfc_namespace *volatile_ns, *asynchronous_ns;
}
diff --git a/gcc/fortran/module.cc b/gcc/fortran/module.cc
index 04ddf6b44..fd67f1ce1 100644
--- a/gcc/fortran/module.cc
+++ b/gcc/fortran/module.cc
@@ -2103,7 +2103,8 @@ enum ab_attribute
AB_OMP_REQ_MEM_ORDER_SEQ_CST, AB_OMP_REQ_MEM_ORDER_ACQ_REL,
AB_OMP_REQ_MEM_ORDER_ACQUIRE, AB_OMP_REQ_MEM_ORDER_RELEASE,
AB_OMP_REQ_MEM_ORDER_RELAXED, AB_OMP_DEVICE_TYPE_NOHOST,
- AB_OMP_DEVICE_TYPE_HOST, AB_OMP_DEVICE_TYPE_ANY, AB_OMP_GROUPPRIVATE
+ AB_OMP_DEVICE_TYPE_HOST, AB_OMP_DEVICE_TYPE_ANY, AB_OMP_GROUPPRIVATE,
+ AB_ALIGN
};
static const mstring attr_bits[] =
@@ -2194,6 +2195,7 @@ static const mstring attr_bits[] =
minit ("OMP_DEVICE_TYPE_HOST", AB_OMP_DEVICE_TYPE_HOST),
minit ("OMP_DEVICE_TYPE_NOHOST", AB_OMP_DEVICE_TYPE_NOHOST),
minit ("OMP_DEVICE_TYPE_ANYHOST", AB_OMP_DEVICE_TYPE_ANY),
+ minit ("ALIGN", AB_ALIGN),
minit (NULL, -1)
};
@@ -2280,6 +2282,12 @@ mio_symbol_attribute (symbol_attribute *attr)
if (iomode == IO_OUTPUT)
{
+ if (attr->ext_align)
+ {
+ MIO_NAME (ab_attribute) (AB_ALIGN, attr_bits);
+ unsigned ea = attr->ext_align;
+ mio_integer ((int *) &ea);
+ }
if (attr->allocatable)
MIO_NAME (ab_attribute) (AB_ALLOCATABLE, attr_bits);
if (attr->artificial)
@@ -2505,6 +2513,13 @@ mio_symbol_attribute (symbol_attribute *attr)
switch ((ab_attribute) find_enum (attr_bits))
{
+ case AB_ALIGN:
+ {
+ unsigned ea;
+ mio_integer ((int *) &ea);
+ attr->ext_align = ea;
+ }
+ break;
case AB_ALLOCATABLE:
attr->allocatable = 1;
break;
diff --git a/gcc/fortran/trans-decl.cc b/gcc/fortran/trans-decl.cc
index 4b3f75ced..d385531b5 100644
--- a/gcc/fortran/trans-decl.cc
+++ b/gcc/fortran/trans-decl.cc
@@ -863,6 +863,25 @@ gfc_finish_var_decl (tree decl, gfc_symbol * sym)
if (sym->attr.ext_attr & (1 << EXT_ATTR_WEAK))
declare_weak (decl);
+ /* Handle !GCC$ ATTRIBUTES ALIGN(n). Allocatables and pointers get their
+ alignment from ALLOCATE instead. */
+ if ((sym->attr.ext_attr & (1 << EXT_ATTR_ALIGN))
+ && VAR_P (decl)
+ && !sym->attr.allocatable && !sym->attr.pointer)
+ {
+ unsigned align_bits = sym->attr.ext_align * BITS_PER_UNIT;
+ if (align_bits < TYPE_ALIGN (TREE_TYPE (decl)))
+ gfc_error ("ALIGN(%u) for %qs at %L is smaller than the natural "
+ "alignment (%u) of its type", sym->attr.ext_align,
+ sym->name, &sym->declared_at,
+ TYPE_ALIGN_UNIT (TREE_TYPE (decl)));
+ else
+ {
+ SET_DECL_ALIGN (decl, align_bits);
+ DECL_USER_ALIGN (decl) = 1;
+ }
+ }
+
/* Handle threadprivate variables. */
if (sym->attr.threadprivate
&& (TREE_STATIC (decl) || DECL_EXTERNAL (decl)))
diff --git a/gcc/testsuite/gfortran.dg/align_1.f90 b/gcc/testsuite/gfortran.dg/align_1.f90
new file mode 100644
index 000000000..ee165174b
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/align_1.f90
@@ -0,0 +1,38 @@
+! { dg-do run }
+!
+! !GCC$ ATTRIBUTES ALIGN(n) on automatic, static and module variables.
+
+module align_1_mod
+ implicit none
+ real(kind=4) :: mod_arr(8)
+ !GCC$ ATTRIBUTES ALIGN(64) :: mod_arr
+end module align_1_mod
+
+program align_1
+ use align_1_mod
+ implicit none
+
+ real(kind=4) :: stack_arr(9)
+ !GCC$ ATTRIBUTES ALIGN(64) :: stack_arr
+
+ real(kind=8) :: stack_scalar
+ !GCC$ ATTRIBUTES ALIGN(32) :: stack_scalar
+
+ integer(kind=4) :: stack_int(5)
+ !GCC$ ATTRIBUTES ALIGN(16) :: stack_int
+
+ ! Several symbols in one ATTRIBUTES statement.
+ real(kind=4) :: pair_a(4), pair_b(4)
+ !GCC$ ATTRIBUTES ALIGN(32) :: pair_a, pair_b
+
+ real(kind=4), save :: static_arr(7)
+ !GCC$ ATTRIBUTES ALIGN(64) :: static_arr
+
+ if (iand (loc (stack_arr), 63_8) /= 0) stop 1
+ if (iand (loc (stack_scalar), 31_8) /= 0) stop 2
+ if (iand (loc (stack_int), 15_8) /= 0) stop 3
+ if (iand (loc (pair_a), 31_8) /= 0) stop 4
+ if (iand (loc (pair_b), 31_8) /= 0) stop 5
+ if (iand (loc (static_arr), 63_8) /= 0) stop 6
+ if (iand (loc (mod_arr), 63_8) /= 0) stop 7
+end program align_1
diff --git a/gcc/testsuite/gfortran.dg/align_2.f90 b/gcc/testsuite/gfortran.dg/align_2.f90
new file mode 100644
index 000000000..534d11c9a
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/align_2.f90
@@ -0,0 +1,19 @@
+! { dg-do compile }
+!
+! Invalid arguments to !GCC$ ATTRIBUTES ALIGN(n), all rejected while
+! parsing. See align_2b.f90 for the translation-time case.
+
+subroutine sub_a
+ real :: a(4)
+ !GCC$ ATTRIBUTES ALIGN(24) :: a ! { dg-error "must be a positive power of two" }
+end subroutine sub_a
+
+subroutine sub_b
+ real :: b(4)
+ !GCC$ ATTRIBUTES ALIGN :: b ! { dg-error "requires a value" }
+end subroutine sub_b
+
+subroutine sub_c
+ real :: c(4)
+ !GCC$ ATTRIBUTES ALIGN(0) :: c ! { dg-error "must be a positive power of two" }
+end subroutine sub_c
diff --git a/gcc/testsuite/gfortran.dg/align_2b.f90 b/gcc/testsuite/gfortran.dg/align_2b.f90
new file mode 100644
index 000000000..3db0dd5f7
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/align_2b.f90
@@ -0,0 +1,12 @@
+! { dg-do compile }
+!
+! ALIGN(n) below the natural alignment of the type. This is only caught
+! during translation, which gfortran skips once any earlier error was
+! seen, so it needs a file of its own. The error is reported on the
+! declaration, not on the ATTRIBUTES statement.
+
+subroutine sub_d
+ real(kind=8) :: d ! { dg-error "smaller than the natural alignment" }
+ !GCC$ ATTRIBUTES ALIGN(2) :: d
+ d = 1.0d0
+end subroutine sub_d
--
2.50.1 (Apple Git-155)