frontend: Add novector C++ pragma

FORTRAN currently has a pragma NOVECTOR for indicating that vectorization should
not be applied to a particular loop.

ICC/ICX also has such a pragma for C and C++ called #pragma novector.

As part of this patch series I need a way to easily turn off vectorization of
particular loops, particularly for testsuite reasons.

This patch proposes a #pragma GCC novector that does the same for C++
as gfortan does for FORTRAN and what ICX/ICX does for C++.

I added only some basic tests here, but the next patch in the series uses this
in the testsuite in about ~800 tests.

gcc/cp/ChangeLog:

	* cp-tree.h (RANGE_FOR_NOVECTOR): New.
	(cp_convert_range_for, finish_while_stmt_cond, finish_do_stmt,
	finish_for_cond): Add novector param.
	* init.cc (build_vec_init): Default novector to false.
	* method.cc (build_comparison_op): Likewise.
	* parser.cc (cp_parser_statement): Likewise.
	(cp_parser_for, cp_parser_c_for, cp_parser_range_for,
	cp_convert_range_for, cp_parser_iteration_statement,
	cp_parser_omp_for_loop, cp_parser_pragma): Support novector.
	(cp_parser_pragma_novector): New.
	* pt.cc (tsubst_expr): Likewise.
	* semantics.cc (finish_while_stmt_cond, finish_do_stmt,
	finish_for_cond): Likewise.

gcc/ChangeLog:

	* doc/extend.texi: Document it.

gcc/testsuite/ChangeLog:

	* g++.dg/vect/vect.exp (support vect- prefix).
	* g++.dg/vect/vect-novector-pragma.cc: New test.
This commit is contained in:
Tamar Christina 2023-08-04 13:50:53 +01:00
parent 451391a647
commit 73b9886076
9 changed files with 219 additions and 76 deletions

View file

@ -5393,6 +5393,7 @@ get_vec_init_expr (tree t)
#define RANGE_FOR_UNROLL(NODE) TREE_OPERAND (RANGE_FOR_STMT_CHECK (NODE), 4)
#define RANGE_FOR_INIT_STMT(NODE) TREE_OPERAND (RANGE_FOR_STMT_CHECK (NODE), 5)
#define RANGE_FOR_IVDEP(NODE) TREE_LANG_FLAG_6 (RANGE_FOR_STMT_CHECK (NODE))
#define RANGE_FOR_NOVECTOR(NODE) TREE_LANG_FLAG_5 (RANGE_FOR_STMT_CHECK (NODE))
/* STMT_EXPR accessor. */
#define STMT_EXPR_STMT(NODE) TREE_OPERAND (STMT_EXPR_CHECK (NODE), 0)
@ -7310,7 +7311,7 @@ extern bool maybe_clone_body (tree);
/* In parser.cc */
extern tree cp_convert_range_for (tree, tree, tree, tree, unsigned int, bool,
unsigned short);
unsigned short, bool);
extern void cp_convert_omp_range_for (tree &, vec<tree, va_gc> *, tree &,
tree &, tree &, tree &, tree &, tree &);
extern void cp_finish_omp_range_for (tree, tree);
@ -7633,16 +7634,19 @@ extern void begin_else_clause (tree);
extern void finish_else_clause (tree);
extern void finish_if_stmt (tree);
extern tree begin_while_stmt (void);
extern void finish_while_stmt_cond (tree, tree, bool, unsigned short);
extern void finish_while_stmt_cond (tree, tree, bool, unsigned short,
bool);
extern void finish_while_stmt (tree);
extern tree begin_do_stmt (void);
extern void finish_do_body (tree);
extern void finish_do_stmt (tree, tree, bool, unsigned short);
extern void finish_do_stmt (tree, tree, bool, unsigned short,
bool);
extern tree finish_return_stmt (tree);
extern tree begin_for_scope (tree *);
extern tree begin_for_stmt (tree, tree);
extern void finish_init_stmt (tree);
extern void finish_for_cond (tree, tree, bool, unsigned short);
extern void finish_for_cond (tree, tree, bool, unsigned short,
bool);
extern void finish_for_expr (tree, tree);
extern void finish_for_stmt (tree);
extern tree begin_range_for_stmt (tree, tree);

View file

@ -4853,7 +4853,7 @@ build_vec_init (tree base, tree maxindex, tree init,
finish_init_stmt (for_stmt);
finish_for_cond (build2 (GT_EXPR, boolean_type_node, iterator,
build_int_cst (TREE_TYPE (iterator), -1)),
for_stmt, false, 0);
for_stmt, false, 0, false);
/* We used to pass this decrement to finish_for_expr; now we add it to
elt_init below so it's part of the same full-expression as the
initialization, and thus happens before any potentially throwing

View file

@ -1645,7 +1645,8 @@ build_comparison_op (tree fndecl, bool defining, tsubst_flags_t complain)
add_stmt (idx);
finish_init_stmt (for_stmt);
finish_for_cond (build2 (LE_EXPR, boolean_type_node, idx,
maxval), for_stmt, false, 0);
maxval), for_stmt, false, 0,
false);
finish_for_expr (cp_build_unary_op (PREINCREMENT_EXPR,
TARGET_EXPR_SLOT (idx),
false, complain),

View file

@ -2333,15 +2333,15 @@ static tree cp_parser_selection_statement
static tree cp_parser_condition
(cp_parser *);
static tree cp_parser_iteration_statement
(cp_parser *, bool *, bool, unsigned short);
(cp_parser *, bool *, bool, unsigned short, bool);
static bool cp_parser_init_statement
(cp_parser *, tree *decl);
static tree cp_parser_for
(cp_parser *, bool, unsigned short);
(cp_parser *, bool, unsigned short, bool);
static tree cp_parser_c_for
(cp_parser *, tree, tree, bool, unsigned short);
(cp_parser *, tree, tree, bool, unsigned short, bool);
static tree cp_parser_range_for
(cp_parser *, tree, tree, tree, bool, unsigned short, bool);
(cp_parser *, tree, tree, tree, bool, unsigned short, bool, bool);
static void do_range_for_auto_deduction
(tree, tree, tree, unsigned int);
static tree cp_parser_perform_range_for_lookup
@ -12422,7 +12422,8 @@ cp_parser_statement (cp_parser* parser, tree in_statement_expr,
case RID_DO:
case RID_FOR:
std_attrs = process_stmt_hotness_attribute (std_attrs, attrs_loc);
statement = cp_parser_iteration_statement (parser, if_p, false, 0);
statement = cp_parser_iteration_statement (parser, if_p, false, 0,
false);
break;
case RID_BREAK:
@ -13602,7 +13603,8 @@ cp_parser_condition (cp_parser* parser)
not included. */
static tree
cp_parser_for (cp_parser *parser, bool ivdep, unsigned short unroll)
cp_parser_for (cp_parser *parser, bool ivdep, unsigned short unroll,
bool novector)
{
tree init, scope, decl;
bool is_range_for;
@ -13632,14 +13634,14 @@ cp_parser_for (cp_parser *parser, bool ivdep, unsigned short unroll)
if (is_range_for)
return cp_parser_range_for (parser, scope, init, decl, ivdep, unroll,
false);
novector, false);
else
return cp_parser_c_for (parser, scope, init, ivdep, unroll);
return cp_parser_c_for (parser, scope, init, ivdep, unroll, novector);
}
static tree
cp_parser_c_for (cp_parser *parser, tree scope, tree init, bool ivdep,
unsigned short unroll)
unsigned short unroll, bool novector)
{
/* Normal for loop */
tree condition = NULL_TREE;
@ -13666,7 +13668,7 @@ cp_parser_c_for (cp_parser *parser, tree scope, tree init, bool ivdep,
"%<GCC unroll%> pragma");
condition = error_mark_node;
}
finish_for_cond (condition, stmt, ivdep, unroll);
finish_for_cond (condition, stmt, ivdep, unroll, novector);
/* Look for the `;'. */
cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON);
@ -13690,7 +13692,8 @@ cp_parser_c_for (cp_parser *parser, tree scope, tree init, bool ivdep,
static tree
cp_parser_range_for (cp_parser *parser, tree scope, tree init, tree range_decl,
bool ivdep, unsigned short unroll, bool is_omp)
bool ivdep, unsigned short unroll, bool novector,
bool is_omp)
{
tree stmt, range_expr;
auto_vec <cxx_binding *, 16> bindings;
@ -13766,6 +13769,8 @@ cp_parser_range_for (cp_parser *parser, tree scope, tree init, tree range_decl,
RANGE_FOR_IVDEP (stmt) = 1;
if (unroll)
RANGE_FOR_UNROLL (stmt) = build_int_cst (integer_type_node, unroll);
if (novector)
RANGE_FOR_NOVECTOR (stmt) = 1;
finish_range_for_decl (stmt, range_decl, range_expr);
if (!type_dependent_expression_p (range_expr)
/* do_auto_deduction doesn't mess with template init-lists. */
@ -13778,7 +13783,7 @@ cp_parser_range_for (cp_parser *parser, tree scope, tree init, tree range_decl,
stmt = begin_for_stmt (scope, init);
stmt = cp_convert_range_for (stmt, range_decl, range_expr,
decomp_first_name, decomp_cnt, ivdep,
unroll);
unroll, novector);
}
return stmt;
}
@ -13956,7 +13961,7 @@ warn_for_range_copy (tree decl, tree expr)
tree
cp_convert_range_for (tree statement, tree range_decl, tree range_expr,
tree decomp_first_name, unsigned int decomp_cnt,
bool ivdep, unsigned short unroll)
bool ivdep, unsigned short unroll, bool novector)
{
tree begin, end;
tree iter_type, begin_expr, end_expr;
@ -14016,7 +14021,7 @@ cp_convert_range_for (tree statement, tree range_decl, tree range_expr,
begin, ERROR_MARK,
end, ERROR_MARK,
NULL_TREE, NULL, tf_warning_or_error);
finish_for_cond (condition, statement, ivdep, unroll);
finish_for_cond (condition, statement, ivdep, unroll, novector);
/* The new increment expression. */
expression = finish_unary_op_expr (input_location,
@ -14183,7 +14188,7 @@ cp_parser_range_for_member_function (tree range, tree identifier)
static tree
cp_parser_iteration_statement (cp_parser* parser, bool *if_p, bool ivdep,
unsigned short unroll)
unsigned short unroll, bool novector)
{
cp_token *token;
enum rid keyword;
@ -14217,7 +14222,7 @@ cp_parser_iteration_statement (cp_parser* parser, bool *if_p, bool ivdep,
parens.require_open (parser);
/* Parse the condition. */
condition = cp_parser_condition (parser);
finish_while_stmt_cond (condition, statement, ivdep, unroll);
finish_while_stmt_cond (condition, statement, ivdep, unroll, novector);
/* Look for the `)'. */
parens.require_close (parser);
/* Parse the dependent statement. */
@ -14252,7 +14257,7 @@ cp_parser_iteration_statement (cp_parser* parser, bool *if_p, bool ivdep,
/* Parse the expression. */
expression = cp_parser_expression (parser);
/* We're done with the do-statement. */
finish_do_stmt (expression, statement, ivdep, unroll);
finish_do_stmt (expression, statement, ivdep, unroll, novector);
/* Look for the `)'. */
parens.require_close (parser);
/* Look for the `;'. */
@ -14266,7 +14271,7 @@ cp_parser_iteration_statement (cp_parser* parser, bool *if_p, bool ivdep,
matching_parens parens;
parens.require_open (parser);
statement = cp_parser_for (parser, ivdep, unroll);
statement = cp_parser_for (parser, ivdep, unroll, novector);
/* Look for the `)'. */
parens.require_close (parser);
@ -43828,7 +43833,7 @@ cp_parser_omp_for_loop (cp_parser *parser, enum tree_code code, tree clauses,
cp_parser_require (parser, CPP_COLON, RT_COLON);
init = cp_parser_range_for (parser, NULL_TREE, NULL_TREE, decl,
false, 0, true);
false, 0, false, true);
cp_convert_omp_range_for (this_pre_body, for_block, decl,
orig_decl, init, orig_init,
@ -49320,6 +49325,15 @@ cp_parser_pragma_unroll (cp_parser *parser, cp_token *pragma_tok)
return unroll;
}
/* Parse a pragma GCC novector. */
static bool
cp_parser_pragma_novector (cp_parser *parser, cp_token *pragma_tok)
{
cp_parser_skip_to_pragma_eol (parser, pragma_tok);
return true;
}
/* Normal parsing of a pragma token. Here we can (and must) use the
regular lexer. */
@ -49625,58 +49639,73 @@ cp_parser_pragma (cp_parser *parser, enum pragma_context context, bool *if_p)
break;
case PRAGMA_IVDEP:
{
if (context == pragma_external)
{
error_at (pragma_tok->location,
"%<#pragma GCC ivdep%> must be inside a function");
break;
}
const bool ivdep = cp_parser_pragma_ivdep (parser, pragma_tok);
unsigned short unroll;
cp_token *tok = cp_lexer_peek_token (the_parser->lexer);
if (tok->type == CPP_PRAGMA
&& cp_parser_pragma_kind (tok) == PRAGMA_UNROLL)
{
tok = cp_lexer_consume_token (parser->lexer);
unroll = cp_parser_pragma_unroll (parser, tok);
tok = cp_lexer_peek_token (the_parser->lexer);
}
else
unroll = 0;
if (tok->type != CPP_KEYWORD
|| (tok->keyword != RID_FOR
&& tok->keyword != RID_WHILE
&& tok->keyword != RID_DO))
{
cp_parser_error (parser, "for, while or do statement expected");
return false;
}
cp_parser_iteration_statement (parser, if_p, ivdep, unroll);
return true;
}
case PRAGMA_UNROLL:
case PRAGMA_NOVECTOR:
{
bool ivdep;
unsigned short unroll = 0;
bool novector = false;
const char *pragma_str;
switch (id)
{
case PRAGMA_IVDEP:
pragma_str = "ivdep";
break;
case PRAGMA_UNROLL:
pragma_str = "unroll";
break;
case PRAGMA_NOVECTOR:
pragma_str = "novector";
break;
default:
gcc_unreachable ();
}
if (context == pragma_external)
{
error_at (pragma_tok->location,
"%<#pragma GCC unroll%> must be inside a function");
"%<#pragma GCC %s%> must be inside a function",
pragma_str);
break;
}
const unsigned short unroll
= cp_parser_pragma_unroll (parser, pragma_tok);
bool ivdep;
cp_token *tok = cp_lexer_peek_token (the_parser->lexer);
if (tok->type == CPP_PRAGMA
&& cp_parser_pragma_kind (tok) == PRAGMA_IVDEP)
cp_token *tok = pragma_tok;
bool has_more = true;
do
{
tok = cp_lexer_consume_token (parser->lexer);
ivdep = cp_parser_pragma_ivdep (parser, tok);
switch (cp_parser_pragma_kind (tok))
{
case PRAGMA_IVDEP:
{
if (tok != pragma_tok)
tok = cp_lexer_consume_token (parser->lexer);
ivdep = cp_parser_pragma_ivdep (parser, tok);
break;
}
case PRAGMA_UNROLL:
{
if (tok != pragma_tok)
tok = cp_lexer_consume_token (parser->lexer);
unroll = cp_parser_pragma_unroll (parser, tok);
break;
}
case PRAGMA_NOVECTOR:
{
if (tok != pragma_tok)
tok = cp_lexer_consume_token (parser->lexer);
novector = cp_parser_pragma_novector (parser, tok);
break;
}
default:
has_more = false;
break;
}
tok = cp_lexer_peek_token (the_parser->lexer);
has_more = has_more && tok->type == CPP_PRAGMA;
}
else
ivdep = false;
while (has_more);
if (tok->type != CPP_KEYWORD
|| (tok->keyword != RID_FOR
&& tok->keyword != RID_WHILE
@ -49685,7 +49714,7 @@ cp_parser_pragma (cp_parser *parser, enum pragma_context context, bool *if_p)
cp_parser_error (parser, "for, while or do statement expected");
return false;
}
cp_parser_iteration_statement (parser, if_p, ivdep, unroll);
cp_parser_iteration_statement (parser, if_p, ivdep, unroll, novector);
return true;
}

View file

@ -19094,7 +19094,7 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
RECUR (FOR_INIT_STMT (t));
finish_init_stmt (stmt);
tmp = RECUR (FOR_COND (t));
finish_for_cond (tmp, stmt, false, 0);
finish_for_cond (tmp, stmt, false, 0, false);
tmp = RECUR (FOR_EXPR (t));
finish_for_expr (tmp, stmt);
{
@ -19131,6 +19131,7 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
{
RANGE_FOR_IVDEP (stmt) = RANGE_FOR_IVDEP (t);
RANGE_FOR_UNROLL (stmt) = RANGE_FOR_UNROLL (t);
RANGE_FOR_NOVECTOR (stmt) = RANGE_FOR_NOVECTOR (t);
finish_range_for_decl (stmt, decl, expr);
if (decomp_first && decl != error_mark_node)
cp_finish_decomp (decl, decomp_first, decomp_cnt);
@ -19141,7 +19142,8 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
? tree_to_uhwi (RANGE_FOR_UNROLL (t)) : 0);
stmt = cp_convert_range_for (stmt, decl, expr,
decomp_first, decomp_cnt,
RANGE_FOR_IVDEP (t), unroll);
RANGE_FOR_IVDEP (t), unroll,
RANGE_FOR_NOVECTOR (t));
}
bool prev = note_iteration_stmt_body_start ();
@ -19154,7 +19156,7 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
case WHILE_STMT:
stmt = begin_while_stmt ();
tmp = RECUR (WHILE_COND (t));
finish_while_stmt_cond (tmp, stmt, false, 0);
finish_while_stmt_cond (tmp, stmt, false, 0, false);
{
bool prev = note_iteration_stmt_body_start ();
RECUR (WHILE_BODY (t));
@ -19172,7 +19174,7 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
}
finish_do_body (stmt);
tmp = RECUR (DO_COND (t));
finish_do_stmt (tmp, stmt, false, 0);
finish_do_stmt (tmp, stmt, false, 0, false);
break;
case IF_STMT:

View file

@ -1148,7 +1148,7 @@ begin_while_stmt (void)
void
finish_while_stmt_cond (tree cond, tree while_stmt, bool ivdep,
unsigned short unroll)
unsigned short unroll, bool novector)
{
cond = maybe_convert_cond (cond);
finish_cond (&WHILE_COND (while_stmt), cond);
@ -1168,6 +1168,13 @@ finish_while_stmt_cond (tree cond, tree while_stmt, bool ivdep,
annot_expr_unroll_kind),
build_int_cst (integer_type_node,
unroll));
if (novector && cond != error_mark_node)
WHILE_COND (while_stmt) = build3 (ANNOTATE_EXPR,
TREE_TYPE (WHILE_COND (while_stmt)),
WHILE_COND (while_stmt),
build_int_cst (integer_type_node,
annot_expr_no_vector_kind),
integer_zero_node);
simplify_loop_decl_cond (&WHILE_COND (while_stmt), WHILE_BODY (while_stmt));
}
@ -1212,7 +1219,8 @@ finish_do_body (tree do_stmt)
COND is as indicated. */
void
finish_do_stmt (tree cond, tree do_stmt, bool ivdep, unsigned short unroll)
finish_do_stmt (tree cond, tree do_stmt, bool ivdep, unsigned short unroll,
bool novector)
{
cond = maybe_convert_cond (cond);
end_maybe_infinite_loop (cond);
@ -1229,6 +1237,10 @@ finish_do_stmt (tree cond, tree do_stmt, bool ivdep, unsigned short unroll)
cond = build3 (ANNOTATE_EXPR, TREE_TYPE (cond), cond,
build_int_cst (integer_type_node, annot_expr_unroll_kind),
build_int_cst (integer_type_node, unroll));
if (novector && cond != error_mark_node)
cond = build3 (ANNOTATE_EXPR, TREE_TYPE (cond), cond,
build_int_cst (integer_type_node, annot_expr_no_vector_kind),
integer_zero_node);
DO_COND (do_stmt) = cond;
}
@ -1327,7 +1339,7 @@ finish_init_stmt (tree for_stmt)
FOR_STMT. */
void
finish_for_cond (tree cond, tree for_stmt, bool ivdep, unsigned short unroll)
finish_for_cond (tree cond, tree for_stmt, bool ivdep, unsigned short unroll, bool novector)
{
cond = maybe_convert_cond (cond);
finish_cond (&FOR_COND (for_stmt), cond);
@ -1347,6 +1359,13 @@ finish_for_cond (tree cond, tree for_stmt, bool ivdep, unsigned short unroll)
annot_expr_unroll_kind),
build_int_cst (integer_type_node,
unroll));
if (novector && cond != error_mark_node)
FOR_COND (for_stmt) = build3 (ANNOTATE_EXPR,
TREE_TYPE (FOR_COND (for_stmt)),
FOR_COND (for_stmt),
build_int_cst (integer_type_node,
annot_expr_no_vector_kind),
integer_zero_node);
simplify_loop_decl_cond (&FOR_COND (for_stmt), FOR_BODY (for_stmt));
}

View file

@ -24581,6 +24581,25 @@ void ignore_vec_dep (int *a, int k, int c, int m)
@}
@end smallexample
@cindex pragma GCC novector
@item #pragma GCC novector
With this pragma, the programmer asserts that the following loop should be
prevented from executing concurrently with SIMD (single instruction multiple
data) instructions.
For example, the compiler cannot vectorize the following loop with the pragma:
@smallexample
void foo (int n, int *a, int *b, int *c)
@{
int i, j;
#pragma GCC novector
for (i = 0; i < n; ++i)
a[i] = b[i] + c[i];
@}
@end smallexample
@cindex pragma GCC unroll @var{n}
@item #pragma GCC unroll @var{n}

View file

@ -0,0 +1,69 @@
/* { dg-do compile } */
/* { dg-require-effective-target vect_int } */
#include <vector>
void f4 (std::vector<int> a, std::vector<int> b, int n)
{
int i = 0;
#pragma GCC novector
while (i < (n & -8))
{
a[i] += b[i];
i++;
}
}
void f5 (std::vector<int> a, std::vector<int> b, int n)
{
int i = 0;
#pragma GCC novector
#pragma GCC ivdep
#pragma GCC unroll 2
while (i < (n & -8))
{
a[i] += b[i];
i++;
}
}
void f6 (std::vector<int> a, std::vector<int> b, int n)
{
int i = 0;
#pragma GCC ivdep
#pragma GCC novector
#pragma GCC unroll 2
while (i < (n & -8))
{
a[i] += b[i];
i++;
}
}
void f7 (std::vector<int> a, std::vector<int> b, int n)
{
int i = 0;
#pragma GCC ivdep
#pragma GCC unroll 2
#pragma GCC novector
while (i < (n & -8))
{
a[i] += b[i];
i++;
}
}
#if __cpp_range_based_for
void f8 (std::vector<int> a, std::vector<int> b, int n)
{
int i = 0;
#pragma GCC novector
for (int x : b)
{
a[i] += x;
i++;
}
}
#endif
/* { dg-final { scan-tree-dump-not "LOOP VECTORIZED" "vect" } } */

View file

@ -64,7 +64,7 @@ dg-init
# Main loop.
et-dg-runtest g++-dg-runtest [lsort [glob -nocomplain \
$srcdir/$subdir/{pr,simd}*.{c,cc,S} ]] "" $DEFAULT_VECTCFLAGS
$srcdir/$subdir/{pr,simd,vect-}*.{c,cc,S} ]] "" $DEFAULT_VECTCFLAGS
et-dg-runtest g++-dg-runtest [lsort [glob -nocomplain \
$srcdir/$subdir/slp-pr*.{c,cc,S} ]] "" $VECT_SLP_CFLAGS