[PATCH 2/2] fortran: honour ALIGN in ALLOCATE and add ALLOCATE ALIGN directive
Magnus Weinmueller <[email protected]>
| Newsgroups | gmane.comp.gcc.patches,gmane.comp.gcc.fortran |
|---|---|
| Message-ID | <[email protected]> |
Make ALLOCATE honour an alignment request by calling aligned_alloc
instead of malloc, rounding the size up to a multiple of the alignment
as C11 requires. The request may come from !GCC$ ATTRIBUTES ALIGN(n)
on the allocate-object or from the new per-statement directive
!GCC$ ALLOCATE (var-list) ALIGN(n)
which follows the !GCC$ UNROLL pattern and applies to the immediately
following ALLOCATE statement only. Where both apply to the same
object, the larger alignment wins.
gcc/fortran/ChangeLog:
* gfortran.h (struct gfc_alloc): Add align field.
(struct gfc_gcc_alloc_align): New.
(directive_allocate_align): Declare.
(gfc_free_gcc_alloc_align): Declare.
* decl.cc (directive_allocate_align): Define.
(gfc_free_gcc_alloc_align): New function.
(gfc_match_gcc_allocate): New function.
* match.h (gfc_match_gcc_allocate): Declare.
* parse.cc (decode_gcc_attribute): Recognise the ALLOCATE
directive.
(parse_executable): Diagnose a directive that is not followed by
an ALLOCATE statement.
* match.cc (gfc_match_allocate): Apply a pending directive to the
matching allocate-objects.
* f95-lang.cc (gfc_init_builtin_functions): Declare aligned_alloc.
* trans.h (gfc_allocate_using_malloc): Add alignment argument.
(gfc_allocate_allocatable): Likewise.
* trans.cc (gfc_allocate_using_malloc): Use aligned_alloc when an
alignment is requested.
(gfc_allocate_allocatable): Pass the alignment through.
* trans-array.h (gfc_array_allocate): Add alignment argument.
* trans-array.cc (gfc_array_allocate): Pass the alignment through.
* trans-stmt.cc (gfc_trans_allocate): Compute the requested
alignment per allocate-object.
gcc/testsuite/ChangeLog:
* gfortran.dg/align_3.f90: New test.
* gfortran.dg/align_4.f90: New test.
---
gcc/fortran/decl.cc | 90 +++++++++++++++++++++++++++
gcc/fortran/f95-lang.cc | 5 ++
gcc/fortran/gfortran.h | 13 ++++
gcc/fortran/match.cc | 22 +++++++
gcc/fortran/match.h | 1 +
gcc/fortran/parse.cc | 8 +++
gcc/fortran/trans-array.cc | 9 ++-
gcc/fortran/trans-array.h | 3 +-
gcc/fortran/trans-stmt.cc | 31 ++++++++-
gcc/fortran/trans.cc | 32 ++++++++--
gcc/fortran/trans.h | 4 +-
gcc/testsuite/gfortran.dg/align_3.f90 | 41 ++++++++++++
gcc/testsuite/gfortran.dg/align_4.f90 | 23 +++++++
13 files changed, 268 insertions(+), 14 deletions(-)
create mode 100644 gcc/testsuite/gfortran.dg/align_3.f90
create mode 100644 gcc/testsuite/gfortran.dg/align_4.f90
diff --git a/gcc/fortran/decl.cc b/gcc/fortran/decl.cc
index c6472cc14..efe6c2837 100644
--- a/gcc/fortran/decl.cc
+++ b/gcc/fortran/decl.cc
@@ -13023,6 +13023,96 @@ gfc_match_gcc_unroll (void)
return MATCH_ERROR;
}
+gfc_gcc_alloc_align *directive_allocate_align = NULL;
+
+void
+gfc_free_gcc_alloc_align (void)
+{
+ gfc_gcc_alloc_align *p, *q;
+ for (p = directive_allocate_align; p; p = q)
+ {
+ q = p->next;
+ free (p);
+ }
+ directive_allocate_align = NULL;
+}
+
+/* Match a !GCC$ ALLOCATE statement of the form:
+ !GCC$ ALLOCATE (var [, var]...) ALIGN(n)
+
+ The named objects are aligned to n bytes by the ALLOCATE that follows.
+ n must be a positive power of two. */
+match
+gfc_match_gcc_allocate (void)
+{
+ char name[GFC_MAX_SYMBOL_LEN + 1];
+ gfc_gcc_alloc_align *head = NULL, *tail = NULL;
+ int align;
+
+ /* Drop a previous directive that was never consumed. */
+ gfc_free_gcc_alloc_align ();
+
+ if (gfc_match_char ('(') != MATCH_YES)
+ goto syntax;
+
+ for (;;)
+ {
+ if (gfc_match_name (name) != MATCH_YES)
+ goto syntax;
+
+ gfc_gcc_alloc_align *n = XCNEW (gfc_gcc_alloc_align);
+ strcpy (n->name, name);
+ if (head == NULL)
+ head = tail = n;
+ else
+ {
+ tail->next = n;
+ tail = n;
+ }
+
+ if (gfc_match_char (',') == MATCH_YES)
+ continue;
+ if (gfc_match_char (')') == MATCH_YES)
+ break;
+ goto syntax;
+ }
+
+ if (gfc_match (" align (") != MATCH_YES)
+ goto syntax;
+ if (gfc_match_small_int (&align) != MATCH_YES)
+ goto syntax;
+ if (gfc_match_char (')') != MATCH_YES)
+ goto syntax;
+ if (gfc_match_eos () != MATCH_YES)
+ goto syntax;
+
+ if (align < 1 || (align & (align - 1)) != 0)
+ {
+ gfc_error ("ALIGN value (%d) in !GCC$ ALLOCATE directive at %C must be "
+ "a positive power of two", align);
+ goto cleanup;
+ }
+
+ for (tail = head; tail; tail = tail->next)
+ tail->align = (unsigned) align;
+
+ directive_allocate_align = head;
+ return MATCH_YES;
+
+syntax:
+ gfc_error ("Syntax error in !GCC$ ALLOCATE directive at %C");
+cleanup:
+ {
+ gfc_gcc_alloc_align *p, *q;
+ for (p = head; p; p = q)
+ {
+ q = p->next;
+ free (p);
+ }
+ }
+ return MATCH_ERROR;
+}
+
/* Match a !GCC$ builtin (b) attributes simd flags if('target') form:
The parameter b is name of a middle-end built-in.
diff --git a/gcc/fortran/f95-lang.cc b/gcc/fortran/f95-lang.cc
index 45aab3486..1335278b9 100644
--- a/gcc/fortran/f95-lang.cc
+++ b/gcc/fortran/f95-lang.cc
@@ -1031,6 +1031,11 @@ gfc_init_builtin_functions (void)
"calloc", ATTR_NOTHROW_LEAF_MALLOC_LIST);
DECL_IS_MALLOC (builtin_decl_explicit (BUILT_IN_CALLOC)) = 1;
+ gfc_define_builtin ("__builtin_aligned_alloc", ftype,
+ BUILT_IN_ALIGNED_ALLOC,
+ "aligned_alloc", ATTR_NOTHROW_LEAF_MALLOC_LIST);
+ DECL_IS_MALLOC (builtin_decl_explicit (BUILT_IN_ALIGNED_ALLOC)) = 1;
+
ftype = build_function_type_list (pvoid_type_node, pvoid_type_node,
size_type_node, NULL_TREE);
gfc_define_builtin ("__builtin_realloc", ftype, BUILT_IN_REALLOC,
diff --git a/gcc/fortran/gfortran.h b/gcc/fortran/gfortran.h
index 561d73e04..10f407846 100644
--- a/gcc/fortran/gfortran.h
+++ b/gcc/fortran/gfortran.h
@@ -3045,6 +3045,8 @@ gfc_iterator;
typedef struct gfc_alloc
{
gfc_expr *expr;
+ /* From !GCC$ ALLOCATE ALIGN(n), in bytes; 0 if none. */
+ unsigned align;
struct gfc_alloc *next;
}
gfc_alloc;
@@ -3466,6 +3468,17 @@ extern bool directive_ivdep;
extern bool directive_vector;
extern bool directive_novector;
+/* Set by !GCC$ ALLOCATE ALIGN(n), used by the next ALLOCATE. */
+typedef struct gfc_gcc_alloc_align
+{
+ char name[GFC_MAX_SYMBOL_LEN + 1];
+ unsigned align;
+ struct gfc_gcc_alloc_align *next;
+}
+gfc_gcc_alloc_align;
+extern gfc_gcc_alloc_align *directive_allocate_align;
+void gfc_free_gcc_alloc_align (void);
+
/* SIMD clause enum. */
enum gfc_simd_clause
{
diff --git a/gcc/fortran/match.cc b/gcc/fortran/match.cc
index 62bdb8687..8b1d9c86c 100644
--- a/gcc/fortran/match.cc
+++ b/gcc/fortran/match.cc
@@ -5416,6 +5416,28 @@ alloc_opt_list:
new_st.ext.alloc.list = head;
new_st.ext.alloc.ts = ts;
+ /* Apply a preceding !GCC$ ALLOCATE ALIGN directive. */
+ if (directive_allocate_align)
+ {
+ gfc_gcc_alloc_align *d;
+ for (d = directive_allocate_align; d; d = d->next)
+ {
+ gfc_alloc *a;
+ bool found = false;
+ for (a = head; a; a = a->next)
+ if (a->expr->symtree
+ && strcmp (a->expr->symtree->n.sym->name, d->name) == 0)
+ {
+ a->align = d->align;
+ found = true;
+ }
+ if (!found)
+ gfc_warning (0, "Object %qs in !GCC$ ALLOCATE ALIGN directive does "
+ "not appear in the ALLOCATE statement at %C", d->name);
+ }
+ gfc_free_gcc_alloc_align ();
+ }
+
if (type_param_spec_list)
gfc_free_actual_arglist (type_param_spec_list);
diff --git a/gcc/fortran/match.h b/gcc/fortran/match.h
index 52cb2f0cd..0b4d2632d 100644
--- a/gcc/fortran/match.h
+++ b/gcc/fortran/match.h
@@ -278,6 +278,7 @@ match gfc_match_contiguous (void);
match gfc_match_dimension (void);
match gfc_match_external (void);
match gfc_match_gcc_attributes (void);
+match gfc_match_gcc_allocate (void);
match gfc_match_gcc_builtin (void);
match gfc_match_gcc_ivdep (void);
match gfc_match_gcc_novector (void);
diff --git a/gcc/fortran/parse.cc b/gcc/fortran/parse.cc
index a41bf090c..fca0e850a 100644
--- a/gcc/fortran/parse.cc
+++ b/gcc/fortran/parse.cc
@@ -1483,6 +1483,7 @@ decode_gcc_attribute (void)
old_locus = gfc_current_locus;
match ("attributes", gfc_match_gcc_attributes, ST_ATTR_DECL);
+ match ("allocate", gfc_match_gcc_allocate, ST_NONE);
match ("unroll", gfc_match_gcc_unroll, ST_NONE);
match ("builtin", gfc_match_gcc_builtin, ST_NONE);
match ("ivdep", gfc_match_gcc_ivdep, ST_NONE);
@@ -6848,6 +6849,13 @@ parse_executable (gfc_statement st)
gfc_error ("%<GCC novector%> "
"directive not at the start of a loop at %C");
+ if (directive_allocate_align)
+ {
+ gfc_error ("%<GCC allocate%> directive not immediately before an "
+ "ALLOCATE statement at %C");
+ gfc_free_gcc_alloc_align ();
+ }
+
st = next_statement ();
}
}
diff --git a/gcc/fortran/trans-array.cc b/gcc/fortran/trans-array.cc
index b57c18aba..64fd44950 100644
--- a/gcc/fortran/trans-array.cc
+++ b/gcc/fortran/trans-array.cc
@@ -6781,7 +6781,8 @@ bool
gfc_array_allocate (gfc_se * se, gfc_expr * expr, tree status, tree errmsg,
tree errlen, tree label_finish, tree expr3_elem_size,
gfc_expr *expr3, tree e3_arr_desc, bool e3_has_nodescriptor,
- gfc_omp_namelist *omp_alloc, bool explicit_ts)
+ gfc_omp_namelist *omp_alloc, bool explicit_ts,
+ unsigned user_align)
{
tree tmp;
tree pointer;
@@ -7026,7 +7027,8 @@ gfc_array_allocate (gfc_se * se, gfc_expr * expr, tree status, tree errmsg,
gfc_allocate_allocatable (&elseblock, pointer, size, token,
status, errmsg, errlen, label_finish, expr,
coref != NULL ? coref->u.ar.as->corank : 0,
- omp_cond, omp_alt_alloc, succ_add_expr);
+ omp_cond, omp_alt_alloc, succ_add_expr,
+ user_align);
else if (non_ulimate_coarray_ptr_comp && token)
/* The token is set only for GFC_FCOARRAY_LIB mode. */
gfc_allocate_using_caf_lib (&elseblock, pointer, size, token, status,
@@ -7034,7 +7036,8 @@ gfc_array_allocate (gfc_se * se, gfc_expr * expr, tree status, tree errmsg,
GFC_CAF_COARRAY_ALLOC_ALLOCATE_ONLY);
else
gfc_allocate_using_malloc (&elseblock, pointer, size, status,
- omp_cond, omp_alt_alloc, succ_add_expr);
+ omp_cond, omp_alt_alloc, succ_add_expr,
+ user_align);
if (dimension)
{
diff --git a/gcc/fortran/trans-array.h b/gcc/fortran/trans-array.h
index 4b51e5469..eaa83fac4 100644
--- a/gcc/fortran/trans-array.h
+++ b/gcc/fortran/trans-array.h
@@ -21,7 +21,8 @@ along with GCC; see the file COPYING3. If not see
/* Generate code to initialize and allocate an array. Statements are added to
se, which should contain an expression for the array descriptor. */
bool gfc_array_allocate (gfc_se *, gfc_expr *, tree, tree, tree, tree, tree,
- gfc_expr *, tree, bool, gfc_omp_namelist *, bool);
+ gfc_expr *, tree, bool, gfc_omp_namelist *, bool,
+ unsigned = 0);
/* Allow the bounds of a loop to be set from a callee's array spec. */
void gfc_set_loop_bounds_from_array_spec (gfc_interface_mapping *,
diff --git a/gcc/fortran/trans-stmt.cc b/gcc/fortran/trans-stmt.cc
index 49f8cd8d7..b2d0ddeb5 100644
--- a/gcc/fortran/trans-stmt.cc
+++ b/gcc/fortran/trans-stmt.cc
@@ -6703,6 +6703,9 @@ gfc_trans_allocate (gfc_code * code, gfc_omp_namelist *omp_allocate)
if (!code->ext.alloc.list)
return NULL_TREE;
+ /* Requested alignment of the current object, in bytes; 0 if none. */
+ unsigned user_align = 0;
+
stat = tmp = memsz = al_vptr = al_len = NULL_TREE;
expr3 = expr3_vptr = expr3_len = expr3_esize = NULL_TREE;
label_errmsg = label_finish = errmsg = errlen = NULL_TREE;
@@ -7171,6 +7174,25 @@ gfc_trans_allocate (gfc_code * code, gfc_omp_namelist *omp_allocate)
for (al = code->ext.alloc.list; al != NULL; al = al->next)
{
expr = gfc_copy_expr (al->expr);
+
+ /* Take the larger of the ALLOCATE directive and the symbol's
+ attribute. The attribute does not apply to component refs. */
+ user_align = al->align;
+ if (expr->symtree
+ && (expr->symtree->n.sym->attr.ext_attr & (1 << EXT_ATTR_ALIGN)))
+ {
+ bool comp_ref = false;
+ for (gfc_ref *r = expr->ref; r; r = r->next)
+ if (r->type == REF_COMPONENT)
+ {
+ comp_ref = true;
+ break;
+ }
+ if (!comp_ref)
+ user_align = MAX (user_align,
+ expr->symtree->n.sym->attr.ext_align);
+ }
+
/* UNLIMITED_POLY () needs the _data component to be set, when
expr is a unlimited polymorphic object. But the _data component
has not been set yet, so check the derived type's attr for the
@@ -7304,7 +7326,8 @@ gfc_trans_allocate (gfc_code * code, gfc_omp_namelist *omp_allocate)
tmp, e3rhs ? e3rhs : code->expr3,
e3_is == E3_DESC ? expr3 : NULL_TREE,
e3_has_nodescriptor, omp_alloc_item,
- code->ext.alloc.ts.type != BT_UNKNOWN))
+ code->ext.alloc.ts.type != BT_UNKNOWN,
+ user_align))
{
/* A scalar or derived type. First compute the size to
allocate.
@@ -7476,10 +7499,12 @@ gfc_trans_allocate (gfc_code * code, gfc_omp_namelist *omp_allocate)
gfc_allocate_allocatable (&se.pre, se.expr, memsz,
NULL_TREE, stat, errmsg, errlen,
label_finish, expr, 0,
- omp_cond, omp_alt_alloc, succ_add_expr);
+ omp_cond, omp_alt_alloc, succ_add_expr,
+ user_align);
else
gfc_allocate_using_malloc (&se.pre, se.expr, memsz, stat,
- omp_cond, omp_alt_alloc, succ_add_expr);
+ omp_cond, omp_alt_alloc, succ_add_expr,
+ user_align);
}
else
{
diff --git a/gcc/fortran/trans.cc b/gcc/fortran/trans.cc
index 1d7006a69..21c3d4ea9 100644
--- a/gcc/fortran/trans.cc
+++ b/gcc/fortran/trans.cc
@@ -819,7 +819,7 @@ gfc_call_malloc (stmtblock_t * block, tree type, tree size)
void
gfc_allocate_using_malloc (stmtblock_t * block, tree pointer,
tree size, tree status, tree cond, tree alt_alloc,
- tree extra_success_expr)
+ tree extra_success_expr, unsigned align)
{
tree tmp, error_cond;
stmtblock_t on_error;
@@ -838,8 +838,29 @@ gfc_allocate_using_malloc (stmtblock_t * block, tree pointer,
size, build_int_cst (size_type_node, 1));
if (!cond_is_true)
- tmp = build_call_expr_loc (input_location,
- builtin_decl_explicit (BUILT_IN_MALLOC), 1, tmp);
+ {
+ if (align > 0)
+ {
+ /* aligned_alloc wants the size to be a multiple of the
+ alignment, so round it up. */
+ tree mask = build_int_cst (size_type_node, align - 1);
+ tmp = fold_build2_loc (input_location, PLUS_EXPR, size_type_node,
+ tmp, mask);
+ tmp = fold_build2_loc (input_location, BIT_AND_EXPR,
+ size_type_node, tmp,
+ fold_build1_loc (input_location, BIT_NOT_EXPR,
+ size_type_node, mask));
+ tmp = build_call_expr_loc (input_location,
+ builtin_decl_explicit
+ (BUILT_IN_ALIGNED_ALLOC), 2,
+ build_int_cst (size_type_node, align),
+ tmp);
+ }
+ else
+ tmp = build_call_expr_loc (input_location,
+ builtin_decl_explicit (BUILT_IN_MALLOC),
+ 1, tmp);
+ }
else
tmp = alt_alloc;
@@ -960,7 +981,8 @@ void
gfc_allocate_allocatable (stmtblock_t * block, tree mem, tree size,
tree token, tree status, tree errmsg, tree errlen,
tree label_finish, gfc_expr* expr, int corank,
- tree cond, tree alt_alloc, tree extra_success_expr)
+ tree cond, tree alt_alloc, tree extra_success_expr,
+ unsigned align)
{
stmtblock_t alloc_block;
tree tmp, null_mem, alloc, error;
@@ -1059,7 +1081,7 @@ gfc_allocate_allocatable (stmtblock_t * block, tree mem, tree size,
}
else
gfc_allocate_using_malloc (&alloc_block, mem, size, status,
- cond, alt_alloc, extra_success_expr);
+ cond, alt_alloc, extra_success_expr, align);
alloc = gfc_finish_block (&alloc_block);
diff --git a/gcc/fortran/trans.h b/gcc/fortran/trans.h
index f97fefd2a..c310a650d 100644
--- a/gcc/fortran/trans.h
+++ b/gcc/fortran/trans.h
@@ -764,12 +764,12 @@ void gfc_allocate_using_caf_lib (stmtblock_t *, tree, tree, tree, tree, tree,
void gfc_allocate_allocatable (stmtblock_t*, tree, tree, tree, tree,
tree, tree, tree, gfc_expr*, int,
tree = NULL_TREE, tree = NULL_TREE,
- tree = NULL_TREE);
+ tree = NULL_TREE, unsigned = 0);
/* Allocate memory, with optional status variable. */
void gfc_allocate_using_malloc (stmtblock_t *, tree, tree, tree,
tree = NULL_TREE, tree = NULL_TREE,
- tree = NULL_TREE);
+ tree = NULL_TREE, unsigned = 0);
/* Generate code to deallocate an array. */
tree gfc_deallocate_with_status (tree, tree, tree, tree, tree, bool, gfc_expr *,
diff --git a/gcc/testsuite/gfortran.dg/align_3.f90 b/gcc/testsuite/gfortran.dg/align_3.f90
new file mode 100644
index 000000000..f82682388
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/align_3.f90
@@ -0,0 +1,41 @@
+! { dg-do run }
+!
+! ALLOCATE honours ALIGN from both the attribute and the directive.
+! Repeated because plain malloc may return an aligned block by chance.
+
+program align_3
+ implicit none
+
+ real(kind=4), allocatable :: attr_arr(:)
+ !GCC$ ATTRIBUTES ALIGN(64) :: attr_arr
+
+ real(kind=4), allocatable :: attr_scalar
+ !GCC$ ATTRIBUTES ALIGN(64) :: attr_scalar
+
+ ! Aligned by the directive only.
+ real(kind=4), allocatable :: dir_arr(:)
+
+ ! Attribute says 32, directive below says 128; 128 must win.
+ real(kind=4), allocatable :: both(:)
+ !GCC$ ATTRIBUTES ALIGN(32) :: both
+
+ integer :: i
+
+ do i = 1, 8
+ allocate (attr_arr(13))
+ if (iand (loc (attr_arr), 63_8) /= 0) stop 1
+
+ allocate (attr_scalar)
+ if (iand (loc (attr_scalar), 63_8) /= 0) stop 2
+
+ !GCC$ ALLOCATE (dir_arr) ALIGN(128)
+ allocate (dir_arr(7))
+ if (iand (loc (dir_arr), 127_8) /= 0) stop 3
+
+ !GCC$ ALLOCATE (both) ALIGN(128)
+ allocate (both(9))
+ if (iand (loc (both), 127_8) /= 0) stop 4
+
+ deallocate (attr_arr, attr_scalar, dir_arr, both)
+ end do
+end program align_3
diff --git a/gcc/testsuite/gfortran.dg/align_4.f90 b/gcc/testsuite/gfortran.dg/align_4.f90
new file mode 100644
index 000000000..ead366536
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/align_4.f90
@@ -0,0 +1,23 @@
+! { dg-do compile }
+!
+! Invalid uses of the !GCC$ ALLOCATE (...) ALIGN(n) directive.
+
+subroutine bad_value
+ real, allocatable :: a(:)
+ !GCC$ ALLOCATE (a) ALIGN(24) ! { dg-error "must be a positive power of two" }
+ allocate (a(4))
+end subroutine bad_value
+
+subroutine bad_syntax
+ real, allocatable :: b(:)
+ !GCC$ ALLOCATE (b) ! { dg-error "Syntax error in !GCC\\\$ ALLOCATE directive" }
+ allocate (b(4))
+end subroutine bad_syntax
+
+subroutine not_before_allocate
+ real, allocatable :: c(:)
+ integer :: i
+ !GCC$ ALLOCATE (c) ALIGN(64)
+ i = 1 ! { dg-error "not immediately before an ALLOCATE statement" }
+ allocate (c(4))
+end subroutine not_before_allocate
--
2.50.1 (Apple Git-155)