From patchwork Fri Mar 24 15:30:40 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Frederik Harwath X-Patchwork-Id: 74598 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:b0ea:0:b0:3b6:4342:cba0 with SMTP id b10csp718262vqo; Fri, 24 Mar 2023 08:38:08 -0700 (PDT) X-Google-Smtp-Source: AKy350Y2T9ZqpP+zoRw+Va+xFxGJX+4FdKwl1VzSIXsL+H70vx2qvQ/esV8qutEr+/EIzdvZ+OVs X-Received: by 2002:a05:6402:517b:b0:501:d4f9:3141 with SMTP id d27-20020a056402517b00b00501d4f93141mr3381242ede.32.1679672288309; Fri, 24 Mar 2023 08:38:08 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1679672288; cv=none; d=google.com; s=arc-20160816; b=A7XGLR9fpWoFntPhyGNwDO5poRwMgvN548JgRfHhfawDYKNz4jRO06l22tjFdrol0p /i2qb58xIatiYrwEAZXzd9/byQft1ChiLrS79eywu/RfUVagzjdBIKsyFZDIRPVroTph +xkYO7geZWpY0VGZpNs/qFJTbB5kLTM4swnTOn5NniwUBeQqc6HLbWkoymCKxno6LDFU lNOQIvu+8zFpCdbmmqdD86Tbnwc2c4woi/dvf+W1CWKomZppKe0wO25sRVj0YlAwaWmF zZm9HrpusyZ8A3fxCJTDrIhY+gFinQK5fL2JZw8U+C8Zfq31BV5kUTyD9SMcn425b2to op6g== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:list-subscribe:list-help:list-post:list-archive :list-unsubscribe:list-id:precedence:content-transfer-encoding :mime-version:references:in-reply-to:message-id:date:subject:to:from :ironport-sdr:dmarc-filter:delivered-to; bh=567oOAyM976HtCAIFoPt/cMYtF8+kCIwwMwrfLnQIoY=; b=rnofbBtMErXwN7q1QgPMwzBy6yGLgkQP0N3kIeCKWgOc8dQSFR0Y37D5uja7gY0EcZ knaQGaImjH82ecKchiBnOuEjy+fltuouV9JdgnxJJO7hMOMR2atKvSveaRwpxxB9vTeA MLMBwuImRRfzbJnT/lPCUZrtCrXu35rgkmiT+CcnOH2ESS4xIPLxrGywwIb9IdHXQleg eJZHqDCcdKoC1uBmT7TIWL36oZtoSpoBHCQpjzoABZFR13RfEcRcUIC33oQYrJsBO6Wa 8Kyo3eNaqQv2Da62yElEO6fdlxwiWBqsRkdsFJBraRVr10VP3tpIEXmPCpaBzDDkWiw/ hoDQ== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 2620:52:3:1:0:246e:9693:128c as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org" Received: from sourceware.org (server2.sourceware.org. [2620:52:3:1:0:246e:9693:128c]) by mx.google.com with ESMTPS id x13-20020aa7d6cd000000b004bd1a7dba86si21266080edr.410.2023.03.24.08.38.07 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 24 Mar 2023 08:38:08 -0700 (PDT) Received-SPF: pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 2620:52:3:1:0:246e:9693:128c as permitted sender) client-ip=2620:52:3:1:0:246e:9693:128c; Authentication-Results: mx.google.com; spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 2620:52:3:1:0:246e:9693:128c as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org" Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id AC7F03889800 for ; Fri, 24 Mar 2023 15:33:42 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from esa1.mentor.iphmx.com (esa1.mentor.iphmx.com [68.232.129.153]) by sourceware.org (Postfix) with ESMTPS id 8A7AE3858291 for ; Fri, 24 Mar 2023 15:33:02 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 8A7AE3858291 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=codesourcery.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=mentor.com X-IronPort-AV: E=Sophos;i="5.98,288,1673942400"; d="scan'208";a="324687" Received: from orw-gwy-02-in.mentorg.com ([192.94.38.167]) by esa1.mentor.iphmx.com with ESMTP; 24 Mar 2023 07:31:09 -0800 IronPort-SDR: lsb2wNYMdtH0XpBBY3uUbFl9IQG5kWvXclcPgp7V1k8yHR493GLOXyAdyGeJj1i7yXe6nJ032b +NX/2LgLqXyCuzaXCyBLRZp0vUZ7WsL2KYZMrL7YTreXykND+BhNYO6AvNOb01Cm8CLmPq5aSy teaQL1XYlZF2EziKZKSXz7a8RIrV0Jgx+fHlaub4/emMaG05NVqFno9tSCfmf3UlIz/nQkmgKU bDZ0xp3/tq1z+SGUfHr0etKa4vkGz0+xWjiNf3OyCF49+AXU4wxfFzX01C40VjVTnq0fXN08/6 ewc= From: Frederik Harwath To: , , , , Subject: [PATCH 2/7] openmp: Add C/C++ support for "omp unroll" directive Date: Fri, 24 Mar 2023 16:30:40 +0100 Message-ID: <20230324153046.3996092-3-frederik@codesourcery.com> X-Mailer: git-send-email 2.36.1 In-Reply-To: <20230324153046.3996092-1-frederik@codesourcery.com> References: <20230324153046.3996092-1-frederik@codesourcery.com> MIME-Version: 1.0 X-Originating-IP: [137.202.0.90] X-ClientProxiedBy: svr-ies-mbx-10.mgc.mentorg.com (139.181.222.10) To svr-ies-mbx-10.mgc.mentorg.com (139.181.222.10) X-Spam-Status: No, score=-12.5 required=5.0 tests=BAYES_00, GIT_PATCH_0, HEADER_FROM_DIFFERENT_DOMAINS, KAM_DMARC_STATUS, SPF_HELO_PASS, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org Sender: "Gcc-patches" X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1761264049486129505?= X-GMAIL-MSGID: =?utf-8?q?1761264049486129505?= This commit implements the C and the C++ front end changes to support the "omp unroll" directive. The execution of the loop transformation relies on the pass that has been added as a part of the earlier Fortran patch. gcc/c-family/ChangeLog: * c-gimplify.cc (c_genericize_control_stmt): Handle OMP_UNROLL. * c-omp.cc: Add "unroll" to omp_directives[]. * c-pragma.cc: Add "unroll" to omp_pragmas_simd[]. * c-pragma.h (enum pragma_kind): Add PRAGMA_OMP_UNROLL to pragma_kind and adjust PRAGMA_OMP__LAST_. (enum pragma_omp_clause): Add PRAGMA_OMP_CLAUSE_FULL and PRAGMA_OMP_CLAUSE_PARTIAL. gcc/c/ChangeLog: * c-parser.cc (c_parser_omp_clause_name): Handle "full" and "partial" clauses. (check_no_duplicate_clause): Change return type to bool and return check result. (c_parser_omp_clause_unroll_full): New function for parsing the "unroll clause". (c_parser_omp_clause_unroll_partial): New function for parsing the "partial" clause. (c_parser_omp_all_clauses): Handle PRAGMA_OMP_CLAUSE_FULL and PRAGMA_OMP_CLAUSE_PARTIAL. (c_parser_nested_omp_unroll_clauses): New function for parsing "omp unroll" directives following another directive. (OMP_UNROLL_CLAUSE_MASK): New definition. (c_parser_omp_unroll): New function for parsing "omp unroll" loops that are not associated with another directive. (c_parser_omp_construct): Handle PRAGMA_OMP_UNROLL. * c-typeck.cc (c_finish_omp_clauses): Handle OMP_CLAUSE_UNROLL_FULL, OMP_CLAUSE_UNROLL_PARTIAL, and OMP_CLAUSE_UNROLL_NONE. gcc/cp/ChangeLog: * cp-gimplify.cc (cp_gimplify_expr): Handle OMP_UNROLL. (cp_fold_r): Likewise. (cp_genericize_r): Likewise. * parser.cc (cp_parser_omp_clause_name): Handle "full" clause. (check_no_duplicate_clause): Change return type to bool and return check result. (cp_parser_omp_clause_unroll_full): New function for parsing the "unroll clause". (cp_parser_omp_clause_unroll_partial): New function for parsing the "partial" clause. (cp_parser_omp_all_clauses): Handle OMP_CLAUSE_UNROLL and OMP_CLAUSE_FULL. (cp_parser_nested_omp_unroll_clauses): New function for parsing "omp unroll" directives following another directive. (cp_parser_omp_for_loop): Handle "omp unroll" directives between directive and loop. (OMP_UNROLL_CLAUSE_MASK): New definition. (cp_parser_omp_unroll): New function for parsing "omp unroll" loops that are not associated with another directive. (cp_parser_omp_construct): Handle PRAGMA_OMP_UNROLL. (cp_parser_pragma): Handle PRAGMA_OMP_UNROLL. * pt.cc (tsubst_omp_clauses): Handle OMP_CLAUSE_UNROLL_PARTIAL, OMP_CLAUSE_UNROLL_FULL, and OMP_CLAUSE_UNROLL_NONE. (tsubst_expr): Handle OMP_UNROLL. * semantics.cc (finish_omp_clauses): Handle OMP_CLAUSE_UNROLL_FULL, OMP_CLAUSE_UNROLL_PARTIAL, and OMP_CLAUSE_UNROLL_NONE. libgomp/ChangeLog: * testsuite/libgomp.c++/loop-transforms/unroll-1.C: New test. * testsuite/libgomp.c++/loop-transforms/unroll-2.C: New test. * testsuite/libgomp.c-c++-common/loop-transforms/unroll-1.c: New test. gcc/testsuite/ChangeLog: * c-c++-common/gomp/loop-transforms/unroll-1.c: New test. * c-c++-common/gomp/loop-transforms/unroll-2.c: New test. * c-c++-common/gomp/loop-transforms/unroll-3.c: New test. * c-c++-common/gomp/loop-transforms/unroll-4.c: New test. * c-c++-common/gomp/loop-transforms/unroll-5.c: New test. * c-c++-common/gomp/loop-transforms/unroll-6.c: New test. * g++.dg/gomp/loop-transforms/unroll-1.C: New test. * g++.dg/gomp/loop-transforms/unroll-2.C: New test. * g++.dg/gomp/loop-transforms/unroll-3.C: New test. --- gcc/c-family/c-gimplify.cc | 1 + gcc/c-family/c-omp.cc | 6 +- gcc/c-family/c-pragma.cc | 1 + gcc/c-family/c-pragma.h | 5 +- gcc/c/c-parser.cc | 161 ++++++++++++++++- gcc/c/c-typeck.cc | 8 + gcc/cp/cp-gimplify.cc | 3 + gcc/cp/parser.cc | 164 +++++++++++++++++- gcc/cp/pt.cc | 4 + gcc/cp/semantics.cc | 56 ++++++ .../gomp/loop-transforms/unroll-1.c | 133 ++++++++++++++ .../gomp/loop-transforms/unroll-2.c | 99 +++++++++++ .../gomp/loop-transforms/unroll-3.c | 18 ++ .../gomp/loop-transforms/unroll-4.c | 19 ++ .../gomp/loop-transforms/unroll-5.c | 19 ++ .../gomp/loop-transforms/unroll-6.c | 20 +++ .../gomp/loop-transforms/unroll-7.c | 144 +++++++++++++++ .../gomp/loop-transforms/unroll-simd-1.c | 84 +++++++++ .../g++.dg/gomp/loop-transforms/unroll-1.C | 42 +++++ .../g++.dg/gomp/loop-transforms/unroll-2.C | 47 +++++ .../g++.dg/gomp/loop-transforms/unroll-3.C | 37 ++++ .../libgomp.c++/loop-transforms/unroll-1.C | 73 ++++++++ .../libgomp.c++/loop-transforms/unroll-2.C | 34 ++++ .../loop-transforms/unroll-1.c | 76 ++++++++ 24 files changed, 1246 insertions(+), 8 deletions(-) create mode 100644 gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-1.c create mode 100644 gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-2.c create mode 100644 gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-3.c create mode 100644 gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-4.c create mode 100644 gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-5.c create mode 100644 gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-6.c create mode 100644 gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-7.c create mode 100644 gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-simd-1.c create mode 100644 gcc/testsuite/g++.dg/gomp/loop-transforms/unroll-1.C create mode 100644 gcc/testsuite/g++.dg/gomp/loop-transforms/unroll-2.C create mode 100644 gcc/testsuite/g++.dg/gomp/loop-transforms/unroll-3.C create mode 100644 libgomp/testsuite/libgomp.c++/loop-transforms/unroll-1.C create mode 100644 libgomp/testsuite/libgomp.c++/loop-transforms/unroll-2.C create mode 100644 libgomp/testsuite/libgomp.c-c++-common/loop-transforms/unroll-1.c -- 2.36.1 ----------------- Siemens Electronic Design Automation GmbH; Anschrift: Arnulfstraße 201, 80634 München; Gesellschaft mit beschränkter Haftung; Geschäftsführer: Thomas Heurung, Frank Thürauf; Sitz der Gesellschaft: München; Registergericht München, HRB 106955 diff --git a/gcc/c-family/c-gimplify.cc b/gcc/c-family/c-gimplify.cc index ef5c7d919fc..82c88bd70e1 100644 --- a/gcc/c-family/c-gimplify.cc +++ b/gcc/c-family/c-gimplify.cc @@ -506,6 +506,7 @@ c_genericize_control_stmt (tree *stmt_p, int *walk_subtrees, void *data, case OMP_DISTRIBUTE: case OMP_LOOP: case OMP_TASKLOOP: + case OMP_LOOP_TRANS: case OACC_LOOP: genericize_omp_for_stmt (stmt_p, walk_subtrees, data, func, lh); break; diff --git a/gcc/c-family/c-omp.cc b/gcc/c-family/c-omp.cc index f72ca4c6acd..85ba9c528c8 100644 --- a/gcc/c-family/c-omp.cc +++ b/gcc/c-family/c-omp.cc @@ -3212,9 +3212,9 @@ const struct c_omp_directive c_omp_directives[] = { { "teams", nullptr, nullptr, PRAGMA_OMP_TEAMS, C_OMP_DIR_CONSTRUCT, true }, { "threadprivate", nullptr, nullptr, PRAGMA_OMP_THREADPRIVATE, - C_OMP_DIR_DECLARATIVE, false } - /* { "unroll", nullptr, nullptr, PRAGMA_OMP_UNROLL, - C_OMP_DIR_CONSTRUCT, false }, */ + C_OMP_DIR_DECLARATIVE, false }, + { "unroll", nullptr, nullptr, PRAGMA_OMP_UNROLL, + C_OMP_DIR_CONSTRUCT, false }, }; /* Find (non-combined/composite) OpenMP directive (if any) which starts diff --git a/gcc/c-family/c-pragma.cc b/gcc/c-family/c-pragma.cc index 0d2b333cebb..96a28ac1b0c 100644 --- a/gcc/c-family/c-pragma.cc +++ b/gcc/c-family/c-pragma.cc @@ -1593,6 +1593,7 @@ static const struct omp_pragma_def omp_pragmas_simd[] = { { "target", PRAGMA_OMP_TARGET }, { "taskloop", PRAGMA_OMP_TASKLOOP }, { "teams", PRAGMA_OMP_TEAMS }, + { "unroll", PRAGMA_OMP_UNROLL }, }; void diff --git a/gcc/c-family/c-pragma.h b/gcc/c-family/c-pragma.h index 9cc95ab3ee3..6686abdc94d 100644 --- a/gcc/c-family/c-pragma.h +++ b/gcc/c-family/c-pragma.h @@ -81,8 +81,9 @@ enum pragma_kind { PRAGMA_OMP_TASKYIELD, PRAGMA_OMP_THREADPRIVATE, PRAGMA_OMP_TEAMS, + PRAGMA_OMP_UNROLL, /* PRAGMA_OMP__LAST_ should be equal to the last PRAGMA_OMP_* code. */ - PRAGMA_OMP__LAST_ = PRAGMA_OMP_TEAMS, + PRAGMA_OMP__LAST_ = PRAGMA_OMP_UNROLL, PRAGMA_GCC_PCH_PREPROCESS, PRAGMA_IVDEP, @@ -118,6 +119,7 @@ enum pragma_omp_clause { PRAGMA_OMP_CLAUSE_FIRSTPRIVATE, PRAGMA_OMP_CLAUSE_FOR, PRAGMA_OMP_CLAUSE_FROM, + PRAGMA_OMP_CLAUSE_FULL, PRAGMA_OMP_CLAUSE_GRAINSIZE, PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR, PRAGMA_OMP_CLAUSE_HINT, @@ -140,6 +142,7 @@ enum pragma_omp_clause { PRAGMA_OMP_CLAUSE_ORDER, PRAGMA_OMP_CLAUSE_ORDERED, PRAGMA_OMP_CLAUSE_PARALLEL, + PRAGMA_OMP_CLAUSE_PARTIAL, PRAGMA_OMP_CLAUSE_PRIORITY, PRAGMA_OMP_CLAUSE_PRIVATE, PRAGMA_OMP_CLAUSE_PROC_BIND, diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc index 21bc3167ce2..9d875befccc 100644 --- a/gcc/c/c-parser.cc +++ b/gcc/c/c-parser.cc @@ -13471,6 +13471,8 @@ c_parser_omp_clause_name (c_parser *parser) result = PRAGMA_OMP_CLAUSE_FIRSTPRIVATE; else if (!strcmp ("from", p)) result = PRAGMA_OMP_CLAUSE_FROM; + else if (!strcmp ("full", p)) + result = PRAGMA_OMP_CLAUSE_FULL; break; case 'g': if (!strcmp ("gang", p)) @@ -13545,6 +13547,8 @@ c_parser_omp_clause_name (c_parser *parser) case 'p': if (!strcmp ("parallel", p)) result = PRAGMA_OMP_CLAUSE_PARALLEL; + else if (!strcmp ("partial", p)) + result = PRAGMA_OMP_CLAUSE_PARTIAL; else if (!strcmp ("present", p)) result = PRAGMA_OACC_CLAUSE_PRESENT; /* As of OpenACC 2.5, these are now aliases of the non-present_or @@ -13639,12 +13643,15 @@ c_parser_omp_clause_name (c_parser *parser) /* Validate that a clause of the given type does not already exist. */ -static void +static bool check_no_duplicate_clause (tree clauses, enum omp_clause_code code, const char *name) { - if (tree c = omp_find_clause (clauses, code)) + tree c = omp_find_clause (clauses, code); + if (c) error_at (OMP_CLAUSE_LOCATION (c), "too many %qs clauses", name); + + return c == NULL_TREE; } /* OpenACC 2.0 @@ -17448,6 +17455,65 @@ c_parser_omp_clause_uniform (c_parser *parser, tree list) return list; } +/* OpenMP 5.1 + full */ + +static tree +c_parser_omp_clause_unroll_full (c_parser *parser, tree list) +{ + if (!check_no_duplicate_clause (list, OMP_CLAUSE_UNROLL_FULL, "full")) + return list; + + location_t loc = c_parser_peek_token (parser)->location; + tree c = build_omp_clause (loc, OMP_CLAUSE_UNROLL_FULL); + OMP_CLAUSE_CHAIN (c) = list; + return c; +} + +/* OpenMP 5.1 + partial ( constant-expression ) */ + +static tree +c_parser_omp_clause_unroll_partial (c_parser *parser, tree list) +{ + if (!check_no_duplicate_clause (list, OMP_CLAUSE_UNROLL_PARTIAL, "partial")) + return list; + + tree c, num = error_mark_node; + HOST_WIDE_INT n; + location_t loc; + + loc = c_parser_peek_token (parser)->location; + c = build_omp_clause (loc, OMP_CLAUSE_UNROLL_PARTIAL); + OMP_CLAUSE_UNROLL_PARTIAL_EXPR (c) = NULL_TREE; + OMP_CLAUSE_CHAIN (c) = list; + + if (!c_parser_next_token_is (parser, CPP_OPEN_PAREN)) + return c; + + matching_parens parens; + parens.consume_open (parser); + num = c_parser_expr_no_commas (parser, NULL).value; + parens.skip_until_found_close (parser); + + if (num == error_mark_node) + return list; + + mark_exp_read (num); + num = c_fully_fold (num, false, NULL); + if (!INTEGRAL_TYPE_P (TREE_TYPE (num)) || !tree_fits_shwi_p (num) + || (n = tree_to_shwi (num)) <= 0 || (int)n != n) + { + error_at (loc, + "partial argument needs positive constant integer expression"); + return list; + } + + OMP_CLAUSE_UNROLL_PARTIAL_EXPR (c) = num; + + return c; +} + /* OpenMP 5.0: detach ( event-handle ) */ @@ -18042,6 +18108,14 @@ c_parser_omp_all_clauses (c_parser *parser, omp_clause_mask mask, clauses); c_name = "enter"; break; + case PRAGMA_OMP_CLAUSE_FULL: + c_name = "full"; + clauses = c_parser_omp_clause_unroll_full (parser, clauses); + break; + case PRAGMA_OMP_CLAUSE_PARTIAL: + c_name = "partial"; + clauses = c_parser_omp_clause_unroll_partial (parser, clauses); + break; default: c_parser_error (parser, "expected %<#pragma omp%> clause"); goto saw_error; @@ -20169,6 +20243,8 @@ c_parser_omp_scan_loop_body (c_parser *parser, bool open_brace_parsed) "expected %<}%>"); } +static bool c_parser_nested_omp_unroll_clauses (c_parser *, tree &); + /* Parse the restricted form of loop statements allowed by OpenACC and OpenMP. The real trick here is to determine the loop control variable early so that we can push a new decl if necessary to make it private. @@ -20227,6 +20303,13 @@ c_parser_omp_for_loop (location_t loc, c_parser *parser, enum tree_code code, condv = make_tree_vec (count); incrv = make_tree_vec (count); + if (c_parser_nested_omp_unroll_clauses (parser, clauses) + && count > 1) + { + error_at (loc, "collapse cannot be larger than 1 on an unrolled loop"); + return NULL; + } + if (!c_parser_next_token_is_keyword (parser, RID_FOR)) { c_parser_error (parser, "for statement expected"); @@ -23858,6 +23941,76 @@ c_parser_omp_taskloop (location_t loc, c_parser *parser, return ret; } +#define OMP_UNROLL_CLAUSE_MASK \ + ( (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_PARTIAL) \ + | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_FULL) ) + +/* Parse zero or more '#pragma omp unroll' that follow + another directive that requires a canonical loop nest. */ + +static bool +c_parser_nested_omp_unroll_clauses (c_parser *parser, tree &clauses) +{ + static const char *p_name = "#pragma omp unroll"; + c_token *tok; + bool found_unroll = false; + while (c_parser_next_token_is (parser, CPP_PRAGMA) + && (tok = c_parser_peek_token (parser), + tok->pragma_kind == PRAGMA_OMP_UNROLL)) + { + c_parser_consume_pragma (parser); + tree c = c_parser_omp_all_clauses (parser, OMP_UNROLL_CLAUSE_MASK, + p_name, true); + if (c) + { + gcc_assert (!TREE_CHAIN (c)); + found_unroll = true; + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_UNROLL_FULL) + { + error_at (tok->location, "% clause is invalid here; " + "turns loop into non-loop"); + continue; + } + } + else + { + error_at (tok->location, "%<#pragma omp unroll%> without " + "% clause is invalid here; " + "turns loop into non-loop"); + continue; + } + + clauses = chainon (clauses, c); + } + + return found_unroll; +} + +static tree +c_parser_omp_unroll (location_t loc, c_parser *parser, bool *if_p) +{ + tree block, ret; + static const char *p_name = "#pragma omp unroll"; + omp_clause_mask mask = OMP_UNROLL_CLAUSE_MASK; + + tree clauses = c_parser_omp_all_clauses (parser, mask, p_name, false); + c_parser_nested_omp_unroll_clauses (parser, clauses); + + if (!clauses) + { + tree c = build_omp_clause (loc, OMP_CLAUSE_UNROLL_NONE); + OMP_CLAUSE_CHAIN (c) = clauses; + clauses = c; + } + + block = c_begin_compound_stmt (true); + ret = c_parser_omp_for_loop (loc, parser, OMP_LOOP_TRANS, clauses, NULL, if_p); + block = c_end_compound_stmt (loc, block, true); + add_stmt (block); + + return ret; +} + /* OpenMP 5.1 #pragma omp nothing new-line */ @@ -24249,6 +24402,7 @@ c_parser_omp_construct (c_parser *parser, bool *if_p) p_kind = c_parser_peek_token (parser)->pragma_kind; c_parser_consume_pragma (parser); + gcc_assert (parser->in_pragma); switch (p_kind) { case PRAGMA_OACC_ATOMIC: @@ -24342,6 +24496,9 @@ c_parser_omp_construct (c_parser *parser, bool *if_p) case PRAGMA_OMP_ASSUME: c_parser_omp_assume (parser, if_p); return; + case PRAGMA_OMP_UNROLL: + stmt = c_parser_omp_unroll (loc, parser, if_p); + break; default: gcc_unreachable (); } diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc index 45bacc06c47..bffea79b441 100644 --- a/gcc/c/c-typeck.cc +++ b/gcc/c/c-typeck.cc @@ -15916,6 +15916,14 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort) pc = &OMP_CLAUSE_CHAIN (c); continue; + case OMP_CLAUSE_UNROLL_FULL: + pc = &OMP_CLAUSE_CHAIN (c); + continue; + + case OMP_CLAUSE_UNROLL_PARTIAL: + pc = &OMP_CLAUSE_CHAIN (c); + continue; + case OMP_CLAUSE_INBRANCH: case OMP_CLAUSE_NOTINBRANCH: if (branch_seen) diff --git a/gcc/cp/cp-gimplify.cc b/gcc/cp/cp-gimplify.cc index 4fecd5616bd..bf81097d780 100644 --- a/gcc/cp/cp-gimplify.cc +++ b/gcc/cp/cp-gimplify.cc @@ -638,6 +638,7 @@ cp_gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p) case OMP_DISTRIBUTE: case OMP_LOOP: case OMP_TASKLOOP: + case OMP_LOOP_TRANS: ret = cp_gimplify_omp_for (expr_p, pre_p); break; @@ -1097,6 +1098,7 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_) case OMP_DISTRIBUTE: case OMP_LOOP: case OMP_TASKLOOP: + case OMP_LOOP_TRANS: case OACC_LOOP: cp_walk_tree (&OMP_FOR_BODY (stmt), cp_fold_r, data, NULL); cp_walk_tree (&OMP_FOR_CLAUSES (stmt), cp_fold_r, data, NULL); @@ -1855,6 +1857,7 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data) case OMP_FOR: case OMP_SIMD: case OMP_LOOP: + case OMP_LOOP_TRANS: case OACC_LOOP: case STATEMENT_LIST: /* These cases are handled by shared code. */ diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index a277003ea58..7034fdf49a4 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -37204,6 +37204,8 @@ cp_parser_omp_clause_name (cp_parser *parser) result = PRAGMA_OMP_CLAUSE_FIRSTPRIVATE; else if (!strcmp ("from", p)) result = PRAGMA_OMP_CLAUSE_FROM; + else if (!strcmp ("full", p)) + result = PRAGMA_OMP_CLAUSE_FULL; break; case 'g': if (!strcmp ("gang", p)) @@ -37278,6 +37280,8 @@ cp_parser_omp_clause_name (cp_parser *parser) case 'p': if (!strcmp ("parallel", p)) result = PRAGMA_OMP_CLAUSE_PARALLEL; + if (!strcmp ("partial", p)) + result = PRAGMA_OMP_CLAUSE_PARTIAL; else if (!strcmp ("present", p)) result = PRAGMA_OACC_CLAUSE_PRESENT; else if (!strcmp ("present_or_copy", p) @@ -37368,12 +37372,15 @@ cp_parser_omp_clause_name (cp_parser *parser) /* Validate that a clause of the given type does not already exist. */ -static void +static bool check_no_duplicate_clause (tree clauses, enum omp_clause_code code, const char *name, location_t location) { - if (omp_find_clause (clauses, code)) + bool found = omp_find_clause (clauses, code); + if (found) error_at (location, "too many %qs clauses", name); + + return !found; } /* OpenMP 2.5: @@ -39459,6 +39466,56 @@ cp_parser_omp_clause_thread_limit (cp_parser *parser, tree list, return c; } +/* OpenMP 5.1 + full */ + +static tree +cp_parser_omp_clause_unroll_full (tree list, location_t loc) +{ + if (!check_no_duplicate_clause (list, OMP_CLAUSE_UNROLL_FULL, "full", loc)) + return list; + + tree c = build_omp_clause (loc, OMP_CLAUSE_UNROLL_FULL); + OMP_CLAUSE_CHAIN (c) = list; + return c; +} + +/* OpenMP 5.1 + partial ( constant-expression ) */ + +static tree +cp_parser_omp_clause_unroll_partial (cp_parser *parser, tree list, + location_t loc) +{ + if (!check_no_duplicate_clause (list, OMP_CLAUSE_UNROLL_PARTIAL, "partial", + loc)) + return list; + + tree c, num = error_mark_node; + c = build_omp_clause (loc, OMP_CLAUSE_UNROLL_PARTIAL); + OMP_CLAUSE_UNROLL_PARTIAL_EXPR (c) = NULL_TREE; + OMP_CLAUSE_CHAIN (c) = list; + + if (!cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN)) + return c; + + matching_parens parens; + parens.consume_open (parser); + num = cp_parser_constant_expression (parser); + cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true, + /*or_comma=*/false, + /*consume_paren=*/true); + + if (num == error_mark_node) + return list; + + mark_exp_read (num); + num = fold_non_dependent_expr (num); + + OMP_CLAUSE_UNROLL_PARTIAL_EXPR (c) = num; + return c; +} + /* OpenMP 4.0: aligned ( variable-list ) aligned ( variable-list : constant-expression ) */ @@ -41441,6 +41498,15 @@ cp_parser_omp_all_clauses (cp_parser *parser, omp_clause_mask mask, clauses); c_name = "enter"; break; + case PRAGMA_OMP_CLAUSE_PARTIAL: + clauses = cp_parser_omp_clause_unroll_partial (parser, clauses, + token->location); + c_name = "partial"; + break; + case PRAGMA_OMP_CLAUSE_FULL: + clauses = cp_parser_omp_clause_unroll_full(clauses, token->location); + c_name = "full"; + break; default: cp_parser_error (parser, "expected %<#pragma omp%> clause"); goto saw_error; @@ -43565,6 +43631,8 @@ cp_parser_omp_scan_loop_body (cp_parser *parser) braces.require_close (parser); } +static bool cp_parser_nested_omp_unroll_clauses (cp_parser *, tree &); + /* Parse the restricted form of the for statement allowed by OpenMP. */ static tree @@ -43622,6 +43690,15 @@ cp_parser_omp_for_loop (cp_parser *parser, enum tree_code code, tree clauses, loc_first = cp_lexer_peek_token (parser->lexer)->location; + if (cp_parser_nested_omp_unroll_clauses (parser, clauses) + && count > 1) + { + error_at (loc_first, + "collapse cannot be larger than 1 on an unrolled loop"); + return NULL; + } + + for (i = 0; i < count; i++) { int bracecount = 0; @@ -45657,6 +45734,79 @@ cp_parser_omp_target (cp_parser *parser, cp_token *pragma_tok, return true; } +#define OMP_UNROLL_CLAUSE_MASK \ + ( (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_PARTIAL) \ + | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_FULL) ) + +/* Parse zero or more '#pragma omp unroll' that follow + another directive that requires a canonical loop nest. */ + +static bool +cp_parser_nested_omp_unroll_clauses (cp_parser *parser, tree &clauses) +{ + static const char *p_name = "#pragma omp unroll"; + cp_token *tok; + bool unroll_found = false; + while (cp_lexer_next_token_is (parser->lexer, CPP_PRAGMA) + && (tok = cp_lexer_peek_token (parser->lexer), + cp_parser_pragma_kind (tok) == PRAGMA_OMP_UNROLL)) + { + cp_lexer_consume_token (parser->lexer); + gcc_assert (tok->type == CPP_PRAGMA); + parser->lexer->in_pragma = true; + tree c = cp_parser_omp_all_clauses (parser, OMP_UNROLL_CLAUSE_MASK, + p_name, tok); + if (c) + { + gcc_assert (!TREE_CHAIN (c)); + unroll_found = true; + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_UNROLL_FULL) + { + error_at (tok->location, "% clause is invalid here; " + "turns loop into non-loop"); + continue; + } + + c = finish_omp_clauses (c, C_ORT_OMP); + } + else + { + error_at (tok->location, "%<#pragma omp unroll%> without " + "% clause is invalid here; " + "turns loop into non-loop"); + continue; + } + clauses = chainon (clauses, c); + } + return unroll_found; +} + +static tree +cp_parser_omp_unroll (cp_parser *parser, cp_token *tok, bool *if_p) +{ + tree block, ret; + static const char *p_name = "#pragma omp unroll"; + omp_clause_mask mask = OMP_UNROLL_CLAUSE_MASK; + + tree clauses = cp_parser_omp_all_clauses (parser, mask, p_name, tok, false); + + if (!clauses) + { + tree c = build_omp_clause (tok->location, OMP_CLAUSE_UNROLL_NONE); + OMP_CLAUSE_CHAIN (c) = clauses; + clauses = c; + } + + cp_parser_nested_omp_unroll_clauses (parser, clauses); + + block = begin_omp_structured_block (); + ret = cp_parser_omp_for_loop (parser, OMP_LOOP_TRANS, clauses, NULL, if_p); + block = finish_omp_structured_block (block); + add_stmt (block); + + return ret; +} + /* OpenACC 2.0: # pragma acc cache (variable-list) new-line */ @@ -48750,6 +48900,9 @@ cp_parser_omp_construct (cp_parser *parser, cp_token *pragma_tok, bool *if_p) case PRAGMA_OMP_ASSUME: cp_parser_omp_assume (parser, pragma_tok, if_p); return; + case PRAGMA_OMP_UNROLL: + stmt = cp_parser_omp_unroll (parser, pragma_tok, if_p); + break; default: gcc_unreachable (); } @@ -49376,6 +49529,13 @@ cp_parser_pragma (cp_parser *parser, enum pragma_context context, bool *if_p) cp_parser_omp_construct (parser, pragma_tok, if_p); pop_omp_privatization_clauses (stmt); return true; + case PRAGMA_OMP_UNROLL: + if (context != pragma_stmt && context != pragma_compound) + goto bad_stmt; + stmt = push_omp_privatization_clauses (false); + cp_parser_omp_construct (parser, pragma_tok, if_p); + pop_omp_privatization_clauses (stmt); + return true; case PRAGMA_OMP_REQUIRES: if (context != pragma_external) diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index 40deedc9ba9..63b2d1f7a45 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -18086,6 +18086,7 @@ tsubst_omp_clauses (tree clauses, enum c_omp_region_type ort, case OMP_CLAUSE_ASYNC: case OMP_CLAUSE_WAIT: case OMP_CLAUSE_DETACH: + case OMP_CLAUSE_UNROLL_PARTIAL: OMP_CLAUSE_OPERAND (nc, 0) = tsubst_expr (OMP_CLAUSE_OPERAND (oc, 0), args, complain, in_decl); break; @@ -18169,6 +18170,8 @@ tsubst_omp_clauses (tree clauses, enum c_omp_region_type ort, case OMP_CLAUSE_IF_PRESENT: case OMP_CLAUSE_FINALIZE: case OMP_CLAUSE_NOHOST: + case OMP_CLAUSE_UNROLL_FULL: + case OMP_CLAUSE_UNROLL_NONE: break; default: gcc_unreachable (); @@ -19437,6 +19440,7 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl) case OMP_SIMD: case OMP_DISTRIBUTE: case OMP_TASKLOOP: + case OMP_LOOP_TRANS: case OACC_LOOP: { tree clauses, body, pre_body; diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc index 99a76e3ed65..ac49502eea4 100644 --- a/gcc/cp/semantics.cc +++ b/gcc/cp/semantics.cc @@ -6779,6 +6779,7 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort) bool mergeable_seen = false; bool implicit_moved = false; bool target_in_reduction_seen = false; + bool unroll_full_seen = false; bitmap_obstack_initialize (NULL); bitmap_initialize (&generic_head, &bitmap_default_obstack); @@ -8822,6 +8823,61 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort) } break; + case OMP_CLAUSE_UNROLL_FULL: + if (unroll_full_seen) + { + error_at (OMP_CLAUSE_LOCATION (c), + "% appears more than once"); + remove = true; + } + unroll_full_seen = true; + break; + + case OMP_CLAUSE_UNROLL_PARTIAL: + { + + tree t = OMP_CLAUSE_UNROLL_PARTIAL_EXPR (c); + + if (!t) + break; + + if (t == error_mark_node) + remove = true; + else if (!type_dependent_expression_p (t) + && !INTEGRAL_TYPE_P (TREE_TYPE (t))) + { + error_at (OMP_CLAUSE_LOCATION (c), + "partial argument needs integral type"); + remove = true; + } + else + { + t = mark_rvalue_use (t); + if (!processing_template_decl) + { + t = maybe_constant_value (t); + + int n; + if (!INTEGRAL_TYPE_P (TREE_TYPE (t)) + || !tree_fits_shwi_p (t) + || (n = tree_to_shwi (t)) <= 0 || (int)n != n) + { + error_at (OMP_CLAUSE_LOCATION (c), + "partial argument needs positive constant " + "integer expression"); + remove = true; + } + t = fold_build_cleanup_point_expr (TREE_TYPE (t), t); + } + } + + OMP_CLAUSE_UNROLL_PARTIAL_EXPR (c) = t; + } + break; + + case OMP_CLAUSE_UNROLL_NONE: + break; + default: gcc_unreachable (); } diff --git a/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-1.c b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-1.c new file mode 100644 index 00000000000..d496dc29053 --- /dev/null +++ b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-1.c @@ -0,0 +1,133 @@ +extern void dummy (int); + +void +test1 () +{ +#pragma omp unroll partial + for (int i = 0; i < 100; ++i) + dummy (i); +} + +void +test2 () +{ +#pragma omp unroll partial(10) + for (int i = 0; i < 100; ++i) + dummy (i); +} + +void +test3 () +{ +#pragma omp unroll full + for (int i = 0; i < 100; ++i) + dummy (i); +} + +void +test4 () +{ +#pragma omp unroll full + for (int i = 0; i > 100; ++i) + dummy (i); +} + +void +test5 () +{ +#pragma omp unroll full + for (int i = 1; i <= 100; ++i) + dummy (i); +} + +void +test6 () +{ +#pragma omp unroll full + for (int i = 200; i >= 100; i--) + dummy (i); +} + +void +test7 () +{ +#pragma omp unroll full + for (int i = -100; i > 100; ++i) + dummy (i); +} + +void +test8 () +{ +#pragma omp unroll full + for (int i = 100; i > -200; --i) + dummy (i); +} + +void +test9 () +{ +#pragma omp unroll full + for (int i = -300; i != 100; ++i) + dummy (i); +} + +void +test10 () +{ +#pragma omp unroll full + for (int i = -300; i != 100; ++i) + dummy (i); +} + +void +test12 () +{ +#pragma omp unroll full +#pragma omp unroll partial +#pragma omp unroll partial + for (int i = -300; i != 100; ++i) + dummy (i); +} + +void +test13 () +{ + for (int i = 0; i < 100; ++i) +#pragma omp unroll full +#pragma omp unroll partial +#pragma omp unroll partial + for (int j = -300; j != 100; ++j) + dummy (i); +} + +void +test14 () +{ + #pragma omp for + for (int i = 0; i < 100; ++i) +#pragma omp unroll full +#pragma omp unroll partial +#pragma omp unroll partial + for (int j = -300; j != 100; ++j) + dummy (i); +} + +void +test15 () +{ + #pragma omp for + for (int i = 0; i < 100; ++i) + { + + dummy (i); + +#pragma omp unroll full +#pragma omp unroll partial +#pragma omp unroll partial + for (int j = -300; j != 100; ++j) + dummy (j); + + dummy (i); + } + } diff --git a/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-2.c b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-2.c new file mode 100644 index 00000000000..8f7c3088a2e --- /dev/null +++ b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-2.c @@ -0,0 +1,99 @@ +/* { dg-prune-output "error: invalid controlling predicate" } */ +/* { dg-additional-options "-std=c++11" { target c++} } */ + +extern void dummy (int); + +void +test () +{ +#pragma omp unroll partial +#pragma omp unroll full /* { dg-error {'full' clause is invalid here; turns loop into non-loop} } */ + for (int i = -300; i != 100; ++i) + dummy (i); + +#pragma omp for +#pragma omp unroll full /* { dg-error {'full' clause is invalid here; turns loop into non-loop} } */ +#pragma omp unroll partial + for (int i = -300; i != 100; ++i) + dummy (i); + +#pragma omp for +#pragma omp unroll full /* { dg-error {'full' clause is invalid here; turns loop into non-loop} } */ +#pragma omp unroll full /* { dg-error {'full' clause is invalid here; turns loop into non-loop} } */ + for (int i = -300; i != 100; ++i) + dummy (i); + +#pragma omp for +#pragma omp unroll partial partial /* { dg-error {too many 'partial' clauses} } */ + for (int i = -300; i != 100; ++i) + dummy (i); + +#pragma omp unroll full full /* { dg-error {too many 'full' clauses} } */ + for (int i = -300; i != 100; ++i) + dummy (i); + +#pragma omp unroll partial +#pragma omp unroll /* { dg-error {'#pragma omp unroll' without 'partial' clause is invalid here; turns loop into non-loop} } */ + for (int i = -300; i != 100; ++i) + dummy (i); + +#pragma omp for +#pragma omp unroll /* { dg-error {'#pragma omp unroll' without 'partial' clause is invalid here; turns loop into non-loop} } */ + for (int i = -300; i != 100; ++i) + dummy (i); + + int i; +#pragma omp for +#pragma omp unroll( /* { dg-error {expected '#pragma omp' clause before '\(' token} } */ + /* { dg-error {'#pragma omp unroll' without 'partial' clause is invalid here; turns loop into non-loop} "" { target *-*-* } .-1 } */ + for (int i = -300; i != 100; ++i) + dummy (i); + +#pragma omp for +#pragma omp unroll foo /* { dg-error {expected '#pragma omp' clause before 'foo'} } */ + /* { dg-error {'#pragma omp unroll' without 'partial' clause is invalid here; turns loop into non-loop} "" { target *-*-* } .-1 } */ + for (int i = -300; i != 100; ++i) + dummy (i); + +#pragma omp unroll partial( /* { dg-error {expected expression before end of line} "" { target c } } */ + /* { dg-error {expected primary-expression before end of line} "" { target c++ } .-1 } */ + for (int i = -300; i != 100; ++i) + dummy (i); + +#pragma omp unroll partial() /* { dg-error {expected expression before '\)' token} "" { target c } } */ + /* { dg-error {expected primary-expression before '\)' token} "" { target c++ } .-1 } */ + for (int i = -300; i != 100; ++i) + dummy (i); + +#pragma omp unroll partial(i) + /* { dg-error {the value of 'i' is not usable in a constant expression} "" { target c++ } .-1 } */ + /* { dg-error {partial argument needs positive constant integer expression} "" { target c } .-2 } */ + for (int i = -300; i != 100; ++i) + dummy (i); + +#pragma omp unroll parti /* { dg-error {expected '#pragma omp' clause before 'parti'} } */ + for (int i = -300; i != 100; ++i) + dummy (i); + +#pragma omp for +#pragma omp unroll partial(1) +#pragma omp unroll parti /* { dg-error {expected '#pragma omp' clause before 'parti'} } */ + /* { dg-error {'#pragma omp unroll' without 'partial' clause is invalid here; turns loop into non-loop} "" { target *-*-* } .-1 } */ + for (int i = -300; i != 100; ++i) + dummy (i); + +#pragma omp for +#pragma omp unroll partial(1) +#pragma omp unroll parti /* { dg-error {expected '#pragma omp' clause before 'parti'} } */ + /* { dg-error {'#pragma omp unroll' without 'partial' clause is invalid here; turns loop into non-loop} "" { target *-*-* } .-1 } */ + for (int i = -300; i != 100; ++i) + dummy (i); + +int sum = 0; +#pragma omp parallel for reduction(+ : sum) collapse(2) /* { dg-error {collapse cannot be larger than 1 on an unrolled loop} "" { target c } } */ +#pragma omp unroll partial(1) /* { dg-error {collapse cannot be larger than 1 on an unrolled loop} "" { target c++ } } */ + for (int i = 3; i < 10; ++i) + for (int j = -2; j < 7; ++j) + sum++; +} + diff --git a/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-3.c b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-3.c new file mode 100644 index 00000000000..7ace5657b26 --- /dev/null +++ b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-3.c @@ -0,0 +1,18 @@ +/* { dg-additional-options "-fdump-tree-omp_transform_loops" } + * { dg-additional-options "-fdump-tree-original" } */ + +extern void dummy (int); + +void +test1 () +{ + int i; +#pragma omp unroll full + for (int i = 0; i < 10; i++) + dummy (i); +} + + /* Loop should be removed with 10 copies of the body remaining + * { dg-final { scan-tree-dump-times "dummy" 10 "omp_transform_loops" } } + * { dg-final { scan-tree-dump "#pragma omp loop_transform" "original" } } + * { dg-final { scan-tree-dump-not "#pragma omp" "omp_transform_loops" } } */ diff --git a/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-4.c b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-4.c new file mode 100644 index 00000000000..5e473a099d3 --- /dev/null +++ b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-4.c @@ -0,0 +1,19 @@ +/* { dg-additional-options "-fdump-tree-omp_transform_loops" } + * { dg-additional-options "-fdump-tree-original" } */ + +extern void dummy (int); + +void +test1 () +{ + int i; +#pragma omp unroll + for (int i = 0; i < 100; i++) + dummy (i); +} + +/* Loop should not be unrolled, but the internal representation should be lowered + * { dg-final { scan-tree-dump "#pragma omp loop_transform" "original" } } + * { dg-final { scan-tree-dump-not "#pragma omp" "omp_transform_loops" } } + * { dg-final { scan-tree-dump-times "dummy" 1 "omp_transform_loops" } } + * { dg-final { scan-tree-dump-times {if \(i < .+?.+goto.+else goto.*?$} 1 "omp_transform_loops" } } */ diff --git a/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-5.c b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-5.c new file mode 100644 index 00000000000..9d5101bdc60 --- /dev/null +++ b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-5.c @@ -0,0 +1,19 @@ +/* { dg-additional-options "-fdump-tree-omp_transform_loops -fopt-info-omp-optimized-missed" } + * { dg-additional-options "-fdump-tree-original" } */ + +extern void dummy (int); + +void +test1 () +{ + int i; +#pragma omp unroll partial /* { dg-optimized {'partial' clause without unrolling factor turned into 'partial\(5\)' clause} } */ + for (int i = 0; i < 100; i++) + dummy (i); +} + +/* Loop should be unrolled 5 times and the internal representation should be lowered + * { dg-final { scan-tree-dump "#pragma omp loop_transform" "original" } } + * { dg-final { scan-tree-dump-not "#pragma omp" "omp_transform_loops" } } + * { dg-final { scan-tree-dump-times "dummy" 5 "omp_transform_loops" } } + * { dg-final { scan-tree-dump-times {if \(i < .+?.+goto.+else goto.*?$} 1 "omp_transform_loops" } } */ diff --git a/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-6.c b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-6.c new file mode 100644 index 00000000000..ee2d000239d --- /dev/null +++ b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-6.c @@ -0,0 +1,20 @@ +/* { dg-additional-options "--param=omp-unroll-default-factor=100" } + * { dg-additional-options "-fdump-tree-omp_transform_loops -fopt-info-omp-optimized-missed" } + * { dg-additional-options "-fdump-tree-original" } */ + +extern void dummy (int); + +void +test1 () +{ + int i; +#pragma omp unroll /* { dg-optimized {added 'partial\(100\)' clause to 'omp unroll' directive} } */ + for (int i = 0; i < 100; i++) + dummy (i); +} + +/* Loop should be unrolled 5 times and the internal representation should be lowered + * { dg-final { scan-tree-dump "#pragma omp loop_transform unroll_none" "original" } } + * { dg-final { scan-tree-dump-not "#pragma omp" "omp_transform_loops" } } + * { dg-final { scan-tree-dump-times "dummy" 100 "omp_transform_loops" } } + * { dg-final { scan-tree-dump-times {if \(i < .+?.+goto.+else goto.*?$} 1 "omp_transform_loops" } } */ diff --git a/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-7.c b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-7.c new file mode 100644 index 00000000000..0458cb030a9 --- /dev/null +++ b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-7.c @@ -0,0 +1,144 @@ +/* { dg-do run } */ +/* { dg-options "-O0 -fopenmp-simd" } */ + +#include + +#define ASSERT_EQ(var, val) if (var != val) { fprintf (stderr, "%s:%d: Unexpected value %d\n", __FILE__, __LINE__, var); \ + __builtin_abort (); } + +#define ASSERT_EQ_PTR(var, ptr) if (var != ptr) { fprintf (stderr, "%s:%d: Unexpected value %p\n", __FILE__, __LINE__, var); \ + __builtin_abort (); } + +int +test1 (int data[10]) +{ + int iter = 0; + int *i; + #pragma omp unroll partial(8) + for (i = data; i < data + 10 ; i++) + { + ASSERT_EQ (*i, data[iter]); + ASSERT_EQ_PTR (i, data + iter); + iter++; + } + + return iter; +} + +int +test2 (int data[10]) +{ + int iter = 0; + int *i; + #pragma omp unroll partial(8) + for (i = data; i < data + 10 ; i=i+2) + { + ASSERT_EQ_PTR (i, data + 2 * iter); + ASSERT_EQ (*i, data[2 * iter]); + iter++; + } + + return iter; +} + +int +test3 (int data[10]) +{ + int iter = 0; + int *i; + #pragma omp unroll partial(8) + for (i = data; i <= data + 9 ; i=i+2) + { + ASSERT_EQ (*i, data[2 * iter]); + iter++; + } + + return iter; +} + +int +test4 (int data[10]) +{ + int iter = 0; + int *i; + #pragma omp unroll partial(8) + for (i = data; i != data + 10 ; i=i+1) + { + ASSERT_EQ (*i, data[iter]); + iter++; + } + + return iter; +} + +int +test5 (int data[10]) +{ + int iter = 0; + int *i; + #pragma omp unroll partial(7) + for (i = data + 9; i >= data ; i--) + { + ASSERT_EQ (*i, data[9 - iter]); + iter++; + } + + return iter; +} + +int +test6 (int data[10]) +{ + int iter = 0; + int *i; + #pragma omp unroll partial(7) + for (i = data + 9; i > data - 1 ; i--) + { + ASSERT_EQ (*i, data[9 - iter]); + iter++; + } + + return iter; +} + +int +test7 (int data[10]) +{ + int iter = 0; + #pragma omp unroll partial(7) + for (int *i = data + 9; i != data - 1 ; i--) + { + ASSERT_EQ (*i, data[9 - iter]); + iter++; + } + + return iter; +} + +int +main () +{ + int iter_count; + int data[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + iter_count = test1 (data); + ASSERT_EQ (iter_count, 10); + + iter_count = test2 (data); + ASSERT_EQ (iter_count, 5); + + iter_count = test3 (data); + ASSERT_EQ (iter_count, 5); + + iter_count = test4 (data); + ASSERT_EQ (iter_count, 10); + + iter_count = test5 (data); + ASSERT_EQ (iter_count, 10); + + iter_count = test6 (data); + ASSERT_EQ (iter_count, 10); + + iter_count = test7 (data); + ASSERT_EQ (iter_count, 10); +} diff --git a/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-simd-1.c b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-simd-1.c new file mode 100644 index 00000000000..1cd4d6e7322 --- /dev/null +++ b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-simd-1.c @@ -0,0 +1,84 @@ +/* { dg-options "-fno-openmp -fopenmp-simd" } */ +/* { dg-do run } */ +/* { dg-additional-options "-fdump-tree-original" } */ +/* { dg-additional-options "-fdump-tree-omp_transform_loops" } */ + +#include + +int compute_sum1 () +{ + int sum = 0; + int i,j; + +#pragma omp simd reduction(+:sum) + for (i = 3; i < 10; ++i) + #pragma omp unroll full + for (j = -2; j < 7; ++j) + sum++; + + if (j != 7) + __builtin_abort; + + return sum; +} + +int compute_sum2() +{ + int sum = 0; + int i,j; +#pragma omp simd reduction(+:sum) +#pragma omp unroll partial(5) + for (i = 3; i < 10; ++i) + for (j = -2; j < 7; ++j) + sum++; + + if (j != 7) + __builtin_abort; + + return sum; +} + +int compute_sum3() +{ + int sum = 0; + int i,j; +#pragma omp simd reduction(+:sum) +#pragma omp unroll partial(1) + for (i = 3; i < 10; ++i) + for (j = -2; j < 7; ++j) + sum++; + + if (j != 7) + __builtin_abort; + + return sum; +} + +int main () +{ + int result = compute_sum1 (); + if (result != 7 * 9) + { + fprintf (stderr, "%d: Wrong result %d\n", __LINE__, result); + __builtin_abort (); + } + + result = compute_sum1 (); + if (result != 7 * 9) + { + fprintf (stderr, "%d: Wrong result %d\n", __LINE__, result); + __builtin_abort (); + } + + result = compute_sum3 (); + if (result != 7 * 9) + { + fprintf (stderr, "%d: Wrong result %d\n", __LINE__, result); + __builtin_abort (); + } + + return 0; +} + +/* { dg-final { scan-tree-dump {omp loop_transform} "original" } } */ +/* { dg-final { scan-tree-dump-not {omp loop_transform} "omp_transform_loops" } } */ diff --git a/gcc/testsuite/g++.dg/gomp/loop-transforms/unroll-1.C b/gcc/testsuite/g++.dg/gomp/loop-transforms/unroll-1.C new file mode 100644 index 00000000000..cba37c88ebe --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/loop-transforms/unroll-1.C @@ -0,0 +1,42 @@ +// { dg-do compile } +// { dg-additional-options "-std=c++11" } +#include + +extern void dummy (int); + +void +test1 () +{ + std::vector v; + + for (unsigned i = 0; i < 1000; i++) + v.push_back (i); + +#pragma omp for + for (int i : v) + dummy (i); + +#pragma omp unroll partial(5) + for (int i : v) + dummy (i); +} + +void +test2 () +{ + std::vector> v; + + for (unsigned i = 0; i < 10; i++) + { + std::vector u; + for (unsigned j = 0; j < 10; j++) + u.push_back (j); + v.push_back (u); + } + +#pragma omp for +#pragma omp unroll partial(5) + for (auto u : v) + for (int i : u) + dummy (i); +} diff --git a/gcc/testsuite/g++.dg/gomp/loop-transforms/unroll-2.C b/gcc/testsuite/g++.dg/gomp/loop-transforms/unroll-2.C new file mode 100644 index 00000000000..f606f3de757 --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/loop-transforms/unroll-2.C @@ -0,0 +1,47 @@ +// { dg-do link } +// { dg-additional-options "-std=c++11" } +#include + +extern void dummy (int); + +template void +test_template () +{ + std::vector v; + + for (unsigned i = 0; i < 1000; i++) + v.push_back (i); + +#pragma omp for + for (int i : v) + dummy (i); + +#pragma omp unroll partial(U1) + for (T i : v) + dummy (i); + +#pragma omp unroll partial(U2) // { dg-error {partial argument needs positive constant integer expression} } + for (T i : v) + dummy (i); + +#pragma omp unroll partial(U3) // { dg-error {partial argument needs positive constant integer expression} } + for (T i : v) + dummy (i); + +#pragma omp for +#pragma omp unroll partial(U1) + for (T i : v) + dummy (i); + +#pragma omp for +#pragma omp unroll partial(U2) // { dg-error {partial argument needs positive constant integer expression} } + for (T i : v) + dummy (i); + +#pragma omp for +#pragma omp unroll partial(U3) // { dg-error {partial argument needs positive constant integer expression} } + for (T i : v) + dummy (i); +} + +void test () { test_template (); }; diff --git a/gcc/testsuite/g++.dg/gomp/loop-transforms/unroll-3.C b/gcc/testsuite/g++.dg/gomp/loop-transforms/unroll-3.C new file mode 100644 index 00000000000..ae9f5500360 --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/loop-transforms/unroll-3.C @@ -0,0 +1,37 @@ +// { dg-do compile } +// { dg-additional-options "-std=c++11" } +// { dg-additional-options "-fdump-tree-omp_transform_loops -fopt-info-omp-optimized-missed" } +// { dg-additional-options "-fdump-tree-original" } +#include + +extern void dummy (int); + +constexpr unsigned fib (unsigned n) +{ + return n <= 2 ? 1 : fib (n-1) + fib (n-2); +} + +void +test1 () +{ + std::vector v; + + for (unsigned i = 0; i < 1000; i++) + v.push_back (i); + +#pragma omp unroll partial(fib(10)) + for (int i : v) + dummy (i); +} + + +// Loop should be unrolled fib(10) = 55 times +// ! { dg-final { scan-tree-dump {#pragma omp loop_transform unroll_partial\(55\)} "original" } } +// ! { dg-final { scan-tree-dump-not "#pragma omp" "omp_transform_loops" } } +// ! { dg-final { scan-tree-dump-times "dummy" 55 "omp_transform_loops" } } + +// There should be one loop that fills the vector ... +// ! { dg-final { scan-tree-dump-times {if \(i.*? <= .+?.+goto.+else goto.*?$} 1 "omp_transform_loops" } } + +// ... and one resulting from the lowering of the unrolled loop +// ! { dg-final { scan-tree-dump-times {if \(D\.[0-9]+ < retval.+?.+goto.+else goto.*?$} 1 "omp_transform_loops" } } diff --git a/libgomp/testsuite/libgomp.c++/loop-transforms/unroll-1.C b/libgomp/testsuite/libgomp.c++/loop-transforms/unroll-1.C new file mode 100644 index 00000000000..004eef91649 --- /dev/null +++ b/libgomp/testsuite/libgomp.c++/loop-transforms/unroll-1.C @@ -0,0 +1,73 @@ +// { dg-additional-options "-std=c++11" } +// { dg-additional-options "-O0" } + +#include +#include + +constexpr unsigned fib (unsigned n) +{ + return n <= 2 ? 1 : fib (n-1) + fib (n-2); +} + +int +test1 () +{ + std::vector v; + + for (unsigned i = 0; i <= 9; i++) + v.push_back (1); + + int sum = 0; + for (int k = 0; k < 10; k++) +#pragma omp unroll partial(fib(3)) + for (int i : v) { + for (int j = 8; j != -2; --j) + sum = sum + i; + } + + return sum; +} + +int +test2 () +{ + std::vector v; + + for (unsigned i = 0; i <= 10; i++) + v.push_back (i); + + int sum = 0; +#pragma omp parallel for reduction(+:sum) + for (int k = 0; k < 10; k++) +#pragma omp unroll +#pragma omp unroll partial(fib(4)) + for (int i : v) + { + #pragma omp unroll full + for (int j = 8; j != -2; --j) + sum = sum + i; + } + + return sum; +} + +int +main () +{ + int result = test1 (); + + if (result != 1000) + { + fprintf (stderr, "Wrong result: %d\n", result); + __builtin_abort (); + } + + result = test2 (); + if (result != 5500) + { + fprintf (stderr, "Wrong result: %d\n", result); + __builtin_abort (); + } + + return 0; +} diff --git a/libgomp/testsuite/libgomp.c++/loop-transforms/unroll-2.C b/libgomp/testsuite/libgomp.c++/loop-transforms/unroll-2.C new file mode 100644 index 00000000000..90d2775c95b --- /dev/null +++ b/libgomp/testsuite/libgomp.c++/loop-transforms/unroll-2.C @@ -0,0 +1,34 @@ +// { dg-do run } +// { dg-additional-options "-std=c++11" } +#include +#include + +int +main () +{ + std::vector> v; + std::vector w; + + for (unsigned i = 0; i < 10; i++) + { + std::vector u; + for (unsigned j = 0; j < 10; j++) + u.push_back (j); + v.push_back (u); + } + +#pragma omp for +#pragma omp unroll partial(7) + for (auto u : v) + for (int x : u) + w.push_back (x); + + std::size_t l = w.size (); + for (std::size_t i = 0; i < l; i++) + { + if (w[i] != i % 10) + __builtin_abort (); + } + + return 0; +} diff --git a/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/unroll-1.c b/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/unroll-1.c new file mode 100644 index 00000000000..2ac0fff16af --- /dev/null +++ b/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/unroll-1.c @@ -0,0 +1,76 @@ +#include + +int compute_sum1 () +{ + int sum = 0; + int i,j; +#pragma omp parallel for reduction(+:sum) lastprivate(j) +#pragma omp unroll partial + for (i = 3; i < 10; ++i) + for (j = -2; j < 7; ++j) + sum++; + + if (j != 7) + __builtin_abort; + + return sum; +} + +int compute_sum2() +{ + int sum = 0; + int i,j; +#pragma omp parallel for reduction(+:sum) lastprivate(j) +#pragma omp unroll partial(5) + for (i = 3; i < 10; ++i) + for (j = -2; j < 7; ++j) + sum++; + + if (j != 7) + __builtin_abort; + + return sum; +} + +int compute_sum3() +{ + int sum = 0; + int i,j; +#pragma omp parallel for reduction(+:sum) +#pragma omp unroll partial(1) + for (i = 3; i < 10; ++i) + for (j = -2; j < 7; ++j) + sum++; + + if (j != 7) + __builtin_abort; + + return sum; +} + +int main () +{ + int result; + result = compute_sum1 (); + if (result != 7 * 9) + { + fprintf (stderr, "%d: Wrong result %d\n", __LINE__, result); + __builtin_abort (); + } + + result = compute_sum2 (); + if (result != 7 * 9) + { + fprintf (stderr, "%d: Wrong result %d\n", __LINE__, result); + __builtin_abort (); + } + + result = compute_sum3 (); + if (result != 7 * 9) + { + fprintf (stderr, "%d: Wrong result %d\n", __LINE__, result); + __builtin_abort (); + } + + return 0; +}