From d21597aec91bbd41960923385f6a1feb31f14a0c Mon Sep 17 00:00:00 2001 From: Goetz Lindenmaier Date: Thu, 20 Apr 2023 08:20:41 +0000 Subject: [PATCH] 8299179: ArrayFill with store on backedge needs to reduce length by 1 Backport-of: d716ec5d3034240b7dd0aed86d9bb371bc3e5f5a --- src/hotspot/share/opto/loopTransform.cpp | 20 ++ .../loopopts/TestBackedgeLoadArrayFill.jasm | 178 ++++++++++++++ .../TestBackedgeLoadArrayFillMain.java | 231 ++++++++++++++++++ 3 files changed, 429 insertions(+) create mode 100644 test/hotspot/jtreg/compiler/loopopts/TestBackedgeLoadArrayFill.jasm create mode 100644 test/hotspot/jtreg/compiler/loopopts/TestBackedgeLoadArrayFillMain.java diff --git a/src/hotspot/share/opto/loopTransform.cpp b/src/hotspot/share/opto/loopTransform.cpp index f7e7073dd90..b6262332708 100644 --- a/src/hotspot/share/opto/loopTransform.cpp +++ b/src/hotspot/share/opto/loopTransform.cpp @@ -3947,6 +3947,19 @@ bool PhaseIdealLoop::intrinsify_fill(IdealLoopTree* lpt) { Node* len = new SubINode(head->limit(), head->init_trip()); _igvn.register_new_node_with_optimizer(len); + // If the store is on the backedge, it is not executed in the last + // iteration, and we must subtract 1 from the len. + Node* backedge = head->loopexit()->proj_out(1); + if (store->in(0) == backedge) { + len = new SubINode(len, _igvn.intcon(1)); + _igvn.register_new_node_with_optimizer(len); +#ifndef PRODUCT + if (TraceOptimizeFill) { + tty->print_cr("ArrayFill store on backedge, subtract 1 from len."); + } +#endif + } + BasicType t = store->as_Mem()->memory_type(); bool aligned = false; if (offset != NULL && head->init_trip()->is_Con()) { @@ -4049,5 +4062,12 @@ bool PhaseIdealLoop::intrinsify_fill(IdealLoopTree* lpt) { _igvn.replace_node(n, C->top()); } +#ifndef PRODUCT + if (TraceOptimizeFill) { + tty->print("ArrayFill call "); + call->dump(); + } +#endif + return true; } diff --git a/test/hotspot/jtreg/compiler/loopopts/TestBackedgeLoadArrayFill.jasm b/test/hotspot/jtreg/compiler/loopopts/TestBackedgeLoadArrayFill.jasm new file mode 100644 index 00000000000..a2bec062e91 --- /dev/null +++ b/test/hotspot/jtreg/compiler/loopopts/TestBackedgeLoadArrayFill.jasm @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +super public class TestBackedgeLoadArrayFill +{ + public Method "":"()V" + stack 2 locals 1 + { + aload_0; + invokespecial Method java/lang/Object."":"()V"; + return; + } + + static Method test_101:"()V" + stack 20 locals 20 + { + // test_002 in jasm: using try-catch + ldc 6; + istore_0; // i = 6 + ldc 25; + newarray int; + astore_1; // arr = new int[25] + HEAD: + aload_1; + iload_0; + iconst_1; + iastore; // arr[i] = 1 + // second block - the only one -> head block can be copied: one before, one on backedge + try t0; + aload_1; + iload_0; + aload_1; + iload_0; + iaload; + iastore; // arr[i] = arr[i] + goto FINALLY; + endtry t0; + catch t0 java/lang/Exception; + pop; // exception + FINALLY: + iinc 0, 1; // i++ + iload_0; + ldc 21; + if_icmplt HEAD; // if i < 21 + // write array + aload_1; + putstatic Field TestBackedgeLoadArrayFillMain.intA:"[I"; + return; + } + + static Method test_102:"()V" + stack 20 locals 20 + { + // test_002 in jasm: without try-catch + ldc 5; + istore_0; // i = 5 + ldc 25; + newarray int; + astore_1; // arr = new int[25] + HEAD: + aload_1; + iload_0; + iconst_1; + iastore; // arr[i] = 1 + goto SECOND; + // second block - the only one -> head block can be copied: one before, one on backedge + // must have some material before inc, else it is partial peeled away + // And if we set -XX:-PartialPeelLoop, then the counted loop is never detected + SECOND: + aload_1; + iload_0; + aload_1; + iload_0; + iaload; + iastore; // arr[i] = arr[i] + iinc 0, 1; // i++ + iload_0; + ldc 21; + if_icmplt HEAD; // if i < 21 + // write array + aload_1; + putstatic Field TestBackedgeLoadArrayFillMain.intA:"[I"; + return; + } + + static Method test_103:"()V" + stack 20 locals 20 + { + // test_002 in jasm: without try-catch, and second array + ldc 7; + istore_0; // i = 7 + ldc 25; + newarray int; + astore_1; // arr = new int[25] + ldc 25; + newarray int; + astore_2; // arr2 = new int[25] + HEAD: + aload_1; + iload_0; + iconst_1; + iastore; // arr[i] = 1 + goto SECOND; + // second block - the only one -> head block can be copied: one before, one on backedge + SECOND: + // we can also do the identity read-write on another array - it just has to eventually disappear + aload_2; + iload_0; + aload_2; + iload_0; + iaload; + iastore; // arr2[i] = arr2[i] + + iinc 0, 1; // i++ + iload_0; + ldc 21; + if_icmplt HEAD; // if i < 21 + // write array + aload_1; + putstatic Field TestBackedgeLoadArrayFillMain.intA:"[I"; + return; + } + + static Method test_104:"()V" + stack 20 locals 20 + { + ldc 9; + istore_0; // i = 9 + ldc 25; + newarray int; + astore_1; // arr = new int[25] + HEAD: + aload_1; + iload_0; + iconst_1; + iastore; // arr[i] = 1 + goto SECOND; + // second block - the only one -> head block can be copied: one before, one on backedge + SECOND: + // CFG leads to partial peel -> load moved into loop body, then intrinsified + iload_0; + ldc 2; + irem; + ifeq SKIP; + + SKIP: + + iinc 0, 1; // i++ + iload_0; + ldc 21; + if_icmplt HEAD; // if i < 21 + // write array + aload_1; + putstatic Field TestBackedgeLoadArrayFillMain.intA:"[I"; + return; + } +} diff --git a/test/hotspot/jtreg/compiler/loopopts/TestBackedgeLoadArrayFillMain.java b/test/hotspot/jtreg/compiler/loopopts/TestBackedgeLoadArrayFillMain.java new file mode 100644 index 00000000000..878c04ecd83 --- /dev/null +++ b/test/hotspot/jtreg/compiler/loopopts/TestBackedgeLoadArrayFillMain.java @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +/* + * @test + * @bug 8299179 + * @summary ArrayFill: if store is on backedge, last iteration is not to be executed. + * @library /test/lib + * @compile TestBackedgeLoadArrayFill.jasm + * @run main/othervm + * -XX:CompileCommand=compileonly,TestBackedgeLoadArrayFill*::test* + * -XX:-TieredCompilation -Xcomp -XX:+OptimizeFill + * TestBackedgeLoadArrayFillMain + * @run main/othervm + * -XX:CompileCommand=compileonly,TestBackedgeLoadArrayFill*::test* + * -XX:-TieredCompilation -Xcomp -XX:+OptimizeFill + * -XX:LoopUnrollLimit=1 + * TestBackedgeLoadArrayFillMain + */ + +import jdk.test.lib.Asserts; + +public class TestBackedgeLoadArrayFillMain { + static long[] longA; + static int[] intA; + static short[] shortA; + static byte[] byteA; + + static class Data { + long longValue; + int intValue; + short shortValue; + byte byteValue; + + Data(int value) { + longValue = (long) value; + intValue = (int) value; + shortValue = (short) value; + longValue = (byte) value; + } + } + + public static long longSum() { + long s = 0; + for (long v : longA) { s += v; } + return s; + } + + public static int intSum() { + int s = 0; + for (int v : intA) { s += v; } + return s; + } + + public static short shortSum() { + short s = 0; + for (short v : shortA) { s += v; } + return s; + } + + public static byte byteSum() { + byte s = 0; + for (byte v : byteA) { s += v; } + return s; + } + + static void test_001() { + // long seems not yet supported + int i = 6; + long arr[] = new long[22]; + do { + arr[i] = 1; + try { + arr[i] = arr[i]; + } catch (Exception e) { + } + } while (++i < 20); + longA = arr; + } + + static void test_002() { + // jint_fill + int i = 6; + int arr[] = new int[22]; + do { + arr[i] = 1; + try { + arr[i] = arr[i]; + } catch (Exception e) { + } + } while (++i < 20); + intA = arr; + } + + static void test_003() { + // jshort_fill + int i = 6; + short arr[] = new short[22]; + do { + // first block of loop: copied before loop, and onto backedge -> store on backedge + arr[i] = 1; + // second block of loop + try { + arr[i] = arr[i]; + } catch (Exception e) { + } + } while (++i < 20); + shortA = arr; + } + + static void test_004() { + // jbyte_fill + int i = 6; + byte arr[] = new byte[22]; + do { + arr[i] = 1; + try { + arr[i] = arr[i]; + } catch (Exception e) { + } + } while (++i < 20); + byteA = arr; + } + + static void test_005() { + // Note: currently unrolled, not intrinsified (unless -XX:LoopUnrollLimit=1) + int arr[] = new int[22]; + for (int i = 6; i < 20; i++) { + arr[i] = 1; + } + intA = arr; + } + + static void test_006() { + // Note: currently unrolled, not intrinsified (unless -XX:LoopUnrollLimit=1) + // Load in normal body, because not moved to backedge during parsing. + int i = 6; + int arr[] = new int[22]; + do { + arr[i] = 1; + } while (++i < 20); + intA = arr; + } + + static void test_007() { + int i = 6; + int arr[] = new int[22]; + do { + // still not on backedge [7,20) partial peel + arr[i] = 1; + try { int x = arr[i]; } catch (Exception e) {} + } while (++i < 20); + intA = arr; + } + + static void test_008(Data data) { + // Because of conditional in loop, at first not intrinsified, and also not unrolled. + // After unswitching both loops are intrinsified. + // I stole this idea from TestOptimizeFillWithStripMinedLoop.java + int i = 6; + int arr[] = new int[22]; + do { + arr[i] = (data == null) ? 1 : data.intValue; + } while (++i < 20); + intA = arr; + } + + static void test_009() { + // Cast to int leads to "missing use of index", not intrinsified + int arr[] = new int[22]; + for (long i = 6; i < 20; i++) { + arr[(int)i] = 1; + } + intA = arr; + } + + + public static void main(String[] strArr) { + test_001(); + Asserts.assertEQ(longSum(), (long)14); + test_002(); + Asserts.assertEQ(intSum(), 14); + test_003(); + Asserts.assertEQ(shortSum(), (short)14); + test_004(); + Asserts.assertEQ(byteSum(), (byte)14); + test_005(); + Asserts.assertEQ(intSum(), 14); + test_006(); + Asserts.assertEQ(intSum(), 14); + test_007(); + Asserts.assertEQ(intSum(), 14); + test_008(new Data(1)); + Asserts.assertEQ(intSum(), 14); + test_008(null); + Asserts.assertEQ(intSum(), 14); + test_009(); + Asserts.assertEQ(intSum(), 14); + TestBackedgeLoadArrayFill t = new TestBackedgeLoadArrayFill(); + t.test_101(); + Asserts.assertEQ(intSum(), 15); + t.test_102(); + Asserts.assertEQ(intSum(), 16); + t.test_103(); + Asserts.assertEQ(intSum(), 14); + t.test_104(); + Asserts.assertEQ(intSum(), 12); + } +} +