From bae0d5e7174fed8ffa8a30408c3cffa6e4dd3ddc Mon Sep 17 00:00:00 2001 From: TejeshR13 Date: Fri, 4 Mar 2022 22:50:32 +0000 Subject: [PATCH 001/345] 8236907: JTable added to nested panels does not paint last visible row Reviewed-by: psadhukhan, prr --- .../classes/javax/swing/TablePrintable.java | 11 +- .../javax/swing/plaf/basic/BasicTableUI.java | 17 -- .../swing/JTable/8236907/LastVisibleRow.java | 221 ++++++++++++++++++ 3 files changed, 228 insertions(+), 21 deletions(-) create mode 100644 test/jdk/javax/swing/JTable/8236907/LastVisibleRow.java diff --git a/src/java.desktop/share/classes/javax/swing/TablePrintable.java b/src/java.desktop/share/classes/javax/swing/TablePrintable.java index 4c8eb0529ab..a6de3de2b09 100644 --- a/src/java.desktop/share/classes/javax/swing/TablePrintable.java +++ b/src/java.desktop/share/classes/javax/swing/TablePrintable.java @@ -395,11 +395,14 @@ class TablePrintable implements Printable { // draw a box around the table g2d.setColor(Color.BLACK); + Rectangle bounds = table.getBounds(); + bounds.x = bounds.y = 0; + // compute the visible portion of table and draw the rect around it - Rectangle visibleBounds = clip.intersection(table.getBounds()); + Rectangle visibleBounds = clip.intersection(bounds); Point upperLeft = visibleBounds.getLocation(); - Point lowerRight = new Point(visibleBounds.x + visibleBounds.width, - visibleBounds.y + visibleBounds.height); + Point lowerRight = new Point(visibleBounds.x + visibleBounds.width - 1, + visibleBounds.y + visibleBounds.height - 1); int rMin = table.rowAtPoint(upperLeft); int rMax = table.rowAtPoint(lowerRight); @@ -410,7 +413,7 @@ class TablePrintable implements Printable { rMax = table.getRowCount(); } int rowHeight = 0; - for(int visrow = rMin; visrow < rMax; visrow++) { + for(int visrow = rMin; visrow <= rMax; visrow++) { rowHeight += table.getRowHeight(visrow); } // If PrintMode is FIT_WIDTH, then draw rect for entire column width while diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTableUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTableUI.java index 37ebb12823c..59f966df2c3 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTableUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTableUI.java @@ -1872,23 +1872,6 @@ public class BasicTableUI extends TableUI comp = comp.getParent(); } - if (comp != null && !(comp instanceof JViewport) && !(comp instanceof JScrollPane)) { - // We did rMax-1 to paint the same number of rows that are drawn on console - // otherwise 1 extra row is printed per page than that are displayed - // when there is no scrollPane and we do printing of table - // but not when rmax is already pointing to index of last row - // and if there is any selected rows - if (rMax != (table.getRowCount() - 1) && - (table.getSelectedRow() == -1)) { - // Do not decrement rMax if rMax becomes - // less than or equal to rMin - // else cells will not be painted - if (rMax - rMin > 1) { - rMax = rMax - 1; - } - } - } - // Paint the grid. paintGrid(g, rMin, rMax, cMin, cMax); diff --git a/test/jdk/javax/swing/JTable/8236907/LastVisibleRow.java b/test/jdk/javax/swing/JTable/8236907/LastVisibleRow.java new file mode 100644 index 00000000000..6ca33c96356 --- /dev/null +++ b/test/jdk/javax/swing/JTable/8236907/LastVisibleRow.java @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2022, 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 + * @key headful + * @bug 8236907 + * @summary Verifies if JTable last row is visible. + * @run main LastVisibleRow + */ + +import java.awt.BorderLayout; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.event.InputEvent; +import java.awt.image.BufferedImage; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Robot; +import java.lang.reflect.InvocationTargetException; + +import javax.print.attribute.HashPrintRequestAttributeSet; +import javax.print.attribute.PrintRequestAttributeSet; + +import javax.swing.BorderFactory; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JTable; +import javax.swing.SwingUtilities; +import javax.swing.WindowConstants; + +public class LastVisibleRow { + static JFrame frame; + static JTable table; + static Robot testRobot; + + public static void main(String[] args) throws Exception { + Point clkPoint; + try { + testRobot = new Robot(); + + SwingUtilities.invokeAndWait(new Runnable() { + + public void run() { + createAndShowGUI(); + } + }); + testRobot.delay(1000); + testRobot.waitForIdle(); + BufferedImage bufferedImageBefore = testRobot.createScreenCapture(getCaptureRect()); + testRobot.delay(1000); + testRobot.waitForIdle(); + clkPoint = getMousePosition(); + mouseEvents(clkPoint); + testRobot.waitForIdle(); + clearSelect(); + testRobot.waitForIdle(); + BufferedImage bufferedImageAfter = testRobot.createScreenCapture(getCaptureRect()); + testRobot.delay(1000); + + if (!compare(bufferedImageBefore, bufferedImageAfter)) { + throw new RuntimeException("Test Case Failed!!"); + } + } finally { + if (frame != null) SwingUtilities.invokeAndWait(() -> frame.dispose()); + } + } + + /* + * + * Get clickable screen point for particular row and column of a table + * param row Row Number + * param column Column Number + * return Point + */ + private static Point getCellClickPoint(final int row, final int column) { + Point result; + + Rectangle rect = table.getCellRect(row, column, false); + Point point = new Point(rect.x + rect.width / 2, + rect.y + rect.height / 2); + SwingUtilities.convertPointToScreen(point, table); + result = point; + + return result; + } + + private static void createAndShowGUI() { + final PrintRequestAttributeSet printReqAttr = new HashPrintRequestAttributeSet(); + printReqAttr.add(javax.print.attribute.standard.OrientationRequested.LANDSCAPE); + frame = new JFrame(); + frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); + Container contentPane = frame.getContentPane(); + JPanel centerPane = new JPanel(new BorderLayout()); + centerPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); + JPanel tablePaneContainer = new JPanel(new BorderLayout()); + JPanel tablePane = new JPanel(new BorderLayout()); + table = new JTable(new Object[][]{{"row_1_col_1", "row_1_col_2", "row_1_col_3"}, {"row_2_col_1", "row_2_col_2", "row_2_col_3"}, {"row_3_col_1", "row_3_col_2", "row_3_col_3"}, {"row_4_col_1", "row_4_col_2", "row_4_col_3"}}, new String[]{"Col1", "Col2", "Col3"}); + table.setPreferredSize(new Dimension(0, (table.getRowHeight() * 3))); + + tablePane.add(table.getTableHeader(), BorderLayout.NORTH); + tablePane.add(table, BorderLayout.CENTER); + tablePaneContainer.add(tablePane, BorderLayout.CENTER); + centerPane.add(tablePaneContainer, BorderLayout.NORTH); + contentPane.add(centerPane, BorderLayout.CENTER); + frame.setSize(400, 120); + frame.setVisible(true); + frame.setLocationRelativeTo(null); + + } + + /* + * + * mouseEvents for last row click + */ + + private static void mouseEvents(Point clkPnt) { + testRobot.mouseMove(clkPnt.x, clkPnt.y); + testRobot.delay(50); + testRobot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + testRobot.delay(50); + testRobot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + testRobot.delay(50); + } + /* + * + * getMousePosition Actions for last row click + * returns Point + * throws Exception + */ + + private static Point getMousePosition() throws Exception { + final Point[] clickPoint = new Point[1]; + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + clickPoint[0] = getCellClickPoint(2, 0); + } + }); + return clickPoint[0]; + } + + /* + * + * Clears the selected table row + * throws Exception + */ + + private static void clearSelect() throws Exception { + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + table.getSelectionModel().clearSelection(); + table.setFocusable(false); + } + }); + } + + /* + * getCaptureRect Method - To Compute the Rectangle for + * Screen Capturing the Last Row for comparison + * return Rectangle + */ + + private static Rectangle getCaptureRect() throws InterruptedException, InvocationTargetException { + final Rectangle[] captureRect = new Rectangle[1]; + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + Rectangle cellRect = table.getCellRect(2, 0, true); + Point point = new Point(cellRect.x, cellRect.y); + SwingUtilities.convertPointToScreen(point, table); + + captureRect[0] = new Rectangle(point.x, point.y, table.getColumnCount() * cellRect.width, cellRect.height); + } + }); + return captureRect[0]; + } + + /* + * Compare method - to compare two images. + * param bufferedImage1 Buffered Image Before click + * param bufferedImage2 Buffered Image After click + * return Boolean + */ + + static Boolean compare(BufferedImage bufferedImage1, BufferedImage bufferedImage2) { + if (bufferedImage1.getWidth() == bufferedImage2.getWidth() + && bufferedImage1.getHeight() == bufferedImage2.getHeight()) { + for (int x = 0; x < bufferedImage1.getWidth(); x++) { + for (int y = 0; y < bufferedImage1.getHeight(); y++) { + if (bufferedImage1.getRGB(x, y) != bufferedImage2.getRGB(x, y)) { + return false; + } + } + } + } else { + return false; + } + return true; + } +} -- GitLab From c459f8f406a99cf78814bb5722f546ae1cdb6c6f Mon Sep 17 00:00:00 2001 From: wanghaomin Date: Sat, 5 Mar 2022 00:01:49 +0000 Subject: [PATCH 002/345] 8282142: [TestCase] compiler/inlining/ResolvedClassTest.java will fail when --with-jvm-features=-compiler1 Reviewed-by: jiefu, kvn --- test/hotspot/jtreg/compiler/inlining/ResolvedClassTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/compiler/inlining/ResolvedClassTest.java b/test/hotspot/jtreg/compiler/inlining/ResolvedClassTest.java index dfeee6cdd7b..44b21cb6815 100644 --- a/test/hotspot/jtreg/compiler/inlining/ResolvedClassTest.java +++ b/test/hotspot/jtreg/compiler/inlining/ResolvedClassTest.java @@ -25,7 +25,7 @@ * @test * @bug 8279515 * - * @requires vm.flagless + * @requires vm.flagless & vm.compiler1.enabled & vm.compiler2.enabled * @modules java.base/jdk.internal.misc * @library /test/lib / * -- GitLab From 52278b80c4b68af566327cf46b53dda5eda25a51 Mon Sep 17 00:00:00 2001 From: Mikael Vidstedt Date: Sat, 5 Mar 2022 01:36:02 +0000 Subject: [PATCH 003/345] 8282694: ProblemList runtime/CommandLine/VMDeprecatedOptions.java Reviewed-by: jjg --- test/hotspot/jtreg/ProblemList.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index 518238d9c0a..5f007410904 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -102,6 +102,7 @@ runtime/os/TestTracePageSizes.java#compiler-options 8267460 linux-aarch64 runtime/os/TestTracePageSizes.java#G1 8267460 linux-aarch64 runtime/os/TestTracePageSizes.java#Parallel 8267460 linux-aarch64 runtime/os/TestTracePageSizes.java#Serial 8267460 linux-aarch64 +runtime/CommandLine/VMDeprecatedOptions.java 8282690 generic-all runtime/ErrorHandling/CreateCoredumpOnCrash.java 8267433 macosx-x64 applications/jcstress/copy.java 8229852 linux-all -- GitLab From bc42e7cbbfd586308338bfdf535c4fcab0cdbc48 Mon Sep 17 00:00:00 2001 From: Thomas Stuefe Date: Sat, 5 Mar 2022 06:37:39 +0000 Subject: [PATCH 004/345] 8282382: Report glibc malloc tunables in error reports Reviewed-by: zgu, dholmes --- src/hotspot/os/linux/os_linux.cpp | 47 ++++++++++++++++++++++++++----- 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index 18b908cfc8f..33acc06df53 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2022 SAP SE. 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 @@ -2092,6 +2093,34 @@ bool os::Linux::query_process_memory_info(os::Linux::meminfo_t* info) { return false; } +#ifdef __GLIBC__ +// For Glibc, print a one-liner with the malloc tunables. +// Most important and popular is MALLOC_ARENA_MAX, but we are +// thorough and print them all. +static void print_glibc_malloc_tunables(outputStream* st) { + static const char* var[] = { + // the new variant + "GLIBC_TUNABLES", + // legacy variants + "MALLOC_CHECK_", "MALLOC_TOP_PAD_", "MALLOC_PERTURB_", + "MALLOC_MMAP_THRESHOLD_", "MALLOC_TRIM_THRESHOLD_", + "MALLOC_MMAP_MAX_", "MALLOC_ARENA_TEST", "MALLOC_ARENA_MAX", + NULL}; + st->print("glibc malloc tunables: "); + bool printed = false; + for (int i = 0; var[i] != NULL; i ++) { + const char* const val = ::getenv(var[i]); + if (val != NULL) { + st->print("%s%s=%s", (printed ? ", " : ""), var[i], val); + printed = true; + } + } + if (!printed) { + st->print("(default)"); + } +} +#endif // __GLIBC__ + void os::Linux::print_process_memory_info(outputStream* st) { st->print_cr("Process Memory:"); @@ -2114,8 +2143,9 @@ void os::Linux::print_process_memory_info(outputStream* st) { st->print_cr("Could not open /proc/self/status to get process memory related information"); } - // Print glibc outstanding allocations. - // (note: there is no implementation of mallinfo for muslc) + // glibc only: + // - Print outstanding allocations using mallinfo + // - Print glibc tunables #ifdef __GLIBC__ size_t total_allocated = 0; bool might_have_wrapped = false; @@ -2123,9 +2153,10 @@ void os::Linux::print_process_memory_info(outputStream* st) { struct glibc_mallinfo2 mi = _mallinfo2(); total_allocated = mi.uordblks; } else if (_mallinfo != NULL) { - // mallinfo is an old API. Member names mean next to nothing and, beyond that, are int. - // So values may have wrapped around. Still useful enough to see how much glibc thinks - // we allocated. + // mallinfo is an old API. Member names mean next to nothing and, beyond that, are 32-bit signed. + // So for larger footprints the values may have wrapped around. We try to detect this here: if the + // process whole resident set size is smaller than 4G, malloc footprint has to be less than that + // and the numbers are reliable. struct glibc_mallinfo mi = _mallinfo(); total_allocated = (size_t)(unsigned)mi.uordblks; // Since mallinfo members are int, glibc values may have wrapped. Warn about this. @@ -2136,8 +2167,10 @@ void os::Linux::print_process_memory_info(outputStream* st) { total_allocated / K, might_have_wrapped ? " (may have wrapped)" : ""); } -#endif // __GLIBC__ - + // Tunables + print_glibc_malloc_tunables(st); + st->cr(); +#endif } bool os::Linux::print_ld_preload_file(outputStream* st) { -- GitLab From 974ef5542fe52f9cb8ffd8751df8a020bca503c9 Mon Sep 17 00:00:00 2001 From: Jaikiran Pai Date: Sun, 6 Mar 2022 08:16:39 +0000 Subject: [PATCH 005/345] 8282617: sun.net.www.protocol.https.HttpsClient#putInKeepAliveCache() doesn't use a lock while dealing with "inCache" field Reviewed-by: dfuchs, michaelm --- .../sun/net/www/protocol/https/HttpsClient.java | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/java.base/share/classes/sun/net/www/protocol/https/HttpsClient.java b/src/java.base/share/classes/sun/net/www/protocol/https/HttpsClient.java index 4818f945bcc..1cb435f7fdf 100644 --- a/src/java.base/share/classes/sun/net/www/protocol/https/HttpsClient.java +++ b/src/java.base/share/classes/sun/net/www/protocol/https/HttpsClient.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2022, 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 @@ -661,12 +661,17 @@ final class HttpsClient extends HttpClient @Override protected void putInKeepAliveCache() { - if (inCache) { - assert false : "Duplicate put to keep alive cache"; - return; + lock(); + try { + if (inCache) { + assert false : "Duplicate put to keep alive cache"; + return; + } + inCache = true; + kac.put(url, sslSocketFactory, this); + } finally { + unlock(); } - inCache = true; - kac.put(url, sslSocketFactory, this); } /* -- GitLab From 415bf44191632cd8dbcc158c0ff0992c0b61c3ba Mon Sep 17 00:00:00 2001 From: Masanori Yano Date: Sun, 6 Mar 2022 23:53:50 +0000 Subject: [PATCH 006/345] 8275715: D3D pipeline processes multiple PaintEvent at initial drawing Reviewed-by: prr --- .../java2d/d3d/D3DScreenUpdateManager.java | 10 ++- .../MultiPaintEventTest.java | 86 +++++++++++++++++++ 2 files changed, 92 insertions(+), 4 deletions(-) create mode 100644 test/jdk/sun/java2d/DirectX/MultiPaintEventTest/MultiPaintEventTest.java diff --git a/src/java.desktop/windows/classes/sun/java2d/d3d/D3DScreenUpdateManager.java b/src/java.desktop/windows/classes/sun/java2d/d3d/D3DScreenUpdateManager.java index d2850927120..4d65e2700ab 100644 --- a/src/java.desktop/windows/classes/sun/java2d/d3d/D3DScreenUpdateManager.java +++ b/src/java.desktop/windows/classes/sun/java2d/d3d/D3DScreenUpdateManager.java @@ -258,7 +258,7 @@ public class D3DScreenUpdateManager extends ScreenUpdateManager { if (!done && sd instanceof D3DWindowSurfaceData) { D3DWindowSurfaceData d3dw = (D3DWindowSurfaceData)sd; - if (!d3dw.isSurfaceLost() || validate(d3dw)) { + if (!d3dw.isSurfaceLost() || validate(d3dw, false)) { trackScreenSurface(d3dw); return new SunGraphics2D(sd, fgColor, bgColor, font); } @@ -452,7 +452,7 @@ public class D3DScreenUpdateManager extends ScreenUpdateManager } finally { rq.unlock(); } - } else if (!validate(sd)) { + } else if (!validate(sd, true)) { // it is possible that the validation may never // succeed, we need to detect this and replace // the d3dw surface with gdi; the replacement of @@ -474,7 +474,7 @@ public class D3DScreenUpdateManager extends ScreenUpdateManager * @return true if surface wasn't lost or if restoration was successful, * false otherwise */ - private boolean validate(D3DWindowSurfaceData sd) { + private boolean validate(D3DWindowSurfaceData sd, boolean postEvent) { if (sd.isSurfaceLost()) { try { sd.restoreSurface(); @@ -491,7 +491,9 @@ public class D3DScreenUpdateManager extends ScreenUpdateManager sd.markClean(); // since the surface was successfully restored we need to // repaint whole window to repopulate the back-buffer - repaintPeerTarget(sd.getPeer()); + if (postEvent) { + repaintPeerTarget(sd.getPeer()); + } } catch (InvalidPipeException ipe) { return false; } diff --git a/test/jdk/sun/java2d/DirectX/MultiPaintEventTest/MultiPaintEventTest.java b/test/jdk/sun/java2d/DirectX/MultiPaintEventTest/MultiPaintEventTest.java new file mode 100644 index 00000000000..69042d07635 --- /dev/null +++ b/test/jdk/sun/java2d/DirectX/MultiPaintEventTest/MultiPaintEventTest.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2021 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 + * @key headful + * @bug 8275715 + * @summary Tests that paint method is not called twice + * @run main/othervm MultiPaintEventTest + */ + +import java.awt.*; + +public class MultiPaintEventTest extends Canvas { + + private int count = 0; + private final Object lock = new Object(); + + public void paint(Graphics g) { + synchronized(lock) { + count++; + } + + int w = getWidth(); + int h = getHeight(); + + Graphics2D g2d = (Graphics2D)g; + if (count % 2 == 1) { + g2d.setColor(Color.green); + } else { + g2d.setColor(Color.red); + } + g2d.fillRect(0, 0, w, h); + } + + public int getCount() { + synchronized(lock) { + return count; + } + } + + public Dimension getPreferredSize() { + return new Dimension(400, 400); + } + + public static void main(String[] args) { + MultiPaintEventTest test = new MultiPaintEventTest(); + Frame frame = new Frame(); + frame.setUndecorated(true); + frame.add(test); + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + + try { + Thread.sleep(2000); + if (test.getCount() > 1) { + throw new RuntimeException("Processed unnecessary paint()."); + } + } catch (InterruptedException ex) { + throw new RuntimeException("Failed: Interrupted"); + } finally { + frame.dispose(); + } + } +} -- GitLab From 894ffb098c80bfeb4209038c017d01dbf53fac0f Mon Sep 17 00:00:00 2001 From: Masanori Yano Date: Mon, 7 Mar 2022 01:33:41 +0000 Subject: [PATCH 007/345] 8282713: Invalid copyright notice in new test added by JDK-8275715 Reviewed-by: dholmes --- .../java2d/DirectX/MultiPaintEventTest/MultiPaintEventTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/jdk/sun/java2d/DirectX/MultiPaintEventTest/MultiPaintEventTest.java b/test/jdk/sun/java2d/DirectX/MultiPaintEventTest/MultiPaintEventTest.java index 69042d07635..27d54af6a0b 100644 --- a/test/jdk/sun/java2d/DirectX/MultiPaintEventTest/MultiPaintEventTest.java +++ b/test/jdk/sun/java2d/DirectX/MultiPaintEventTest/MultiPaintEventTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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 -- GitLab From 6fc73f709ba9a7f4810027f6c888b63a4604f004 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Mon, 7 Mar 2022 13:52:19 +0000 Subject: [PATCH 008/345] 8282620: G1/Parallel: Constify is_in_young() predicates Reviewed-by: iwalulya, ayang --- src/hotspot/share/gc/g1/g1CollectedHeap.hpp | 2 +- src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp | 2 +- src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp | 3 +-- src/hotspot/share/gc/parallel/parallelScavengeHeap.inline.hpp | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp index 1fad936447d..6c1b60a3faa 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp @@ -1179,7 +1179,7 @@ public: size_t max_tlab_size() const override; size_t unsafe_max_tlab_alloc(Thread* ignored) const override; - inline bool is_in_young(const oop obj); + inline bool is_in_young(const oop obj) const; // Returns "true" iff the given word_size is "very large". static bool is_humongous(size_t word_size) { diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp index af778e43b13..13231603533 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp @@ -208,7 +208,7 @@ void G1CollectedHeap::register_optional_region_with_region_attr(HeapRegion* r) { _region_attr.set_optional(r->hrm_index(), r->rem_set()->is_tracked()); } -inline bool G1CollectedHeap::is_in_young(const oop obj) { +inline bool G1CollectedHeap::is_in_young(const oop obj) const { if (obj == NULL) { return false; } diff --git a/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp b/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp index cda8d7446ca..3ee16e5d8b9 100644 --- a/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp +++ b/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp @@ -184,8 +184,7 @@ class ParallelScavengeHeap : public CollectedHeap { bool is_in_reserved(const void* p) const; - bool is_in_young(oop p); // reserved part - bool is_in_old(oop p); // reserved part + bool is_in_young(const oop p) const; MemRegion reserved_region() const { return _reserved; } HeapWord* base() const { return _reserved.start(); } diff --git a/src/hotspot/share/gc/parallel/parallelScavengeHeap.inline.hpp b/src/hotspot/share/gc/parallel/parallelScavengeHeap.inline.hpp index a867c689c98..80efa1b8fc6 100644 --- a/src/hotspot/share/gc/parallel/parallelScavengeHeap.inline.hpp +++ b/src/hotspot/share/gc/parallel/parallelScavengeHeap.inline.hpp @@ -43,7 +43,7 @@ inline void ParallelScavengeHeap::invoke_scavenge() { PSScavenge::invoke(); } -inline bool ParallelScavengeHeap::is_in_young(oop p) { +inline bool ParallelScavengeHeap::is_in_young(const oop p) const { // Assumes the the old gen address range is lower than that of the young gen. bool result = cast_from_oop(p) >= young_gen()->reserved().start(); assert(result == young_gen()->is_in_reserved(p), -- GitLab From 104e3cb24b4de5512abf9f5491f9c530b26838d3 Mon Sep 17 00:00:00 2001 From: Joe Darcy Date: Mon, 7 Mar 2022 14:47:52 +0000 Subject: [PATCH 009/345] 8282696: Add constructors taking a cause to InvalidObjectException and InvalidClassException Reviewed-by: lancea --- .../java/io/InvalidClassException.java | 27 ++++++++- .../java/io/InvalidObjectException.java | 18 +++++- .../classes/java/io/ObjectInputStream.java | 44 +++++--------- .../classes/java/io/ObjectStreamClass.java | 7 ++- .../java/io/ObjectStreamException.java | 24 +++++++- .../TestIceConstructors.java | 58 +++++++++++++++++++ .../TestIoeConstructors.java | 56 ++++++++++++++++++ 7 files changed, 198 insertions(+), 36 deletions(-) create mode 100644 test/jdk/java/io/Serializable/InvalidClassException/TestIceConstructors.java create mode 100644 test/jdk/java/io/Serializable/InvalidObjectException/TestIoeConstructors.java diff --git a/src/java.base/share/classes/java/io/InvalidClassException.java b/src/java.base/share/classes/java/io/InvalidClassException.java index 6de0e70f760..be187597726 100644 --- a/src/java.base/share/classes/java/io/InvalidClassException.java +++ b/src/java.base/share/classes/java/io/InvalidClassException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2022, 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 @@ -73,9 +73,34 @@ public class InvalidClassException extends ObjectStreamException { classname = cname; } + /** + * Report an InvalidClassException for the reason and cause specified. + * + * @param reason String describing the reason for the exception. + * @param cause the cause + * @since 19 + */ + public InvalidClassException(String reason, Throwable cause) { + super(reason, cause); + } + + /** + * Report an InvalidClassException for the reason and cause specified. + * + * @param cname a String naming the invalid class. + * @param reason String describing the reason for the exception. + * @param cause the cause + * @since 19 + */ + public InvalidClassException(String cname, String reason, Throwable cause) { + super(reason, cause); + classname = cname; + } + /** * Produce the message and include the classname, if present. */ + @Override public String getMessage() { if (classname == null) return super.getMessage(); diff --git a/src/java.base/share/classes/java/io/InvalidObjectException.java b/src/java.base/share/classes/java/io/InvalidObjectException.java index c6e626b238f..a6331de45ab 100644 --- a/src/java.base/share/classes/java/io/InvalidObjectException.java +++ b/src/java.base/share/classes/java/io/InvalidObjectException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2022, 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 @@ -45,7 +45,21 @@ public class InvalidObjectException extends ObjectStreamException { * * @see ObjectInputValidation */ - public InvalidObjectException(String reason) { + public InvalidObjectException(String reason) { super(reason); } + + /** + * Constructs an {@code InvalidObjectException} with the given + * reason and cause. + * + * @param reason Detailed message explaining the reason for the failure. + * @param cause the cause + * + * @see ObjectInputValidation + * @since 19 + */ + public InvalidObjectException(String reason, Throwable cause) { + super(reason, cause); + } } diff --git a/src/java.base/share/classes/java/io/ObjectInputStream.java b/src/java.base/share/classes/java/io/ObjectInputStream.java index 714785cd117..14812fea9ad 100644 --- a/src/java.base/share/classes/java/io/ObjectInputStream.java +++ b/src/java.base/share/classes/java/io/ObjectInputStream.java @@ -1429,9 +1429,7 @@ public class ObjectInputStream event.commit(); } if (serialFilter != null && (status == null || status == ObjectInputFilter.Status.REJECTED)) { - InvalidClassException ice = new InvalidClassException("filter status: " + status); - ice.initCause(ex); - throw ice; + throw new InvalidClassException("filter status: " + status, ex); } } @@ -1996,14 +1994,10 @@ public class ObjectInputStream } catch (ClassNotFoundException ex) { resolveEx = ex; } catch (IllegalAccessError aie) { - IOException ice = new InvalidClassException(aie.getMessage()); - ice.initCause(aie); - throw ice; + throw new InvalidClassException(aie.getMessage(), aie); } catch (OutOfMemoryError memerr) { - IOException ex = new InvalidObjectException("Proxy interface limit exceeded: " + - Arrays.toString(ifaces)); - ex.initCause(memerr); - throw ex; + throw new InvalidObjectException("Proxy interface limit exceeded: " + + Arrays.toString(ifaces), memerr); } // Call filterCheck on the class before reading anything else @@ -2016,10 +2010,8 @@ public class ObjectInputStream depth++; desc.initProxy(cl, resolveEx, readClassDesc(false)); } catch (OutOfMemoryError memerr) { - IOException ex = new InvalidObjectException("Proxy interface limit exceeded: " + - Arrays.toString(ifaces)); - ex.initCause(memerr); - throw ex; + throw new InvalidObjectException("Proxy interface limit exceeded: " + + Arrays.toString(ifaces), memerr); } finally { depth--; } @@ -2050,8 +2042,8 @@ public class ObjectInputStream try { readDesc = readClassDescriptor(); } catch (ClassNotFoundException ex) { - throw (IOException) new InvalidClassException( - "failed to read class descriptor").initCause(ex); + throw new InvalidClassException("failed to read class descriptor", + ex); } Class cl = null; @@ -2221,9 +2213,8 @@ public class ObjectInputStream Enum en = Enum.valueOf((Class)cl, name); result = en; } catch (IllegalArgumentException ex) { - throw (IOException) new InvalidObjectException( - "enum constant " + name + " does not exist in " + - cl).initCause(ex); + throw new InvalidObjectException("enum constant " + + name + " does not exist in " + cl, ex); } if (!unshared) { handles.setObject(enumHandle, result); @@ -2262,9 +2253,8 @@ public class ObjectInputStream try { obj = desc.isInstantiable() ? desc.newInstance() : null; } catch (Exception ex) { - throw (IOException) new InvalidClassException( - desc.forClass().getName(), - "unable to create instance").initCause(ex); + throw new InvalidClassException(desc.forClass().getName(), + "unable to create instance", ex); } passHandle = handles.assign(unshared ? unsharedMarker : obj); @@ -2388,16 +2378,12 @@ public class ObjectInputStream try { return (Object) ctrMH.invokeExact(fieldValues.primValues, fieldValues.objValues); } catch (Exception e) { - InvalidObjectException ioe = new InvalidObjectException(e.getMessage()); - ioe.initCause(e); - throw ioe; + throw new InvalidObjectException(e.getMessage(), e); } catch (Error e) { throw e; } catch (Throwable t) { - ObjectStreamException ose = new InvalidObjectException( - "ReflectiveOperationException during deserialization"); - ose.initCause(t); - throw ose; + throw new InvalidObjectException("ReflectiveOperationException " + + "during deserialization", t); } } diff --git a/src/java.base/share/classes/java/io/ObjectStreamClass.java b/src/java.base/share/classes/java/io/ObjectStreamClass.java index 86ed2b839d1..c41e8b1744a 100644 --- a/src/java.base/share/classes/java/io/ObjectStreamClass.java +++ b/src/java.base/share/classes/java/io/ObjectStreamClass.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2022, 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 @@ -709,8 +709,9 @@ public class ObjectStreamClass implements Serializable { try { fields[i] = new ObjectStreamField(fname, signature, false); } catch (RuntimeException e) { - throw (IOException) new InvalidClassException(name, - "invalid descriptor for field " + fname).initCause(e); + throw new InvalidClassException(name, + "invalid descriptor for field " + + fname, e); } } computeFieldOffsets(); diff --git a/src/java.base/share/classes/java/io/ObjectStreamException.java b/src/java.base/share/classes/java/io/ObjectStreamException.java index 779cc0627d3..a7d19cdc1fa 100644 --- a/src/java.base/share/classes/java/io/ObjectStreamException.java +++ b/src/java.base/share/classes/java/io/ObjectStreamException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2022, 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 @@ -44,10 +44,32 @@ public abstract class ObjectStreamException extends IOException { super(message); } + /** + * Create an ObjectStreamException with the specified message and + * cause. + * + * @param message the detailed message for the exception + * @param cause the cause + * @since 19 + */ + protected ObjectStreamException(String message, Throwable cause) { + super(message, cause); + } + /** * Create an ObjectStreamException. */ protected ObjectStreamException() { super(); } + + /** + * Create an ObjectStreamException with the specified cause. + * + * @param cause the cause + * @since 19 + */ + protected ObjectStreamException(Throwable cause) { + super(cause); + } } diff --git a/test/jdk/java/io/Serializable/InvalidClassException/TestIceConstructors.java b/test/jdk/java/io/Serializable/InvalidClassException/TestIceConstructors.java new file mode 100644 index 00000000000..991ec32ec8f --- /dev/null +++ b/test/jdk/java/io/Serializable/InvalidClassException/TestIceConstructors.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2022, 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 8282696 + * @summary Verify message and cause handling of InvalidClassException + */ +import java.io.*; +import java.util.Objects; + +public class TestIceConstructors { + public static void main(String... args) { + String reason = "reason"; + Throwable cause = new RuntimeException(); + + testException(new InvalidClassException(reason), + reason, null); + testException(new InvalidClassException(reason, cause), + reason, cause); + testException(new InvalidClassException("prefix", reason, cause), + "prefix" + "; " + reason, cause); + } + + private static void testException(InvalidClassException ice, + String expectedMessage, + Throwable expectedCause) { + var message = ice.getMessage(); + if (!Objects.equals(message, expectedMessage)) { + throw new RuntimeException("Unexpected message " + message); + } + + var cause = ice.getCause(); + if (cause != expectedCause) { + throw new RuntimeException("Unexpected cause"); + } + } +} diff --git a/test/jdk/java/io/Serializable/InvalidObjectException/TestIoeConstructors.java b/test/jdk/java/io/Serializable/InvalidObjectException/TestIoeConstructors.java new file mode 100644 index 00000000000..d3a952e62df --- /dev/null +++ b/test/jdk/java/io/Serializable/InvalidObjectException/TestIoeConstructors.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2022, 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 8282696 + * @summary Verify message and cause handling of InvalidObjectException + */ +import java.io.*; +import java.util.Objects; + +public class TestIoeConstructors { + public static void main(String... args) { + String reason = "reason"; + Throwable cause = new RuntimeException(); + + testException(new InvalidObjectException(reason), + reason, null); + testException(new InvalidObjectException(reason, cause), + reason, cause); + } + + private static void testException(InvalidObjectException ioe, + String expectedMessage, + Throwable expectedCause) { + var message = ioe.getMessage(); + if (!Objects.equals(message, expectedMessage)) { + throw new RuntimeException("Unexpected message " + message); + } + + var cause = ioe.getCause(); + if (cause != expectedCause) { + throw new RuntimeException("Unexpected cause"); + } + } +} -- GitLab From e544e354a425a242f23cee1049d6ba31b30740e0 Mon Sep 17 00:00:00 2001 From: Ivan Walulya Date: Mon, 7 Mar 2022 15:05:42 +0000 Subject: [PATCH 010/345] 8282621: G1: G1SegmentedArray remove unnecessary template parameter Reviewed-by: kbarrett, tschatzl --- src/hotspot/share/gc/g1/g1CardSetMemory.cpp | 54 ++++++++++-------- src/hotspot/share/gc/g1/g1CardSetMemory.hpp | 56 ++++--------------- .../share/gc/g1/g1CardSetMemory.inline.hpp | 13 ++--- src/hotspot/share/gc/g1/g1SegmentedArray.hpp | 2 +- .../share/gc/g1/g1SegmentedArray.inline.hpp | 42 +++++++------- 5 files changed, 70 insertions(+), 97 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1CardSetMemory.cpp b/src/hotspot/share/gc/g1/g1CardSetMemory.cpp index 24b482ac597..b68c50b5cb1 100644 --- a/src/hotspot/share/gc/g1/g1CardSetMemory.cpp +++ b/src/hotspot/share/gc/g1/g1CardSetMemory.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2022, 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 @@ -30,10 +30,9 @@ #include "runtime/atomic.hpp" #include "utilities/ostream.hpp" -template -G1CardSetAllocator::G1CardSetAllocator(const char* name, - const G1CardSetAllocOptions* alloc_options, - G1CardSetFreeList* free_segment_list) : +G1CardSetAllocator::G1CardSetAllocator(const char* name, + const G1CardSetAllocOptions* alloc_options, + G1CardSetFreeList* free_segment_list) : _segmented_array(alloc_options, free_segment_list), _free_slots_list(name, &_segmented_array) { @@ -41,26 +40,38 @@ G1CardSetAllocator::G1CardSetAllocator(const char* name, assert(slot_size >= sizeof(G1CardSetContainer), "Slot instance size %u for allocator %s too small", slot_size, name); } -template -G1CardSetAllocator::~G1CardSetAllocator() { +G1CardSetAllocator::~G1CardSetAllocator() { drop_all(); } -template -void G1CardSetAllocator::free(Slot* slot) { +void G1CardSetAllocator::free(void* slot) { assert(slot != nullptr, "precondition"); - slot->~Slot(); _free_slots_list.release(slot); } -template -void G1CardSetAllocator::drop_all() { +void G1CardSetAllocator::drop_all() { _free_slots_list.reset(); _segmented_array.drop_all(); } -template -void G1CardSetAllocator::print(outputStream* os) { +size_t G1CardSetAllocator::mem_size() const { + return sizeof(*this) + + _segmented_array.num_segments() * sizeof(G1CardSetSegment) + + _segmented_array.num_available_slots() * _segmented_array.slot_size(); +} + +size_t G1CardSetAllocator::wasted_mem_size() const { + uint num_wasted_slots = _segmented_array.num_available_slots() - + _segmented_array.num_allocated_slots() - + (uint)_free_slots_list.pending_count(); + return num_wasted_slots * _segmented_array.slot_size(); +} + +uint G1CardSetAllocator::num_segments() const { + return _segmented_array.num_segments(); +} + +void G1CardSetAllocator::print(outputStream* os) { uint num_allocated_slots = _segmented_array.num_allocated_slots(); uint num_available_slots = _segmented_array.num_available_slots(); uint highest = _segmented_array.first_array_segment() != nullptr @@ -82,13 +93,13 @@ void G1CardSetAllocator::print(outputStream* os) { G1CardSetMemoryManager::G1CardSetMemoryManager(G1CardSetConfiguration* config, G1CardSetFreePool* free_list_pool) : _config(config) { - _allocators = NEW_C_HEAP_ARRAY(G1CardSetAllocator, + _allocators = NEW_C_HEAP_ARRAY(G1CardSetAllocator, _config->num_mem_object_types(), mtGC); for (uint i = 0; i < num_mem_object_types(); i++) { - new (&_allocators[i]) G1CardSetAllocator(_config->mem_object_type_name_str(i), - _config->mem_object_alloc_options(i), - free_list_pool->free_list(i)); + new (&_allocators[i]) G1CardSetAllocator(_config->mem_object_type_name_str(i), + _config->mem_object_alloc_options(i), + free_list_pool->free_list(i)); } } @@ -106,7 +117,7 @@ G1CardSetMemoryManager::~G1CardSetMemoryManager() { void G1CardSetMemoryManager::free(uint type, void* value) { assert(type < num_mem_object_types(), "must be"); - _allocators[type].free((G1CardSetContainer*)value); + _allocators[type].free(value); } void G1CardSetMemoryManager::flush() { @@ -127,9 +138,8 @@ size_t G1CardSetMemoryManager::mem_size() const { for (uint i = 0; i < num_mem_object_types(); i++) { result += _allocators[i].mem_size(); } - return sizeof(*this) - - (sizeof(G1CardSetAllocator) * num_mem_object_types()) + - result; + return sizeof(*this) + result - + (sizeof(G1CardSetAllocator) * num_mem_object_types()); } size_t G1CardSetMemoryManager::wasted_mem_size() const { diff --git a/src/hotspot/share/gc/g1/g1CardSetMemory.hpp b/src/hotspot/share/gc/g1/g1CardSetMemory.hpp index c455706a616..c2663b41cf1 100644 --- a/src/hotspot/share/gc/g1/g1CardSetMemory.hpp +++ b/src/hotspot/share/gc/g1/g1CardSetMemory.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2022, 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 @@ -62,37 +62,12 @@ typedef G1SegmentedArraySegment G1CardSetSegment; typedef G1SegmentedArrayFreeList G1CardSetFreeList; -// Arena-like allocator for (card set) heap memory objects (Slot slots). +// Arena-like allocator for (card set) heap memory objects. // -// Allocation and deallocation in the first phase on G1CardSetContainer basis -// may occur by multiple threads at once. -// -// Allocation occurs from an internal free list of G1CardSetContainers first, -// only then trying to bump-allocate from the current G1CardSetSegment. If there is -// none, this class allocates a new G1CardSetSegment (allocated from the C heap, -// asking the G1CardSetAllocOptions instance about sizes etc) and uses that one. -// -// The SegmentStack free list is a linked list of G1CardSetContainers -// within all G1CardSetSegment instances allocated so far. It uses a separate -// pending list and global synchronization to avoid the ABA problem when the -// user frees a memory object. -// -// The class also manages a few counters for statistics using atomic operations. -// Their values are only consistent within each other with extra global -// synchronization. -// -// Since it is expected that every CardSet (and in extension each region) has its -// own set of allocators, there is intentionally no padding between them to save -// memory. -template +// Allocation occurs from an internal free list of objects first. If the free list is +// empty then tries to allocate from the G1SegmentedArray. class G1CardSetAllocator { - // G1CardSetSegment management. - - typedef G1SegmentedArray SegmentedArray; - // G1CardSetContainer slot management within the G1CardSetSegments allocated - // by this allocator. - - SegmentedArray _segmented_array; + G1SegmentedArray _segmented_array; FreeListAllocator _free_slots_list; public: @@ -101,27 +76,18 @@ public: G1CardSetFreeList* free_segment_list); ~G1CardSetAllocator(); - Slot* allocate(); - void free(Slot* slot); + void* allocate(); + void free(void* slot); // Deallocate all segments to the free segment list and reset this allocator. Must // be called in a globally synchronized area. void drop_all(); - size_t mem_size() const { - return sizeof(*this) + - _segmented_array.num_segments() * sizeof(G1CardSetSegment) + - _segmented_array.num_available_slots() * _segmented_array.slot_size(); - } + size_t mem_size() const; - size_t wasted_mem_size() const { - uint num_wasted_slots = _segmented_array.num_available_slots() - - _segmented_array.num_allocated_slots() - - (uint)_free_slots_list.pending_count(); - return num_wasted_slots * _segmented_array.slot_size(); - } + size_t wasted_mem_size() const; - inline uint num_segments() { return _segmented_array.num_segments(); } + uint num_segments() const; void print(outputStream* os); }; @@ -131,7 +97,7 @@ typedef G1SegmentedArrayFreePool G1CardSetFreePool; class G1CardSetMemoryManager : public CHeapObj { G1CardSetConfiguration* _config; - G1CardSetAllocator* _allocators; + G1CardSetAllocator* _allocators; uint num_mem_object_types() const; public: diff --git a/src/hotspot/share/gc/g1/g1CardSetMemory.inline.hpp b/src/hotspot/share/gc/g1/g1CardSetMemory.inline.hpp index 7d75f86bb7d..bdf69e227df 100644 --- a/src/hotspot/share/gc/g1/g1CardSetMemory.inline.hpp +++ b/src/hotspot/share/gc/g1/g1CardSetMemory.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2022, 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 @@ -26,16 +26,13 @@ #define SHARE_GC_G1_G1CARDSETMEMORY_INLINE_HPP #include "gc/g1/g1CardSetMemory.hpp" -#include "gc/g1/g1CardSetContainers.hpp" -#include "gc/g1/g1SegmentedArray.inline.hpp" -#include "utilities/ostream.hpp" - #include "gc/g1/g1CardSetContainers.inline.hpp" +#include "gc/g1/g1SegmentedArray.inline.hpp" #include "utilities/globalCounter.inline.hpp" +#include "utilities/ostream.hpp" -template -Slot* G1CardSetAllocator::allocate() { - Slot* slot = ::new (_free_slots_list.allocate()) Slot(); +inline void* G1CardSetAllocator::allocate() { + void* slot = _free_slots_list.allocate(); assert(slot != nullptr, "must be"); return slot; } diff --git a/src/hotspot/share/gc/g1/g1SegmentedArray.hpp b/src/hotspot/share/gc/g1/g1SegmentedArray.hpp index bddb6af15fe..6c73e9855cc 100644 --- a/src/hotspot/share/gc/g1/g1SegmentedArray.hpp +++ b/src/hotspot/share/gc/g1/g1SegmentedArray.hpp @@ -181,7 +181,7 @@ public: // The class also manages a few counters for statistics using atomic operations. // Their values are only consistent within each other with extra global // synchronization. -template +template class G1SegmentedArray : public FreeListConfig { // G1SegmentedArrayAllocOptions provides parameters for allocation segment // sizing and expansion. diff --git a/src/hotspot/share/gc/g1/g1SegmentedArray.inline.hpp b/src/hotspot/share/gc/g1/g1SegmentedArray.inline.hpp index 51b778d82f0..69c3526e58d 100644 --- a/src/hotspot/share/gc/g1/g1SegmentedArray.inline.hpp +++ b/src/hotspot/share/gc/g1/g1SegmentedArray.inline.hpp @@ -115,8 +115,8 @@ void G1SegmentedArrayFreeList::free_all() { Atomic::sub(&_mem_size, mem_size_freed, memory_order_relaxed); } -template -G1SegmentedArraySegment* G1SegmentedArray::create_new_segment(G1SegmentedArraySegment* const prev) { +template +G1SegmentedArraySegment* G1SegmentedArray::create_new_segment(G1SegmentedArraySegment* const prev) { // Take an existing segment if available. G1SegmentedArraySegment* next = _free_segment_list->get(); if (next == nullptr) { @@ -125,7 +125,7 @@ G1SegmentedArraySegment* G1SegmentedArray::create_new_segment( next = new G1SegmentedArraySegment(slot_size(), num_slots, prev); } else { assert(slot_size() == next->slot_size() , - "Mismatch %d != %d Slot %zu", slot_size(), next->slot_size(), sizeof(Slot)); + "Mismatch %d != %d", slot_size(), next->slot_size()); next->reset(prev); } @@ -148,14 +148,14 @@ G1SegmentedArraySegment* G1SegmentedArray::create_new_segment( } } -template -uint G1SegmentedArray::slot_size() const { +template +uint G1SegmentedArray::slot_size() const { return _alloc_options->slot_size(); } -template -G1SegmentedArray::G1SegmentedArray(const G1SegmentedArrayAllocOptions* alloc_options, - G1SegmentedArrayFreeList* free_segment_list) : +template +G1SegmentedArray::G1SegmentedArray(const G1SegmentedArrayAllocOptions* alloc_options, + G1SegmentedArrayFreeList* free_segment_list) : _alloc_options(alloc_options), _first(nullptr), _last(nullptr), @@ -167,13 +167,13 @@ G1SegmentedArray::G1SegmentedArray(const G1SegmentedArrayAllocOption assert(_free_segment_list != nullptr, "precondition!"); } -template -G1SegmentedArray::~G1SegmentedArray() { +template +G1SegmentedArray::~G1SegmentedArray() { drop_all(); } -template -void G1SegmentedArray::drop_all() { +template +void G1SegmentedArray::drop_all() { G1SegmentedArraySegment* cur = Atomic::load_acquire(&_first); if (cur != nullptr) { @@ -209,8 +209,8 @@ void G1SegmentedArray::drop_all() { _num_allocated_slots = 0; } -template -void* G1SegmentedArray::allocate() { +template +void* G1SegmentedArray::allocate() { assert(slot_size() > 0, "instance size not set."); G1SegmentedArraySegment* cur = Atomic::load_acquire(&_first); @@ -219,7 +219,7 @@ void* G1SegmentedArray::allocate() { } while (true) { - Slot* slot = (Slot*)cur->get_new_slot(); + void* slot = cur->get_new_slot(); if (slot != nullptr) { Atomic::inc(&_num_allocated_slots, memory_order_relaxed); guarantee(is_aligned(slot, _alloc_options->slot_alignment()), @@ -232,8 +232,8 @@ void* G1SegmentedArray::allocate() { } } -template -inline uint G1SegmentedArray::num_segments() const { +template +inline uint G1SegmentedArray::num_segments() const { return Atomic::load(&_num_segments); } @@ -251,17 +251,17 @@ public: } }; -template -uint G1SegmentedArray::calculate_length() const { +template +uint G1SegmentedArray::calculate_length() const { LengthClosure closure; iterate_segments(closure); return closure.length(); } #endif -template +template template -void G1SegmentedArray::iterate_segments(SegmentClosure& closure) const { +void G1SegmentedArray::iterate_segments(SegmentClosure& closure) const { G1SegmentedArraySegment* cur = Atomic::load_acquire(&_first); assert((cur != nullptr) == (_last != nullptr), -- GitLab From 8e70f4c3dca4cefe813c5b0fd39c386230ca2fd7 Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Mon, 7 Mar 2022 15:23:10 +0000 Subject: [PATCH 011/345] 8282224: Correct TIG::bang_stack_shadow_pages comments Reviewed-by: coleenp --- src/hotspot/cpu/x86/templateInterpreterGenerator_x86.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hotspot/cpu/x86/templateInterpreterGenerator_x86.cpp b/src/hotspot/cpu/x86/templateInterpreterGenerator_x86.cpp index ca7bcd8e50e..7177d7ca34c 100644 --- a/src/hotspot/cpu/x86/templateInterpreterGenerator_x86.cpp +++ b/src/hotspot/cpu/x86/templateInterpreterGenerator_x86.cpp @@ -755,8 +755,8 @@ void TemplateInterpreterGenerator::bang_stack_shadow_pages(bool native_call) { __ bang_stack_with_offset(p*page_size); } - // Record a new watermark, unless the update is above the safe limit. - // Otherwise, the next time around a check above would pass the safe limit. + // Record the new watermark, but only if update is above the safe limit. + // Otherwise, the next time around the check above would pass the safe limit. __ cmpptr(rsp, Address(thread, JavaThread::shadow_zone_safe_limit())); __ jccb(Assembler::belowEqual, L_done); __ movptr(Address(thread, JavaThread::shadow_zone_growth_watermark()), rsp); -- GitLab From f0995abe62b81cf9c96cc07caa0ac27d00c96ff1 Mon Sep 17 00:00:00 2001 From: Lance Andersen Date: Mon, 7 Mar 2022 16:10:31 +0000 Subject: [PATCH 012/345] 8280404: Unexpected exception thrown when CEN file entry comment length is not valid Reviewed-by: alanb --- .../share/classes/java/util/zip/ZipFile.java | 11 +- .../zip/ZipFile/InvalidCommentLengthTest.java | 345 ++++++++++++++++++ 2 files changed, 355 insertions(+), 1 deletion(-) create mode 100644 test/jdk/java/util/zip/ZipFile/InvalidCommentLengthTest.java diff --git a/src/java.base/share/classes/java/util/zip/ZipFile.java b/src/java.base/share/classes/java/util/zip/ZipFile.java index b89de2cef62..8538b4aef89 100644 --- a/src/java.base/share/classes/java/util/zip/ZipFile.java +++ b/src/java.base/share/classes/java/util/zip/ZipFile.java @@ -1206,8 +1206,17 @@ public class ZipFile implements ZipConstants, Closeable { entries[index++] = hash; entries[index++] = next; entries[index ] = pos; + // Validate comment if it exists + // if the bytes representing the comment cannot be converted to + // a String via zcp.toString, an Exception will be thrown + int clen = CENCOM(cen, pos); + if (clen > 0) { + int elen = CENEXT(cen, pos); + int start = entryPos + nlen + elen; + zcp.toString(cen, start, clen); + } } catch (Exception e) { - zerror("invalid CEN header (bad entry name)"); + zerror("invalid CEN header (bad entry name or comment)"); } return nlen; } diff --git a/test/jdk/java/util/zip/ZipFile/InvalidCommentLengthTest.java b/test/jdk/java/util/zip/ZipFile/InvalidCommentLengthTest.java new file mode 100644 index 00000000000..1910107f0d3 --- /dev/null +++ b/test/jdk/java/util/zip/ZipFile/InvalidCommentLengthTest.java @@ -0,0 +1,345 @@ +/* + * Copyright (c) 2022, 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. + * + */ + +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.jar.JarFile; +import java.util.zip.ZipEntry; +import java.util.zip.ZipException; +import java.util.zip.ZipFile; + +import static org.testng.Assert.*; + +/** + * @test + * @bug 8280404 + * @summary Validate that Zip/JarFile will throw a ZipException when the CEN + * comment length field contains an incorrect value + * @run testng/othervm InvalidCommentLengthTest + */ +public class InvalidCommentLengthTest { + + // Name used to create a JAR with an invalid comment length + public static final Path INVALID_CEN_COMMENT_LENGTH_JAR = + Path.of("Invalid-CEN-Comment-Length.jar"); + // Name used to create a JAR with a valid comment length + public static final Path VALID_CEN_COMMENT_LENGTH_JAR = + Path.of("Valid-CEN-Comment-Length.jar"); + // Zip/Jar CEN file header entry that will be modified + public static final String META_INF_MANIFEST_MF = "META-INF/MANIFEST.MF"; + // Expected ZipException message when the comment length corrupts the + // Zip/Jar file + public static final String INVALID_CEN_HEADER_BAD_ENTRY_NAME_OR_COMMENT = + "invalid CEN header (bad entry name or comment)"; + + /** + * Byte array representing a valid jar file prior modifying the comment length + * entry in a CEN file header. + * The "Valid-CEN-Comment-Length.jar" jar file was created via: + *
+     *     {@code
+     *     jar cvf Valid-CEN-Comment-Length.jar Hello.txt Tennis.txt BruceWayne.txt
+     *     added manifest
+     *     adding: Hello.txt(in = 12) (out= 14)(deflated -16%)
+     *     adding: Tennis.txt(in = 53) (out= 53)(deflated 0%)
+     *     adding: BruceWayne.txt(in = 12) (out= 14)(deflated -16%)
+     *     }
+     * 
+ * Its contents are: + *
+     *     {@code
+     *     jar tvf Valid-CEN-Comment-Length.jar
+     *      0 Wed Mar 02 06:39:24 EST 2022 META-INF/
+     *     66 Wed Mar 02 06:39:24 EST 2022 META-INF/MANIFEST.MF
+     *     12 Wed Mar 02 06:39:06 EST 2022 Hello.txt
+     *     53 Wed Mar 02 13:04:48 EST 2022 Tennis.txt
+     *     12 Wed Mar 02 15:15:34 EST 2022 BruceWayne.txt
+     *     }
+     * 
+ * The ByteArray was created by: + *
+     *  {@code
+     *     var jar = Files.readAllBytes("Valid-CEN-Comment-Length.jar");
+     *     var validEntryName = createByteArray(fooJar,
+     *           "VALID_ZIP_WITH_NO_COMMENTS_BYTES");
+     *  }
+     * 
+ */ + public static byte[] VALID_ZIP_WITH_NO_COMMENTS_BYTES = { + (byte) 0x50, (byte) 0x4b, (byte) 0x3, (byte) 0x4, (byte) 0x14, + (byte) 0x0, (byte) 0x8, (byte) 0x8, (byte) 0x8, (byte) 0x0, + (byte) 0xec, (byte) 0x34, (byte) 0x62, (byte) 0x54, (byte) 0x0, + (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, + (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, + (byte) 0x0, (byte) 0x9, (byte) 0x0, (byte) 0x4, (byte) 0x0, + (byte) 0x4d, (byte) 0x45, (byte) 0x54, (byte) 0x41, (byte) 0x2d, + (byte) 0x49, (byte) 0x4e, (byte) 0x46, (byte) 0x2f, (byte) 0xfe, + (byte) 0xca, (byte) 0x0, (byte) 0x0, (byte) 0x3, (byte) 0x0, + (byte) 0x50, (byte) 0x4b, (byte) 0x7, (byte) 0x8, (byte) 0x0, + (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x2, (byte) 0x0, + (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, + (byte) 0x0, (byte) 0x50, (byte) 0x4b, (byte) 0x3, (byte) 0x4, + (byte) 0x14, (byte) 0x0, (byte) 0x8, (byte) 0x8, (byte) 0x8, + (byte) 0x0, (byte) 0xec, (byte) 0x34, (byte) 0x62, (byte) 0x54, + (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, + (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, + (byte) 0x0, (byte) 0x0, (byte) 0x14, (byte) 0x0, (byte) 0x0, + (byte) 0x0, (byte) 0x4d, (byte) 0x45, (byte) 0x54, (byte) 0x41, + (byte) 0x2d, (byte) 0x49, (byte) 0x4e, (byte) 0x46, (byte) 0x2f, + (byte) 0x4d, (byte) 0x41, (byte) 0x4e, (byte) 0x49, (byte) 0x46, + (byte) 0x45, (byte) 0x53, (byte) 0x54, (byte) 0x2e, (byte) 0x4d, + (byte) 0x46, (byte) 0xf3, (byte) 0x4d, (byte) 0xcc, (byte) 0xcb, + (byte) 0x4c, (byte) 0x4b, (byte) 0x2d, (byte) 0x2e, (byte) 0xd1, + (byte) 0xd, (byte) 0x4b, (byte) 0x2d, (byte) 0x2a, (byte) 0xce, + (byte) 0xcc, (byte) 0xcf, (byte) 0xb3, (byte) 0x52, (byte) 0x30, + (byte) 0xd4, (byte) 0x33, (byte) 0xe0, (byte) 0xe5, (byte) 0x72, + (byte) 0x2e, (byte) 0x4a, (byte) 0x4d, (byte) 0x2c, (byte) 0x49, + (byte) 0x4d, (byte) 0xd1, (byte) 0x75, (byte) 0xaa, (byte) 0x4, + (byte) 0xa, (byte) 0x98, (byte) 0xe8, (byte) 0x19, (byte) 0xe8, + (byte) 0x19, (byte) 0x2a, (byte) 0x68, (byte) 0xf8, (byte) 0x17, + (byte) 0x25, (byte) 0x26, (byte) 0xe7, (byte) 0xa4, (byte) 0x2a, + (byte) 0x38, (byte) 0xe7, (byte) 0x17, (byte) 0x15, (byte) 0xe4, + (byte) 0x17, (byte) 0x25, (byte) 0x96, (byte) 0x0, (byte) 0x15, + (byte) 0x6b, (byte) 0xf2, (byte) 0x72, (byte) 0xf1, (byte) 0x72, + (byte) 0x1, (byte) 0x0, (byte) 0x50, (byte) 0x4b, (byte) 0x7, + (byte) 0x8, (byte) 0xf4, (byte) 0x59, (byte) 0xdc, (byte) 0xa6, + (byte) 0x42, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x42, + (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x50, (byte) 0x4b, + (byte) 0x3, (byte) 0x4, (byte) 0x14, (byte) 0x0, (byte) 0x8, + (byte) 0x8, (byte) 0x8, (byte) 0x0, (byte) 0xe3, (byte) 0x34, + (byte) 0x62, (byte) 0x54, (byte) 0x0, (byte) 0x0, (byte) 0x0, + (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, + (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x9, + (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x48, (byte) 0x65, + (byte) 0x6c, (byte) 0x6c, (byte) 0x6f, (byte) 0x2e, (byte) 0x74, + (byte) 0x78, (byte) 0x74, (byte) 0xf3, (byte) 0x48, (byte) 0xcd, + (byte) 0xc9, (byte) 0xc9, (byte) 0x57, (byte) 0x28, (byte) 0xcf, + (byte) 0x2f, (byte) 0xca, (byte) 0x49, (byte) 0xe1, (byte) 0x2, + (byte) 0x0, (byte) 0x50, (byte) 0x4b, (byte) 0x7, (byte) 0x8, + (byte) 0xd5, (byte) 0xe0, (byte) 0x39, (byte) 0xb7, (byte) 0xe, + (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0xc, (byte) 0x0, + (byte) 0x0, (byte) 0x0, (byte) 0x50, (byte) 0x4b, (byte) 0x3, + (byte) 0x4, (byte) 0x14, (byte) 0x0, (byte) 0x8, (byte) 0x8, + (byte) 0x8, (byte) 0x0, (byte) 0x98, (byte) 0x68, (byte) 0x62, + (byte) 0x54, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, + (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, + (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0xa, (byte) 0x0, + (byte) 0x0, (byte) 0x0, (byte) 0x54, (byte) 0x65, (byte) 0x6e, + (byte) 0x6e, (byte) 0x69, (byte) 0x73, (byte) 0x2e, (byte) 0x74, + (byte) 0x78, (byte) 0x74, (byte) 0x73, (byte) 0xf2, (byte) 0xb, + (byte) 0x50, (byte) 0x8, (byte) 0x48, (byte) 0x2c, (byte) 0xca, + (byte) 0x4c, (byte) 0x4a, (byte) 0x2c, (byte) 0x56, (byte) 0xf0, + (byte) 0x2f, (byte) 0x48, (byte) 0xcd, (byte) 0x53, (byte) 0xc8, + (byte) 0x2c, (byte) 0x56, (byte) 0x48, (byte) 0x54, (byte) 0x48, + (byte) 0x2b, (byte) 0xcd, (byte) 0x53, (byte) 0x8, (byte) 0x49, + (byte) 0xcd, (byte) 0xcb, (byte) 0x3, (byte) 0x72, (byte) 0x42, + (byte) 0xf2, (byte) 0x4b, (byte) 0x8b, (byte) 0xf2, (byte) 0x12, + (byte) 0x73, (byte) 0x53, (byte) 0xf3, (byte) 0x4a, (byte) 0x14, + (byte) 0x4a, (byte) 0xf2, (byte) 0x15, (byte) 0xca, (byte) 0x13, + (byte) 0x4b, (byte) 0x92, (byte) 0x33, (byte) 0xb8, (byte) 0x0, + (byte) 0x50, (byte) 0x4b, (byte) 0x7, (byte) 0x8, (byte) 0xaa, + (byte) 0xad, (byte) 0x14, (byte) 0xd, (byte) 0x35, (byte) 0x0, + (byte) 0x0, (byte) 0x0, (byte) 0x35, (byte) 0x0, (byte) 0x0, + (byte) 0x0, (byte) 0x50, (byte) 0x4b, (byte) 0x3, (byte) 0x4, + (byte) 0x14, (byte) 0x0, (byte) 0x8, (byte) 0x8, (byte) 0x8, + (byte) 0x0, (byte) 0xf1, (byte) 0x79, (byte) 0x62, (byte) 0x54, + (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, + (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, + (byte) 0x0, (byte) 0x0, (byte) 0xe, (byte) 0x0, (byte) 0x0, + (byte) 0x0, (byte) 0x42, (byte) 0x72, (byte) 0x75, (byte) 0x63, + (byte) 0x65, (byte) 0x57, (byte) 0x61, (byte) 0x79, (byte) 0x6e, + (byte) 0x65, (byte) 0x2e, (byte) 0x74, (byte) 0x78, (byte) 0x74, + (byte) 0xf3, (byte) 0x54, (byte) 0x48, (byte) 0xcc, (byte) 0x55, + (byte) 0x70, (byte) 0x4a, (byte) 0x2c, (byte) 0xc9, (byte) 0x4d, + (byte) 0xcc, (byte) 0xe3, (byte) 0x2, (byte) 0x0, (byte) 0x50, + (byte) 0x4b, (byte) 0x7, (byte) 0x8, (byte) 0x6c, (byte) 0x70, + (byte) 0x60, (byte) 0xbd, (byte) 0xe, (byte) 0x0, (byte) 0x0, + (byte) 0x0, (byte) 0xc, (byte) 0x0, (byte) 0x0, (byte) 0x0, + (byte) 0x50, (byte) 0x4b, (byte) 0x1, (byte) 0x2, (byte) 0x14, + (byte) 0x0, (byte) 0x14, (byte) 0x0, (byte) 0x8, (byte) 0x8, + (byte) 0x8, (byte) 0x0, (byte) 0xec, (byte) 0x34, (byte) 0x62, + (byte) 0x54, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, + (byte) 0x2, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, + (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x9, (byte) 0x0, + (byte) 0x4, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, + (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, + (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, + (byte) 0x0, (byte) 0x4d, (byte) 0x45, (byte) 0x54, (byte) 0x41, + (byte) 0x2d, (byte) 0x49, (byte) 0x4e, (byte) 0x46, (byte) 0x2f, + (byte) 0xfe, (byte) 0xca, (byte) 0x0, (byte) 0x0, (byte) 0x50, + (byte) 0x4b, (byte) 0x1, (byte) 0x2, (byte) 0x14, (byte) 0x0, + (byte) 0x14, (byte) 0x0, (byte) 0x8, (byte) 0x8, (byte) 0x8, + (byte) 0x0, (byte) 0xec, (byte) 0x34, (byte) 0x62, (byte) 0x54, + (byte) 0xf4, (byte) 0x59, (byte) 0xdc, (byte) 0xa6, (byte) 0x42, + (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x42, (byte) 0x0, + (byte) 0x0, (byte) 0x0, (byte) 0x14, (byte) 0x0, (byte) 0x0, + (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, + (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, + (byte) 0x0, (byte) 0x3d, (byte) 0x0, (byte) 0x0, (byte) 0x0, + (byte) 0x4d, (byte) 0x45, (byte) 0x54, (byte) 0x41, (byte) 0x2d, + (byte) 0x49, (byte) 0x4e, (byte) 0x46, (byte) 0x2f, (byte) 0x4d, + (byte) 0x41, (byte) 0x4e, (byte) 0x49, (byte) 0x46, (byte) 0x45, + (byte) 0x53, (byte) 0x54, (byte) 0x2e, (byte) 0x4d, (byte) 0x46, + (byte) 0x50, (byte) 0x4b, (byte) 0x1, (byte) 0x2, (byte) 0x14, + (byte) 0x0, (byte) 0x14, (byte) 0x0, (byte) 0x8, (byte) 0x8, + (byte) 0x8, (byte) 0x0, (byte) 0xe3, (byte) 0x34, (byte) 0x62, + (byte) 0x54, (byte) 0xd5, (byte) 0xe0, (byte) 0x39, (byte) 0xb7, + (byte) 0xe, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0xc, + (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x9, (byte) 0x0, + (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, + (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, + (byte) 0x0, (byte) 0x0, (byte) 0xc1, (byte) 0x0, (byte) 0x0, + (byte) 0x0, (byte) 0x48, (byte) 0x65, (byte) 0x6c, (byte) 0x6c, + (byte) 0x6f, (byte) 0x2e, (byte) 0x74, (byte) 0x78, (byte) 0x74, + (byte) 0x50, (byte) 0x4b, (byte) 0x1, (byte) 0x2, (byte) 0x14, + (byte) 0x0, (byte) 0x14, (byte) 0x0, (byte) 0x8, (byte) 0x8, + (byte) 0x8, (byte) 0x0, (byte) 0x98, (byte) 0x68, (byte) 0x62, + (byte) 0x54, (byte) 0xaa, (byte) 0xad, (byte) 0x14, (byte) 0xd, + (byte) 0x35, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x35, + (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0xa, (byte) 0x0, + (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, + (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, + (byte) 0x0, (byte) 0x0, (byte) 0x6, (byte) 0x1, (byte) 0x0, + (byte) 0x0, (byte) 0x54, (byte) 0x65, (byte) 0x6e, (byte) 0x6e, + (byte) 0x69, (byte) 0x73, (byte) 0x2e, (byte) 0x74, (byte) 0x78, + (byte) 0x74, (byte) 0x50, (byte) 0x4b, (byte) 0x1, (byte) 0x2, + (byte) 0x14, (byte) 0x0, (byte) 0x14, (byte) 0x0, (byte) 0x8, + (byte) 0x8, (byte) 0x8, (byte) 0x0, (byte) 0xf1, (byte) 0x79, + (byte) 0x62, (byte) 0x54, (byte) 0x6c, (byte) 0x70, (byte) 0x60, + (byte) 0xbd, (byte) 0xe, (byte) 0x0, (byte) 0x0, (byte) 0x0, + (byte) 0xc, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0xe, + (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, + (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, + (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x73, (byte) 0x1, + (byte) 0x0, (byte) 0x0, (byte) 0x42, (byte) 0x72, (byte) 0x75, + (byte) 0x63, (byte) 0x65, (byte) 0x57, (byte) 0x61, (byte) 0x79, + (byte) 0x6e, (byte) 0x65, (byte) 0x2e, (byte) 0x74, (byte) 0x78, + (byte) 0x74, (byte) 0x50, (byte) 0x4b, (byte) 0x5, (byte) 0x6, + (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x5, + (byte) 0x0, (byte) 0x5, (byte) 0x0, (byte) 0x28, (byte) 0x1, + (byte) 0x0, (byte) 0x0, (byte) 0xbd, (byte) 0x1, (byte) 0x0, + (byte) 0x0, (byte) 0x0, (byte) 0x0, + }; + + /** + * Create Jar files used by the tests. + * The {@code byte} array {@code VALID_ZIP_WITH_NO_COMMENTS_BYTES} is written + * to disk to create the jar file: {@code Valid-CEN-Comment-Length.jar}. + * + * The jar file {@code InValid-CEN-Comment-Length.jar} is created by copying + * the {@code byte} array {@code VALID_ZIP_WITH_NO_COMMENTS_BYTES} and modifying + * the CEN file header comment length entry for "META-INF/MANIFEST.MF" so that + * new comment length will forward the CEN to a subsequent CEN file header + * entry. + * + * For {@code InValid-CEN-Comment-Length.jar}, the comment length is changed + * from {@code 0x0} to the {@code 0x37}. + * + * @throws IOException If an error occurs + */ + @BeforeTest + public void setup() throws IOException { + Files.deleteIfExists(VALID_CEN_COMMENT_LENGTH_JAR); + Files.deleteIfExists(INVALID_CEN_COMMENT_LENGTH_JAR); + // Create the valid jar + Files.write(VALID_CEN_COMMENT_LENGTH_JAR, VALID_ZIP_WITH_NO_COMMENTS_BYTES); + // Now create an invalid jar + byte[] invalid_bytes = Arrays.copyOf(VALID_ZIP_WITH_NO_COMMENTS_BYTES, + VALID_ZIP_WITH_NO_COMMENTS_BYTES.length); + // Change CEN file Header comment length so that the length will + // result in the offset pointing to a subsequent CEN file header + // resulting in an invalid comment + invalid_bytes[536] = 55; + Files.write(INVALID_CEN_COMMENT_LENGTH_JAR, invalid_bytes); + } + + /** + * Clean up after the test run + * + * @throws IOException If an error occurs + */ + @AfterTest + public static void cleanup() throws IOException { + Files.deleteIfExists(VALID_CEN_COMMENT_LENGTH_JAR); + Files.deleteIfExists(INVALID_CEN_COMMENT_LENGTH_JAR); + } + + /** + * Validate that the original(valid) Jar file can be opened by {@code ZipFile} + * and the expected Zip entry can be found + * @throws IOException If an error occurs + */ + @Test + public static void ZipFileValidCommentLengthTest() throws IOException { + try (ZipFile jf = new ZipFile(VALID_CEN_COMMENT_LENGTH_JAR.toFile())) { + ZipEntry ze = jf.getEntry(META_INF_MANIFEST_MF); + assertNotNull(ze); + assertEquals(ze.getName(), META_INF_MANIFEST_MF); + } + } + + /** + * Validate that the original(valid) Jar file can be opened by {@code JarFile} + * and the expected Zip entry can be found + * @throws IOException If an error occurs + */ + @Test + public static void JarFileValidCommentLengthTest() throws IOException { + try (JarFile jf = new JarFile(VALID_CEN_COMMENT_LENGTH_JAR.toFile())) { + ZipEntry ze = jf.getEntry(META_INF_MANIFEST_MF); + assertNotNull(ze); + assertEquals(ze.getName(), META_INF_MANIFEST_MF); + } + } + + /** + * Validate that a ZipException is thrown when the CEN file header comment + * length is non-zero and the CEN entry does not contain a comment when + * the Jar file is opened by {@code ZipFile} + */ + @Test + public static void ZipFileInValidCommentLengthTest() { + var ex= expectThrows(ZipException.class, + () -> new ZipFile(INVALID_CEN_COMMENT_LENGTH_JAR.toFile())); + assertEquals(ex.getMessage(), INVALID_CEN_HEADER_BAD_ENTRY_NAME_OR_COMMENT); + } + + /** + * Validate that a ZipException is thrown when the CEN file header comment + * length is non-zero and the CEN entry does not contain a comment when + * the Jar file is opened by {@code JarFile} + */ + @Test + public static void JarFileInValidCommentLengthTest() { + var ex= expectThrows(ZipException.class, + () -> new JarFile(INVALID_CEN_COMMENT_LENGTH_JAR.toFile())); + assertEquals(ex.getMessage(), INVALID_CEN_HEADER_BAD_ENTRY_NAME_OR_COMMENT); + } +} -- GitLab From ef266d77b6eb54d7e30a0aafd8a3e8c8f4f0e43a Mon Sep 17 00:00:00 2001 From: Roland Westrelin Date: Mon, 7 Mar 2022 16:26:19 +0000 Subject: [PATCH 013/345] 8278296: Generalize long range check transformation Reviewed-by: jrose, thartmann --- src/hotspot/share/opto/loopPredicate.cpp | 2 +- src/hotspot/share/opto/loopTransform.cpp | 246 +++++++++++++----- src/hotspot/share/opto/loopnode.cpp | 242 ++++++++++------- src/hotspot/share/opto/loopnode.hpp | 13 +- src/hotspot/share/opto/node.hpp | 7 +- .../c2/irTests/TestLongRangeChecks.java | 146 +++++++++++ .../rangechecks/TestLongRangeCheck.java | 222 +++++++++++----- 7 files changed, 644 insertions(+), 234 deletions(-) diff --git a/src/hotspot/share/opto/loopPredicate.cpp b/src/hotspot/share/opto/loopPredicate.cpp index 584df763156..ee88d0fabf4 100644 --- a/src/hotspot/share/opto/loopPredicate.cpp +++ b/src/hotspot/share/opto/loopPredicate.cpp @@ -759,7 +759,7 @@ bool IdealLoopTree::is_range_check_if(IfNode *iff, PhaseIdealLoop *phase, BasicT } scale = 0; offset = NULL; - if (!phase->is_scaled_iv_plus_offset(cmp->in(1), iv, &scale, &offset, bt)) { + if (!phase->is_scaled_iv_plus_offset(cmp->in(1), iv, bt, &scale, &offset)) { return false; } return true; diff --git a/src/hotspot/share/opto/loopTransform.cpp b/src/hotspot/share/opto/loopTransform.cpp index 21dab9335c0..a997439f43f 100644 --- a/src/hotspot/share/opto/loopTransform.cpp +++ b/src/hotspot/share/opto/loopTransform.cpp @@ -1121,18 +1121,18 @@ bool IdealLoopTree::policy_range_check(PhaseIdealLoop* phase, bool provisional, continue; // not RC } Node *cmp = bol->in(1); - Node *rc_exp = cmp->in(1); - Node *limit = cmp->in(2); if (provisional) { // Try to pattern match with either cmp inputs, do not check // whether one of the inputs is loop independent as it may not // have had a chance to be hoisted yet. - if (!phase->is_scaled_iv_plus_offset(cmp->in(1), trip_counter, NULL, NULL, bt) && - !phase->is_scaled_iv_plus_offset(cmp->in(2), trip_counter, NULL, NULL, bt)) { + if (!phase->is_scaled_iv_plus_offset(cmp->in(1), trip_counter, bt, NULL, NULL) && + !phase->is_scaled_iv_plus_offset(cmp->in(2), trip_counter, bt, NULL, NULL)) { continue; } } else { + Node *rc_exp = cmp->in(1); + Node *limit = cmp->in(2); Node *limit_c = phase->get_ctrl(limit); if (limit_c == phase->C->top()) { return false; // Found dead test on live IF? No RCE! @@ -1147,7 +1147,7 @@ bool IdealLoopTree::policy_range_check(PhaseIdealLoop* phase, bool provisional, } } - if (!phase->is_scaled_iv_plus_offset(rc_exp, trip_counter, NULL, NULL, bt)) { + if (!phase->is_scaled_iv_plus_offset(rc_exp, trip_counter, bt, NULL, NULL)) { continue; } } @@ -2522,127 +2522,202 @@ void PhaseIdealLoop::add_constraint(jlong stride_con, jlong scale_con, Node* off } } +//----------------------------------is_iv------------------------------------ +// Return true if exp is the value (of type bt) of the given induction var. +// This grammar of cases is recognized, where X is I|L according to bt: +// VIV[iv] = iv | (CastXX VIV[iv]) | (ConvI2X VIV[iv]) bool PhaseIdealLoop::is_iv(Node* exp, Node* iv, BasicType bt) { - if (exp == iv) { + exp = exp->uncast(); + if (exp == iv && iv->bottom_type()->isa_integer(bt)) { return true; } - if (bt == T_LONG && iv->bottom_type()->isa_int() && exp->Opcode() == Op_ConvI2L && exp->in(1) == iv) { + if (bt == T_LONG && iv->bottom_type()->isa_int() && exp->Opcode() == Op_ConvI2L && exp->in(1)->uncast() == iv) { return true; } return false; } //------------------------------is_scaled_iv--------------------------------- -// Return true if exp is a constant times an induction var -bool PhaseIdealLoop::is_scaled_iv(Node* exp, Node* iv, jlong* p_scale, BasicType bt, bool* converted) { - exp = exp->uncast(); - assert(bt == T_INT || bt == T_LONG, "unexpected int type"); - if (is_iv(exp, iv, bt)) { +// Return true if exp is a constant times the given induction var (of type bt). +// The multiplication is either done in full precision (exactly of type bt), +// or else bt is T_LONG but iv is scaled using 32-bit arithmetic followed by a ConvI2L. +// This grammar of cases is recognized, where X is I|L according to bt: +// SIV[iv] = VIV[iv] | (CastXX SIV[iv]) +// | (MulX VIV[iv] ConX) | (MulX ConX VIV[iv]) +// | (LShiftX VIV[iv] ConI) +// | (ConvI2L SIV[iv]) -- a "short-scale" can occur here; note recursion +// | (SubX 0 SIV[iv]) -- same as MulX(iv, -scale); note recursion +// VIV[iv] = [either iv or its value converted; see is_iv() above] +// On success, the constant scale value is stored back to *p_scale. +// The value (*p_short_scale) reports if such a ConvI2L conversion was present. +bool PhaseIdealLoop::is_scaled_iv(Node* exp, Node* iv, BasicType bt, jlong* p_scale, bool* p_short_scale, int depth) { + BasicType exp_bt = bt; + exp = exp->uncast(); //strip casts + assert(exp_bt == T_INT || exp_bt == T_LONG, "unexpected int type"); + if (is_iv(exp, iv, exp_bt)) { if (p_scale != NULL) { *p_scale = 1; } + if (p_short_scale != NULL) { + *p_short_scale = false; + } return true; } - if (bt == T_LONG && iv->bottom_type()->isa_int() && exp->Opcode() == Op_ConvI2L) { + if (exp_bt == T_LONG && iv->bottom_type()->isa_int() && exp->Opcode() == Op_ConvI2L) { exp = exp->in(1); - bt = T_INT; - if (converted != NULL) { - *converted = true; - } + exp_bt = T_INT; } int opc = exp->Opcode(); + int which = 0; // this is which subexpression we find the iv in // Can't use is_Mul() here as it's true for AndI and AndL - if (opc == Op_Mul(bt)) { - if (is_iv(exp->in(1)->uncast(), iv, bt) && exp->in(2)->is_Con()) { + if (opc == Op_Mul(exp_bt)) { + if ((is_iv(exp->in(which = 1), iv, exp_bt) && exp->in(2)->is_Con()) || + (is_iv(exp->in(which = 2), iv, exp_bt) && exp->in(1)->is_Con())) { + Node* factor = exp->in(which == 1 ? 2 : 1); // the other argument + jlong scale = factor->find_integer_as_long(exp_bt, 0); + if (scale == 0) { + return false; // might be top + } if (p_scale != NULL) { - *p_scale = exp->in(2)->get_integer_as_long(bt); + *p_scale = scale; + } + if (p_short_scale != NULL) { + // (ConvI2L (MulI iv K)) can be 64-bit linear if iv is kept small enough... + *p_short_scale = (exp_bt != bt && scale != 1); } return true; } - if (is_iv(exp->in(2)->uncast(), iv, bt) && exp->in(1)->is_Con()) { + } else if (opc == Op_LShift(exp_bt)) { + if (is_iv(exp->in(1), iv, exp_bt) && exp->in(2)->is_Con()) { + jint shift_amount = exp->in(2)->find_int_con(min_jint); + if (shift_amount == min_jint) { + return false; // might be top + } + jlong scale; + if (exp_bt == T_INT) { + scale = java_shift_left((jint)1, (juint)shift_amount); + } else if (exp_bt == T_LONG) { + scale = java_shift_left((jlong)1, (julong)shift_amount); + } if (p_scale != NULL) { - *p_scale = exp->in(1)->get_integer_as_long(bt); + *p_scale = scale; + } + if (p_short_scale != NULL) { + // (ConvI2L (MulI iv K)) can be 64-bit linear if iv is kept small enough... + *p_short_scale = (exp_bt != bt && scale != 1); } return true; } - } else if (opc == Op_LShift(bt)) { - if (is_iv(exp->in(1)->uncast(), iv, bt) && exp->in(2)->is_Con()) { + } else if (opc == Op_Sub(exp_bt) && + exp->in(1)->find_integer_as_long(exp_bt, -1) == 0) { + jlong scale = 0; + if (depth == 0 && is_scaled_iv(exp->in(2), iv, exp_bt, &scale, p_short_scale, depth + 1)) { + // SubX(0, iv*K) => iv*(-K) + if (scale == min_signed_integer(exp_bt)) { + // This should work even if -K overflows, but let's not. + return false; + } + scale = java_multiply(scale, (jlong)-1); if (p_scale != NULL) { - jint shift_amount = exp->in(2)->get_int(); - if (bt == T_INT) { - *p_scale = java_shift_left((jint)1, (juint)shift_amount); - } else if (bt == T_LONG) { - *p_scale = java_shift_left((jlong)1, (julong)shift_amount); - } + *p_scale = scale; + } + if (p_short_scale != NULL) { + // (ConvI2L (MulI iv K)) can be 64-bit linear if iv is kept small enough... + *p_short_scale = *p_short_scale || (exp_bt != bt && scale != 1); } return true; } } + // We could also recognize (iv*K1)*K2, even with overflow, but let's not. return false; } -//-----------------------------is_scaled_iv_plus_offset------------------------------ -// Return true if exp is a simple induction variable expression: k1*iv + (invar + k2) -bool PhaseIdealLoop::is_scaled_iv_plus_offset(Node* exp, Node* iv, jlong* p_scale, Node** p_offset, BasicType bt, bool* converted, int depth) { +//-------------------------is_scaled_iv_plus_offset-------------------------- +// Return true if exp is a simple linear transform of the given induction var. +// The scale must be constant and the addition tree (if any) must be simple. +// This grammar of cases is recognized, where X is I|L according to bt: +// +// OIV[iv] = SIV[iv] | (CastXX OIV[iv]) +// | (AddX SIV[iv] E) | (AddX E SIV[iv]) +// | (SubX SIV[iv] E) | (SubX E SIV[iv]) +// SSIV[iv] = (ConvI2X SIV[iv]) -- a "short scale" might occur here +// SIV[iv] = [a possibly scaled value of iv; see is_scaled_iv() above] +// +// On success, the constant scale value is stored back to *p_scale unless null. +// Likewise, the addend (perhaps a synthetic AddX node) is stored to *p_offset. +// Also, (*p_short_scale) reports if a ConvI2L conversion was seen after a MulI, +// meaning bt is T_LONG but iv was scaled using 32-bit arithmetic. +// To avoid looping, the match is depth-limited, and so may fail to match the grammar to complex expressions. +bool PhaseIdealLoop::is_scaled_iv_plus_offset(Node* exp, Node* iv, BasicType bt, jlong* p_scale, Node** p_offset, bool* p_short_scale, int depth) { assert(bt == T_INT || bt == T_LONG, "unexpected int type"); - if (is_scaled_iv(exp, iv, p_scale, bt, converted)) { + jlong scale = 0; // to catch result from is_scaled_iv() + BasicType exp_bt = bt; + exp = exp->uncast(); + if (is_scaled_iv(exp, iv, exp_bt, &scale, p_short_scale)) { + if (p_scale != NULL) { + *p_scale = scale; + } if (p_offset != NULL) { - Node *zero = _igvn.integercon(0, bt); + Node *zero = _igvn.zerocon(bt); set_ctrl(zero, C->root()); *p_offset = zero; } return true; } - exp = exp->uncast(); + if (exp_bt != bt) { + // We would now be matching inputs like (ConvI2L exp:(AddI (MulI iv S) E)). + // It's hard to make 32-bit arithmetic linear if it overflows. Although we do + // cope with overflowing multiplication by S, it would be even more work to + // handle overflowing addition of E. So we bail out here on ConvI2L input. + return false; + } int opc = exp->Opcode(); - if (opc == Op_Add(bt)) { - if (is_scaled_iv(exp->in(1), iv, p_scale, bt, converted)) { + int which = 0; // this is which subexpression we find the iv in + Node* offset = NULL; + if (opc == Op_Add(exp_bt)) { + // Check for a scaled IV in (AddX (MulX iv S) E) or (AddX E (MulX iv S)). + if (is_scaled_iv(exp->in(which = 1), iv, bt, &scale, p_short_scale) || + is_scaled_iv(exp->in(which = 2), iv, bt, &scale, p_short_scale)) { + offset = exp->in(which == 1 ? 2 : 1); // the other argument + if (p_scale != NULL) { + *p_scale = scale; + } if (p_offset != NULL) { - *p_offset = exp->in(2); + *p_offset = offset; } return true; } - if (is_scaled_iv(exp->in(2), iv, p_scale, bt, converted)) { - if (p_offset != NULL) { - *p_offset = exp->in(1); - } + // Check for more addends, like (AddX (AddX (MulX iv S) E1) E2), etc. + if (is_scaled_iv_plus_extra_offset(exp->in(1), exp->in(2), iv, bt, p_scale, p_offset, p_short_scale, depth) || + is_scaled_iv_plus_extra_offset(exp->in(2), exp->in(1), iv, bt, p_scale, p_offset, p_short_scale, depth)) { return true; } - if (exp->in(2)->is_Con()) { - Node* offset2 = NULL; - if (depth < 2 && - is_scaled_iv_plus_offset(exp->in(1), iv, p_scale, - p_offset != NULL ? &offset2 : NULL, bt, converted, depth+1)) { - if (p_offset != NULL) { - Node *ctrl_off2 = get_ctrl(offset2); - Node* offset = AddNode::make(offset2, exp->in(2), bt); - register_new_node(offset, ctrl_off2); - *p_offset = offset; + } else if (opc == Op_Sub(exp_bt)) { + if (is_scaled_iv(exp->in(which = 1), iv, bt, &scale, p_short_scale) || + is_scaled_iv(exp->in(which = 2), iv, bt, &scale, p_short_scale)) { + // Match (SubX SIV[iv] E) as if (AddX SIV[iv] (SubX 0 E)), and + // match (SubX E SIV[iv]) as if (AddX E (SubX 0 SIV[iv])). + offset = exp->in(which == 1 ? 2 : 1); // the other argument + if (which == 2) { + // We can't handle a scale of min_jint (or min_jlong) here as -1 * min_jint = min_jint + if (scale == min_signed_integer(bt)) { + return false; // cannot negate the scale of the iv } - return true; + scale = java_multiply(scale, (jlong)-1); } - } - } else if (opc == Op_Sub(bt)) { - if (is_scaled_iv(exp->in(1), iv, p_scale, bt, converted)) { - if (p_offset != NULL) { - Node *zero = _igvn.integercon(0, bt); - set_ctrl(zero, C->root()); - Node *ctrl_off = get_ctrl(exp->in(2)); - Node* offset = SubNode::make(zero, exp->in(2), bt); - register_new_node(offset, ctrl_off); - *p_offset = offset; + if (p_scale != NULL) { + *p_scale = scale; } - return true; - } - if (is_scaled_iv(exp->in(2), iv, p_scale, bt, converted)) { if (p_offset != NULL) { - // We can't handle a scale of min_jint (or min_jlong) here as -1 * min_jint = min_jint - if (*p_scale == min_signed_integer(bt)) { - return false; + if (which == 1) { // must negate the extracted offset + Node *zero = _igvn.integercon(0, exp_bt); + set_ctrl(zero, C->root()); + Node *ctrl_off = get_ctrl(offset); + offset = SubNode::make(zero, offset, exp_bt); + register_new_node(offset, ctrl_off); } - *p_scale *= -1; - *p_offset = exp->in(1); + *p_offset = offset; } return true; } @@ -2650,6 +2725,33 @@ bool PhaseIdealLoop::is_scaled_iv_plus_offset(Node* exp, Node* iv, jlong* p_scal return false; } +// Helper for is_scaled_iv_plus_offset(), not called separately. +// The caller encountered (AddX exp1 offset3) or (AddX offset3 exp1). +// Here, exp1 is inspected to see if it is a simple linear transform of iv. +// If so, the offset3 is combined with any other offset2 from inside exp1. +bool PhaseIdealLoop::is_scaled_iv_plus_extra_offset(Node* exp1, Node* offset3, Node* iv, + BasicType bt, + jlong* p_scale, Node** p_offset, + bool* p_short_scale, int depth) { + // By the time we reach here, it is unlikely that exp1 is a simple iv*K. + // If is a linear iv transform, it is probably an add or subtract. + // Let's collect the internal offset2 from it. + Node* offset2 = NULL; + if (offset3->is_Con() && + depth < 2 && + is_scaled_iv_plus_offset(exp1, iv, bt, p_scale, + &offset2, p_short_scale, depth+1)) { + if (p_offset != NULL) { + Node* ctrl_off2 = get_ctrl(offset2); + Node* offset = AddNode::make(offset2, offset3, bt); + register_new_node(offset, ctrl_off2); + *p_offset = offset; + } + return true; + } + return false; +} + // Same as PhaseIdealLoop::duplicate_predicates() but for range checks // eliminated by iteration splitting. Node* PhaseIdealLoop::add_range_check_predicate(IdealLoopTree* loop, CountedLoopNode* cl, diff --git a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp index 25bf93883d9..6732c6dee01 100644 --- a/src/hotspot/share/opto/loopnode.cpp +++ b/src/hotspot/share/opto/loopnode.cpp @@ -1122,9 +1122,6 @@ void PhaseIdealLoop::strip_mined_nest_back_to_counted_loop(IdealLoopTree* loop, int PhaseIdealLoop::extract_long_range_checks(const IdealLoopTree* loop, jlong stride_con, int iters_limit, PhiNode* phi, Node_List& range_checks) { - if (stride_con < 0) { // only for stride_con > 0 && scale > 0 for now - return iters_limit; - } const jlong min_iters = 2; jlong reduced_iters_limit = iters_limit; jlong original_iters_limit = iters_limit; @@ -1139,7 +1136,6 @@ int PhaseIdealLoop::extract_long_range_checks(const IdealLoopTree* loop, jlong s RangeCheckNode* rc = c->in(0)->as_RangeCheck(); if (loop->is_range_check_if(rc, this, T_LONG, phi, range, offset, scale) && loop->is_invariant(range) && loop->is_invariant(offset) && - scale > 0 && // only for stride_con > 0 && scale > 0 for now original_iters_limit / ABS(scale * stride_con) >= min_iters) { reduced_iters_limit = MIN2(reduced_iters_limit, original_iters_limit/ABS(scale)); range_checks.push(c); @@ -1154,27 +1150,29 @@ int PhaseIdealLoop::extract_long_range_checks(const IdealLoopTree* loop, jlong s // One execution of the inner loop covers a sub-range of the entire iteration range of the loop: [A,Z), aka [A=init, // Z=limit). If the loop has at least one trip (which is the case here), the iteration variable i always takes A as its // first value, followed by A+S (S is the stride), next A+2S, etc. The limit is exclusive, so that the final value B of -// i is never Z. It will be B=Z-1 if S=1, or B=Z+1 if S=-1. If |S|>1 the formula for the last value requires a floor -// operation, specifically B=floor((Z-sgn(S)-A)/S)*S+A. Thus i ranges as i:[A,B] or i:[A,Z) or i:[A,Z-U) for some U 0) to simplify the logic for clamping 32-bit bounds (L_2, R_2). -// For restrictions on S and K, see the guards in extract_long_range_checks. +// If |S|>1 the formula for the last value B would require a floor operation, specifically B=floor((Z-sgn(S)-A)/S)*S+A, +// which is B=Z-sgn(S)U for some U in [1,|S|]. So when S>0, i ranges as i:[A,Z) or i:[A,B=Z-U], or else (in reverse) +// as i:(Z,A] or i:[B=Z+U,A]. It will become important to reason about this inclusive range [A,B] or [B,A]. // Within the loop there may be many range checks. Each such range check (R.C.) is of the form 0 <= i*K+L < R, where K // is a scale factor applied to the loop iteration variable i, and L is some offset; K, L, and R are loop-invariant. -// Because R is never negative, this check can always be simplified to an unsigned check i*K+L 0), where the limit is +// chosen to prevent various cases of 32-bit overflow (including multiplications j*K below). In the sub-loop the +// logical value i is offset from j by a 64-bit constant C, so i ranges in i:C+[0,Z_2). -// The union of all the C+[0,Z_2) ranges from the sub-loops must be identical to the whole range [A,B]. Assuming S>0, -// the first C must be A itself, and the next C value is the previous C+Z_2. In each sub-loop, j counts up from zero -// and exits just before i=C+Z_2. +// For S<0, j ranges (in reverse!) through j:[-|B_2|,0] or (-|Z_2|,0]. For either sign of S, we can say i=j+C and j +// ranges through 32-bit ranges [A_2,B_2] or [B_2,A_2] (A_2=0 of course). -// (N.B. If S<0 the formulas are different, because all the loops count downward.) +// The disjoint union of all the C+[A_2,B_2] ranges from the sub-loops must be identical to the whole range [A,B]. +// Assuming S>0, the first C must be A itself, and the next C value is the previous C+B_2, plus S. If |S|=1, the next +// C value is also the previous C+Z_2. In each sub-loop, j counts from j=A_2=0 and i counts from C+0 and exits at +// j=B_2 (i=C+B_2), just before it gets to i=C+Z_2. Both i and j count up (from C and 0) if S>0; otherwise they count +// down (from C and 0 again). // Returning to range checks, we see that each i*K+L 0 and K>0), Q_min=A_2*K+Q and Q_max=Q (if S<0 and K>0), +// Q_min=B_2*K+Q and Q_max=Q if (S>0 and K<0), Q_min=Q and Q_max=A_2*K+Q (if S<0 and K<0) -// N.B. If (S*K)<0 then the formulas for Q_min and Q_max may differ; the values may need to be swapped and adjusted to -// the correct type of bound (inclusive or exclusive). +// Note that the first R.C. value is always Q=(S*K>0 ? Q_min : Q_max). Also Q_{min,max} = Q + {min,max}(A_2*K,B_2*K). +// If S*K>0 then, as the loop iterations progress, each R.C. value i*K+L = j*K+Q goes up from Q=Q_min towards Q_max. +// If S*K<0 then j*K+Q starts at Q=Q_max and goes down towards Q_min. // Case A: Some Negatives (but no overflow). // Number line: // |s64_min . . . 0 . . . s64_max| // | . Q_min..Q_max . 0 . . . . | s64 negative +// | . . . . R=0 R< R< R< R< | (against R values) // | . . . Q_min..0..Q_max . . . | small mixed +// | . . . . R R R< R< R< | (against R values) +// +// R values which are out of range (>Q_max+1) are reduced to max(0,Q_max+1). They are marked on the number line as R<. // -// if Q_min 0 (R.C.E. steps upward) +// j*K + s32_trunc(Q_max) R R R< R< | (against R values) // | . . . . 0 . Q_min..Q_max . | s64 positive +// | . . . . R> R> R R R< | (against R values) +// +// R values which are out of range (Q_max+1) are reduced as marked: R> up to Q_min, R< down to Q_max+1. +// Then the whole comparison is shifted left by Q_min, so it can take place at zero, which is a nice 32-bit value. // -// if both Q_min, Q_max >=s64 0, then use this test: -// j*K + 0 =s64 0, then use this test: +// j*K + 0 0 +// More generally: +// j*K + Q - Q_min R> R> R> R | (against R values) +// +// In this case, Q_min >s64 Q_max+1, even though the mathematical values of Q_min and Q_max+1 are correctly ordered. +// The formulas from the previous case can be used, except that the bad upper bound Q_max is replaced by max_jlong. +// (In fact, we could use any replacement bound from R to max_jlong inclusive, as the input to the clamp function.) +// +// So if Q_min >=s64 0 but Q_max+1 0 +// More generally: +// j*K + Q - Q_min =s64 0 but Q_max H, it returns L not H. // -// Tests above can be merged into a single one: -// L_clamp = Q_min < 0 ? 0 : Q_min -// H_clamp = Q_max < Q_min ? R : Q_max -// j*K + Q_min - L_clamp = 0 ? max_jlong : Q_max+1 +// Q_first = Q = (S*K>0 ? Q_min : Q_max) = (C*K+L) +// R_clamp = clamp(R, L_clamp, H_clamp) --reduced dynamic range +// replacement R.C.: +// j*K + Q_first - L_clamp root()); + Node* int_zero = _igvn.intcon(0); + set_ctrl(int_zero, this->C->root()); + Node* long_one = _igvn.longcon(1); + set_ctrl(long_one, this->C->root()); + Node* int_stride = _igvn.intcon(checked_cast(stride_con)); + set_ctrl(int_stride, this->C->root()); for (uint i = 0; i < range_checks.size(); i++) { ProjNode* proj = range_checks.at(i)->as_Proj(); @@ -1266,8 +1303,8 @@ void PhaseIdealLoop::transform_long_range_checks(int stride_con, const Node_List // could be shared and have already been taken care of continue; } - bool converted = false; - bool ok = is_scaled_iv_plus_offset(rc_cmp->in(1), iv_add, &scale, &offset, T_LONG, &converted); + bool short_scale = false; + bool ok = is_scaled_iv_plus_offset(rc_cmp->in(1), iv_add, T_LONG, &scale, &offset, &short_scale); assert(ok, "inconsistent: was tested before"); Node* range = rc_cmp->in(2); Node* c = rc->in(0); @@ -1279,33 +1316,33 @@ void PhaseIdealLoop::transform_long_range_checks(int stride_con, const Node_List Node* L = offset; - if (converted) { + if (short_scale) { // This converts: - // i*K + L (long)max_jint and < R - // and so i*(long)K + L u64 (long)max_jint and still is root()); Node* max_range = new AddLNode(max_jint_plus_one_long, L); @@ -1315,26 +1352,38 @@ void PhaseIdealLoop::transform_long_range_checks(int stride_con, const Node_List } Node* C = outer_phi; - Node* Z_2 = new ConvI2LNode(inner_iters_actual_int, TypeLong::LONG); - register_new_node(Z_2, entry_control); // Start with 64-bit values: // i*K + L C, int_zero, inner_iters_actual_int, int_stride); + register_new_node(B_2, entry_control); + B_2 = new SubINode(B_2, int_stride); + register_new_node(B_2, entry_control); + B_2 = new ConvI2LNode(B_2); + register_new_node(B_2, entry_control); + + Node* Q_max = new MulLNode(B_2, K); register_new_node(Q_max, entry_control); - Q_max = new AddLNode(Q_max, L_2); + Q_max = new AddLNode(Q_max, Q_first); register_new_node(Q_max, entry_control); + if (scale * stride_con < 0) { + swap(Q_min, Q_max); + } + // Now, mathematically, Q_max > Q_min, and they are close enough so that (Q_max-Q_min) fits in 32 bits. + // L_clamp = Q_min < 0 ? 0 : Q_min Node* Q_min_cmp = new CmpLNode(Q_min, long_zero); register_new_node(Q_min_cmp, entry_control); @@ -1342,38 +1391,53 @@ void PhaseIdealLoop::transform_long_range_checks(int stride_con, const Node_List register_new_node(Q_min_bool, entry_control); Node* L_clamp = new CMoveLNode(Q_min_bool, Q_min, long_zero, TypeLong::LONG); register_new_node(L_clamp, entry_control); + // (This could also be coded bitwise as L_clamp = Q_min & ~(Q_min>>63).) + + Node* Q_max_plus_one = new AddLNode(Q_max, long_one); + register_new_node(Q_max_plus_one, entry_control); - // H_clamp = Q_max < Q_min ? R : Q_max - Node* Q_max_cmp = new CmpLNode(Q_max, Q_min); + // H_clamp = Q_max+1 < Q_min ? max_jlong : Q_max+1 + // (Because Q_min and Q_max are close, the overflow check could also be encoded as Q_max+1 < 0 & Q_min >= 0.) + Node* max_jlong_long = _igvn.longcon(max_jlong); + set_ctrl(max_jlong_long, this->C->root()); + Node* Q_max_cmp = new CmpLNode(Q_max_plus_one, Q_min); register_new_node(Q_max_cmp, entry_control); Node* Q_max_bool = new BoolNode(Q_max_cmp, BoolTest::lt); register_new_node(Q_max_bool, entry_control); - Node* H_clamp = new CMoveLNode(Q_max_bool, Q_max, R, TypeLong::LONG); + Node* H_clamp = new CMoveLNode(Q_max_bool, Q_max_plus_one, max_jlong_long, TypeLong::LONG); register_new_node(H_clamp, entry_control); + // (This could also be coded bitwise as H_clamp = ((Q_max+1)<<1 | M)>>>1 where M = (Q_max+1)>>63 & ~Q_min>>63.) // R_2 = clamp(R, L_clamp, H_clamp) - L_clamp - // that is: R_2 = clamp(R, L_clamp, H_clamp) if Q_min < 0 - // or: R_2 = clamp(R, L_clamp, H_clamp) - Q_min if Q_min > 0 + // that is: R_2 = clamp(R, L_clamp=0, H_clamp=Q_max) if Q_min < 0 + // or else: R_2 = clamp(R, L_clamp, H_clamp) - Q_min if Q_min >= 0 + // and also: R_2 = clamp(R, L_clamp, Q_max+1) - L_clamp if Q_min < Q_max+1 (no overflow) + // or else: R_2 = clamp(R, L_clamp, *no limit*)- L_clamp if Q_max+1 < Q_min (overflow) Node* R_2 = clamp(R, L_clamp, H_clamp); R_2 = new SubLNode(R_2, L_clamp); register_new_node(R_2, entry_control); R_2 = new ConvL2INode(R_2, TypeInt::POS); register_new_node(R_2, entry_control); - // Q = Q_min - L_clamp - // that is: Q = Q_min - 0 if Q_min < 0 - // or: Q = Q_min - Q_min = 0 if Q_min > 0 - Node* Q = new SubLNode(Q_min, L_clamp); - register_new_node(Q, entry_control); - Q = new ConvL2INode(Q, TypeInt::INT); - register_new_node(Q, entry_control); + // L_2 = Q_first - L_clamp + // We are subtracting L_clamp from both sides of the 0, then Q_first == 0 and the R.C. expression at -L_clamp and steps upward to Q_max-L_clamp. + // If S*K<0, then Q_first != 0 and the R.C. expression starts high and steps downward to Q_min-L_clamp. + Node* L_2 = new SubLNode(Q_first, L_clamp); + register_new_node(L_2, entry_control); + L_2 = new ConvL2INode(L_2, TypeInt::INT); + register_new_node(L_2, entry_control); - // Transform the range check + // Transform the range check using the computed values L_2/R_2 + // from: i*K + L (scale)); set_ctrl(K, this->C->root()); Node* scaled_iv = new MulINode(inner_phi, K); register_new_node(scaled_iv, c); - Node* scaled_iv_plus_offset = scaled_iv_plus_offset = new AddINode(scaled_iv, Q); + Node* scaled_iv_plus_offset = scaled_iv_plus_offset = new AddINode(scaled_iv, L_2); register_new_node(scaled_iv_plus_offset, c); Node* new_rc_cmp = new CmpUNode(scaled_iv_plus_offset, R_2); diff --git a/src/hotspot/share/opto/loopnode.hpp b/src/hotspot/share/opto/loopnode.hpp index 9b9a52f80af..606d9c46331 100644 --- a/src/hotspot/share/opto/loopnode.hpp +++ b/src/hotspot/share/opto/loopnode.hpp @@ -1265,15 +1265,15 @@ public: void mark_reductions( IdealLoopTree *loop ); // Return true if exp is a constant times an induction var - bool is_scaled_iv(Node* exp, Node* iv, jlong* p_scale, BasicType bt, bool* converted); + bool is_scaled_iv(Node* exp, Node* iv, BasicType bt, jlong* p_scale, bool* p_short_scale, int depth = 0); bool is_iv(Node* exp, Node* iv, BasicType bt); // Return true if exp is a scaled induction var plus (or minus) constant - bool is_scaled_iv_plus_offset(Node* exp, Node* iv, jlong* p_scale, Node** p_offset, BasicType bt, bool* converted = NULL, int depth = 0); + bool is_scaled_iv_plus_offset(Node* exp, Node* iv, BasicType bt, jlong* p_scale, Node** p_offset, bool* p_short_scale = NULL, int depth = 0); bool is_scaled_iv_plus_offset(Node* exp, Node* iv, int* p_scale, Node** p_offset) { jlong long_scale; - if (is_scaled_iv_plus_offset(exp, iv, &long_scale, p_offset, T_INT)) { + if (is_scaled_iv_plus_offset(exp, iv, T_INT, &long_scale, p_offset)) { int int_scale = checked_cast(long_scale); if (p_scale != NULL) { *p_scale = int_scale; @@ -1282,6 +1282,12 @@ public: } return false; } + // Helper for finding more complex matches to is_scaled_iv_plus_offset. + bool is_scaled_iv_plus_extra_offset(Node* exp1, Node* offset2, Node* iv, + BasicType bt, + jlong* p_scale, Node** p_offset, + bool* p_short_scale, int depth); + // Enum to determine the action to be performed in create_new_if_for_predicate() when processing phis of UCT regions. enum class UnswitchingAction { @@ -1658,6 +1664,7 @@ public: void strip_mined_nest_back_to_counted_loop(IdealLoopTree* loop, const BaseCountedLoopNode* head, Node* back_control, IfNode*&exit_test, SafePointNode*&safepoint); + void push_pinned_nodes_thru_region(IfNode* dom_if, Node* region); bool try_merge_identical_ifs(Node* n); diff --git a/src/hotspot/share/opto/node.hpp b/src/hotspot/share/opto/node.hpp index 6a03a080c82..748db286710 100644 --- a/src/hotspot/share/opto/node.hpp +++ b/src/hotspot/share/opto/node.hpp @@ -1153,7 +1153,12 @@ public: jlong get_integer_as_long(BasicType bt) const { const TypeInteger* t = find_integer_type(bt); - guarantee(t != NULL, "must be con"); + guarantee(t != NULL && t->is_con(), "must be con"); + return t->get_con_as_long(bt); + } + jlong find_integer_as_long(BasicType bt, jlong value_if_unknown) const { + const TypeInteger* t = find_integer_type(bt); + if (t == NULL || !t->is_con()) return value_if_unknown; return t->get_con_as_long(bt); } const TypePtr* get_ptr_type() const; diff --git a/test/hotspot/jtreg/compiler/c2/irTests/TestLongRangeChecks.java b/test/hotspot/jtreg/compiler/c2/irTests/TestLongRangeChecks.java index 4fd677ab4c0..e84f3062f14 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/TestLongRangeChecks.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/TestLongRangeChecks.java @@ -96,4 +96,150 @@ public class TestLongRangeChecks { private void testStridePosScalePosInIntLoop2_runner() { testStridePosScalePosInIntLoop2(0, 100, 200, 0); } + + @Test + @IR(counts = { IRNode.LOOP, "1"}) + @IR(failOn = { IRNode.COUNTEDLOOP}) + public static void testStrideNegScaleNeg(long start, long stop, long length, long offset) { + final long scale = -1; + final long stride = 1; + for (long i = stop; i > start; i -= stride) { + Objects.checkIndex(scale * i + offset, length); + } + } + + @Run(test = "testStrideNegScaleNeg") + private void testStrideNegScaleNeg_runner() { + testStrideNegScaleNeg(0, 100, 100, 100); + } + + @Test + @IR(counts = { IRNode.LOOP, "1" }) + @IR(failOn = { IRNode.COUNTEDLOOP }) + public static void testStrideNegScaleNegInIntLoop1(int start, int stop, long length, long offset) { + final long scale = -2; + final int stride = 1; + + for (int i = stop; i > start; i -= stride) { + Objects.checkIndex(scale * i + offset, length); + } + } + + @Run(test = "testStrideNegScaleNegInIntLoop1") + private void testStrideNegScaleNegInIntLoop1_runner() { + testStrideNegScaleNegInIntLoop1(0, 100, 200, 200); + } + + @Test + @IR(counts = { IRNode.LOOP, "1" }) + @IR(failOn = { IRNode.COUNTEDLOOP }) + public static void testStrideNegScaleNegInIntLoop2(int start, int stop, long length, long offset) { + final int scale = -2; + final int stride = 1; + + for (int i = stop; i > start; i -= stride) { + Objects.checkIndex(scale * i + offset, length); + } + } + + @Run(test = "testStrideNegScaleNegInIntLoop2") + private void testStrideNegScaleNegInIntLoop2_runner() { + testStrideNegScaleNegInIntLoop2(0, 100, 200, 200); + } + + @Test + @IR(counts = { IRNode.LOOP, "1"}) + @IR(failOn = { IRNode.COUNTEDLOOP}) + public static void testStrideNegScalePos(long start, long stop, long length, long offset) { + final long scale = 1; + final long stride = 1; + for (long i = stop-1; i >= start; i -= stride) { + Objects.checkIndex(scale * i + offset, length); + } + } + + @Run(test = "testStrideNegScalePos") + private void testStrideNegScalePos_runner() { + testStrideNegScalePos(0, 100, 100, 0); + } + + @Test + @IR(counts = { IRNode.LOOP, "1" }) + @IR(failOn = { IRNode.COUNTEDLOOP }) + public static void testStrideNegScalePosInIntLoop1(int start, int stop, long length, long offset) { + final long scale = 2; + final int stride = 1; + for (int i = stop-1; i >= start; i -= stride) { + Objects.checkIndex(scale * i + offset, length); + } + } + + @Run(test = "testStrideNegScalePosInIntLoop1") + private void testStrideNegScalePosInIntLoop1_runner() { + testStrideNegScalePosInIntLoop1(0, 100, 200, 0); + } + + @Test + @IR(counts = { IRNode.LOOP, "1" }) + @IR(failOn = { IRNode.COUNTEDLOOP }) + public static void testStrideNegScalePosInIntLoop2(int start, int stop, long length, long offset) { + final int scale = 2; + final int stride = 1; + for (int i = stop-1; i >= start; i -= stride) { + Objects.checkIndex(scale * i + offset, length); + } + } + + @Run(test = "testStrideNegScalePosInIntLoop2") + private void testStrideNegScalePosInIntLoop2_runner() { + testStrideNegScalePosInIntLoop1(0, 100, 200, 0); + } + + @Test + @IR(counts = { IRNode.LOOP, "1"}) + @IR(failOn = { IRNode.COUNTEDLOOP}) + public static void testStridePosScaleNeg(long start, long stop, long length, long offset) { + final long scale = -1; + final long stride = 1; + for (long i = start; i < stop; i += stride) { + Objects.checkIndex(scale * i + offset, length); + } + } + + @Run(test = "testStridePosScaleNeg") + private void testStridePosScaleNeg_runner() { + testStridePosScaleNeg(0, 100, 100, 99); + } + + @Test + @IR(counts = { IRNode.LOOP, "1"}) + @IR(failOn = { IRNode.COUNTEDLOOP}) + public static void testStridePosScaleNegInIntLoop1(int start, int stop, long length, long offset) { + final long scale = -2; + final int stride = 1; + for (int i = start; i < stop; i += stride) { + Objects.checkIndex(scale * i + offset, length); + } + } + + @Run(test = "testStridePosScaleNegInIntLoop1") + private void testStridePosScaleNegInIntLoop1_runner() { + testStridePosScaleNegInIntLoop1(0, 100, 200, 198); + } + + @Test + @IR(counts = { IRNode.LOOP, "1"}) + @IR(failOn = { IRNode.COUNTEDLOOP}) + public static void testStridePosScaleNegInIntLoop2(int start, int stop, long length, long offset) { + final int scale = -2; + final int stride = 1; + for (int i = start; i < stop; i += stride) { + Objects.checkIndex(scale * i + offset, length); + } + } + + @Run(test = "testStridePosScaleNegInIntLoop2") + private void testStridePosScaleNegInIntLoop2_runner() { + testStridePosScaleNegInIntLoop1(0, 100, 200, 198); + } } diff --git a/test/hotspot/jtreg/compiler/rangechecks/TestLongRangeCheck.java b/test/hotspot/jtreg/compiler/rangechecks/TestLongRangeCheck.java index d56d19278cf..1cd04523ba7 100644 --- a/test/hotspot/jtreg/compiler/rangechecks/TestLongRangeCheck.java +++ b/test/hotspot/jtreg/compiler/rangechecks/TestLongRangeCheck.java @@ -127,6 +127,73 @@ public class TestLongRangeCheck { assertIsNotCompiled(m); } + private static void testOverflow(String method, long start, long stop, long length, long offset0, long offset1) throws Exception { + Method m = newClassLoader().loadClass("TestLongRangeCheck").getDeclaredMethod(method, long.class, long.class, long.class, long.class); + m.invoke(null, start, stop, length, offset0); + compile(m); + + m.invoke(null, start, stop, length, offset0); + assertIsCompiled(m); + try { + m.invoke(null, start, stop, length, offset1); + throw new RuntimeException("should have thrown"); + } catch(InvocationTargetException e) { + if (!(e.getCause() instanceof IndexOutOfBoundsException)) { + throw new RuntimeException("unexpected exception"); + } + } + assertIsNotCompiled(m); + } + + private static void testConditional(String method, long start, long stop, long length, long offset0, long offset1, long start1, long stop1) throws Exception { + Method m; + + if (start1 != start) { + m = newClassLoader().loadClass("TestLongRangeCheck").getDeclaredMethod(method, long.class, long.class, long.class, long.class, long.class, long.class); + m.invoke(null, start, stop, length, offset0, start, stop); + compile(m); + + m.invoke(null, start, stop, length, offset0, start, stop); + assertIsCompiled(m); + try { + m.invoke(null, start, stop, length, offset1, start1-1, stop1); + throw new RuntimeException("should have thrown"); + } catch(InvocationTargetException e) { + if (!(e.getCause() instanceof IndexOutOfBoundsException)) { + throw new RuntimeException("unexpected exception"); + } + } + assertIsNotCompiled(m); + } + + if (stop1 != stop) { + m = newClassLoader().loadClass("TestLongRangeCheck").getDeclaredMethod(method, long.class, long.class, long.class, long.class, long.class, long.class); + m.invoke(null, start, stop, length, offset0, start, stop); + compile(m); + + m.invoke(null, start, stop, length, offset0, start, stop); + assertIsCompiled(m); + try { + m.invoke(null, start, stop, length, offset1, start1, stop1+1); + throw new RuntimeException("should have thrown"); + } catch(InvocationTargetException e) { + if (!(e.getCause() instanceof IndexOutOfBoundsException)) { + throw new RuntimeException("unexpected exception"); + } + } + assertIsNotCompiled(m); + } + + m = newClassLoader().loadClass("TestLongRangeCheck").getDeclaredMethod(method, long.class, long.class, long.class, long.class, long.class, long.class); + m.invoke(null, start, stop, length, offset0, start, stop); + compile(m); + + m.invoke(null, start, stop, length, offset0, start, stop); + assertIsCompiled(m); + + m.invoke(null, start, stop, length, offset1, start1, stop1); + assertIsCompiled(m); + } public static void main(String[] args) throws Exception { @@ -157,42 +224,20 @@ public class TestLongRangeCheck { test("testStridePosNotOneScaleNeg", -v, v, v * 2, v-1); // offset causes overflow - - { - Method m = newClassLoader().loadClass("TestLongRangeCheck").getDeclaredMethod("testStridePosScalePos", long.class, long.class, long.class, long.class); - m.invoke(null, 0, 100, 100, 0); - compile(m); - - m.invoke(null, 0, 100, 100, 0); - assertIsCompiled(m); - try { - m.invoke(null, 0, 100, 100, Long.MAX_VALUE - 50); - throw new RuntimeException("should have thrown"); - } catch(InvocationTargetException e) { - if (!(e.getCause() instanceof IndexOutOfBoundsException)) { - throw new RuntimeException("unexpected exception"); - } - } - assertIsNotCompiled(m); - } + testOverflow("testStridePosScalePos", 0, 100, 100, 0, Long.MAX_VALUE - 50); + testOverflow("testStrideNegScaleNeg", 0, 100, 100, 100, Long.MIN_VALUE + 50); + testOverflow("testStrideNegScalePos", 0, 100, 100, 0, Long.MAX_VALUE - 50); + testOverflow("testStridePosScaleNeg", 0, 100, 100, 99, Long.MIN_VALUE + 50); // no spurious deopt if the range check doesn't fail because not executed - { - Method m = newClassLoader().loadClass("TestLongRangeCheck").getDeclaredMethod("testStridePosScalePosConditional", long.class, long.class, long.class, long.class, long.class, long.class); - m.invoke(null, 0, 100, 100, 0, 0, 100); - compile(m); - - m.invoke(null, 0, 100, 100, -50, 50, 100); - assertIsCompiled(m); - } - { - Method m = newClassLoader().loadClass("TestLongRangeCheck").getDeclaredMethod("testStridePosScalePosConditional", long.class, long.class, long.class, long.class, long.class, long.class); - m.invoke(null, 0, 100, 100, 0, 0, 100); - compile(m); - - m.invoke(null, 0, 100, Long.MAX_VALUE, Long.MAX_VALUE - 50, 0, 50); - assertIsCompiled(m); - } + testConditional("testStridePosScalePosConditional", 0, 100, 100, 0, -50, 50, 100); + testConditional("testStridePosScalePosConditional", 0, 100, Long.MAX_VALUE, 0, Long.MAX_VALUE - 50, 0, 50); + testConditional("testStrideNegScaleNegConditional", 0, 100, 100, 100, 50, 0, 51); + testConditional("testStrideNegScaleNegConditional", 0, 100, Long.MAX_VALUE, 100, Long.MIN_VALUE + 50, 52, 100); + testConditional("testStrideNegScalePosConditional", 0, 100, 100, 0, -50, 50, 100); + testConditional("testStrideNegScalePosConditional", 0, 100, Long.MAX_VALUE, 100, Long.MAX_VALUE - 50, 0, 50); + testConditional("testStridePosScaleNegConditional", 0, 100, 100, 99, 50, 0, 51); + testConditional("testStridePosScaleNegConditional", 0, 100, Long.MAX_VALUE, 99, Long.MIN_VALUE + 50, 52, 100); test("testStridePosScalePosInIntLoop", 0, 100, 100, 0); @@ -221,40 +266,19 @@ public class TestLongRangeCheck { test("testStridePosNotOneScaleNegInIntLoop", -v, v, v * 4, 2 * v - 1); // offset causes overflow - { - Method m = newClassLoader().loadClass("TestLongRangeCheck").getDeclaredMethod("testStridePosScalePosInIntLoop", long.class, long.class, long.class, long.class); - m.invoke(null, 0, 100, 100, 0); - compile(m); - - m.invoke(null, 0, 100, 100, 0); - assertIsCompiled(m); - try { - m.invoke(null, 0, 100, 100, Long.MAX_VALUE - 50); - throw new RuntimeException("should have thrown"); - } catch(InvocationTargetException e) { - if (!(e.getCause() instanceof IndexOutOfBoundsException)) { - throw new RuntimeException("unexpected exception"); - } - } - assertIsNotCompiled(m); - } + testOverflow("testStridePosScalePosInIntLoop", 0, 100, 100, 0, Long.MAX_VALUE - 50); + testOverflow("testStrideNegScaleNegInIntLoop", 0, 100, 100, 100, Long.MIN_VALUE + 50); + testOverflow("testStrideNegScalePosInIntLoop", 0, 100, 100, 0, Long.MAX_VALUE - 50); + testOverflow("testStridePosScaleNegInIntLoop", 0, 100, 100, 99, Long.MIN_VALUE + 50); // no spurious deopt if the range check doesn't fail because not executed - { - Method m = newClassLoader().loadClass("TestLongRangeCheck").getDeclaredMethod("testStridePosScalePosConditional", long.class, long.class, long.class, long.class, long.class, long.class); - m.invoke(null, 0, 100, 100, 0, 0, 100); - compile(m); - - m.invoke(null, 0, 100, 100, -50, 50, 100); - assertIsCompiled(m); - } - { - Method m = newClassLoader().loadClass("TestLongRangeCheck").getDeclaredMethod("testStridePosScalePosConditional", long.class, long.class, long.class, long.class, long.class, long.class); - m.invoke(null, 0, 100, 100, 0, 0, 100); - compile(m); - - m.invoke(null, 0, 100, Long.MAX_VALUE, Long.MAX_VALUE - 50, 0, 50); - assertIsCompiled(m); - } + testConditional("testStridePosScalePosConditionalInIntLoop", 0, 100, 100, 0, -50, 50, 100); + testConditional("testStridePosScalePosConditionalInIntLoop", 0, 100, Long.MAX_VALUE, 0, Long.MAX_VALUE - 50, 0, 50); + testConditional("testStrideNegScaleNegConditionalInIntLoop", 0, 100, 100, 100, 50, 0, 51); + testConditional("testStrideNegScaleNegConditionalInIntLoop", 0, 100, Long.MAX_VALUE, 100, Long.MIN_VALUE + 50, 52, 100); + testConditional("testStrideNegScalePosConditionalInIntLoop", 0, 100, 100, 0, -50, 50, 100); + testConditional("testStrideNegScalePosConditionalInIntLoop", 0, 100, Long.MAX_VALUE, 100, Long.MAX_VALUE - 50, 0, 50); + testConditional("testStridePosScaleNegConditionalInIntLoop", 0, 100, 100, 99, 50, 0, 51); + testConditional("testStridePosScaleNegConditionalInIntLoop", 0, 100, Long.MAX_VALUE, 99, Long.MIN_VALUE + 50, 52, 100); test("testStridePosScalePosNotOneInIntLoop2", 0, 100, 1090, 0); @@ -411,6 +435,36 @@ public class TestLongRangeCheck { } } + public static void testStrideNegScaleNegConditional(long start, long stop, long length, long offset, long start2, long stop2) { + final long scale = -1; + final long stride = 1; + for (long i = stop; i > start; i -= stride) { + if (i >= start2 && i < stop2) { + Preconditions.checkIndex(scale * i + offset, length, null); + } + } + } + + public static void testStrideNegScalePosConditional(long start, long stop, long length, long offset, long start2, long stop2) { + final long scale = 1; + final long stride = 1; + for (long i = stop-1; i >= start; i -= stride) { + if (i >= start2 && i < stop2) { + Preconditions.checkIndex(scale * i + offset, length, null); + } + } + } + + public static void testStridePosScaleNegConditional(long start, long stop, long length, long offset, long start2, long stop2) { + final long scale = -1; + final long stride = 1; + for (long i = start; i < stop; i += stride) { + if (i >= start2 && i < stop2) { + Preconditions.checkIndex(scale * i + offset, length, null); + } + } + } + private static void checkInputs(long... inputs) { for (int i = 0; i < inputs.length; i++) { if ((long)((int)inputs[i]) != inputs[i]) { @@ -529,7 +583,6 @@ public class TestLongRangeCheck { public static void testStridePosScalePosConditionalInIntLoop(long start, long stop, long length, long offset, long start2, long stop2) { checkInputs(start, stop, start2, stop2); - Preconditions.checkIndex(0, length, null); final long scale = 1; final int stride = 1; for (int i = (int)start; i < (int)stop; i += stride) { @@ -539,6 +592,39 @@ public class TestLongRangeCheck { } } + public static void testStrideNegScaleNegConditionalInIntLoop(long start, long stop, long length, long offset, long start2, long stop2) { + checkInputs(start, stop, start2, stop2); + final long scale = -1; + final int stride = 1; + for (int i = (int)stop; i > (int)start; i -= stride) { + if (i >= (int)start2 && i < (int)stop2) { + Preconditions.checkIndex(scale * i + offset, length, null); + } + } + } + + public static void testStrideNegScalePosConditionalInIntLoop(long start, long stop, long length, long offset, long start2, long stop2) { + checkInputs(start, stop, start2, stop2); + final long scale = 1; + final int stride = 1; + for (int i = (int)(stop-1); i >= (int)start; i -= stride) { + if (i >= (int)start2 && i < (int)stop2) { + Preconditions.checkIndex(scale * i + offset, length, null); + } + } + } + + public static void testStridePosScaleNegConditionalInIntLoop(long start, long stop, long length, long offset, long start2, long stop2) { + checkInputs(start, stop, start2, stop2); + final long scale = -1; + final int stride = 1; + for (int i = (int)start; i < (int)stop; i += stride) { + if (i >= (int)start2 && i < (int)stop2) { + Preconditions.checkIndex(scale * i + offset, length, null); + } + } + } + public static void testStridePosScalePosNotOneInIntLoop2(long start, long stop, long length, long offset) { checkInputs(start, stop); final int scale = 11; -- GitLab From 7194097bcae7e0fd32488834277bb18cb97cea8b Mon Sep 17 00:00:00 2001 From: Kim Barrett Date: Mon, 7 Mar 2022 17:35:21 +0000 Subject: [PATCH 014/345] 8252577: HotSpot Style Guide should link to One-True-Brace-Style description Reviewed-by: stuefe, dcubed, dholmes --- doc/hotspot-style.html | 2 +- doc/hotspot-style.md | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/doc/hotspot-style.html b/doc/hotspot-style.html index eb0c8de2ae5..75211788c6b 100644 --- a/doc/hotspot-style.html +++ b/doc/hotspot-style.html @@ -153,7 +153,7 @@

Whitespace