From patchwork Wed Nov 17 16:03:16 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Frederik Harwath X-Patchwork-Id: 1556253 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=2620:52:3:1:0:246e:9693:128c; helo=sourceware.org; envelope-from=gcc-patches-bounces+incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=) Received: from sourceware.org (server2.sourceware.org [IPv6:2620:52:3:1:0:246e:9693:128c]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by bilbo.ozlabs.org (Postfix) with ESMTPS id 4HvSdv4nkxz9s1l for ; Thu, 18 Nov 2021 03:13:23 +1100 (AEDT) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 8263C385E448 for ; Wed, 17 Nov 2021 16:13:21 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from esa3.mentor.iphmx.com (esa3.mentor.iphmx.com [68.232.137.180]) by sourceware.org (Postfix) with ESMTPS id 6B93E385841C for ; Wed, 17 Nov 2021 16:04:09 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 6B93E385841C Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=codesourcery.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=mentor.com IronPort-SDR: ycesRS+fM/h/zaYZKoxBGV8p4XqTqgpKaSBdfwzRvfUCkEcBbCE7nw9oJFxInAQuRmUWpn2XT0 PXMP0rPhge4dgfC7K8gD/8ps4HSTFNzmepzaGJ3rrBjgu7FJ1nnVExCRZyS2VEkVRa6xHjy6Sj WdYKkbsIw+egmoM4iHkZLyMQ3PmC/wDXlXK6GPKVhAd+zJ4PEWRqQn4EfFhdfv4abDrNy48QY3 iMtngWpRwOtY3dAZkBFwK1JDIFRW3gkSNBMtGr71YC5h1EXbPwOk9smOCZqbODg8wBL+9tSo/3 vzbIt/HDSD4CvEK64dGxNpQx X-IronPort-AV: E=Sophos;i="5.87,241,1631606400"; d="scan'208";a="68445335" Received: from orw-gwy-01-in.mentorg.com ([192.94.38.165]) by esa3.mentor.iphmx.com with ESMTP; 17 Nov 2021 08:04:09 -0800 IronPort-SDR: B8HIxyB2iBtsKAf2E8pKuK0UzKluoP+ZH8z3CrwEv+IaIENd6VQHyXGraUuxi1WEOLQl9/eM5C qyKXYlTKaQBBkBqHSPpEknWz2zcnmUeDhDsqmRpkPTKcsdxXzYus5RFqb+yww1VSKSnCR/MGLO Tv7hCiOMIjzqq7kLKI7eBdtiny/4N8N3SwHHkkaUF1WaQ4QHNb464dVGk98D0somq7Jfwxm4xu ZdA1ds7ymmhEeCCIKrRxbraHwCqak1rrW9UfkkrtkDKxhMoAbgR/aP3iJjJxht1+D/+X1ktMWA eVY= From: Frederik Harwath To: Subject: [OG11][committed][PATCH 08/22] graphite: Add runtime alias checking Date: Wed, 17 Nov 2021 17:03:16 +0100 Message-ID: <20211117160330.20029-8-frederik@codesourcery.com> X-Mailer: git-send-email 2.33.0 In-Reply-To: <20211117160330.20029-1-frederik@codesourcery.com> References: <20211117160330.20029-1-frederik@codesourcery.com> MIME-Version: 1.0 X-Originating-IP: [137.202.0.90] X-ClientProxiedBy: svr-ies-mbx-01.mgc.mentorg.com (139.181.222.1) To SVR-IES-MBX-04.mgc.mentorg.com (139.181.222.4) X-Spam-Status: No, score=-12.4 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.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) 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+incoming=patchwork.ozlabs.org@gcc.gnu.org Sender: "Gcc-patches" Graphite rejects a SCoP if it contains a pair of data references for which it cannot determine statically if they may alias. This happens very often, for instance in C code which does not use explicit "restrict". This commit adds the possibility to analyze a SCoP nevertheless and perform an alias check at runtime. Then, if aliasing is detected, the execution will fall back to the unoptimized SCoP. TODO This needs more testing on non-OpenACC code. gcc/ChangeLog: * common.opt: Add fgraphite-runtime-alias-checks. * graphite-isl-ast-to-gimple.c (generate_alias_cond): New function. (graphite_regenerate_ast_isl): Use from here. * graphite-poly.c (new_scop): Create unhandled_alias_ddrs vec ... (free_scop): and release here. * graphite-scop-detection.c (dr_defs_outside_region): New function. (dr_well_analyzed_for_runtime_alias_check_p): New function. (graphite_runtime_alias_check_p): New function. (build_alias_set): Record unhandled alias ddrs for later alias check creation if flag_graphite_runtime_alias_checks is true instead of failing. * graphite.h (struct scop): Add field unhandled_alias_ddrs. * sese.h (has_operands_from_region_p): New function. gcc/testsuite/ChangeLog: * gcc.dg/graphite/alias-1.c: New test. --- gcc/common.opt | 4 + gcc/graphite-isl-ast-to-gimple.c | 60 ++++++ gcc/graphite-poly.c | 2 + gcc/graphite-scop-detection.c | 239 +++++++++++++++++++++--- gcc/graphite.h | 4 + gcc/sese.h | 18 ++ gcc/testsuite/gcc.dg/graphite/alias-1.c | 22 +++ 7 files changed, 326 insertions(+), 23 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/graphite/alias-1.c -- 2.33.0 ----------------- 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/common.opt b/gcc/common.opt index 771398bc03de..aa695e56dc48 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -1636,6 +1636,10 @@ fgraphite-identity Common Var(flag_graphite_identity) Optimization Enable Graphite Identity transformation. +fgraphite-runtime-alias-checks +Common Var(flag_graphite_runtime_alias_checks) Optimization Init(1) +Allow Graphite to add runtime alias checks to loop-nests if aliasing cannot be resolved statically. + fhoist-adjacent-loads Common Var(flag_hoist_adjacent_loads) Optimization Enable hoisting adjacent loads to encourage generating conditional move diff --git a/gcc/graphite-isl-ast-to-gimple.c b/gcc/graphite-isl-ast-to-gimple.c index 44c06016f1a2..caa0160b9bce 100644 --- a/gcc/graphite-isl-ast-to-gimple.c +++ b/gcc/graphite-isl-ast-to-gimple.c @@ -1456,6 +1456,34 @@ generate_entry_out_of_ssa_copies (edge false_entry, } } +/* Create a condition that evaluates to TRUE if all ALIAS_DDRS are free of + aliasing. */ + +static tree +generate_alias_cond (vec &alias_ddrs, loop_p context_loop) +{ + gcc_checking_assert (flag_graphite_runtime_alias_checks + && alias_ddrs.length () > 0); + gcc_checking_assert (context_loop); + + auto_vec check_pairs; + compute_alias_check_pairs (context_loop, &alias_ddrs, &check_pairs); + gcc_checking_assert (check_pairs.length () > 0); + + tree alias_cond = NULL_TREE; + create_runtime_alias_checks (context_loop, &check_pairs, &alias_cond); + gcc_checking_assert (alias_cond); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Generated runtime alias check: "); + print_generic_expr (dump_file, alias_cond, dump_flags); + fprintf (dump_file, "\n"); + } + + return alias_cond; +} + /* GIMPLE Loop Generator: generates loops in GIMPLE form for the given SCOP. Return true if code generation succeeded. */ @@ -1496,12 +1524,44 @@ graphite_regenerate_ast_isl (scop_p scop) region->if_region = if_region; loop_p context_loop = region->region.entry->src->loop_father; + gcc_checking_assert (context_loop); edge e = single_succ_edge (if_region->true_region->region.entry->dest); basic_block bb = split_edge (e); /* Update the true_region exit edge. */ region->if_region->true_region->region.exit = single_succ_edge (bb); + if (flag_graphite_runtime_alias_checks + && scop->unhandled_alias_ddrs.length () > 0) + { + /* SCoP detection has failed to handle the aliasing between some data + references of the SCoP statically. Generate an alias check that selects + the newly generated version of the SCoP in the true-branch of the + conditional if aliasing can be ruled out at runtime and the original + version of the SCoP, otherwise. */ + + loop_p loop + = find_common_loop (scop->scop_info->region.entry->dest->loop_father, + scop->scop_info->region.exit->src->loop_father); + tree cond = generate_alias_cond (scop->unhandled_alias_ddrs, loop); + tree non_alias_cond = build1 (TRUTH_NOT_EXPR, boolean_type_node, cond); + set_ifsese_condition (region->if_region, non_alias_cond); + + /* The loop-nest vec is shared by all DDRs. */ + DDR_LOOP_NEST (scop->unhandled_alias_ddrs[0]).release (); + + unsigned int i; + struct data_dependence_relation *ddr; + + FOR_EACH_VEC_ELT (scop->unhandled_alias_ddrs, i, ddr) + if (ddr) + free_dependence_relation (ddr); + scop->unhandled_alias_ddrs.truncate (0); + } + + if (dump_file) + fprintf (dump_file, "[codegen] isl AST to Gimple succeeded.\n"); + t.translate_isl_ast (context_loop, root_node, e, ip); if (! t.codegen_error_p ()) { diff --git a/gcc/graphite-poly.c b/gcc/graphite-poly.c index 2e31b2782c24..27d5e43af125 100644 --- a/gcc/graphite-poly.c +++ b/gcc/graphite-poly.c @@ -264,6 +264,7 @@ new_scop (edge entry, edge exit) scop_set_region (s, region); s->pbbs.create (3); s->drs.create (3); + s->unhandled_alias_ddrs.create (1); s->dependence = NULL; return s; } @@ -284,6 +285,7 @@ free_scop (scop_p scop) scop->pbbs.release (); scop->drs.release (); + scop->unhandled_alias_ddrs.release (); isl_set_free (scop->param_context); scop->param_context = NULL; diff --git a/gcc/graphite-scop-detection.c b/gcc/graphite-scop-detection.c index 46c470210d05..26ba61d1d601 100644 --- a/gcc/graphite-scop-detection.c +++ b/gcc/graphite-scop-detection.c @@ -1542,6 +1542,123 @@ try_generate_gimple_bb (scop_p scop, basic_block bb) return new_gimple_poly_bb (bb, drs, reads, writes); } +/* Checks if all parts of DR are defined outside of REGION. This allows an + alias check involving DR to be placed in front of the region. */ + +static opt_result +dr_defs_outside_region (const sese_l ®ion, data_reference_p dr) +{ + static const char *pre + = "cannot create alias check for SCoP. Data reference's"; + static const char *suf = "uses definitions from SCoP.\n"; + opt_result res = opt_result::success (); + + if (has_operands_from_region_p (DR_BASE_OBJECT (dr), region)) + res = opt_result::failure_at (DR_STMT (dr), "%s base %s", pre, suf); + else if (has_operands_from_region_p (DR_INIT (dr), region)) + res = opt_result::failure_at (DR_STMT (dr), "%s constant offset %s", pre, + suf); + else if (has_operands_from_region_p (DR_STEP (dr), region)) + res = opt_result::failure_at (DR_STMT (dr), "%s step %s", pre, suf); + else if (has_operands_from_region_p (DR_OFFSET (dr), region)) + res = opt_result::failure_at (DR_STMT (dr), "%s loop-invariant offset %s", + pre, suf); + else if (has_operands_from_region_p (DR_BASE_ADDRESS (dr), region)) + res = opt_result::failure_at (DR_STMT (dr), "%s base address %s", pre, + suf); + else + for (unsigned i = 0; i < DR_NUM_DIMENSIONS (dr); ++i) + if (has_operands_from_region_p (DR_ACCESS_FN (dr, i), region)) + { + res = opt_result::failure_at ( + DR_STMT (dr), "%s %d-th access function %s", pre, i + 1, pre); + break; + } + + return opt_result::success (); +} + +/* Check that all constituents of DR that are used by the + "compute_alias_check_pairs" function have been analyzed as required. */ + +static opt_result +dr_well_analyzed_for_runtime_alias_check_p (data_reference_p dr) +{ + static const char* error = + "data-reference not well-analyzed for runtime check."; + gimple* stmt = DR_STMT (dr); + + if (! DR_BASE_ADDRESS (dr)) + return opt_result::failure_at (stmt, "%s no base address.\n", error); + else if (! DR_OFFSET (dr)) + return opt_result::failure_at (stmt, "%s no offset.\n", error); + else if (! DR_INIT (dr)) + return opt_result::failure_at (stmt, "%s no init.\n", error); + else if (! DR_STEP (dr)) + return opt_result::failure_at (stmt, "%s no step.\n", error); + else if (! tree_fits_uhwi_p (DR_STEP (dr))) + return opt_result::failure_at (stmt, "%s step too large.\n", error); + + DEBUG_PRINT (dump_data_reference (dump_file, dr)); + + return opt_result::success (); +} + +/* Return TRUE if it is possible to create a runtime alias check for + data-references DR1 and DR2 from LOOP and place it in front of REGION. */ + +static opt_result +graphite_runtime_alias_check_p (data_reference_p dr1, data_reference_p dr2, + class loop *loop, const sese_l ®ion) +{ + gcc_checking_assert (loop); + gcc_checking_assert (dr1); + gcc_checking_assert (dr2); + + if (dump_file) + { + fprintf (dump_file, + "Attempting runtime alias check creation for DRs:\n"); + dump_data_reference (dump_file, dr1); + dump_data_reference (dump_file, dr2); + } + + if (!optimize_loop_for_speed_p (loop)) + return opt_result::failure_at (DR_STMT (dr1), + "runtime alias check not supported when" + " optimizing for size.\n"); + + /* Verify that we have enough information about the data-references and + context loop to construct a runtime alias check expression with + "compute_alias_check_pairs". */ + tree niters = number_of_latch_executions (loop); + if (niters == NULL_TREE || niters == chrec_dont_know) + return opt_result::failure_at (DR_STMT (dr1), + "failed to obtain number of iterations of " + "loop %d.\n", loop->num); + + opt_result ok = dr_well_analyzed_for_runtime_alias_check_p (dr1); + if (!ok) + return ok; + + ok = dr_well_analyzed_for_runtime_alias_check_p (dr2); + if (!ok) + return ok; + + /* The runtime alias check would be placed before REGION and hence it cannot + use definitions made within REGION. */ + + ok = dr_defs_outside_region (region, dr1); + if (!ok) + return ok; + + ok = dr_defs_outside_region (region, dr2); + if (!ok) + return ok; + + return opt_result::success (); +} + /* Compute alias-sets for all data references in DRS. */ static bool @@ -1549,7 +1666,7 @@ build_alias_set (scop_p scop) { int num_vertices = scop->drs.length (); struct graph *g = new_graph (num_vertices); - dr_info *dr1, *dr2; + dr_info *dri1, *dri2; int i, j; int *all_vertices; @@ -1557,33 +1674,110 @@ build_alias_set (scop_p scop) = find_common_loop (scop->scop_info->region.entry->dest->loop_father, scop->scop_info->region.exit->src->loop_father); - FOR_EACH_VEC_ELT (scop->drs, i, dr1) - for (j = i+1; scop->drs.iterate (j, &dr2); j++) - if (dr_may_alias_p (dr1->dr, dr2->dr, nest)) - { - /* Dependences in the same alias set need to be handled - by just looking at DR_ACCESS_FNs. */ - if (DR_NUM_DIMENSIONS (dr1->dr) == 0 - || DR_NUM_DIMENSIONS (dr1->dr) != DR_NUM_DIMENSIONS (dr2->dr) - || ! operand_equal_p (DR_BASE_OBJECT (dr1->dr), - DR_BASE_OBJECT (dr2->dr), - OEP_ADDRESS_OF) - || ! types_compatible_p (TREE_TYPE (DR_BASE_OBJECT (dr1->dr)), - TREE_TYPE (DR_BASE_OBJECT (dr2->dr)))) - { - free_graph (g); - return false; - } - add_edge (g, i, j); - add_edge (g, j, i); - } + gcc_checking_assert (nest); + + vec nest_vec; + nest_vec.create (1); + if (flag_graphite_runtime_alias_checks) + nest_vec.safe_push (nest); + + FOR_EACH_VEC_ELT (scop->drs, i, dri1) + { + data_reference_p dr1 = dri1->dr; + + for (j = i + 1; scop->drs.iterate (j, &dri2); j++) + { + + data_reference_p dr2 = dri2->dr; + if (!(DR_IS_READ (dr1) && DR_IS_READ (dr2)) + && dr_may_alias_p (dr1, dr2, nest)) + { + /* Dependences in the same alias set need to be handled + by just looking at DR_ACCESS_FNs. */ + bool dimension_zero = DR_NUM_DIMENSIONS (dr1) == 0; + bool different_dimensions + = DR_NUM_DIMENSIONS (dr1) != DR_NUM_DIMENSIONS (dr2); + bool different_base_objects = !operand_equal_p ( + DR_BASE_OBJECT (dr1), DR_BASE_OBJECT (dr2), OEP_ADDRESS_OF); + bool incompatible_types + = !types_compatible_p (TREE_TYPE (DR_BASE_OBJECT (dr1)), + TREE_TYPE (DR_BASE_OBJECT (dr2))); + bool ddr_can_be_handled + = !(dimension_zero || different_dimensions + || different_base_objects || incompatible_types); + + if (!ddr_can_be_handled) + { + DEBUG_PRINT ( + dp << "[build_alias_set] " + "Cannot handle aliasing between data references:\n"; + print_gimple_stmt (dump_file, dr1->stmt, 2, TDF_DETAILS); + print_gimple_stmt (dump_file, dr2->stmt, 2, TDF_DETAILS); + dp << "\n"); + if (dimension_zero) + DEBUG_PRINT (dp << "DR1 has dimension 0.\n"); + if (different_base_objects) + DEBUG_PRINT (dp << "DRs have different base objects.\n"); + if (different_dimensions) + DEBUG_PRINT (dp << "DRs have different dimensions.\n"); + if (incompatible_types) + DEBUG_PRINT (dp << + "DRs have incompatible base object types.\n"); + } + + if (ddr_can_be_handled) + { + add_edge (g, i, j); + add_edge (g, j, i); + continue; + } + + loop_p common_loop + = find_common_loop ((DR_STMT (dr1))->bb->loop_father, + (DR_STMT (dr2))->bb->loop_father); + edge scop_entry = scop->scop_info->region.entry; + dr1 = create_data_ref (scop_entry, common_loop, DR_REF (dr1), + DR_STMT (dr1), DR_IS_READ (dr1), + DR_IS_CONDITIONAL_IN_STMT (dr1)); + dr2 = create_data_ref (scop_entry, common_loop, DR_REF (dr2), + DR_STMT (dr2), DR_IS_READ (dr2), + DR_IS_CONDITIONAL_IN_STMT (dr2)); + + if (flag_graphite_runtime_alias_checks + && graphite_runtime_alias_check_p (dr1, dr2, nest, + scop->scop_info->region)) + { + ddr_p ddr = initialize_data_dependence_relation (dr1, dr2, + nest_vec); + scop->unhandled_alias_ddrs.safe_push (ddr); + } + else + { + if (flag_graphite_runtime_alias_checks) + { + unsigned int i; + struct data_dependence_relation *ddr; + + FOR_EACH_VEC_ELT (scop->unhandled_alias_ddrs, i, ddr) + if (ddr) + free_dependence_relation (ddr); + scop->unhandled_alias_ddrs.truncate (0); + } + + nest_vec.release (); + free_graph (g); + return false; + } + } + } + } all_vertices = XNEWVEC (int, num_vertices); for (i = 0; i < num_vertices; i++) all_vertices[i] = i; scop->max_alias_set - = graphds_dfs (g, all_vertices, num_vertices, NULL, true, NULL) + 1; + = graphds_dfs (g, all_vertices, num_vertices, NULL, true, NULL) + 1; free (all_vertices); for (i = 0; i < g->n_vertices; i++) @@ -1703,7 +1897,6 @@ gather_bbs::after_dom_children (basic_block bb) } } - /* Compute sth like an execution order, dominator order with first executing edges that stay inside the current loop, delaying processing exit edges. */ diff --git a/gcc/graphite.h b/gcc/graphite.h index 6464d2f50ce7..03febfa39986 100644 --- a/gcc/graphite.h +++ b/gcc/graphite.h @@ -368,6 +368,10 @@ struct scop /* The maximum alias set as assigned to drs by build_alias_sets. */ unsigned max_alias_set; + /* A set of ddrs that were rejected by build_alias_set during scop detection + and that must be handled by other means (runtime checking). */ + vec unhandled_alias_ddrs; + /* All the basic blocks in this scop that contain memory references and that will be represented as statements in the polyhedral representation. */ diff --git a/gcc/sese.h b/gcc/sese.h index cd19e6010196..c51ea68bfb47 100644 --- a/gcc/sese.h +++ b/gcc/sese.h @@ -153,6 +153,24 @@ defined_in_sese_p (tree name, const sese_l &r) return stmt_in_sese_p (SSA_NAME_DEF_STMT (name), r); } +/* Returns true if EXPR has operands that are defined in REGION. */ + +static bool +has_operands_from_region_p (tree expr, const sese_l ®ion) +{ + if (!expr || is_gimple_min_invariant (expr)) + return false; + + if (TREE_CODE (expr) == SSA_NAME) + return defined_in_sese_p (expr, region); + + for (int i = 0; i < TREE_OPERAND_LENGTH (expr); i++) + if (has_operands_from_region_p (TREE_OPERAND (expr, i), region)) + return true; + + return false; +} + /* Returns true when LOOP is in REGION. */ static inline bool diff --git a/gcc/testsuite/gcc.dg/graphite/alias-1.c b/gcc/testsuite/gcc.dg/graphite/alias-1.c new file mode 100644 index 000000000000..ee80dae1df33 --- /dev/null +++ b/gcc/testsuite/gcc.dg/graphite/alias-1.c @@ -0,0 +1,22 @@ +/* This test demonstrates a loop nest that Graphite cannot handle + because of aliasing. It should be possible to handle this loop nest + by creating a runtime alias check like in the very similar test + alias-0-runtime-check.c. However Graphite analyses the data + reference with respect to the innermost loop that contains the data + reference, the variable "i" remains uninstantiated (in contrast to + "j"), and consequently the alias check cannot be placed outside of + the SCoP since "i" is not defined there. */ + +/* { dg-options "-O2 -fgraphite-identity -fgraphite-runtime-alias-checks -fdump-tree-graphite-details" } */ + +void sum(int *x, int *y, unsigned *sum) +{ + unsigned i,j; + *sum = 0; + + for (i = 0; i < 10000; i=i+1) + for (j = 0; j < 22222; j=j+1) + *sum += x[i] + y[j]; +} + +/* { dg-final { scan-tree-dump "number of SCoPs: 1" "graphite" { xfail *-*-* } } } */