| Message ID | patch-20512-tamar@arm.com |
|---|---|
| State | New |
| Headers |
Return-Path: <gcc-patches-bounces~patchwork=sourceware.org@gcc.gnu.org> X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from vm01.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 2F1404BA23E6 for <patchwork@sourceware.org>; Wed, 6 May 2026 13:06:56 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 2F1404BA23E6 Authentication-Results: sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=arm.com header.i=@arm.com header.a=rsa-sha256 header.s=selector1 header.b=EPxz+16n; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.a=rsa-sha256 header.s=selector1 header.b=EPxz+16n X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from OSPPR02CU001.outbound.protection.outlook.com (mail-norwayeastazon11013016.outbound.protection.outlook.com [40.107.159.16]) by sourceware.org (Postfix) with ESMTPS id 263DE4BA23D8 for <gcc-patches@gcc.gnu.org>; Wed, 6 May 2026 13:02:52 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 263DE4BA23D8 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=arm.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 263DE4BA23D8 Authentication-Results: sourceware.org; arc=pass smtp.remote-ip=40.107.159.16 ARC-Seal: i=3; a=rsa-sha256; d=sourceware.org; s=key; t=1778072572; cv=pass; b=Zad9o7QFFxIKZeTXSsC9sp+DVfskDZLdBMDfgt6aGT7yKn09dKS2E9ipcu5gw5+a55qBHbFmtKQClB5PdaYmtv4JgwXF79wGC5zndehGZs3dkwwr3/XUBElYmY1pO9VoD73oQ/gU/ZVuh4ucH7RSRfB66RHKo3fkFeISWh62LT8= ARC-Message-Signature: i=3; a=rsa-sha256; d=sourceware.org; s=key; t=1778072572; c=relaxed/simple; bh=KtLWxvtxTdakPtZPjyGZ5YngXu4NdtIQQiSxoh8QQfA=; h=DKIM-Signature:DKIM-Signature:Date:From:To:Subject:Message-ID: MIME-Version; b=PA0zZoztkbH8BJpSIRsw6U1VbM1kdpYk0GG2wWXH6VXrM+8Ff0L92ejZ3G/aTuiGkH9mO/zAEEF9wWO6RX/V+Jt0TvpfMy8plsrOPetkYAXjSmmop2hmd+kIibssWy3jUk8w+DsEON2pjfFKc7RNrkqJOqokKueRhI087GB7xqA= ARC-Authentication-Results: i=3; sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=arm.com header.i=@arm.com header.a=rsa-sha256 header.s=selector1 header.b=EPxz+16n; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.a=rsa-sha256 header.s=selector1 header.b=EPxz+16n DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 263DE4BA23D8 ARC-Seal: i=2; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=pass; b=L4/mDamNl4k8+ezXTzMNZCExFRnqw0W6zy3Xf2U4JSMIqZ/p8+4vXuzy/T8tcCcfSOotSngNt64Or8Ntzx3X1R6000EloWzHyUzXUkEX9bT3OqLVOxtlfmcfyTypq5zbhV+LXSEY8yV5c3fw7NqAmz7PCvRio1bGo8IaJVSWwMkS2n9aFIIVeDRzRZtBiOvO5/j9S+nBYgH/zjAwXuNPRa6f4LYaxVWvl9i530MGY1kbSP4RBiRjWxl3S8hQzbyPUrDflQuN1/hAcu8wCYRPh+1dCC7MJDm8UJYYLlVxym4XIBYnz1wVXv9kS7E+4c2szubBUj6+b7mdBv/qDM49JA== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=SvkVy1XmJjiyZNWkDkctsr/AiK21VdjbwGBuC/GCsgA=; b=DdE/9JXKI+RogFdlgSV8/zXZEOB8tSV+ZuCB2s9zTJtCCv/IFagTXaqsSQMrJw0uGagMqec1hLlTSiczSmX86O6JvIWKHnE3Jlimo8YeC0YO0lKkYim1eO3IU/JNZS5BaVqyv0/J6AhrIzNTLKgAK0CEKemacE5TqTxQydqn6ADdWnlWteWs99bhB7QVTSJzaAwM4netD0THT7+LRnsTTpZ7k6d0i/qYHQ+dUgHLqEGKePUnPFTCKMiBlpCFxJSA7MH4JBp/rHG7WZWelZWRSwaJIDJBc+/1WuwqZiP63nm5AYfgDXUAM6tHv8587Gf/i2hlYs76yXQOO32loDwKiA== ARC-Authentication-Results: i=2; mx.microsoft.com 1; spf=pass (sender ip is 4.158.2.129) smtp.rcpttodomain=gcc.gnu.org smtp.mailfrom=arm.com; dmarc=pass (p=none sp=none pct=100) action=none header.from=arm.com; dkim=pass (signature was verified) header.d=arm.com; arc=pass (0 oda=1 ltdi=1 spf=[1,1,smtp.mailfrom=arm.com] dkim=[1,1,header.d=arm.com] dmarc=[1,1,header.from=arm.com]) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=arm.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=SvkVy1XmJjiyZNWkDkctsr/AiK21VdjbwGBuC/GCsgA=; b=EPxz+16nkyJEIeK6Ury8Uy+/IXzOmXkZqd4H5NrKfn180YgjGbMu9slq5M/kx3aVlGwwAPWzOcMxBxxGpaoAI8TZySXB2AO2V91PvZSNp2gVs/TEKiN65JxkhC2RvIit9dQmLeBBWaNNo8FZPJhg2ug4b5bd+ML+oAt/WtXdVh8= Received: from CWLP265CA0464.GBRP265.PROD.OUTLOOK.COM (2603:10a6:400:1d4::19) by PAWPR08MB11390.eurprd08.prod.outlook.com (2603:10a6:102:512::14) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9891.15; Wed, 6 May 2026 13:02:49 +0000 Received: from AM3PEPF0000A798.eurprd04.prod.outlook.com (2603:10a6:400:1d4:cafe::48) by CWLP265CA0464.outlook.office365.com (2603:10a6:400:1d4::19) with Microsoft SMTP Server (version=TLS1_3, cipher=TLS_AES_256_GCM_SHA384) id 15.20.9891.15 via Frontend Transport; Wed, 6 May 2026 13:02:49 +0000 X-MS-Exchange-Authentication-Results: spf=pass (sender IP is 4.158.2.129) smtp.mailfrom=arm.com; dkim=pass (signature was verified) header.d=arm.com;dmarc=pass action=none header.from=arm.com; Received-SPF: Pass (protection.outlook.com: domain of arm.com designates 4.158.2.129 as permitted sender) receiver=protection.outlook.com; client-ip=4.158.2.129; helo=outbound-uk1.az.dlp.m.darktrace.com; pr=C Received: from outbound-uk1.az.dlp.m.darktrace.com (4.158.2.129) by AM3PEPF0000A798.mail.protection.outlook.com (10.167.16.103) with Microsoft SMTP Server (version=TLS1_3, cipher=TLS_AES_256_GCM_SHA384) id 15.20.9891.9 via Frontend Transport; Wed, 6 May 2026 13:02:48 +0000 ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=x+2P6ytloHAyw9FfYnyrRI5peVLQR9rk6tK8dohvKkYzO5kf4Rg6u3PjvGSmthFL/Ds2q/9vDPWKhFtp/Hue+cRfzxd+WmsUF/IMDvtIE1YSx0fIR9mD3JnzwVgtEdWQZ49zTWyaERNnFxGl8HLErSkk1jNB3S6U3FPd1yBo0a2jkmbEDk/zNQS6Ldak27Jzdj/FHHv8JD72vH/hJpNdQTvWidp7eKvIrHi4rORCrwM0HBzotKvugIX8o6RhG6aN2ukQQvAF3wYHumNp5z4FjzOsZL6nJpGjfKG7h2OErcwVOyYvstZdmMH8ehTxAd4AOW70gA8RJGoqQbW9lJo0Gw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=SvkVy1XmJjiyZNWkDkctsr/AiK21VdjbwGBuC/GCsgA=; b=o5Muy/s9aG7pYrQwL9Fs5VO0drsTsQqIAafiaO/I4uLCmGVEyw/ADlSx8mBe0/aDm+0+Ouobr9Y/Ye+ACZuBbmBQfy2eRCG4jwZEeNa/rmOLTWur9+Zffb5sP0g2dM8mA95356ZLi5weYaEFPiUfd4ZySUkaI9IGn3S9Qwi5WDthy13FeHe4pPdKKmBXkqFIGpY6tFzOvc2jxpOvrHoBDLb91CT3VfYprQML4jTcfZCHSgLm3xhw6e8Q+3hegfNAtUBNm7TAs50zkQjUPIGuU0kLAt56qgaIydYNzPszatELbR/oMKxvHXOjDTRqNd2zliGk9qEoUJBuK4ZoahiTRg== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=arm.com; dmarc=pass action=none header.from=arm.com; dkim=pass header.d=arm.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=arm.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=SvkVy1XmJjiyZNWkDkctsr/AiK21VdjbwGBuC/GCsgA=; b=EPxz+16nkyJEIeK6Ury8Uy+/IXzOmXkZqd4H5NrKfn180YgjGbMu9slq5M/kx3aVlGwwAPWzOcMxBxxGpaoAI8TZySXB2AO2V91PvZSNp2gVs/TEKiN65JxkhC2RvIit9dQmLeBBWaNNo8FZPJhg2ug4b5bd+ML+oAt/WtXdVh8= Authentication-Results-Original: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=arm.com; Received: from VI0PR08MB10392.eurprd08.prod.outlook.com (2603:10a6:800:210::10) by AS1PR08MB7452.eurprd08.prod.outlook.com (2603:10a6:20b:4dc::13) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9870.25; Wed, 6 May 2026 13:01:42 +0000 Received: from VI0PR08MB10392.eurprd08.prod.outlook.com ([fe80::cd7c:e19a:2072:75cc]) by VI0PR08MB10392.eurprd08.prod.outlook.com ([fe80::cd7c:e19a:2072:75cc%4]) with mapi id 15.20.9870.023; Wed, 6 May 2026 13:01:42 +0000 Date: Wed, 6 May 2026 14:01:40 +0100 From: Tamar Christina <tamar.christina@arm.com> To: gcc-patches@gcc.gnu.org Cc: nd@arm.com, rguenther@suse.de Subject: [PATCH] ivcannon: support aggregate ranged on loop bounds Message-ID: <patch-20512-tamar@arm.com> Content-Type: multipart/mixed; boundary="ywdWFXCtmWH4lV5j" Content-Disposition: inline X-ClientProxiedBy: PR1P264CA0142.FRAP264.PROD.OUTLOOK.COM (2603:10a6:102:346::14) To VI0PR08MB10392.eurprd08.prod.outlook.com (2603:10a6:800:210::10) MIME-Version: 1.0 X-MS-TrafficTypeDiagnostic: VI0PR08MB10392:EE_|AS1PR08MB7452:EE_|AM3PEPF0000A798:EE_|PAWPR08MB11390:EE_ X-MS-Office365-Filtering-Correlation-Id: 2920b6e3-5af8-4943-900e-08deab6fc5ed x-checkrecipientrouted: true NoDisclaimer: true X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam-Untrusted: BCL:0; ARA:13230040|366016|1800799024|376014|6049299003|56012099003|4053099003|18002099003; X-Microsoft-Antispam-Message-Info-Original: FumyrqWNnqJDL0uv3/fU0A9YBiR+qquPpzkNbC55XWiC2gv9ebYL9QtnHTybhzFbb+e+wqBzNl/shrxLGG3DouDIw98JbrsHjtCz/VkQfhRHQwWBmzVlIxVtLuifCoHhDyRQUrPrST6MnqIvZsOPAHWkSGCwfW1M/trby5DhY+y9AoD/J0e8Q3w3GfUTQviX9eHg7TjwqkZDy6K86oYqr6iRjzleVLIXCWJFE9lsR+Cg/i9Bior9vq9TUCyyazOXGBm0OioVYdWpeQfIEQznTUfBlXbyHrRki6DizPQjWcEszfUZMgPHnhSXeZ5ghYE/rklWwznSqBwBe/l34utO1M0XytFaSm1dQHZGSWcV1gLYy3xNnXcCaoipHdRYs2IvxTChi+0lkWyzyx7zooBU1EA/7Nmn7MhRm6F6K62zlgjdOc7hdSWU6hTW8Pj2UfRS6oTNutf1irCdnqEF1bySWhxn7JuSXkZrf+cB9UZowaT6bsiPVoDSEiC1JSKcLn6ROIkSr/qUPwcAK9il5uAglFu28EdxP6T/w/BeRl4KjagJuaAnNM3cLXGEVY6taT6V8QM2/Cpm/Cqao3XHdRXC3xznWKpORHZ0RUwXgQ0Ox0F0hKPxJnEBS8JCOfbMNNA+lz7o8r10GXoBBZIzxHJVPQ== X-Forefront-Antispam-Report-Untrusted: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:VI0PR08MB10392.eurprd08.prod.outlook.com; PTR:; CAT:NONE; SFS:(13230040)(366016)(1800799024)(376014)(6049299003)(56012099003)(4053099003)(18002099003); DIR:OUT; SFP:1101; X-Exchange-RoutingPolicyChecked: IzmIY9Wiqw+Hk1fM66Hv7NY0rg/gHxLEbTkwjWkQRsTplHB88Xz/7oiCPdq1pYYfW0xVvVlIZ05OvvuFmg8Za8mpcSpwDSws1yR0NkcwpeJ51101Mc4YKivoh/+UegdKmBc2BinWLuS/x2/4jazLxrU6uPXaRP45J7lkC1q9Oh7flJcfj9ZjroFdzpp8TJCTN2Yac/o2oINMK62ygaEcpwBPSZv7sDIg0g4buidFDmGPIli5nKsFfemRyOLqwijR8aNrJ6VEo8mF6O0P0p7XKRCV6AmnOSvuc4xjkCSe4D4kAS7eGLQ0rMCmDHDPxoBUQZr+AYQ9Z4VpAqOWMQiVhA== X-MS-Exchange-Transport-CrossTenantHeadersStamped: AS1PR08MB7452 X-EOPAttributedMessage: 0 X-MS-Exchange-Transport-CrossTenantHeadersStripped: AM3PEPF0000A798.eurprd04.prod.outlook.com X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id-Prvs: e6d2f12d-0ad1-4c3e-af5b-08deab6f9e74 X-Microsoft-Antispam: BCL:0; ARA:13230040|14060799003|35042699022|376014|82310400026|36860700016|1800799024|6049299003|4053099003|18002099003|56012099003; X-Microsoft-Antispam-Message-Info: XZiA48CXHcJvz3Mes8W27lZHHZy/9joTdTjxHj/lNcDcfsU+xjG2TprKYTJBAfzdyoNaFc8iDNenkHerhcMZkraV3Qn+0lmLlcB9R8M1QJxb2MC/Aszi2Cdj1znJ2qvJv/ScaxsnR79qAk6V7NWXFKl3IjTj1CG4CBfmofm0ByHiIkxBUCaad7RvXckeRZbKHv2DDj41qOaV96uQkGV/mWwtixZS1CAqeHVVXASFCO9zs2Kk9cs+IW6zd1yelBZFPFM3SXNoZfoNnpznlpU+HdyI9qAjzGmwOtBaYC7yt3LvJJ2toqxENLOU67B1ukHPtUC/P9TD7PfIYP0A5mMm94uoRG5s70feaUPq+iS2TGudWy3krBcxB80lCOdu663zEg0H3bD1vvsH6nvii8m4X2M4TVG5314y+mE6Lzz+xztQWLHsS/2f5/p+zIegs1ny6XSmcTEX6sFu9oDidYNeWMgH4gM2jHHrYNDzIdhT8hXssK6g7Zj9WVZK0Lb1Tt72d8DSz01sWEZedtpXwMl+6txZwNrRo3VNawp5K8r9cuEkC432kpnJv/5T8s+Uii+u4caVrHosi4+Mvk3kFdrNIwLVjDJBHh7sYk2pIbhp4+39irjck56e1ykpufrmxS3d7uedl0dysniQL2y0f108+XJ0H/lv2e+Xi0TDmMdcEoPSYP8Xid72fj01RJ0sUh2+ X-Forefront-Antispam-Report: CIP:4.158.2.129; CTRY:GB; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:outbound-uk1.az.dlp.m.darktrace.com; PTR:InfoDomainNonexistent; CAT:NONE; SFS:(13230040)(14060799003)(35042699022)(376014)(82310400026)(36860700016)(1800799024)(6049299003)(4053099003)(18002099003)(56012099003); DIR:OUT; SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: rSJHA+ehZ8a4H7aFgmaOXfZea4sH1gFIJKNQSHNbHr+Bc9M04epxBqyxRB9my/H8sPImTXq8ZOyex+71Ur5NByOFLBewrSro/ftYXq1AeoX0qGUtc+OYk8LJRSiN/ZJe+N441xNv+/LmhABEa4OJd+UXzyfxSYcVhscKq/ExIal9Qi//d2Qg1umX4oqAXm85gFG2v8QJD62Gok115/ODu7+M3TI0Tw24Ecprf7R3dukeB+TfohqKSIo7RGZ7f9xWtzH2OTfb99o8pmrF+VyyD7Ixtj9o+5w2zQsUqrDR71NmxbxKSdiQFSoeKDw1YybM7a5dBlFMCsMdcCO51uImZCS6MhrD3X6xm1M8BiZ6biB/2DGJAiE+uU2rGVr/PBGDLS+AfythDkjceike1+JjfCrOi7hlensaiFh9ulzovYRMTVNH/mSsVzah7fgAdLix X-OriginatorOrg: arm.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 06 May 2026 13:02:48.3040 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: 2920b6e3-5af8-4943-900e-08deab6fc5ed X-MS-Exchange-CrossTenant-Id: f34e5979-57d9-4aaa-ad4d-b122a662184d X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=f34e5979-57d9-4aaa-ad4d-b122a662184d; Ip=[4.158.2.129]; Helo=[outbound-uk1.az.dlp.m.darktrace.com] X-MS-Exchange-CrossTenant-AuthSource: AM3PEPF0000A798.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Anonymous X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: PAWPR08MB11390 X-Spam-Status: No, score=-11.4 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FORGED_SPF_HELO, GIT_PATCH_0, KAM_LOTSOFHASH, RCVD_IN_DNSWL_BLOCKED, SPF_HELO_PASS, SPF_NONE, TXREP, URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gcc-patches mailing list <gcc-patches.gcc.gnu.org> List-Unsubscribe: <https://gcc.gnu.org/mailman/options/gcc-patches>, <mailto:gcc-patches-request@gcc.gnu.org?subject=unsubscribe> List-Archive: <https://gcc.gnu.org/pipermail/gcc-patches/> List-Post: <mailto:gcc-patches@gcc.gnu.org> List-Help: <mailto:gcc-patches-request@gcc.gnu.org?subject=help> List-Subscribe: <https://gcc.gnu.org/mailman/listinfo/gcc-patches>, <mailto:gcc-patches-request@gcc.gnu.org?subject=subscribe> Errors-To: gcc-patches-bounces~patchwork=sourceware.org@gcc.gnu.org |
| Series |
ivcannon: support aggregate ranged on loop bounds
|
|
Commit Message
Tamar Christina
May 6, 2026, 1:01 p.m. UTC
The loop
void add(int N, int *__restrict a, int *__restrict b, int *__restrict c) {
for (int i = 0; i < N; i++) {
c[i] = a[i] + b[i];
if (i > 1000) {
break;
}
}
}
is an example of a loop that we have started vectorizing using early break
vectorization. However the given loop is not actually an early break loop as
the condition on the exit introduces a MAX bound on N.
This patch teaches IVcannon to identify such cases and rewrite the latch
condition from i < N to i < MIN (N, C) where C is a constant.
It then tries to find an appropriate edge to remove from the loop iteration.
Concretely instead of
.L8:
add v31.4s, v30.4s, v27.4s
add v30.4s, v30.4s, v28.4s
cmpeq p15.s, p7/z, z31.s, #0
b.any .L41
ldr q31, [x9, x4]
add w5, w5, 4
ldr q29, [x8, x4]
add v31.4s, v31.4s, v29.4s
str q31, [x6, x4]
add x4, x4, 16
cmp w7, w5
bne .L8
we now generate:
.L4:
ldr q31, [x1, x0]
ldr q30, [x2, x0]
add v30.4s, v31.4s, v30.4s
str q30, [x3, x0]
add x0, x0, 16
cmp x4, x0
bne .L4
Bootstrapped Regtested on aarch64-none-linux-gnu,
arm-none-linux-gnueabihf, x86_64-pc-linux-gnu
-m32, -m64 and no issues.
Ok for master?
Thanks,
Tamar
gcc/ChangeLog:
PR tree-optimization/113134
* tree-ssa-loop-ivcanon.cc (redundant_with_constrained_latch_exit_p,
(find_latch_constrained_niter): New.
(try_unroll_loop_completely): Explicitly check exit.
(canonicalize_loop_induction_variables): Use them.
gcc/testsuite/ChangeLog:
PR tree-optimization/113134
* gcc.dg/ivcannon-pr113134.c: New test.
---
--
Comments
On Wed, 6 May 2026, Tamar Christina wrote: > The loop > > void add(int N, int *__restrict a, int *__restrict b, int *__restrict c) { > for (int i = 0; i < N; i++) { > c[i] = a[i] + b[i]; > if (i > 1000) { > break; > } > } > } > > is an example of a loop that we have started vectorizing using early break > vectorization. However the given loop is not actually an early break loop as > the condition on the exit introduces a MAX bound on N. > > This patch teaches IVcannon to identify such cases and rewrite the latch > condition from i < N to i < MIN (N, C) where C is a constant. The pattern-matching in IVCANON looks quite ugly. loop header copying (run after ifcombine...) exposes if (i_21 == 1001) goto <bb 5>; [1.00%] else goto <bb 4>; [99.00%] <bb 4> [local count: 1004539166]: i_18 = i_21 + 1; if (N_13(D) > i_18) goto <bb 3>; [94.50%] else goto <bb 5>; [5.50%] <bb 5> [local count: 69202658]: which looks like a ifcombine opportunity to me. I recently reviewed a patch to do extra if-combining from tail merging. That said, IVCANON doesn't look like the correct place to fuse two exit tests? > It then tries to find an appropriate edge to remove from the loop iteration. > > Concretely instead of > > .L8: > add v31.4s, v30.4s, v27.4s > add v30.4s, v30.4s, v28.4s > cmpeq p15.s, p7/z, z31.s, #0 > b.any .L41 > ldr q31, [x9, x4] > add w5, w5, 4 > ldr q29, [x8, x4] > add v31.4s, v31.4s, v29.4s > str q31, [x6, x4] > add x4, x4, 16 > cmp w7, w5 > bne .L8 > > we now generate: > > .L4: > ldr q31, [x1, x0] > ldr q30, [x2, x0] > add v30.4s, v31.4s, v30.4s > str q30, [x3, x0] > add x0, x0, 16 > cmp x4, x0 > bne .L4 > > Bootstrapped Regtested on aarch64-none-linux-gnu, > arm-none-linux-gnueabihf, x86_64-pc-linux-gnu > -m32, -m64 and no issues. > Ok for master? > > Thanks, > Tamar > > gcc/ChangeLog: > > PR tree-optimization/113134 > * tree-ssa-loop-ivcanon.cc (redundant_with_constrained_latch_exit_p, > (find_latch_constrained_niter): New. > (try_unroll_loop_completely): Explicitly check exit. > (canonicalize_loop_induction_variables): Use them. > > gcc/testsuite/ChangeLog: > > PR tree-optimization/113134 > * gcc.dg/ivcannon-pr113134.c: New test. > > --- > diff --git a/gcc/testsuite/gcc.dg/ivcannon-pr113134.c b/gcc/testsuite/gcc.dg/ivcannon-pr113134.c > new file mode 100644 > index 0000000000000000000000000000000000000000..49fd231cf436211ae3779c96def8a833446ca4fd > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/ivcannon-pr113134.c > @@ -0,0 +1,13 @@ > +/* { dg-do compile } */ > +/* { dg-additional-options "-O3 -fdump-tree-ivcanon-details -std=c99" } */ > + > +void add(int N, int *__restrict a, int *__restrict b, int *__restrict c) { > + for (int i = 0; i < N; i++) { > + c[i] = a[i] + b[i]; > + if (i > 1000) { > + break; > + } > + } > +} > + > +/* { dg-final { scan-tree-dump {exit edges updated and IV rewritten to} "ivcanon" } } */ > diff --git a/gcc/tree-ssa-loop-ivcanon.cc b/gcc/tree-ssa-loop-ivcanon.cc > index fb2ef44a3d3c8504823b75747da1656ce2854bfd..beb3157db44b51bf656d8af143cc1706e322ea6b 100644 > --- a/gcc/tree-ssa-loop-ivcanon.cc > +++ b/gcc/tree-ssa-loop-ivcanon.cc > @@ -129,6 +129,133 @@ create_canonical_iv (class loop *loop, edge exit, tree niter, > update_stmt (cond); > } > > +/* Return true if exit E becomes redundant once LATCH_EXIT is rewritten with a > + constrained latch count. This is intentionally narrow: the non-exit path > + must flow directly to LATCH_EXIT in the same iteration. */ > + > +static bool > +redundant_with_constrained_latch_exit_p (class loop *loop, edge e, > + edge latch_exit, > + HOST_WIDE_INT maxiter) > +{ > + class tree_niter_desc desc; > + edge nonexit; > + gphi_iterator gpi; > + > + /* Candidate exit must not be the latch exit and must execute at most > + once per iteration. i.e. no other flow through the loop. */ > + if (e == latch_exit || !just_once_each_iteration_p (loop, e->src)) > + return false; > + > + /* The candidate exit must dominate the latch, otherwise it would be unsafe > + to elide the candidate edge and have the latch assume it's role. */ > + if (!dominated_by_p (CDI_DOMINATORS, latch_exit->src, e->src)) > + return false; > + > + nonexit = EDGE_SUCC (e->src, 0); > + if (nonexit == e) > + nonexit = EDGE_SUCC (e->src, 1); > + > + /* The condidate edge must fall through to the latch. This might be too > + strict, but it's safe for now. The reason for this is to prevent a second > + branch like a diamond shape that can reach the latch from the edge. */ > + if (nonexit->dest != latch_exit->src) > + return false; > + > + /* Since we want to use the latch for the candidate edge exit, they must have > + been going to the same destination. */ > + if (e->dest != latch_exit->dest) > + return false; > + > + /* The candidate edge must have a known, constant niters and it must be > + smaller or equal to our maximum iteration count. */ > + if (!number_of_iterations_exit (loop, e, &desc, false) > + || !integer_zerop (desc.may_be_zero) > + || TREE_CODE (desc.niter) != INTEGER_CST > + || !wi::geu_p (wi::to_widest (desc.niter), maxiter)) > + return false; > + > + /* Check that the candidate exit and the latch exit compute the same value > + otherwise rewriting the candidate exit to go through the latch is not a > + valid thing to do. */ > + for (gpi = gsi_start_phis (e->dest); !gsi_end_p (gpi); gsi_next (&gpi)) > + { > + gphi *phi = gpi.phi (); > + tree e_arg = PHI_ARG_DEF_FROM_EDGE (phi, e); > + tree latch_arg = PHI_ARG_DEF_FROM_EDGE (phi, latch_exit); > + > + if (!operand_equal_p (e_arg, latch_arg, 0)) > + return false; > + } > + > + return true; > +} > + > +/* Return a constrained latch bound for LATCH_EXIT, using only exits that > + dominate LATCH_EXIT and are executed once per iteration. The original > + latch count is stored in *LATCH_NITER_OUT. */ > + > +static tree > +find_latch_constrained_niter (class loop *loop, edge latch_exit, > + tree *latch_niter_out) > +{ > + class tree_niter_desc desc; > + > + *latch_niter_out = NULL_TREE; > + if (!number_of_iterations_exit (loop, latch_exit, &desc, false) > + || !integer_zerop (desc.may_be_zero)) > + return chrec_dont_know; > + > + tree niter = desc.niter; > + *latch_niter_out = niter; > + > + for (auto e : get_loop_exit_edges (loop)) > + { > + /* Ignore the latch edge, and the exit must dominate the latch. If it > + doesn't then the form is broken. */ > + if (e == latch_exit > + || !just_once_each_iteration_p (loop, e->src) > + || !dominated_by_p (CDI_DOMINATORS, latch_exit->src, e->src)) > + continue; > + > + /* The edge must have a non-zero iteration count. */ > + if (!number_of_iterations_exit (loop, e, &desc, false) > + || !integer_zerop (desc.may_be_zero)) > + continue; > + > + tree aniter = desc.niter; > + tree ty1 = TREE_TYPE (niter); > + tree ty2 = TREE_TYPE (aniter); > + if (ty1 != ty2) > + { > + if (TYPE_UNSIGNED (ty1) != TYPE_UNSIGNED (ty2)) > + return chrec_dont_know; > + > + if (CONSTANT_CLASS_P (niter) > + && TYPE_PRECISION (ty1) <= TYPE_PRECISION (ty2)) > + niter = fold_convert (ty2, niter); > + else if (CONSTANT_CLASS_P (aniter) > + && TYPE_PRECISION (ty2) <= TYPE_PRECISION (ty1)) > + aniter = fold_convert (ty1, aniter); > + else > + return chrec_dont_know; > + } > + > + if (TREE_CODE (aniter) != INTEGER_CST) > + continue; > + > + if (TREE_CODE (niter) != INTEGER_CST) > + { > + niter = fold_build2 (MIN_EXPR, TREE_TYPE (niter), niter, aniter); > + continue; > + } > + > + niter = tree_int_cst_lt (aniter, niter) ? aniter : niter; > + } > + > + return niter; > +} > + > /* Describe size of loop as detected by tree_estimate_loop_size. */ > struct loop_size > { > @@ -766,7 +893,7 @@ try_unroll_loop_completely (class loop *loop, > If the number of execution of loop is determined by standard induction > variable test, then EXIT and EDGE_TO_CANCEL are the two edges leaving > from the iv test. */ > - if (tree_fits_uhwi_p (niter)) > + if (exit && tree_fits_uhwi_p (niter)) > { > n_unroll = tree_to_uhwi (niter); > n_unroll_found = true; > @@ -1354,7 +1481,77 @@ canonicalize_loop_induction_variables (class loop *loop, > innermost_cunrolli_p)) > return true; > > - if ((create_iv || by_eval) > + edge latch_e = NULL; > + bool aggr_rewritten_p = false; > + if (create_iv && !single_exit (loop)) > + { > + tree latch_niter = NULL_TREE; > + tree niter_aggr = chrec_dont_know; > + > + latch_e = loop_latch_edge (loop); > + if (single_pred_p (latch_e->src)) > + { > + latch_e = single_pred_edge (latch_e->src); > + auto bb_exit = latch_e->src; > + if (EDGE_COUNT (bb_exit->succs) != 2) > + latch_e = NULL; > + else if (loop_exit_edge_p (loop, EDGE_SUCC (bb_exit, 0))) > + latch_e = EDGE_SUCC (bb_exit, 0); > + else if (loop_exit_edge_p (loop, EDGE_SUCC (bb_exit, 1))) > + latch_e = EDGE_SUCC (bb_exit, 1); > + else > + latch_e = NULL; > + } > + else > + latch_e = NULL; > + > + if (latch_e) > + { > + niter_aggr = find_latch_constrained_niter (loop, latch_e, > + &latch_niter); > + if (!niter_aggr) > + niter_aggr = chrec_dont_know; > + if (chrec_contains_undetermined (latch_niter) > + || chrec_contains_undetermined (niter_aggr) > + || operand_equal_p (latch_niter, niter_aggr, 0)) > + latch_e = NULL; > + } > + > + if (latch_e) > + { > + create_canonical_iv (loop, latch_e, niter_aggr); > + aggr_rewritten_p = true; > + > + for (auto e : get_loop_exit_edges (loop)) > + if (redundant_with_constrained_latch_exit_p (loop, e, latch_e, > + maxiter)) > + { > + gcond *cond_stmt > + = as_a <gcond *> (gsi_stmt (gsi_last_nondebug_bb > + (e->src))); > + if (e->flags & EDGE_TRUE_VALUE) > + gimple_cond_make_false (cond_stmt); > + else > + gimple_cond_make_true (cond_stmt); > + update_stmt (cond_stmt); > + } > + > + if (dump_file && (dump_flags & TDF_DETAILS)) > + { > + fprintf (dump_file, > + "Loop %d exit edges updated and IV rewritten to ", > + loop->num); > + print_generic_expr (dump_file, niter_aggr, TDF_SLIM); > + fprintf (dump_file, ".\n"); > + } > + } > + } > + > + /* Only accept the new MIN based bounds if we can actually simplify the loop, > + otherwise the MIN_EXPR just causes more instruction in loop pre-headers > + without any benefits. */ > + if (!aggr_rewritten_p > + && (create_iv || by_eval) > && niter && !chrec_contains_undetermined (niter) > && exit && just_once_each_iteration_p (loop, exit->src)) > { > @@ -1798,5 +1995,3 @@ make_pass_complete_unrolli (gcc::context *ctxt) > { > return new pass_complete_unrolli (ctxt); > } > - > - > > >
> -----Original Message----- > From: Richard Biener <rguenther@suse.de> > Sent: 08 May 2026 07:35 > To: Tamar Christina <Tamar.Christina@arm.com> > Cc: gcc-patches@gcc.gnu.org; nd <nd@arm.com> > Subject: Re: [PATCH] ivcannon: support aggregate ranged on loop bounds > > On Wed, 6 May 2026, Tamar Christina wrote: > > > The loop > > > > void add(int N, int *__restrict a, int *__restrict b, int *__restrict c) { > > for (int i = 0; i < N; i++) { > > c[i] = a[i] + b[i]; > > if (i > 1000) { > > break; > > } > > } > > } > > > > is an example of a loop that we have started vectorizing using early break > > vectorization. However the given loop is not actually an early break loop as > > the condition on the exit introduces a MAX bound on N. > > > > This patch teaches IVcannon to identify such cases and rewrite the latch > > condition from i < N to i < MIN (N, C) where C is a constant. > > The pattern-matching in IVCANON looks quite ugly. loop header copying > (run after ifcombine...) exposes > > if (i_21 == 1001) > goto <bb 5>; [1.00%] > else > goto <bb 4>; [99.00%] > > <bb 4> [local count: 1004539166]: > i_18 = i_21 + 1; > if (N_13(D) > i_18) > goto <bb 3>; [94.50%] > else > goto <bb 5>; [5.50%] > > <bb 5> [local count: 69202658]: > > which looks like a ifcombine opportunity to me. I recently reviewed > a patch to do extra if-combining from tail merging. > > That said, IVCANON doesn't look like the correct place to fuse > two exit tests? I only did so based on your own suggestion https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113134#c20 But fair enough. Tamar > > > It then tries to find an appropriate edge to remove from the loop iteration. > > > > Concretely instead of > > > > .L8: > > add v31.4s, v30.4s, v27.4s > > add v30.4s, v30.4s, v28.4s > > cmpeq p15.s, p7/z, z31.s, #0 > > b.any .L41 > > ldr q31, [x9, x4] > > add w5, w5, 4 > > ldr q29, [x8, x4] > > add v31.4s, v31.4s, v29.4s > > str q31, [x6, x4] > > add x4, x4, 16 > > cmp w7, w5 > > bne .L8 > > > > we now generate: > > > > .L4: > > ldr q31, [x1, x0] > > ldr q30, [x2, x0] > > add v30.4s, v31.4s, v30.4s > > str q30, [x3, x0] > > add x0, x0, 16 > > cmp x4, x0 > > bne .L4 > > > > Bootstrapped Regtested on aarch64-none-linux-gnu, > > arm-none-linux-gnueabihf, x86_64-pc-linux-gnu > > -m32, -m64 and no issues. > > Ok for master? > > > > Thanks, > > Tamar > > > > gcc/ChangeLog: > > > > PR tree-optimization/113134 > > * tree-ssa-loop-ivcanon.cc > (redundant_with_constrained_latch_exit_p, > > (find_latch_constrained_niter): New. > > (try_unroll_loop_completely): Explicitly check exit. > > (canonicalize_loop_induction_variables): Use them. > > > > gcc/testsuite/ChangeLog: > > > > PR tree-optimization/113134 > > * gcc.dg/ivcannon-pr113134.c: New test. > > > > --- > > diff --git a/gcc/testsuite/gcc.dg/ivcannon-pr113134.c > b/gcc/testsuite/gcc.dg/ivcannon-pr113134.c > > new file mode 100644 > > index > 0000000000000000000000000000000000000000..49fd231cf436211ae37 > 79c96def8a833446ca4fd > > --- /dev/null > > +++ b/gcc/testsuite/gcc.dg/ivcannon-pr113134.c > > @@ -0,0 +1,13 @@ > > +/* { dg-do compile } */ > > +/* { dg-additional-options "-O3 -fdump-tree-ivcanon-details -std=c99" } */ > > + > > +void add(int N, int *__restrict a, int *__restrict b, int *__restrict c) { > > + for (int i = 0; i < N; i++) { > > + c[i] = a[i] + b[i]; > > + if (i > 1000) { > > + break; > > + } > > + } > > +} > > + > > +/* { dg-final { scan-tree-dump {exit edges updated and IV rewritten to} > "ivcanon" } } */ > > diff --git a/gcc/tree-ssa-loop-ivcanon.cc b/gcc/tree-ssa-loop-ivcanon.cc > > index > fb2ef44a3d3c8504823b75747da1656ce2854bfd..beb3157db44b51bf656d > 8af143cc1706e322ea6b 100644 > > --- a/gcc/tree-ssa-loop-ivcanon.cc > > +++ b/gcc/tree-ssa-loop-ivcanon.cc > > @@ -129,6 +129,133 @@ create_canonical_iv (class loop *loop, edge exit, > tree niter, > > update_stmt (cond); > > } > > > > +/* Return true if exit E becomes redundant once LATCH_EXIT is rewritten > with a > > + constrained latch count. This is intentionally narrow: the non-exit path > > + must flow directly to LATCH_EXIT in the same iteration. */ > > + > > +static bool > > +redundant_with_constrained_latch_exit_p (class loop *loop, edge e, > > + edge latch_exit, > > + HOST_WIDE_INT maxiter) > > +{ > > + class tree_niter_desc desc; > > + edge nonexit; > > + gphi_iterator gpi; > > + > > + /* Candidate exit must not be the latch exit and must execute at most > > + once per iteration. i.e. no other flow through the loop. */ > > + if (e == latch_exit || !just_once_each_iteration_p (loop, e->src)) > > + return false; > > + > > + /* The candidate exit must dominate the latch, otherwise it would be > unsafe > > + to elide the candidate edge and have the latch assume it's role. */ > > + if (!dominated_by_p (CDI_DOMINATORS, latch_exit->src, e->src)) > > + return false; > > + > > + nonexit = EDGE_SUCC (e->src, 0); > > + if (nonexit == e) > > + nonexit = EDGE_SUCC (e->src, 1); > > + > > + /* The condidate edge must fall through to the latch. This might be too > > + strict, but it's safe for now. The reason for this is to prevent a second > > + branch like a diamond shape that can reach the latch from the edge. */ > > + if (nonexit->dest != latch_exit->src) > > + return false; > > + > > + /* Since we want to use the latch for the candidate edge exit, they must > have > > + been going to the same destination. */ > > + if (e->dest != latch_exit->dest) > > + return false; > > + > > + /* The candidate edge must have a known, constant niters and it must be > > + smaller or equal to our maximum iteration count. */ > > + if (!number_of_iterations_exit (loop, e, &desc, false) > > + || !integer_zerop (desc.may_be_zero) > > + || TREE_CODE (desc.niter) != INTEGER_CST > > + || !wi::geu_p (wi::to_widest (desc.niter), maxiter)) > > + return false; > > + > > + /* Check that the candidate exit and the latch exit compute the same value > > + otherwise rewriting the candidate exit to go through the latch is not a > > + valid thing to do. */ > > + for (gpi = gsi_start_phis (e->dest); !gsi_end_p (gpi); gsi_next (&gpi)) > > + { > > + gphi *phi = gpi.phi (); > > + tree e_arg = PHI_ARG_DEF_FROM_EDGE (phi, e); > > + tree latch_arg = PHI_ARG_DEF_FROM_EDGE (phi, latch_exit); > > + > > + if (!operand_equal_p (e_arg, latch_arg, 0)) > > + return false; > > + } > > + > > + return true; > > +} > > + > > +/* Return a constrained latch bound for LATCH_EXIT, using only exits that > > + dominate LATCH_EXIT and are executed once per iteration. The original > > + latch count is stored in *LATCH_NITER_OUT. */ > > + > > +static tree > > +find_latch_constrained_niter (class loop *loop, edge latch_exit, > > + tree *latch_niter_out) > > +{ > > + class tree_niter_desc desc; > > + > > + *latch_niter_out = NULL_TREE; > > + if (!number_of_iterations_exit (loop, latch_exit, &desc, false) > > + || !integer_zerop (desc.may_be_zero)) > > + return chrec_dont_know; > > + > > + tree niter = desc.niter; > > + *latch_niter_out = niter; > > + > > + for (auto e : get_loop_exit_edges (loop)) > > + { > > + /* Ignore the latch edge, and the exit must dominate the latch. If it > > + doesn't then the form is broken. */ > > + if (e == latch_exit > > + || !just_once_each_iteration_p (loop, e->src) > > + || !dominated_by_p (CDI_DOMINATORS, latch_exit->src, e->src)) > > + continue; > > + > > + /* The edge must have a non-zero iteration count. */ > > + if (!number_of_iterations_exit (loop, e, &desc, false) > > + || !integer_zerop (desc.may_be_zero)) > > + continue; > > + > > + tree aniter = desc.niter; > > + tree ty1 = TREE_TYPE (niter); > > + tree ty2 = TREE_TYPE (aniter); > > + if (ty1 != ty2) > > + { > > + if (TYPE_UNSIGNED (ty1) != TYPE_UNSIGNED (ty2)) > > + return chrec_dont_know; > > + > > + if (CONSTANT_CLASS_P (niter) > > + && TYPE_PRECISION (ty1) <= TYPE_PRECISION (ty2)) > > + niter = fold_convert (ty2, niter); > > + else if (CONSTANT_CLASS_P (aniter) > > + && TYPE_PRECISION (ty2) <= TYPE_PRECISION (ty1)) > > + aniter = fold_convert (ty1, aniter); > > + else > > + return chrec_dont_know; > > + } > > + > > + if (TREE_CODE (aniter) != INTEGER_CST) > > + continue; > > + > > + if (TREE_CODE (niter) != INTEGER_CST) > > + { > > + niter = fold_build2 (MIN_EXPR, TREE_TYPE (niter), niter, aniter); > > + continue; > > + } > > + > > + niter = tree_int_cst_lt (aniter, niter) ? aniter : niter; > > + } > > + > > + return niter; > > +} > > + > > /* Describe size of loop as detected by tree_estimate_loop_size. */ > > struct loop_size > > { > > @@ -766,7 +893,7 @@ try_unroll_loop_completely (class loop *loop, > > If the number of execution of loop is determined by standard induction > > variable test, then EXIT and EDGE_TO_CANCEL are the two edges leaving > > from the iv test. */ > > - if (tree_fits_uhwi_p (niter)) > > + if (exit && tree_fits_uhwi_p (niter)) > > { > > n_unroll = tree_to_uhwi (niter); > > n_unroll_found = true; > > @@ -1354,7 +1481,77 @@ canonicalize_loop_induction_variables (class > loop *loop, > > innermost_cunrolli_p)) > > return true; > > > > - if ((create_iv || by_eval) > > + edge latch_e = NULL; > > + bool aggr_rewritten_p = false; > > + if (create_iv && !single_exit (loop)) > > + { > > + tree latch_niter = NULL_TREE; > > + tree niter_aggr = chrec_dont_know; > > + > > + latch_e = loop_latch_edge (loop); > > + if (single_pred_p (latch_e->src)) > > + { > > + latch_e = single_pred_edge (latch_e->src); > > + auto bb_exit = latch_e->src; > > + if (EDGE_COUNT (bb_exit->succs) != 2) > > + latch_e = NULL; > > + else if (loop_exit_edge_p (loop, EDGE_SUCC (bb_exit, 0))) > > + latch_e = EDGE_SUCC (bb_exit, 0); > > + else if (loop_exit_edge_p (loop, EDGE_SUCC (bb_exit, 1))) > > + latch_e = EDGE_SUCC (bb_exit, 1); > > + else > > + latch_e = NULL; > > + } > > + else > > + latch_e = NULL; > > + > > + if (latch_e) > > + { > > + niter_aggr = find_latch_constrained_niter (loop, latch_e, > > + &latch_niter); > > + if (!niter_aggr) > > + niter_aggr = chrec_dont_know; > > + if (chrec_contains_undetermined (latch_niter) > > + || chrec_contains_undetermined (niter_aggr) > > + || operand_equal_p (latch_niter, niter_aggr, 0)) > > + latch_e = NULL; > > + } > > + > > + if (latch_e) > > + { > > + create_canonical_iv (loop, latch_e, niter_aggr); > > + aggr_rewritten_p = true; > > + > > + for (auto e : get_loop_exit_edges (loop)) > > + if (redundant_with_constrained_latch_exit_p (loop, e, latch_e, > > + maxiter)) > > + { > > + gcond *cond_stmt > > + = as_a <gcond *> (gsi_stmt (gsi_last_nondebug_bb > > + (e->src))); > > + if (e->flags & EDGE_TRUE_VALUE) > > + gimple_cond_make_false (cond_stmt); > > + else > > + gimple_cond_make_true (cond_stmt); > > + update_stmt (cond_stmt); > > + } > > + > > + if (dump_file && (dump_flags & TDF_DETAILS)) > > + { > > + fprintf (dump_file, > > + "Loop %d exit edges updated and IV rewritten to ", > > + loop->num); > > + print_generic_expr (dump_file, niter_aggr, TDF_SLIM); > > + fprintf (dump_file, ".\n"); > > + } > > + } > > + } > > + > > + /* Only accept the new MIN based bounds if we can actually simplify the > loop, > > + otherwise the MIN_EXPR just causes more instruction in loop pre- > headers > > + without any benefits. */ > > + if (!aggr_rewritten_p > > + && (create_iv || by_eval) > > && niter && !chrec_contains_undetermined (niter) > > && exit && just_once_each_iteration_p (loop, exit->src)) > > { > > @@ -1798,5 +1995,3 @@ make_pass_complete_unrolli (gcc::context *ctxt) > > { > > return new pass_complete_unrolli (ctxt); > > } > > - > > - > > > > > > > > -- > Richard Biener <rguenther@suse.de> > SUSE Software Solutions Germany GmbH, > Frankenstrasse 146, 90461 Nuernberg, Germany; > GF: Jochen Jaser, Andrew McDonald, Werner Knoblich; (HRB 36809, AG > Nuernberg)
On Fri, 8 May 2026, Tamar Christina wrote: > > -----Original Message----- > > From: Richard Biener <rguenther@suse.de> > > Sent: 08 May 2026 07:35 > > To: Tamar Christina <Tamar.Christina@arm.com> > > Cc: gcc-patches@gcc.gnu.org; nd <nd@arm.com> > > Subject: Re: [PATCH] ivcannon: support aggregate ranged on loop bounds > > > > On Wed, 6 May 2026, Tamar Christina wrote: > > > > > The loop > > > > > > void add(int N, int *__restrict a, int *__restrict b, int *__restrict c) { > > > for (int i = 0; i < N; i++) { > > > c[i] = a[i] + b[i]; > > > if (i > 1000) { > > > break; > > > } > > > } > > > } > > > > > > is an example of a loop that we have started vectorizing using early break > > > vectorization. However the given loop is not actually an early break loop as > > > the condition on the exit introduces a MAX bound on N. > > > > > > This patch teaches IVcannon to identify such cases and rewrite the latch > > > condition from i < N to i < MIN (N, C) where C is a constant. > > > > The pattern-matching in IVCANON looks quite ugly. loop header copying > > (run after ifcombine...) exposes > > > > if (i_21 == 1001) > > goto <bb 5>; [1.00%] > > else > > goto <bb 4>; [99.00%] > > > > <bb 4> [local count: 1004539166]: > > i_18 = i_21 + 1; > > if (N_13(D) > i_18) > > goto <bb 3>; [94.50%] > > else > > goto <bb 5>; [5.50%] > > > > <bb 5> [local count: 69202658]: > > > > which looks like a ifcombine opportunity to me. I recently reviewed > > a patch to do extra if-combining from tail merging. > > > > That said, IVCANON doesn't look like the correct place to fuse > > two exit tests? > > I only did so based on your own suggestion > https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113134#c20 > > But fair enough. I guess I thought it would be less ugly. > Tamar > > > > > > It then tries to find an appropriate edge to remove from the loop iteration. > > > > > > Concretely instead of > > > > > > .L8: > > > add v31.4s, v30.4s, v27.4s > > > add v30.4s, v30.4s, v28.4s > > > cmpeq p15.s, p7/z, z31.s, #0 > > > b.any .L41 > > > ldr q31, [x9, x4] > > > add w5, w5, 4 > > > ldr q29, [x8, x4] > > > add v31.4s, v31.4s, v29.4s > > > str q31, [x6, x4] > > > add x4, x4, 16 > > > cmp w7, w5 > > > bne .L8 > > > > > > we now generate: > > > > > > .L4: > > > ldr q31, [x1, x0] > > > ldr q30, [x2, x0] > > > add v30.4s, v31.4s, v30.4s > > > str q30, [x3, x0] > > > add x0, x0, 16 > > > cmp x4, x0 > > > bne .L4 > > > > > > Bootstrapped Regtested on aarch64-none-linux-gnu, > > > arm-none-linux-gnueabihf, x86_64-pc-linux-gnu > > > -m32, -m64 and no issues. > > > Ok for master? > > > > > > Thanks, > > > Tamar > > > > > > gcc/ChangeLog: > > > > > > PR tree-optimization/113134 > > > * tree-ssa-loop-ivcanon.cc > > (redundant_with_constrained_latch_exit_p, > > > (find_latch_constrained_niter): New. > > > (try_unroll_loop_completely): Explicitly check exit. > > > (canonicalize_loop_induction_variables): Use them. > > > > > > gcc/testsuite/ChangeLog: > > > > > > PR tree-optimization/113134 > > > * gcc.dg/ivcannon-pr113134.c: New test. > > > > > > --- > > > diff --git a/gcc/testsuite/gcc.dg/ivcannon-pr113134.c > > b/gcc/testsuite/gcc.dg/ivcannon-pr113134.c > > > new file mode 100644 > > > index > > 0000000000000000000000000000000000000000..49fd231cf436211ae37 > > 79c96def8a833446ca4fd > > > --- /dev/null > > > +++ b/gcc/testsuite/gcc.dg/ivcannon-pr113134.c > > > @@ -0,0 +1,13 @@ > > > +/* { dg-do compile } */ > > > +/* { dg-additional-options "-O3 -fdump-tree-ivcanon-details -std=c99" } */ > > > + > > > +void add(int N, int *__restrict a, int *__restrict b, int *__restrict c) { > > > + for (int i = 0; i < N; i++) { > > > + c[i] = a[i] + b[i]; > > > + if (i > 1000) { > > > + break; > > > + } > > > + } > > > +} > > > + > > > +/* { dg-final { scan-tree-dump {exit edges updated and IV rewritten to} > > "ivcanon" } } */ > > > diff --git a/gcc/tree-ssa-loop-ivcanon.cc b/gcc/tree-ssa-loop-ivcanon.cc > > > index > > fb2ef44a3d3c8504823b75747da1656ce2854bfd..beb3157db44b51bf656d > > 8af143cc1706e322ea6b 100644 > > > --- a/gcc/tree-ssa-loop-ivcanon.cc > > > +++ b/gcc/tree-ssa-loop-ivcanon.cc > > > @@ -129,6 +129,133 @@ create_canonical_iv (class loop *loop, edge exit, > > tree niter, > > > update_stmt (cond); > > > } > > > > > > +/* Return true if exit E becomes redundant once LATCH_EXIT is rewritten > > with a > > > + constrained latch count. This is intentionally narrow: the non-exit path > > > + must flow directly to LATCH_EXIT in the same iteration. */ > > > + > > > +static bool > > > +redundant_with_constrained_latch_exit_p (class loop *loop, edge e, > > > + edge latch_exit, > > > + HOST_WIDE_INT maxiter) > > > +{ > > > + class tree_niter_desc desc; > > > + edge nonexit; > > > + gphi_iterator gpi; > > > + > > > + /* Candidate exit must not be the latch exit and must execute at most > > > + once per iteration. i.e. no other flow through the loop. */ > > > + if (e == latch_exit || !just_once_each_iteration_p (loop, e->src)) > > > + return false; > > > + > > > + /* The candidate exit must dominate the latch, otherwise it would be > > unsafe > > > + to elide the candidate edge and have the latch assume it's role. */ > > > + if (!dominated_by_p (CDI_DOMINATORS, latch_exit->src, e->src)) > > > + return false; > > > + > > > + nonexit = EDGE_SUCC (e->src, 0); > > > + if (nonexit == e) > > > + nonexit = EDGE_SUCC (e->src, 1); > > > + > > > + /* The condidate edge must fall through to the latch. This might be too > > > + strict, but it's safe for now. The reason for this is to prevent a second > > > + branch like a diamond shape that can reach the latch from the edge. */ > > > + if (nonexit->dest != latch_exit->src) > > > + return false; > > > + > > > + /* Since we want to use the latch for the candidate edge exit, they must > > have > > > + been going to the same destination. */ > > > + if (e->dest != latch_exit->dest) > > > + return false; > > > + > > > + /* The candidate edge must have a known, constant niters and it must be > > > + smaller or equal to our maximum iteration count. */ > > > + if (!number_of_iterations_exit (loop, e, &desc, false) > > > + || !integer_zerop (desc.may_be_zero) > > > + || TREE_CODE (desc.niter) != INTEGER_CST > > > + || !wi::geu_p (wi::to_widest (desc.niter), maxiter)) > > > + return false; > > > + > > > + /* Check that the candidate exit and the latch exit compute the same value > > > + otherwise rewriting the candidate exit to go through the latch is not a > > > + valid thing to do. */ > > > + for (gpi = gsi_start_phis (e->dest); !gsi_end_p (gpi); gsi_next (&gpi)) > > > + { > > > + gphi *phi = gpi.phi (); > > > + tree e_arg = PHI_ARG_DEF_FROM_EDGE (phi, e); > > > + tree latch_arg = PHI_ARG_DEF_FROM_EDGE (phi, latch_exit); > > > + > > > + if (!operand_equal_p (e_arg, latch_arg, 0)) > > > + return false; > > > + } > > > + > > > + return true; > > > +} > > > + > > > +/* Return a constrained latch bound for LATCH_EXIT, using only exits that > > > + dominate LATCH_EXIT and are executed once per iteration. The original > > > + latch count is stored in *LATCH_NITER_OUT. */ > > > + > > > +static tree > > > +find_latch_constrained_niter (class loop *loop, edge latch_exit, > > > + tree *latch_niter_out) > > > +{ > > > + class tree_niter_desc desc; > > > + > > > + *latch_niter_out = NULL_TREE; > > > + if (!number_of_iterations_exit (loop, latch_exit, &desc, false) > > > + || !integer_zerop (desc.may_be_zero)) > > > + return chrec_dont_know; > > > + > > > + tree niter = desc.niter; > > > + *latch_niter_out = niter; > > > + > > > + for (auto e : get_loop_exit_edges (loop)) > > > + { > > > + /* Ignore the latch edge, and the exit must dominate the latch. If it > > > + doesn't then the form is broken. */ > > > + if (e == latch_exit > > > + || !just_once_each_iteration_p (loop, e->src) > > > + || !dominated_by_p (CDI_DOMINATORS, latch_exit->src, e->src)) > > > + continue; > > > + > > > + /* The edge must have a non-zero iteration count. */ > > > + if (!number_of_iterations_exit (loop, e, &desc, false) > > > + || !integer_zerop (desc.may_be_zero)) > > > + continue; > > > + > > > + tree aniter = desc.niter; > > > + tree ty1 = TREE_TYPE (niter); > > > + tree ty2 = TREE_TYPE (aniter); > > > + if (ty1 != ty2) > > > + { > > > + if (TYPE_UNSIGNED (ty1) != TYPE_UNSIGNED (ty2)) > > > + return chrec_dont_know; > > > + > > > + if (CONSTANT_CLASS_P (niter) > > > + && TYPE_PRECISION (ty1) <= TYPE_PRECISION (ty2)) > > > + niter = fold_convert (ty2, niter); > > > + else if (CONSTANT_CLASS_P (aniter) > > > + && TYPE_PRECISION (ty2) <= TYPE_PRECISION (ty1)) > > > + aniter = fold_convert (ty1, aniter); > > > + else > > > + return chrec_dont_know; > > > + } > > > + > > > + if (TREE_CODE (aniter) != INTEGER_CST) > > > + continue; > > > + > > > + if (TREE_CODE (niter) != INTEGER_CST) > > > + { > > > + niter = fold_build2 (MIN_EXPR, TREE_TYPE (niter), niter, aniter); > > > + continue; > > > + } > > > + > > > + niter = tree_int_cst_lt (aniter, niter) ? aniter : niter; > > > + } > > > + > > > + return niter; > > > +} > > > + > > > /* Describe size of loop as detected by tree_estimate_loop_size. */ > > > struct loop_size > > > { > > > @@ -766,7 +893,7 @@ try_unroll_loop_completely (class loop *loop, > > > If the number of execution of loop is determined by standard induction > > > variable test, then EXIT and EDGE_TO_CANCEL are the two edges leaving > > > from the iv test. */ > > > - if (tree_fits_uhwi_p (niter)) > > > + if (exit && tree_fits_uhwi_p (niter)) > > > { > > > n_unroll = tree_to_uhwi (niter); > > > n_unroll_found = true; > > > @@ -1354,7 +1481,77 @@ canonicalize_loop_induction_variables (class > > loop *loop, > > > innermost_cunrolli_p)) > > > return true; > > > > > > - if ((create_iv || by_eval) > > > + edge latch_e = NULL; > > > + bool aggr_rewritten_p = false; > > > + if (create_iv && !single_exit (loop)) > > > + { > > > + tree latch_niter = NULL_TREE; > > > + tree niter_aggr = chrec_dont_know; > > > + > > > + latch_e = loop_latch_edge (loop); > > > + if (single_pred_p (latch_e->src)) > > > + { > > > + latch_e = single_pred_edge (latch_e->src); > > > + auto bb_exit = latch_e->src; > > > + if (EDGE_COUNT (bb_exit->succs) != 2) > > > + latch_e = NULL; > > > + else if (loop_exit_edge_p (loop, EDGE_SUCC (bb_exit, 0))) > > > + latch_e = EDGE_SUCC (bb_exit, 0); > > > + else if (loop_exit_edge_p (loop, EDGE_SUCC (bb_exit, 1))) > > > + latch_e = EDGE_SUCC (bb_exit, 1); > > > + else > > > + latch_e = NULL; > > > + } > > > + else > > > + latch_e = NULL; > > > + > > > + if (latch_e) > > > + { > > > + niter_aggr = find_latch_constrained_niter (loop, latch_e, > > > + &latch_niter); > > > + if (!niter_aggr) > > > + niter_aggr = chrec_dont_know; > > > + if (chrec_contains_undetermined (latch_niter) > > > + || chrec_contains_undetermined (niter_aggr) > > > + || operand_equal_p (latch_niter, niter_aggr, 0)) > > > + latch_e = NULL; > > > + } > > > + > > > + if (latch_e) > > > + { > > > + create_canonical_iv (loop, latch_e, niter_aggr); > > > + aggr_rewritten_p = true; > > > + > > > + for (auto e : get_loop_exit_edges (loop)) > > > + if (redundant_with_constrained_latch_exit_p (loop, e, latch_e, > > > + maxiter)) > > > + { > > > + gcond *cond_stmt > > > + = as_a <gcond *> (gsi_stmt (gsi_last_nondebug_bb > > > + (e->src))); > > > + if (e->flags & EDGE_TRUE_VALUE) > > > + gimple_cond_make_false (cond_stmt); > > > + else > > > + gimple_cond_make_true (cond_stmt); > > > + update_stmt (cond_stmt); > > > + } > > > + > > > + if (dump_file && (dump_flags & TDF_DETAILS)) > > > + { > > > + fprintf (dump_file, > > > + "Loop %d exit edges updated and IV rewritten to ", > > > + loop->num); > > > + print_generic_expr (dump_file, niter_aggr, TDF_SLIM); > > > + fprintf (dump_file, ".\n"); > > > + } > > > + } > > > + } > > > + > > > + /* Only accept the new MIN based bounds if we can actually simplify the > > loop, > > > + otherwise the MIN_EXPR just causes more instruction in loop pre- > > headers > > > + without any benefits. */ > > > + if (!aggr_rewritten_p > > > + && (create_iv || by_eval) > > > && niter && !chrec_contains_undetermined (niter) > > > && exit && just_once_each_iteration_p (loop, exit->src)) > > > { > > > @@ -1798,5 +1995,3 @@ make_pass_complete_unrolli (gcc::context *ctxt) > > > { > > > return new pass_complete_unrolli (ctxt); > > > } > > > - > > > - > > > > > > > > > > > > > -- > > Richard Biener <rguenther@suse.de> > > SUSE Software Solutions Germany GmbH, > > Frankenstrasse 146, 90461 Nuernberg, Germany; > > GF: Jochen Jaser, Andrew McDonald, Werner Knoblich; (HRB 36809, AG > > Nuernberg) >
diff --git a/gcc/testsuite/gcc.dg/ivcannon-pr113134.c b/gcc/testsuite/gcc.dg/ivcannon-pr113134.c new file mode 100644 index 0000000000000000000000000000000000000000..49fd231cf436211ae3779c96def8a833446ca4fd --- /dev/null +++ b/gcc/testsuite/gcc.dg/ivcannon-pr113134.c @@ -0,0 +1,13 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-O3 -fdump-tree-ivcanon-details -std=c99" } */ + +void add(int N, int *__restrict a, int *__restrict b, int *__restrict c) { + for (int i = 0; i < N; i++) { + c[i] = a[i] + b[i]; + if (i > 1000) { + break; + } + } +} + +/* { dg-final { scan-tree-dump {exit edges updated and IV rewritten to} "ivcanon" } } */ diff --git a/gcc/tree-ssa-loop-ivcanon.cc b/gcc/tree-ssa-loop-ivcanon.cc index fb2ef44a3d3c8504823b75747da1656ce2854bfd..beb3157db44b51bf656d8af143cc1706e322ea6b 100644 --- a/gcc/tree-ssa-loop-ivcanon.cc +++ b/gcc/tree-ssa-loop-ivcanon.cc @@ -129,6 +129,133 @@ create_canonical_iv (class loop *loop, edge exit, tree niter, update_stmt (cond); } +/* Return true if exit E becomes redundant once LATCH_EXIT is rewritten with a + constrained latch count. This is intentionally narrow: the non-exit path + must flow directly to LATCH_EXIT in the same iteration. */ + +static bool +redundant_with_constrained_latch_exit_p (class loop *loop, edge e, + edge latch_exit, + HOST_WIDE_INT maxiter) +{ + class tree_niter_desc desc; + edge nonexit; + gphi_iterator gpi; + + /* Candidate exit must not be the latch exit and must execute at most + once per iteration. i.e. no other flow through the loop. */ + if (e == latch_exit || !just_once_each_iteration_p (loop, e->src)) + return false; + + /* The candidate exit must dominate the latch, otherwise it would be unsafe + to elide the candidate edge and have the latch assume it's role. */ + if (!dominated_by_p (CDI_DOMINATORS, latch_exit->src, e->src)) + return false; + + nonexit = EDGE_SUCC (e->src, 0); + if (nonexit == e) + nonexit = EDGE_SUCC (e->src, 1); + + /* The condidate edge must fall through to the latch. This might be too + strict, but it's safe for now. The reason for this is to prevent a second + branch like a diamond shape that can reach the latch from the edge. */ + if (nonexit->dest != latch_exit->src) + return false; + + /* Since we want to use the latch for the candidate edge exit, they must have + been going to the same destination. */ + if (e->dest != latch_exit->dest) + return false; + + /* The candidate edge must have a known, constant niters and it must be + smaller or equal to our maximum iteration count. */ + if (!number_of_iterations_exit (loop, e, &desc, false) + || !integer_zerop (desc.may_be_zero) + || TREE_CODE (desc.niter) != INTEGER_CST + || !wi::geu_p (wi::to_widest (desc.niter), maxiter)) + return false; + + /* Check that the candidate exit and the latch exit compute the same value + otherwise rewriting the candidate exit to go through the latch is not a + valid thing to do. */ + for (gpi = gsi_start_phis (e->dest); !gsi_end_p (gpi); gsi_next (&gpi)) + { + gphi *phi = gpi.phi (); + tree e_arg = PHI_ARG_DEF_FROM_EDGE (phi, e); + tree latch_arg = PHI_ARG_DEF_FROM_EDGE (phi, latch_exit); + + if (!operand_equal_p (e_arg, latch_arg, 0)) + return false; + } + + return true; +} + +/* Return a constrained latch bound for LATCH_EXIT, using only exits that + dominate LATCH_EXIT and are executed once per iteration. The original + latch count is stored in *LATCH_NITER_OUT. */ + +static tree +find_latch_constrained_niter (class loop *loop, edge latch_exit, + tree *latch_niter_out) +{ + class tree_niter_desc desc; + + *latch_niter_out = NULL_TREE; + if (!number_of_iterations_exit (loop, latch_exit, &desc, false) + || !integer_zerop (desc.may_be_zero)) + return chrec_dont_know; + + tree niter = desc.niter; + *latch_niter_out = niter; + + for (auto e : get_loop_exit_edges (loop)) + { + /* Ignore the latch edge, and the exit must dominate the latch. If it + doesn't then the form is broken. */ + if (e == latch_exit + || !just_once_each_iteration_p (loop, e->src) + || !dominated_by_p (CDI_DOMINATORS, latch_exit->src, e->src)) + continue; + + /* The edge must have a non-zero iteration count. */ + if (!number_of_iterations_exit (loop, e, &desc, false) + || !integer_zerop (desc.may_be_zero)) + continue; + + tree aniter = desc.niter; + tree ty1 = TREE_TYPE (niter); + tree ty2 = TREE_TYPE (aniter); + if (ty1 != ty2) + { + if (TYPE_UNSIGNED (ty1) != TYPE_UNSIGNED (ty2)) + return chrec_dont_know; + + if (CONSTANT_CLASS_P (niter) + && TYPE_PRECISION (ty1) <= TYPE_PRECISION (ty2)) + niter = fold_convert (ty2, niter); + else if (CONSTANT_CLASS_P (aniter) + && TYPE_PRECISION (ty2) <= TYPE_PRECISION (ty1)) + aniter = fold_convert (ty1, aniter); + else + return chrec_dont_know; + } + + if (TREE_CODE (aniter) != INTEGER_CST) + continue; + + if (TREE_CODE (niter) != INTEGER_CST) + { + niter = fold_build2 (MIN_EXPR, TREE_TYPE (niter), niter, aniter); + continue; + } + + niter = tree_int_cst_lt (aniter, niter) ? aniter : niter; + } + + return niter; +} + /* Describe size of loop as detected by tree_estimate_loop_size. */ struct loop_size { @@ -766,7 +893,7 @@ try_unroll_loop_completely (class loop *loop, If the number of execution of loop is determined by standard induction variable test, then EXIT and EDGE_TO_CANCEL are the two edges leaving from the iv test. */ - if (tree_fits_uhwi_p (niter)) + if (exit && tree_fits_uhwi_p (niter)) { n_unroll = tree_to_uhwi (niter); n_unroll_found = true; @@ -1354,7 +1481,77 @@ canonicalize_loop_induction_variables (class loop *loop, innermost_cunrolli_p)) return true; - if ((create_iv || by_eval) + edge latch_e = NULL; + bool aggr_rewritten_p = false; + if (create_iv && !single_exit (loop)) + { + tree latch_niter = NULL_TREE; + tree niter_aggr = chrec_dont_know; + + latch_e = loop_latch_edge (loop); + if (single_pred_p (latch_e->src)) + { + latch_e = single_pred_edge (latch_e->src); + auto bb_exit = latch_e->src; + if (EDGE_COUNT (bb_exit->succs) != 2) + latch_e = NULL; + else if (loop_exit_edge_p (loop, EDGE_SUCC (bb_exit, 0))) + latch_e = EDGE_SUCC (bb_exit, 0); + else if (loop_exit_edge_p (loop, EDGE_SUCC (bb_exit, 1))) + latch_e = EDGE_SUCC (bb_exit, 1); + else + latch_e = NULL; + } + else + latch_e = NULL; + + if (latch_e) + { + niter_aggr = find_latch_constrained_niter (loop, latch_e, + &latch_niter); + if (!niter_aggr) + niter_aggr = chrec_dont_know; + if (chrec_contains_undetermined (latch_niter) + || chrec_contains_undetermined (niter_aggr) + || operand_equal_p (latch_niter, niter_aggr, 0)) + latch_e = NULL; + } + + if (latch_e) + { + create_canonical_iv (loop, latch_e, niter_aggr); + aggr_rewritten_p = true; + + for (auto e : get_loop_exit_edges (loop)) + if (redundant_with_constrained_latch_exit_p (loop, e, latch_e, + maxiter)) + { + gcond *cond_stmt + = as_a <gcond *> (gsi_stmt (gsi_last_nondebug_bb + (e->src))); + if (e->flags & EDGE_TRUE_VALUE) + gimple_cond_make_false (cond_stmt); + else + gimple_cond_make_true (cond_stmt); + update_stmt (cond_stmt); + } + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, + "Loop %d exit edges updated and IV rewritten to ", + loop->num); + print_generic_expr (dump_file, niter_aggr, TDF_SLIM); + fprintf (dump_file, ".\n"); + } + } + } + + /* Only accept the new MIN based bounds if we can actually simplify the loop, + otherwise the MIN_EXPR just causes more instruction in loop pre-headers + without any benefits. */ + if (!aggr_rewritten_p + && (create_iv || by_eval) && niter && !chrec_contains_undetermined (niter) && exit && just_once_each_iteration_p (loop, exit->src)) { @@ -1798,5 +1995,3 @@ make_pass_complete_unrolli (gcc::context *ctxt) { return new pass_complete_unrolli (ctxt); } - -