From 8e7f0108efedcd90c9b7536004f1ba3d2b150408 Mon Sep 17 00:00:00 2001 From: Sergey Bylokhov Date: Thu, 24 Sep 2026 03:58:58 +0000 Subject: [PATCH 001/103] 8392733: Add missing @Override annotations in "javax.imageio.*" packages Reviewed-by: prr, azvegint --- .../share/classes/javax/imageio/ImageIO.java | 13 +++ .../classes/javax/imageio/ImageReadParam.java | 3 +- .../javax/imageio/ImageTypeSpecifier.java | 8 +- .../classes/javax/imageio/ImageWriter.java | 2 + .../metadata/IIOMetadataFormatImpl.java | 28 ++++++- .../imageio/metadata/IIOMetadataNode.java | 81 ++++++++++++++++++- .../plugins/jpeg/JPEGHuffmanTable.java | 3 +- .../plugins/jpeg/JPEGImageWriteParam.java | 6 +- .../imageio/plugins/jpeg/JPEGQTable.java | 3 +- .../javax/imageio/spi/IIOServiceProvider.java | 4 +- .../imageio/spi/PartiallyOrderedSet.java | 11 ++- .../javax/imageio/spi/ServiceRegistry.java | 3 + 12 files changed, 156 insertions(+), 9 deletions(-) diff --git a/src/java.desktop/share/classes/javax/imageio/ImageIO.java b/src/java.desktop/share/classes/javax/imageio/ImageIO.java index 5d3526df9174..bf7f61cc7819 100644 --- a/src/java.desktop/share/classes/javax/imageio/ImageIO.java +++ b/src/java.desktop/share/classes/javax/imageio/ImageIO.java @@ -469,10 +469,12 @@ public ImageReaderIterator(Iterator iter) { this.iter = iter; } + @Override public boolean hasNext() { return iter.hasNext(); } + @Override public ImageReader next() { ImageReaderSpi spi = null; try { @@ -486,6 +488,7 @@ public ImageReader next() { return null; } + @Override public void remove() { throw new UnsupportedOperationException(); } @@ -500,6 +503,7 @@ public CanDecodeInputFilter(Object input) { this.input = input; } + @Override public boolean filter(Object elt) { try { ImageReaderSpi spi = (ImageReaderSpi)elt; @@ -542,6 +546,7 @@ public CanEncodeImageAndFormatFilter(ImageTypeSpecifier type, this.formatName = formatName; } + @Override public boolean filter(Object elt) { ImageWriterSpi spi = (ImageWriterSpi)elt; return Arrays.asList(spi.getFormatNames()).contains(formatName) && @@ -562,6 +567,7 @@ public ContainsFilter(Method method, this.name = name; } + @Override public boolean filter(Object elt) { try { return contains((String[])method.invoke(elt), name); @@ -786,10 +792,12 @@ public ImageWriterIterator(Iterator iter) { this.iter = iter; } + @Override public boolean hasNext() { return iter.hasNext(); } + @Override public ImageWriter next() { ImageWriterSpi spi = null; try { @@ -802,6 +810,7 @@ public ImageWriter next() { return null; } + @Override public void remove() { throw new UnsupportedOperationException(); } @@ -1138,16 +1147,19 @@ public ImageTranscoderIterator(Iterator iter) { this.iter = iter; } + @Override public boolean hasNext() { return iter.hasNext(); } + @Override public ImageTranscoder next() { ImageTranscoderSpi spi = null; spi = iter.next(); return spi.createTranscoderInstance(); } + @Override public void remove() { throw new UnsupportedOperationException(); } @@ -1165,6 +1177,7 @@ public TranscoderFilter(ImageReaderSpi readerSpi, this.writerSpiName = writerSpi.getClass().getName(); } + @Override public boolean filter(Object elt) { ImageTranscoderSpi spi = (ImageTranscoderSpi)elt; String readerName = spi.getReaderServiceProviderName(); diff --git a/src/java.desktop/share/classes/javax/imageio/ImageReadParam.java b/src/java.desktop/share/classes/javax/imageio/ImageReadParam.java index 00866f60e7b1..ad32764ef9f9 100644 --- a/src/java.desktop/share/classes/javax/imageio/ImageReadParam.java +++ b/src/java.desktop/share/classes/javax/imageio/ImageReadParam.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, 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 @@ -197,6 +197,7 @@ public class ImageReadParam extends IIOParam { public ImageReadParam() {} // Comment inherited + @Override public void setDestinationType(ImageTypeSpecifier destinationType) { super.setDestinationType(destinationType); setDestination(null); diff --git a/src/java.desktop/share/classes/javax/imageio/ImageTypeSpecifier.java b/src/java.desktop/share/classes/javax/imageio/ImageTypeSpecifier.java index 148239509513..e271a7946dd3 100644 --- a/src/java.desktop/share/classes/javax/imageio/ImageTypeSpecifier.java +++ b/src/java.desktop/share/classes/javax/imageio/ImageTypeSpecifier.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -327,6 +327,7 @@ public Interleaved(ColorSpace colorSpace, bandOffsets); } + @Override public boolean equals(Object o) { if (!(o instanceof Interleaved that)) { return false; @@ -349,6 +350,7 @@ public boolean equals(Object o) { return true; } + @Override public int hashCode() { return (super.hashCode() + (4 * bandOffsets.length) + @@ -467,6 +469,7 @@ public Banded(ColorSpace colorSpace, bandOffsets); } + @Override public boolean equals(Object o) { if (!(o instanceof Banded that)) { return false; @@ -496,6 +499,7 @@ public boolean equals(Object o) { return true; } + @Override public int hashCode() { return (super.hashCode() + (3 * bandOffsets.length) + @@ -1087,6 +1091,7 @@ public BufferedImage createBufferedImage(int width, int height) { * @return {@code true} if the given object is an equivalent * {@code ImageTypeSpecifier}. */ + @Override public boolean equals(Object o) { if (!(o instanceof ImageTypeSpecifier that)) { return false; @@ -1101,6 +1106,7 @@ public boolean equals(Object o) { * * @return a hash code for this ImageTypeSpecifier */ + @Override public int hashCode() { return (9 * colorModel.hashCode()) + (14 * sampleModel.hashCode()); } diff --git a/src/java.desktop/share/classes/javax/imageio/ImageWriter.java b/src/java.desktop/share/classes/javax/imageio/ImageWriter.java index 84c66721efe1..147e516b83be 100644 --- a/src/java.desktop/share/classes/javax/imageio/ImageWriter.java +++ b/src/java.desktop/share/classes/javax/imageio/ImageWriter.java @@ -409,10 +409,12 @@ public ImageWriteParam getDefaultWriteParam() { ImageWriteParam param); // comment inherited + @Override public abstract IIOMetadata convertStreamMetadata(IIOMetadata inData, ImageWriteParam param); // comment inherited + @Override public abstract IIOMetadata convertImageMetadata(IIOMetadata inData, ImageTypeSpecifier imageType, diff --git a/src/java.desktop/share/classes/javax/imageio/metadata/IIOMetadataFormatImpl.java b/src/java.desktop/share/classes/javax/imageio/metadata/IIOMetadataFormatImpl.java index 3f7cacdaaf0a..fedb0c6ea71e 100644 --- a/src/java.desktop/share/classes/javax/imageio/metadata/IIOMetadataFormatImpl.java +++ b/src/java.desktop/share/classes/javax/imageio/metadata/IIOMetadataFormatImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -917,15 +917,18 @@ protected void removeObjectValue(String elementName) { // Root + @Override public String getRootName() { return rootName; } // Multiplicity + @Override public abstract boolean canNodeAppear(String elementName, ImageTypeSpecifier imageType); + @Override public int getElementMinChildren(String elementName) { Element element = getElement(elementName); if (element.childPolicy != CHILD_POLICY_REPEAT) { @@ -934,6 +937,7 @@ public int getElementMinChildren(String elementName) { return element.minChildren; } + @Override public int getElementMaxChildren(String elementName) { Element element = getElement(elementName); if (element.childPolicy != CHILD_POLICY_REPEAT) { @@ -995,6 +999,7 @@ private String getResource(String key, Locale locale) { * * @see #setResourceBaseName */ + @Override public String getElementDescription(String elementName, Locale locale) { Element element = getElement(elementName); @@ -1003,11 +1008,13 @@ public String getElementDescription(String elementName, // Children + @Override public int getChildPolicy(String elementName) { Element element = getElement(elementName); return element.childPolicy; } + @Override public String[] getChildNames(String elementName) { Element element = getElement(elementName); if (element.childPolicy == CHILD_POLICY_EMPTY) { @@ -1018,6 +1025,7 @@ public String[] getChildNames(String elementName) { // Attributes + @Override public String[] getAttributeNames(String elementName) { Element element = getElement(elementName); List names = element.attrList; @@ -1026,27 +1034,32 @@ public String[] getAttributeNames(String elementName) { return names.toArray(result); } + @Override public int getAttributeValueType(String elementName, String attrName) { Attribute attr = getAttribute(elementName, attrName); return attr.valueType; } + @Override public int getAttributeDataType(String elementName, String attrName) { Attribute attr = getAttribute(elementName, attrName); return attr.dataType; } + @Override public boolean isAttributeRequired(String elementName, String attrName) { Attribute attr = getAttribute(elementName, attrName); return attr.required; } + @Override public String getAttributeDefaultValue(String elementName, String attrName) { Attribute attr = getAttribute(elementName, attrName); return attr.defaultValue; } + @Override public String[] getAttributeEnumerations(String elementName, String attrName) { Attribute attr = getAttribute(elementName, attrName); @@ -1060,6 +1073,7 @@ public String[] getAttributeEnumerations(String elementName, return values.toArray(result); } + @Override public String getAttributeMinValue(String elementName, String attrName) { Attribute attr = getAttribute(elementName, attrName); if (attr.valueType != VALUE_RANGE && @@ -1072,6 +1086,7 @@ public String getAttributeMinValue(String elementName, String attrName) { return attr.minValue; } + @Override public String getAttributeMaxValue(String elementName, String attrName) { Attribute attr = getAttribute(elementName, attrName); if (attr.valueType != VALUE_RANGE && @@ -1084,6 +1099,7 @@ public String getAttributeMaxValue(String elementName, String attrName) { return attr.maxValue; } + @Override public int getAttributeListMinLength(String elementName, String attrName) { Attribute attr = getAttribute(elementName, attrName); if (attr.valueType != VALUE_LIST) { @@ -1093,6 +1109,7 @@ public int getAttributeListMinLength(String elementName, String attrName) { return attr.listMinLength; } + @Override public int getAttributeListMaxLength(String elementName, String attrName) { Attribute attr = getAttribute(elementName, attrName); if (attr.valueType != VALUE_LIST) { @@ -1138,6 +1155,7 @@ public int getAttributeListMaxLength(String elementName, String attrName) { * * @see #setResourceBaseName */ + @Override public String getAttributeDescription(String elementName, String attrName, Locale locale) { @@ -1164,6 +1182,7 @@ private ObjectValue getObjectValue(String elementName) { return objv; } + @Override public int getObjectValueType(String elementName) { Element element = getElement(elementName); ObjectValue objv = element.objectValue; @@ -1173,16 +1192,19 @@ public int getObjectValueType(String elementName) { return objv.valueType; } + @Override public Class getObjectClass(String elementName) { ObjectValue objv = getObjectValue(elementName); return objv.classType; } + @Override public Object getObjectDefaultValue(String elementName) { ObjectValue objv = getObjectValue(elementName); return objv.defaultValue; } + @Override public Object[] getObjectEnumerations(String elementName) { ObjectValue objv = getObjectValue(elementName); if (objv.valueType != VALUE_ENUMERATION) { @@ -1193,6 +1215,7 @@ public Object[] getObjectEnumerations(String elementName) { return vlist.toArray(values); } + @Override public Comparable getObjectMinValue(String elementName) { ObjectValue objv = getObjectValue(elementName); if ((objv.valueType & VALUE_RANGE) != VALUE_RANGE) { @@ -1201,6 +1224,7 @@ public Comparable getObjectMinValue(String elementName) { return objv.minValue; } + @Override public Comparable getObjectMaxValue(String elementName) { ObjectValue objv = getObjectValue(elementName); if ((objv.valueType & VALUE_RANGE) != VALUE_RANGE) { @@ -1209,6 +1233,7 @@ public Comparable getObjectMaxValue(String elementName) { return objv.maxValue; } + @Override public int getObjectArrayMinLength(String elementName) { ObjectValue objv = getObjectValue(elementName); if (objv.valueType != VALUE_LIST) { @@ -1217,6 +1242,7 @@ public int getObjectArrayMinLength(String elementName) { return objv.arrayMinLength; } + @Override public int getObjectArrayMaxLength(String elementName) { ObjectValue objv = getObjectValue(elementName); if (objv.valueType != VALUE_LIST) { diff --git a/src/java.desktop/share/classes/javax/imageio/metadata/IIOMetadataNode.java b/src/java.desktop/share/classes/javax/imageio/metadata/IIOMetadataNode.java index 87ac300f1bc0..2f662e01f7eb 100644 --- a/src/java.desktop/share/classes/javax/imageio/metadata/IIOMetadataNode.java +++ b/src/java.desktop/share/classes/javax/imageio/metadata/IIOMetadataNode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -64,10 +64,12 @@ public IIONamedNodeMap(List nodes) { this.nodes = nodes; } + @Override public int getLength() { return nodes.size(); } + @Override public Node getNamedItem(String name) { for (Node node : nodes) { if (name.equals(node.getNodeName())) { @@ -78,16 +80,19 @@ public Node getNamedItem(String name) { return null; } + @Override public Node item(int index) { Node node = nodes.get(index); return node; } + @Override public Node removeNamedItem(java.lang.String name) { throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, "This NamedNodeMap is read-only!"); } + @Override public Node setNamedItem(Node arg) { throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, "This NamedNodeMap is read-only!"); @@ -96,6 +101,7 @@ public Node setNamedItem(Node arg) { /** * Equivalent to {@code getNamedItem(localName)}. */ + @Override public Node getNamedItemNS(String namespaceURI, String localName) { return getNamedItem(localName); } @@ -103,6 +109,7 @@ public Node getNamedItemNS(String namespaceURI, String localName) { /** * Equivalent to {@code setNamedItem(arg)}. */ + @Override public Node setNamedItemNS(Node arg) { return setNamedItem(arg); } @@ -110,6 +117,7 @@ public Node setNamedItemNS(Node arg) { /** * Equivalent to {@code removeNamedItem(localName)}. */ + @Override public Node removeNamedItemNS(String namespaceURI, String localName) { return removeNamedItem(localName); } @@ -123,10 +131,12 @@ public IIONodeList(List nodes) { this.nodes = nodes; } + @Override public int getLength() { return nodes.size(); } + @Override public Node item(int index) { if (index < 0 || index >= nodes.size()) { return null; @@ -147,38 +157,47 @@ public IIOAttr(Element owner, String name, String value) { this.value = value; } + @Override public String getName() { return name; } + @Override public String getNodeName() { return name; } + @Override public short getNodeType() { return ATTRIBUTE_NODE; } + @Override public boolean getSpecified() { return true; } + @Override public String getValue() { return value; } + @Override public String getNodeValue() { return value; } + @Override public void setValue(String value) { this.value = value; } + @Override public void setNodeValue(String value) { this.value = value; } + @Override public Element getOwnerElement() { return owner; } @@ -195,6 +214,7 @@ public void setOwnerElement(Element owner) { * DOMException are publicly documented as such on IIOMetadataNode. * @return false */ + @Override public boolean isId() { return false; } @@ -330,6 +350,7 @@ private void checkNode(Node node) throws DOMException { * * @return the node name, as a {@code String}. */ + @Override public String getNodeName() { return nodeName; } @@ -339,6 +360,7 @@ public String getNodeName() { * * @return the node value, as a {@code String}. */ + @Override public String getNodeValue(){ return nodeValue; } @@ -346,6 +368,7 @@ public String getNodeValue(){ /** * Sets the {@code String} value associated with this node. */ + @Override public void setNodeValue(String nodeValue) { this.nodeValue = nodeValue; } @@ -356,6 +379,7 @@ public void setNodeValue(String nodeValue) { * * @return the {@code short} value {@code ELEMENT_NODE}. */ + @Override public short getNodeType() { return ELEMENT_NODE; } @@ -373,6 +397,7 @@ public short getNodeType() { * @see #replaceChild * @see #appendChild */ + @Override public Node getParentNode() { return parent; } @@ -384,6 +409,7 @@ public Node getParentNode() { * * @return the children as a {@code NodeList} */ + @Override public NodeList getChildNodes() { return this; } @@ -395,6 +421,7 @@ public NodeList getChildNodes() { * @return the first child, as a {@code Node}, or * {@code null} */ + @Override public Node getFirstChild() { return firstChild; } @@ -406,6 +433,7 @@ public Node getFirstChild() { * @return the last child, as a {@code Node}, or * {@code null}. */ + @Override public Node getLastChild() { return lastChild; } @@ -417,6 +445,7 @@ public Node getLastChild() { * @return the previous sibling, as a {@code Node}, or * {@code null}. */ + @Override public Node getPreviousSibling() { return previousSibling; } @@ -428,6 +457,7 @@ public Node getPreviousSibling() { * @return the next sibling, as a {@code Node}, or * {@code null}. */ + @Override public Node getNextSibling() { return nextSibling; } @@ -439,6 +469,7 @@ public Node getNextSibling() { * @return a {@code NamedNodeMap} containing the attributes of * this node. */ + @Override public NamedNodeMap getAttributes() { return new IIONamedNodeMap(attributes); } @@ -449,6 +480,7 @@ public NamedNodeMap getAttributes() { * * @return {@code null}. */ + @Override public Document getOwnerDocument() { return null; } @@ -467,6 +499,7 @@ public Document getOwnerDocument() { * @throws IllegalArgumentException if {@code newChild} is * {@code null}. */ + @Override public Node insertBefore(Node newChild, Node refChild) { if (newChild == null) { @@ -525,6 +558,7 @@ public Node insertBefore(Node newChild, * @throws IllegalArgumentException if {@code newChild} is * {@code null}. */ + @Override public Node replaceChild(Node newChild, Node oldChild) { if (newChild == null) { @@ -576,6 +610,7 @@ public Node replaceChild(Node newChild, * @throws IllegalArgumentException if {@code oldChild} is * {@code null}. */ + @Override public Node removeChild(Node oldChild) { if (oldChild == null) { throw new IllegalArgumentException("oldChild == null!"); @@ -620,6 +655,7 @@ public Node removeChild(Node oldChild) { * @throws IllegalArgumentException if {@code newChild} is * {@code null}. */ + @Override public Node appendChild(Node newChild) { if (newChild == null) { throw new IllegalArgumentException("newChild == null!"); @@ -635,6 +671,7 @@ public Node appendChild(Node newChild) { * * @return {@code true} if this node has children. */ + @Override public boolean hasChildNodes() { return numChildren > 0; } @@ -653,6 +690,7 @@ public boolean hasChildNodes() { * * @return the duplicate node. */ + @Override public Node cloneNode(boolean deep) { IIOMetadataNode newNode = new IIOMetadataNode(this.nodeName); newNode.setUserObject(getUserObject()); @@ -673,6 +711,7 @@ public Node cloneNode(boolean deep) { * Does nothing, since {@code IIOMetadataNode}s do not * contain {@code Text} children. */ + @Override public void normalize() { } @@ -685,6 +724,7 @@ public void normalize() { * @param feature a {@code String}, which is ignored. * @param version a {@code String}, which is ignored. */ + @Override public boolean isSupported(String feature, String version) { return false; } @@ -692,6 +732,7 @@ public boolean isSupported(String feature, String version) { /** * Returns {@code null}, since namespaces are not supported. */ + @Override public String getNamespaceURI() throws DOMException { return null; } @@ -703,6 +744,7 @@ public String getNamespaceURI() throws DOMException { * * @see #setPrefix */ + @Override public String getPrefix() { return null; } @@ -714,6 +756,7 @@ public String getPrefix() { * * @see #getPrefix */ + @Override public void setPrefix(String prefix) { } @@ -722,6 +765,7 @@ public void setPrefix(String prefix) { * * @return the node name, as a {@code String}. */ + @Override public String getLocalName() { return nodeName; } @@ -734,6 +778,7 @@ public String getLocalName() { * * @return the node name, as a {@code String} */ + @Override public String getTagName() { return nodeName; } @@ -744,6 +789,7 @@ public String getTagName() { * @return The {@code Attr} value as a string, or the empty string * if that attribute does not have a specified or default value. */ + @Override public String getAttribute(String name) { Attr attr = getAttributeNode(name); if (attr == null) { @@ -757,10 +803,12 @@ public String getAttribute(String name) { * * @see #setAttributeNS */ + @Override public String getAttributeNS(String namespaceURI, String localName) { return getAttribute(localName); } + @Override public void setAttribute(String name, String value) { // Name must be valid unicode chars boolean valid = true; @@ -784,11 +832,13 @@ public void setAttribute(String name, String value) { * * @see #getAttributeNS */ + @Override public void setAttributeNS(String namespaceURI, String qualifiedName, String value) { setAttribute(qualifiedName, value); } + @Override public void removeAttribute(String name) { removeAttribute(name, true); } @@ -814,11 +864,13 @@ private void removeAttribute(String name, boolean checkPresent) { /** * Equivalent to {@code removeAttribute(localName)}. */ + @Override public void removeAttributeNS(String namespaceURI, String localName) { removeAttribute(localName); } + @Override public Attr getAttributeNode(String name) { Node node = getAttributes().getNamedItem(name); return (Attr)node; @@ -829,11 +881,13 @@ public Attr getAttributeNode(String name) { * * @see #setAttributeNodeNS */ + @Override public Attr getAttributeNodeNS(String namespaceURI, String localName) { return getAttributeNode(localName); } + @Override public Attr setAttributeNode(Attr newAttr) throws DOMException { Element owner = newAttr.getOwnerElement(); if (owner != null) { @@ -870,15 +924,18 @@ public Attr setAttributeNode(Attr newAttr) throws DOMException { * * @see #getAttributeNodeNS */ + @Override public Attr setAttributeNodeNS(Attr newAttr) { return setAttributeNode(newAttr); } + @Override public Attr removeAttributeNode(Attr oldAttr) { removeAttribute(oldAttr.getName()); return oldAttr; } + @Override public NodeList getElementsByTagName(String name) { List l = new ArrayList<>(); getElementsByTagName(name, l); @@ -900,15 +957,18 @@ private void getElementsByTagName(String name, List l) { /** * Equivalent to {@code getElementsByTagName(localName)}. */ + @Override public NodeList getElementsByTagNameNS(String namespaceURI, String localName) { return getElementsByTagName(localName); } + @Override public boolean hasAttributes() { return attributes.size() > 0; } + @Override public boolean hasAttribute(String name) { return getAttributeNode(name) != null; } @@ -916,6 +976,7 @@ public boolean hasAttribute(String name) { /** * Equivalent to {@code hasAttribute(localName)}. */ + @Override public boolean hasAttributeNS(String namespaceURI, String localName) { return hasAttribute(localName); @@ -923,10 +984,12 @@ public boolean hasAttributeNS(String namespaceURI, // Methods from NodeList + @Override public int getLength() { return numChildren; } + @Override public Node item(int index) { if (index < 0) { return null; @@ -968,6 +1031,7 @@ public void setUserObject(Object userObject) { * and will throw a {@code DOMException}. * @throws DOMException always. */ + @Override public void setIdAttribute(String name, boolean isId) throws DOMException { @@ -980,6 +1044,7 @@ public void setIdAttribute(String name, * and will throw a {@code DOMException}. * @throws DOMException always. */ + @Override public void setIdAttributeNS(String namespaceURI, String localName, boolean isId) @@ -993,6 +1058,7 @@ public void setIdAttributeNS(String namespaceURI, * and will throw a {@code DOMException}. * @throws DOMException always. */ + @Override public void setIdAttributeNode(Attr idAttr, boolean isId) throws DOMException { @@ -1005,6 +1071,7 @@ public void setIdAttributeNode(Attr idAttr, * and will throw a {@code DOMException}. * @throws DOMException always. */ + @Override public TypeInfo getSchemaTypeInfo() throws DOMException { throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Method not supported"); @@ -1015,6 +1082,7 @@ public TypeInfo getSchemaTypeInfo() throws DOMException { * and will throw a {@code DOMException}. * @throws DOMException always. */ + @Override public Object setUserData(String key, Object data, UserDataHandler handler) throws DOMException { @@ -1027,6 +1095,7 @@ public Object setUserData(String key, * and will throw a {@code DOMException}. * @throws DOMException always. */ + @Override public Object getUserData(String key) throws DOMException { throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Method not supported"); @@ -1037,6 +1106,7 @@ public Object getUserData(String key) throws DOMException { * and will throw a {@code DOMException}. * @throws DOMException always. */ + @Override public Object getFeature(String feature, String version) throws DOMException { throw new DOMException(DOMException.NOT_SUPPORTED_ERR, @@ -1048,6 +1118,7 @@ public Object getFeature(String feature, String version) * and will throw a {@code DOMException}. * @throws DOMException always. */ + @Override public boolean isSameNode(Node node) throws DOMException { throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Method not supported"); @@ -1058,6 +1129,7 @@ public boolean isSameNode(Node node) throws DOMException { * and will throw a {@code DOMException}. * @throws DOMException always. */ + @Override public boolean isEqualNode(Node node) throws DOMException { throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Method not supported"); @@ -1068,6 +1140,7 @@ public boolean isEqualNode(Node node) throws DOMException { * and will throw a {@code DOMException}. * @throws DOMException always. */ + @Override public String lookupNamespaceURI(String prefix) throws DOMException { throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Method not supported"); @@ -1078,6 +1151,7 @@ public String lookupNamespaceURI(String prefix) throws DOMException { * and will throw a {@code DOMException}. * @throws DOMException always. */ + @Override public boolean isDefaultNamespace(String namespaceURI) throws DOMException { throw new DOMException(DOMException.NOT_SUPPORTED_ERR, @@ -1089,6 +1163,7 @@ public boolean isDefaultNamespace(String namespaceURI) * and will throw a {@code DOMException}. * @throws DOMException always. */ + @Override public String lookupPrefix(String namespaceURI) throws DOMException { throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Method not supported"); @@ -1099,6 +1174,7 @@ public String lookupPrefix(String namespaceURI) throws DOMException { * and will throw a {@code DOMException}. * @throws DOMException always. */ + @Override public String getTextContent() throws DOMException { throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Method not supported"); @@ -1109,6 +1185,7 @@ public String getTextContent() throws DOMException { * and will throw a {@code DOMException}. * @throws DOMException always. */ + @Override public void setTextContent(String textContent) throws DOMException { throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Method not supported"); @@ -1119,6 +1196,7 @@ public void setTextContent(String textContent) throws DOMException { * and will throw a {@code DOMException}. * @throws DOMException always. */ + @Override public short compareDocumentPosition(Node other) throws DOMException { throw new DOMException(DOMException.NOT_SUPPORTED_ERR, @@ -1130,6 +1208,7 @@ public short compareDocumentPosition(Node other) * and will throw a {@code DOMException}. * @throws DOMException always. */ + @Override public String getBaseURI() throws DOMException { throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Method not supported"); diff --git a/src/java.desktop/share/classes/javax/imageio/plugins/jpeg/JPEGHuffmanTable.java b/src/java.desktop/share/classes/javax/imageio/plugins/jpeg/JPEGHuffmanTable.java index 4c5a2a647ee4..702096775ef7 100644 --- a/src/java.desktop/share/classes/javax/imageio/plugins/jpeg/JPEGHuffmanTable.java +++ b/src/java.desktop/share/classes/javax/imageio/plugins/jpeg/JPEGHuffmanTable.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2026, 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 @@ -240,6 +240,7 @@ public short[] getValues() { * Returns a {@code String} representing this Huffman table. * @return a {@code String} representing this Huffman table. */ + @Override public String toString() { String ls = System.getProperty("line.separator", "\n"); StringBuilder sb = new StringBuilder("JPEGHuffmanTable"); diff --git a/src/java.desktop/share/classes/javax/imageio/plugins/jpeg/JPEGImageWriteParam.java b/src/java.desktop/share/classes/javax/imageio/plugins/jpeg/JPEGImageWriteParam.java index de9c077d8c7e..e4f79462d909 100644 --- a/src/java.desktop/share/classes/javax/imageio/plugins/jpeg/JPEGImageWriteParam.java +++ b/src/java.desktop/share/classes/javax/imageio/plugins/jpeg/JPEGImageWriteParam.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -133,6 +133,7 @@ public JPEGImageWriteParam(Locale locale) { * @throws IllegalStateException if the compression mode is not * {@code MODE_EXPLICIT}. */ + @Override public void unsetCompression() { if (getCompressionMode() != MODE_EXPLICIT) { throw new IllegalStateException @@ -150,6 +151,7 @@ public void unsetCompression() { * @throws IllegalStateException if the compression mode is not * {@code MODE_EXPLICIT}. */ + @Override public boolean isCompressionLossless() { if (getCompressionMode() != MODE_EXPLICIT) { throw new IllegalStateException @@ -158,6 +160,7 @@ public boolean isCompressionLossless() { return false; } + @Override public String[] getCompressionQualityDescriptions() { if (getCompressionMode() != MODE_EXPLICIT) { throw new IllegalStateException @@ -170,6 +173,7 @@ public String[] getCompressionQualityDescriptions() { return qualityDescs.clone(); } + @Override public float[] getCompressionQualityValues() { if (getCompressionMode() != MODE_EXPLICIT) { throw new IllegalStateException diff --git a/src/java.desktop/share/classes/javax/imageio/plugins/jpeg/JPEGQTable.java b/src/java.desktop/share/classes/javax/imageio/plugins/jpeg/JPEGQTable.java index ab36e6888173..5b1e8c0e4152 100644 --- a/src/java.desktop/share/classes/javax/imageio/plugins/jpeg/JPEGQTable.java +++ b/src/java.desktop/share/classes/javax/imageio/plugins/jpeg/JPEGQTable.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2026, 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 @@ -195,6 +195,7 @@ public JPEGQTable getScaledInstance(float scaleFactor, * Returns a {@code String} representing this quantization table. * @return a {@code String} representing this quantization table. */ + @Override public String toString() { String ls = System.getProperty("line.separator", "\n"); StringBuilder sb = new StringBuilder("JPEGQTable:"+ls); diff --git a/src/java.desktop/share/classes/javax/imageio/spi/IIOServiceProvider.java b/src/java.desktop/share/classes/javax/imageio/spi/IIOServiceProvider.java index 9bb3cbac97e5..dceff394aa66 100644 --- a/src/java.desktop/share/classes/javax/imageio/spi/IIOServiceProvider.java +++ b/src/java.desktop/share/classes/javax/imageio/spi/IIOServiceProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2004, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -107,6 +107,7 @@ public IIOServiceProvider() { * * @see ServiceRegistry#registerServiceProvider(Object provider) */ + @Override public void onRegistration(ServiceRegistry registry, Class category) {} @@ -120,6 +121,7 @@ public void onRegistration(ServiceRegistry registry, * * @see ServiceRegistry#deregisterServiceProvider(Object provider) */ + @Override public void onDeregistration(ServiceRegistry registry, Class category) {} diff --git a/src/java.desktop/share/classes/javax/imageio/spi/PartiallyOrderedSet.java b/src/java.desktop/share/classes/javax/imageio/spi/PartiallyOrderedSet.java index f88724cce6a3..bfddb4e7b7e0 100644 --- a/src/java.desktop/share/classes/javax/imageio/spi/PartiallyOrderedSet.java +++ b/src/java.desktop/share/classes/javax/imageio/spi/PartiallyOrderedSet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -64,10 +64,12 @@ class PartiallyOrderedSet extends AbstractSet { */ public PartiallyOrderedSet() {} + @Override public int size() { return poNodes.size(); } + @Override public boolean contains(Object o) { return poNodes.containsKey(o); } @@ -77,6 +79,7 @@ public boolean contains(Object o) { * collection, with an ordering that respects the orderings set * by the {@code setOrdering} method. */ + @Override public Iterator iterator() { return new PartialOrderIterator<>(poNodes.values().iterator()); } @@ -85,6 +88,7 @@ public Iterator iterator() { * Adds an {@code Object} to this * {@code PartiallyOrderedSet}. */ + @Override public boolean add(E o) { if (poNodes.containsKey(o)) { return false; @@ -99,6 +103,7 @@ public boolean add(E o) { * Removes an {@code Object} from this * {@code PartiallyOrderedSet}. */ + @Override public boolean remove(Object o) { DigraphNode node = poNodes.get(o); if (node == null) { @@ -110,6 +115,7 @@ public boolean remove(Object o) { return true; } + @Override public void clear() { poNodes.clear(); } @@ -175,10 +181,12 @@ public PartialOrderIterator(Iterator> iter) { } } + @Override public boolean hasNext() { return !zeroList.isEmpty(); } + @Override public E next() { DigraphNode first = zeroList.removeFirst(); @@ -198,6 +206,7 @@ public E next() { return first.getData(); } + @Override public void remove() { throw new UnsupportedOperationException(); } diff --git a/src/java.desktop/share/classes/javax/imageio/spi/ServiceRegistry.java b/src/java.desktop/share/classes/javax/imageio/spi/ServiceRegistry.java index 2d6edaca544d..ccc8e8165378 100644 --- a/src/java.desktop/share/classes/javax/imageio/spi/ServiceRegistry.java +++ b/src/java.desktop/share/classes/javax/imageio/spi/ServiceRegistry.java @@ -837,10 +837,12 @@ private void advance() { next = null; } + @Override public boolean hasNext() { return next != null; } + @Override public T next() { if (next == null) { throw new NoSuchElementException(); @@ -850,6 +852,7 @@ public T next() { return o; } + @Override public void remove() { throw new UnsupportedOperationException(); } From 5950d9fb705d027918a2920c074299b007a095a8 Mon Sep 17 00:00:00 2001 From: Eric Fang Date: Thu, 24 Sep 2026 04:27:23 +0000 Subject: [PATCH 002/103] 8387748: VectorAPI: AArch64: Improve VectorMask.lastTrue() SVE implementation Reviewed-by: ruben, xgong, aph --- src/hotspot/cpu/aarch64/aarch64_vector.ad | 8 ++++---- src/hotspot/cpu/aarch64/aarch64_vector_ad.m4 | 8 ++++---- .../cpu/aarch64/c2_MacroAssembler_aarch64.cpp | 17 +++++++++++------ .../cpu/aarch64/c2_MacroAssembler_aarch64.hpp | 2 +- .../vector/MaskQueryOperationsBenchmark.java | 18 +++++++++++++++++- 5 files changed, 37 insertions(+), 16 deletions(-) diff --git a/src/hotspot/cpu/aarch64/aarch64_vector.ad b/src/hotspot/cpu/aarch64/aarch64_vector.ad index eb36776f7061..d02caeba6fbc 100644 --- a/src/hotspot/cpu/aarch64/aarch64_vector.ad +++ b/src/hotspot/cpu/aarch64/aarch64_vector.ad @@ -6883,14 +6883,14 @@ instruct vmask_lasttrue_neon(iRegINoSp dst, vReg src) %{ ins_pipe(pipe_slow); %} -instruct vmask_lasttrue_sve(iRegINoSp dst, pReg src, pReg ptmp) %{ +instruct vmask_lasttrue_sve(iRegINoSp dst, pRegGov src, vReg vtmp, rFlagsReg cr) %{ predicate(UseSVE > 0); match(Set dst (VectorMaskLastTrue src)); - effect(TEMP ptmp); - format %{ "vmask_lasttrue_sve $dst, $src\t# KILL $ptmp" %} + effect(TEMP vtmp, KILL cr); + format %{ "vmask_lasttrue_sve $dst, $src\t# KILL $vtmp, cr" %} ins_encode %{ BasicType bt = Matcher::vector_element_basic_type(this, $src); - __ sve_vmask_lasttrue($dst$$Register, bt, $src$$PRegister, $ptmp$$PRegister); + __ sve_vmask_lasttrue($dst$$Register, bt, $src$$PRegister, $vtmp$$FloatRegister); %} ins_pipe(pipe_slow); %} diff --git a/src/hotspot/cpu/aarch64/aarch64_vector_ad.m4 b/src/hotspot/cpu/aarch64/aarch64_vector_ad.m4 index 2ecc8f336ad1..ab2c3326a9b9 100644 --- a/src/hotspot/cpu/aarch64/aarch64_vector_ad.m4 +++ b/src/hotspot/cpu/aarch64/aarch64_vector_ad.m4 @@ -4584,14 +4584,14 @@ instruct vmask_lasttrue_neon(iRegINoSp dst, vReg src) %{ ins_pipe(pipe_slow); %} -instruct vmask_lasttrue_sve(iRegINoSp dst, pReg src, pReg ptmp) %{ +instruct vmask_lasttrue_sve(iRegINoSp dst, pRegGov src, vReg vtmp, rFlagsReg cr) %{ predicate(UseSVE > 0); match(Set dst (VectorMaskLastTrue src)); - effect(TEMP ptmp); - format %{ "vmask_lasttrue_sve $dst, $src\t# KILL $ptmp" %} + effect(TEMP vtmp, KILL cr); + format %{ "vmask_lasttrue_sve $dst, $src\t# KILL $vtmp, cr" %} ins_encode %{ BasicType bt = Matcher::vector_element_basic_type(this, $src); - __ sve_vmask_lasttrue($dst$$Register, bt, $src$$PRegister, $ptmp$$PRegister); + __ sve_vmask_lasttrue($dst$$Register, bt, $src$$PRegister, $vtmp$$FloatRegister); %} ins_pipe(pipe_slow); %} diff --git a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp index 96a4f4d3daaf..79842aad05f1 100644 --- a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp @@ -1567,13 +1567,18 @@ void C2_MacroAssembler::sve_compare(PRegister pd, BasicType bt, PRegister pg, } // Get index of the last mask lane that is set -void C2_MacroAssembler::sve_vmask_lasttrue(Register dst, BasicType bt, PRegister src, PRegister ptmp) { +// Clobbers: rflags +void C2_MacroAssembler::sve_vmask_lasttrue(Register dst, BasicType bt, + PRegister src, FloatRegister vtmp) { SIMD_RegVariant size = elemType_to_regVariant(bt); - sve_rev(ptmp, size, src); - sve_brkb(ptmp, ptrue, ptmp, false); - sve_cntp(dst, size, ptrue, ptmp); - movw(rscratch1, MaxVectorSize / type2aelembytes(bt) - 1); - subw(dst, rscratch1, dst); + // vtmp = 0, 1, 2, ... + sve_index(vtmp, size, 0, 1); + // dst = last true or the highest-numbered element if src is all false + sve_lastb(dst, size, src, vtmp); + // Zero flag = 1 iff no active lane + sve_ptest(ptrue, src); + // active: keep; else -1 + csinvw(dst, dst, zr, Assembler::NE); } // Extend integer vector src to dst with the same lane count diff --git a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp index dc20813a2a1d..108191dc87ad 100644 --- a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp @@ -144,7 +144,7 @@ void sve_compare(PRegister pd, BasicType bt, PRegister pg, FloatRegister zn, FloatRegister zm, Condition cond); - void sve_vmask_lasttrue(Register dst, BasicType bt, PRegister src, PRegister ptmp); + void sve_vmask_lasttrue(Register dst, BasicType bt, PRegister src, FloatRegister vtmp); // Vector cast void neon_vector_extend(FloatRegister dst, BasicType dst_bt, unsigned dst_vlen_in_bytes, diff --git a/test/micro/org/openjdk/bench/jdk/incubator/vector/MaskQueryOperationsBenchmark.java b/test/micro/org/openjdk/bench/jdk/incubator/vector/MaskQueryOperationsBenchmark.java index c59a17fdf7f1..f571f061810c 100644 --- a/test/micro/org/openjdk/bench/jdk/incubator/vector/MaskQueryOperationsBenchmark.java +++ b/test/micro/org/openjdk/bench/jdk/incubator/vector/MaskQueryOperationsBenchmark.java @@ -1,5 +1,5 @@ // -// Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. +// Copyright (c) 2003, 2026, 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 @@ -43,10 +43,14 @@ public class MaskQueryOperationsBenchmark { VectorSpecies sspecies; VectorSpecies ispecies; VectorSpecies lspecies; + VectorSpecies fspecies; + VectorSpecies dspecies; VectorMask bmask; VectorMask smask; VectorMask imask; VectorMask lmask; + VectorMask fmask; + VectorMask dmask; boolean [] mask_arr; static final boolean [] mask_avg_case = { @@ -88,6 +92,8 @@ public void BmSetup() { sspecies = VectorSpecies.of(short.class, VectorShape.forBitSize(bits)); ispecies = VectorSpecies.of(int.class, VectorShape.forBitSize(bits)); lspecies = VectorSpecies.of(long.class, VectorShape.forBitSize(bits)); + fspecies = VectorSpecies.of(float.class, VectorShape.forBitSize(bits)); + dspecies = VectorSpecies.of(double.class, VectorShape.forBitSize(bits)); if( 1 == inputs) { mask_arr = mask_best_case; @@ -101,6 +107,8 @@ public void BmSetup() { smask = VectorMask.fromArray(sspecies, mask_arr, 0); imask = VectorMask.fromArray(ispecies, mask_arr, 0); lmask = VectorMask.fromArray(lspecies, mask_arr, 0); + fmask = VectorMask.fromArray(fspecies, mask_arr, 0); + dmask = VectorMask.fromArray(dspecies, mask_arr, 0); } @Benchmark @@ -156,6 +164,14 @@ public int testLastTrueInt(Blackhole bh) { public int testLastTrueLong(Blackhole bh) { return lmask.lastTrue(); } + @Benchmark + public int testLastTrueFloat(Blackhole bh) { + return fmask.lastTrue(); + } + @Benchmark + public int testLastTrueDouble(Blackhole bh) { + return dmask.lastTrue(); + } @Benchmark public long testToLongByte(Blackhole bh) { From e37e2a736d876273e8715d3a505802addefb838b Mon Sep 17 00:00:00 2001 From: Tobias Hartmann Date: Thu, 24 Sep 2026 05:02:21 +0000 Subject: [PATCH 003/103] 8392934: Remove unused ComputeExactFPURegisterUsage flag Reviewed-by: mchevalier, mhaessig --- src/hotspot/share/c1/c1_globals.hpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/hotspot/share/c1/c1_globals.hpp b/src/hotspot/share/c1/c1_globals.hpp index e69883bc436d..f65c6c7641d4 100644 --- a/src/hotspot/share/c1/c1_globals.hpp +++ b/src/hotspot/share/c1/c1_globals.hpp @@ -266,9 +266,6 @@ "If GraphBuilder adds this many instructions, bails out") \ range(0, max_jint) \ \ - develop(bool, ComputeExactFPURegisterUsage, true, \ - "Compute additional live set for fpu registers to simplify fpu stack merge (Intel only)") \ - \ product(bool, C1ProfileCalls, true, \ "Profile calls when generating code for updating MDOs") \ \ From 4041940c210c7dc709883c7b20c7844292b49135 Mon Sep 17 00:00:00 2001 From: Tobias Hartmann Date: Thu, 24 Sep 2026 05:02:49 +0000 Subject: [PATCH 004/103] 8392933: Remove unused IEEEPrecision flag Reviewed-by: mchevalier, mhaessig --- src/hotspot/cpu/x86/globals_x86.hpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/hotspot/cpu/x86/globals_x86.hpp b/src/hotspot/cpu/x86/globals_x86.hpp index 4e8d88ad4588..9016c3e89fa3 100644 --- a/src/hotspot/cpu/x86/globals_x86.hpp +++ b/src/hotspot/cpu/x86/globals_x86.hpp @@ -94,9 +94,6 @@ define_pd_global(bool, ValueTypeReturnedAsFields, true); range, \ constraint) \ \ - develop(bool, IEEEPrecision, true, \ - "Enables IEEE precision (for INTEL only)") \ - \ product(bool, UseStoreImmI16, true, \ "Use store immediate 16-bits value instruction on x86") \ \ From dc154c9336b21c3cb9e13f6b276aae4095bd7c2b Mon Sep 17 00:00:00 2001 From: Tobias Hartmann Date: Thu, 24 Sep 2026 05:03:29 +0000 Subject: [PATCH 005/103] 8392936: Remove FreqCountInvocations flag Reviewed-by: mchevalier, mhaessig --- src/hotspot/share/opto/block.hpp | 6 ++---- src/hotspot/share/opto/c2_globals.hpp | 4 ---- src/hotspot/share/opto/loopnode.cpp | 2 +- src/hotspot/share/opto/parse2.cpp | 8 ++++---- 4 files changed, 7 insertions(+), 13 deletions(-) diff --git a/src/hotspot/share/opto/block.hpp b/src/hotspot/share/opto/block.hpp index 645cb98a8356..d66611467529 100644 --- a/src/hotspot/share/opto/block.hpp +++ b/src/hotspot/share/opto/block.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -206,9 +206,7 @@ class Block : public CFGElement { uint compute_loop_alignment(); // BLOCK_FREQUENCY is a sentinel to mark uses of constant block frequencies. - // It is currently also used to scale such frequencies relative to - // FreqCountInvocations relative to the old value of 1500. -#define BLOCK_FREQUENCY(f) ((f * (double) 1500) / FreqCountInvocations) +#define BLOCK_FREQUENCY(f) (f * 1500.0) // Register Pressure (estimate) for Splitting heuristic uint _reg_pressure; diff --git a/src/hotspot/share/opto/c2_globals.hpp b/src/hotspot/share/opto/c2_globals.hpp index 6d2a70f80cf0..b4be75836a9a 100644 --- a/src/hotspot/share/opto/c2_globals.hpp +++ b/src/hotspot/share/opto/c2_globals.hpp @@ -762,10 +762,6 @@ "Clone compares and control flow through merge points to fold " \ "some branches") \ \ - develop(intx, FreqCountInvocations, 1, \ - "Scaling factor for branch frequencies (deprecated)") \ - range(1, max_intx) \ - \ develop(bool, VerifyAliases, false, \ "perform extra checks on the results of alias analysis") \ \ diff --git a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp index 35316d3ee08e..a2c40ab5f4b7 100644 --- a/src/hotspot/share/opto/loopnode.cpp +++ b/src/hotspot/share/opto/loopnode.cpp @@ -4155,7 +4155,7 @@ static float estimate_path_freq( Node *n ) { n = n->in(0); continue; } - return data->as_CounterData()->count()/FreqCountInvocations; + return data->as_CounterData()->count(); } // See if there's a gating IF test Node *n_c = n->in(0); diff --git a/src/hotspot/share/opto/parse2.cpp b/src/hotspot/share/opto/parse2.cpp index 8cee4a1a6b4f..fc406cf0b1ab 100644 --- a/src/hotspot/share/opto/parse2.cpp +++ b/src/hotspot/share/opto/parse2.cpp @@ -1678,13 +1678,13 @@ float Parse::dynamic_branch_prediction(float &cnt, BoolTest::mask btest, Node* t } // Compute frequency that we arrive here - float sum = taken + not_taken; + cnt = taken + not_taken; // Adjust, if this block is a cloned private block but the // Jump counts are shared. Taken the private counts for // just this path instead of the shared counts. - if( block()->count() > 0 ) - sum = block()->count(); - cnt = sum / FreqCountInvocations; + if (block()->count() > 0) { + cnt = block()->count(); + } // Pin probability to sane limits float prob; From a9bd6a93c849ac3de12ef83837695e3b9db1f079 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roberto=20Casta=C3=B1eda=20Lozano?= Date: Thu, 24 Sep 2026 05:35:10 +0000 Subject: [PATCH 006/103] 8392854: IGV: show allocation classes as part of the custom node info filter Reviewed-by: mhaessig, chagedorn --- src/hotspot/share/opto/callnode.cpp | 26 ++++++++++++------- src/hotspot/share/opto/callnode.hpp | 5 +++- src/hotspot/share/opto/idealGraphPrinter.cpp | 8 ++++++ .../filters/customNodeInfo.filter | 4 +++ 4 files changed, 33 insertions(+), 10 deletions(-) diff --git a/src/hotspot/share/opto/callnode.cpp b/src/hotspot/share/opto/callnode.cpp index be98b80bb8bf..3f1b97449101 100644 --- a/src/hotspot/share/opto/callnode.cpp +++ b/src/hotspot/share/opto/callnode.cpp @@ -735,6 +735,18 @@ void CallNode::dump_spec(outputStream *st) const { if (jvms() != nullptr) jvms()->dump_spec(st); } +ciKlass* AllocateNode::allocation_klass() const { + const Node* const klass_node = in(KlassNode); + if (klass_node == nullptr) { + return nullptr; + } + const TypeKlassPtr* const klass_ptr = klass_node->bottom_type()->isa_klassptr(); + if (klass_ptr == nullptr || !klass_ptr->klass_is_exact()) { + return nullptr; + } + return klass_ptr->exact_klass(); +} + void AllocateNode::dump_spec(outputStream* st) const { st->print(" "); if (tf() != nullptr) { @@ -743,20 +755,16 @@ void AllocateNode::dump_spec(outputStream* st) const { if (_cnt != COUNT_UNKNOWN) { st->print(" C=%f", _cnt); } - const Node* const klass_node = in(KlassNode); - if (klass_node != nullptr) { - const TypeKlassPtr* const klass_ptr = klass_node->bottom_type()->isa_klassptr(); - - if (klass_ptr != nullptr && klass_ptr->klass_is_exact()) { - st->print(" allocationKlass:"); - klass_ptr->exact_klass()->print_name_on(st); - } + ciKlass* alloc_klass = allocation_klass(); + if (alloc_klass != nullptr) { + st->print(" allocationKlass:"); + alloc_klass->print_name_on(st); } if (jvms() != nullptr) { jvms()->dump_spec(st); } } -#endif +#endif // !PRODUCT const Type *CallNode::bottom_type() const { return tf()->range_cc(); } const Type* CallNode::Value(PhaseGVN* phase) const { diff --git a/src/hotspot/share/opto/callnode.hpp b/src/hotspot/share/opto/callnode.hpp index 80e6cc1e3688..055d32048ef4 100644 --- a/src/hotspot/share/opto/callnode.hpp +++ b/src/hotspot/share/opto/callnode.hpp @@ -1224,7 +1224,10 @@ class AllocateNode : public CallNode { Node* make_ideal_mark(PhaseGVN* phase, Node* control, Node* mem); - NOT_PRODUCT(virtual void dump_spec(outputStream* st) const;) +#ifndef PRODUCT + ciKlass* allocation_klass() const; + virtual void dump_spec(outputStream *st) const; +#endif // !PRODUCT }; //------------------------------AllocateArray--------------------------------- diff --git a/src/hotspot/share/opto/idealGraphPrinter.cpp b/src/hotspot/share/opto/idealGraphPrinter.cpp index 563a914ea5c6..0409224121ea 100644 --- a/src/hotspot/share/opto/idealGraphPrinter.cpp +++ b/src/hotspot/share/opto/idealGraphPrinter.cpp @@ -124,6 +124,14 @@ void PrintProperties::print_node_details(Node* node) { print_escape_properties(node); + if (node->is_Allocate()) { + ciKlass* alloc_klass = node->as_Allocate()->allocation_klass(); + if (alloc_klass != nullptr) { + stringStream stream; + alloc_klass->print_name_on(&stream); + print_property(true, "allocation_class", stream.freeze()); + } + } print_property(node->is_block_proj() != nullptr, "is_block_proj"); print_property(node->is_block_start(), "is_block_start"); } diff --git a/src/utils/IdealGraphVisualizer/ServerCompiler/src/main/resources/com/sun/hotspot/igv/servercompiler/filters/customNodeInfo.filter b/src/utils/IdealGraphVisualizer/ServerCompiler/src/main/resources/com/sun/hotspot/igv/servercompiler/filters/customNodeInfo.filter index f94f06ab2845..01da5cb8b35a 100644 --- a/src/utils/IdealGraphVisualizer/ServerCompiler/src/main/resources/com/sun/hotspot/igv/servercompiler/filters/customNodeInfo.filter +++ b/src/utils/IdealGraphVisualizer/ServerCompiler/src/main/resources/com/sun/hotspot/igv/servercompiler/filters/customNodeInfo.filter @@ -58,3 +58,7 @@ function assertionPredicateInfo(dump_spec) { } editProperty(matches("name", "If|RangeCheck"), ["dump_spec"], "extra_label", function(dump_spec) { return assertionPredicateInfo(dump_spec[0]);}); + +// Show allocation class in allocation nodes. +editProperty(hasProperty("allocation_class"), ["allocation_class"], "extra_label", + function(allocation_class) { return allocation_class[0]; }); From de51f5ec05ab9a6e7e592c88ed6dc383749cc1d6 Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Thu, 24 Sep 2026 06:43:29 +0000 Subject: [PATCH 007/103] 8392627: Wrong perror usage in dbgsysTlsAlloc for pthread_key_create Reviewed-by: clanger --- src/jdk.jdwp.agent/unix/native/libdt_socket/socket_md.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/jdk.jdwp.agent/unix/native/libdt_socket/socket_md.c b/src/jdk.jdwp.agent/unix/native/libdt_socket/socket_md.c index 9f93f642cbae..4aad683ee4fd 100644 --- a/src/jdk.jdwp.agent/unix/native/libdt_socket/socket_md.c +++ b/src/jdk.jdwp.agent/unix/native/libdt_socket/socket_md.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2026, 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 @@ -283,8 +283,9 @@ dbgsysGetLastIOError(char *buf, jint size) { int dbgsysTlsAlloc() { pthread_key_t key; - if (pthread_key_create(&key, NULL)) { - perror("pthread_key_create"); + int res = pthread_key_create(&key, NULL); + if (res != 0) { + printf("pthread_key_create failed with result %d\n", res); exit(-1); } return (int)key; From 7dd0797a5388db360d709dab988b72ef332511f9 Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Thu, 24 Sep 2026 06:49:10 +0000 Subject: [PATCH 008/103] 8392650: Replace some RegQueryValueEx* function calls in jdk java.base native libs by RegGetValue Reviewed-by: djelinski --- .../windows/native/libjava/TimeZone_md.c | 53 +++++++------------ .../native/libnet/ResolverConfigurationImpl.c | 23 +++----- .../libnio/fs/RegistryFileTypeDetector.c | 30 ++++------- 3 files changed, 35 insertions(+), 71 deletions(-) diff --git a/src/java.base/windows/native/libjava/TimeZone_md.c b/src/java.base/windows/native/libjava/TimeZone_md.c index 31397b6fcee4..65b84f080755 100644 --- a/src/java.base/windows/native/libjava/TimeZone_md.c +++ b/src/java.base/windows/native/libjava/TimeZone_md.c @@ -67,12 +67,9 @@ static void *keyNames[] = { #define STD_NAME 2 /* - * Calls RegQueryValueEx() to get the value for the specified key. If - * the platform is NT, 2000 or XP, it calls the Unicode - * version. Otherwise, it calls the ANSI version and converts the - * value to Unicode. In this case, it assumes that the current ANSI - * Code Page is the same as the native platform code page (e.g., Code - * Page 932 for the Japanese Windows systems. + * Calls RegGetValue to get the value for the specified key. + * First tries the Unicode version, if that fails, + * falls back to the ANSI version and converts to Unicode. * * `keyIndex' is an index value to the keyNames in Unicode * (WCHAR). `keyIndex' + 1 points to its ANSI value. @@ -83,7 +80,6 @@ static void *keyNames[] = { static LONG getValueInRegistry(HKEY hKey, int keyIndex, - LPDWORD typePtr, LPBYTE buf, LPDWORD bufLengthPtr) { @@ -93,22 +89,18 @@ getValueInRegistry(HKEY hKey, DWORD valSize; int len; - *typePtr = 0; - ret = RegQueryValueExW(hKey, (WCHAR *) keyNames[keyIndex], NULL, - typePtr, buf, bufLengthPtr); - if (ret == ERROR_SUCCESS && *typePtr == REG_SZ) { + ret = RegGetValueW(hKey, NULL, (WCHAR*) keyNames[keyIndex], + RRF_RT_REG_SZ, NULL, buf, bufLengthPtr); + if (ret == ERROR_SUCCESS) { return ret; } valSize = sizeof(val); - ret = RegQueryValueExA(hKey, (char *) keyNames[keyIndex + 1], NULL, - typePtr, val, &valSize); + ret = RegGetValueA(hKey, NULL, (char*) keyNames[keyIndex+1], + RRF_RT_REG_SZ, NULL, val, &valSize); if (ret != ERROR_SUCCESS) { return ret; } - if (*typePtr != REG_SZ) { - return ERROR_BADKEY; - } len = MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, (LPCSTR) val, -1, @@ -154,7 +146,6 @@ static int getWinTimeZone(char *winZoneName, size_t winZoneNameBufSize) DWORD val; HANDLE hKey = NULL; LONG ret; - ULONG valueType; /* * Get the dynamic time zone information so that time zone redirection @@ -196,8 +187,8 @@ static int getWinTimeZone(char *winZoneName, size_t winZoneNameBufSize) * Determine if auto-daylight time adjustment is turned off. */ bufSize = sizeof(val); - ret = RegQueryValueExA(hKey, "DynamicDaylightTimeDisabled", NULL, - &valueType, (LPBYTE) &val, &bufSize); + ret = RegGetValueA(hKey, NULL, "DynamicDaylightTimeDisabled", + RRF_RT_REG_DWORD, NULL, (LPBYTE) &val, &bufSize); if (ret != ERROR_SUCCESS) { goto err; } @@ -212,8 +203,8 @@ static int getWinTimeZone(char *winZoneName, size_t winZoneNameBufSize) } bufSize = MAX_ZONE_CHAR; - ret = RegQueryValueExA(hKey, "TimeZoneKeyName", NULL, - &valueType, (LPBYTE) winZoneName, &bufSize); + ret = RegGetValueA(hKey, NULL, "TimeZoneKeyName", + RRF_RT_REG_SZ, NULL, (LPBYTE) winZoneName, &bufSize); if (ret != ERROR_SUCCESS) { goto err; } @@ -226,7 +217,6 @@ static int getWinTimeZone(char *winZoneName, size_t winZoneNameBufSize) TIME_ZONE_INFORMATION tzi; HANDLE hSubKey = NULL; DWORD nSubKeys, i; - ULONG valueType; TCHAR subKeyName[MAX_ZONE_CHAR]; TCHAR szValue[MAX_ZONE_CHAR]; WCHAR stdNameInReg[MAX_ZONE_CHAR]; @@ -245,8 +235,8 @@ static int getWinTimeZone(char *winZoneName, size_t winZoneNameBufSize) * Determine if auto-daylight time adjustment is turned off. */ bufSize = sizeof(val); - ret = RegQueryValueExA(hKey, "DynamicDaylightTimeDisabled", NULL, - &valueType, (LPBYTE) &val, &bufSize); + ret = RegGetValueA(hKey, NULL, "DynamicDaylightTimeDisabled", + RRF_RT_REG_DWORD, NULL, (LPBYTE) &val, &bufSize); if (ret == ERROR_SUCCESS) { if (val == 1 && tzi.DaylightDate.wMonth != 0) { (void) RegCloseKey(hKey); @@ -265,8 +255,7 @@ static int getWinTimeZone(char *winZoneName, size_t winZoneNameBufSize) */ if (tzi.StandardName[0] == 0) { bufSize = sizeof(stdNameInReg); - ret = getValueInRegistry(hKey, STANDARD_NAME, &valueType, - (LPBYTE) stdNameInReg, &bufSize); + ret = getValueInRegistry(hKey, STANDARD_NAME, (LPBYTE) stdNameInReg, &bufSize); if (ret != ERROR_SUCCESS) { goto err; } @@ -315,8 +304,7 @@ static int getWinTimeZone(char *winZoneName, size_t winZoneNameBufSize) } size = sizeof(szValue); - ret = getValueInRegistry(hSubKey, STD_NAME, &valueType, - szValue, &size); + ret = getValueInRegistry(hSubKey, STD_NAME, szValue, &size); if (ret != ERROR_SUCCESS) { RegCloseKey(hSubKey); break; @@ -331,8 +319,8 @@ static int getWinTimeZone(char *winZoneName, size_t winZoneNameBufSize) * zone. */ DWORD tziValueSize = sizeof(tempTzi); - ret = RegQueryValueEx(hSubKey, "TZI", NULL, &valueType, - (unsigned char *) &tempTzi, &tziValueSize); + ret = RegGetValueA(hSubKey, NULL, "TZI", RRF_RT_REG_BINARY, NULL, + (unsigned char*) &tempTzi, &tziValueSize); if (ret == ERROR_SUCCESS) { if ((tzi.Bias != tempTzi.bias) || (memcmp((const void *) &tzi.StandardDate, @@ -540,9 +528,8 @@ getGMTOffsetID() if (ret == ERROR_SUCCESS) { DWORD val; DWORD bufSize = sizeof(val); - ULONG valueType = 0; - ret = RegQueryValueExA(hKey, "ActiveTimeBias", - NULL, &valueType, (LPBYTE) &val, &bufSize); + ret = RegGetValueA(hKey, NULL, "ActiveTimeBias", + RRF_RT_REG_DWORD, NULL, (LPBYTE) &val, &bufSize); if (ret == ERROR_SUCCESS) { bias = (LONG) val; } diff --git a/src/java.base/windows/native/libnet/ResolverConfigurationImpl.c b/src/java.base/windows/native/libnet/ResolverConfigurationImpl.c index 21c472a9baa7..f3bc27f67ca3 100644 --- a/src/java.base/windows/native/libnet/ResolverConfigurationImpl.c +++ b/src/java.base/windows/native/libnet/ResolverConfigurationImpl.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2026, 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 @@ -154,34 +154,23 @@ static int loadConfig(JNIEnv *env, char *sl, char *ns) { WCHAR *suffix; DWORD ret, flags; DWORD dwLen; - ULONG ulType; char result[MAX_STR_LEN]; - HANDLE hKey; SOCKADDR *sockAddr; struct sockaddr_in6 *sockAddrIpv6; /* * First see if there is a global suffix list specified. */ - ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, + dwLen = sizeof(result); + ret = RegGetValueA(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters", - 0, - KEY_READ, - (PHKEY)&hKey); + "SearchList", RRF_RT_REG_SZ, NULL, result, &dwLen); if (ret == ERROR_SUCCESS) { - dwLen = sizeof(result); - ret = RegQueryValueEx(hKey, "SearchList", NULL, &ulType, - (LPBYTE)&result, &dwLen); - if (ret == ERROR_SUCCESS) { - assert(ulType == REG_SZ); - if (strlen(result) > 0) { - strappend(sl, result); - } + if (strlen(result) > 0) { + strappend(sl, result); } - RegCloseKey(hKey); } - // We only need DNS server addresses so skip everything else. flags = GAA_FLAG_SKIP_UNICAST; flags |= GAA_FLAG_SKIP_ANYCAST; diff --git a/src/java.base/windows/native/libnio/fs/RegistryFileTypeDetector.c b/src/java.base/windows/native/libnio/fs/RegistryFileTypeDetector.c index 946fc46601dc..001bfee86a34 100644 --- a/src/java.base/windows/native/libnio/fs/RegistryFileTypeDetector.c +++ b/src/java.base/windows/native/libnio/fs/RegistryFileTypeDetector.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2026, 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 @@ -36,27 +36,15 @@ JNIEXPORT jstring JNICALL Java_sun_nio_fs_RegistryFileTypeDetector_queryStringValue(JNIEnv* env, jclass this, jlong keyAddress, jlong nameAddress) { - LPCWSTR lpSubKey= (LPCWSTR)jlong_to_ptr(keyAddress); + LPCWSTR lpSubKey = (LPCWSTR)jlong_to_ptr(keyAddress); LPWSTR lpValueName = (LPWSTR)jlong_to_ptr(nameAddress); - LONG res; - HKEY hKey; - jstring result = NULL; + WCHAR data[255]; + DWORD size = sizeof(data); - res = RegOpenKeyExW(HKEY_CLASSES_ROOT, lpSubKey, 0, KEY_READ, &hKey); - if (res == ERROR_SUCCESS) { - DWORD type; - BYTE data[255]; - DWORD size = sizeof(data); - - res = RegQueryValueExW(hKey, lpValueName, NULL, &type, (LPBYTE)&data, &size); - if (res == ERROR_SUCCESS) { - if (type == REG_SZ) { - jsize len = (jsize)wcslen((WCHAR*)data); - result = (*env)->NewString(env, (const jchar*)&data, len); - } - } - - RegCloseKey(hKey); + if (RegGetValueW(HKEY_CLASSES_ROOT, lpSubKey, lpValueName, + RRF_RT_REG_SZ, NULL, data, &size) == ERROR_SUCCESS) { + jsize len = (jsize)wcslen(data); + return (*env)->NewString(env, (const jchar*)data, len); } - return result; + return NULL; } From 9f18731c04d73b153fc38d6876191c138ad65c06 Mon Sep 17 00:00:00 2001 From: Ehsan Behrangi Date: Thu, 24 Sep 2026 07:46:56 +0000 Subject: [PATCH 009/103] 8381560: AArch64: Optimize String.equals intrinsic Reviewed-by: aph, ruben --- src/hotspot/cpu/aarch64/aarch64.ad | 44 ++++- .../cpu/aarch64/c2_MacroAssembler_aarch64.cpp | 37 ++++ .../cpu/aarch64/c2_MacroAssembler_aarch64.hpp | 4 + .../cpu/aarch64/macroAssembler_aarch64.cpp | 165 +++++++++++------- .../cpu/aarch64/macroAssembler_aarch64.hpp | 2 +- .../string/TestStringIntrinsics2.java | 63 ++++++- .../openjdk/bench/java/lang/StringEquals.java | 50 ++++++ 7 files changed, 290 insertions(+), 75 deletions(-) diff --git a/src/hotspot/cpu/aarch64/aarch64.ad b/src/hotspot/cpu/aarch64/aarch64.ad index 20d3a59ab592..b424dff19665 100644 --- a/src/hotspot/cpu/aarch64/aarch64.ad +++ b/src/hotspot/cpu/aarch64/aarch64.ad @@ -1,7 +1,7 @@ // // Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. // Copyright (c) 2014, 2024, Red Hat, Inc. All rights reserved. -// Copyright 2025 Arm Limited and/or its affiliates. +// Copyright 2025, 2026 Arm Limited and/or its affiliates. // DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. // // This code is free software; you can redistribute it and/or modify it @@ -16143,18 +16143,50 @@ instruct stringU_indexof_char_sve(iRegP_R1 str1, iRegI_R2 cnt1, iRegI_R3 ch, ins_pipe(pipe_class_memory); %} -instruct string_equalsL(iRegP_R1 str1, iRegP_R3 str2, iRegI_R4 cnt, - iRegI_R0 result, rFlagsReg cr) +instruct string_equalsL_sve( + iRegP_R1 str1, // str1 (kill) + iRegP_R3 str2, // str2 (kill) + iRegINoSp cnt, // int length + iRegINoSp result, // boolean + vecA ztmp1, vecA ztmp2, // SVE z registers + pRegGov pg, pReg pdata, // SVE predicate registers + rFlagsReg cr) %{ - predicate(((StrEqualsNode*)n)->encoding() == StrIntrinsicNode::LL); + predicate(UseSVE > 0 && ((StrEqualsNode*)n)->encoding() == StrIntrinsicNode::LL); match(Set result (StrEquals (Binary str1 str2) cnt)); - effect(USE_KILL str1, USE_KILL str2, USE_KILL cnt, KILL cr); + effect(USE_KILL str1, USE_KILL str2, USE cnt, TEMP ztmp1, TEMP ztmp2, TEMP pg, TEMP pdata, KILL cr); + + format %{ "String Equals $str1,$str2,$cnt -> $result" %} + ins_encode %{ + // Count is in 8-bit bytes; non-Compact chars are 16 bits. + __ string_equals_sve($str1$$Register, $str2$$Register, + $result$$Register, $cnt$$Register, + $ztmp1$$FloatRegister, + $ztmp2$$FloatRegister, $pg$$PRegister, + $pdata$$PRegister); + %} + ins_pipe(pipe_class_memory); +%} + +instruct string_equalsL( + iRegP_R1 str1, // str1 (kill) + iRegP_R3 str2, // str2 (kill) + iRegI_R4 cnt, // int length (kill) + iRegI_R0 result, // boolean + iRegINoSp str1_hi, // temp: str1 high 8B + iRegINoSp str2_hi, // temp: str2 high 8B (reused as shift amount in SMALL) + rFlagsReg cr) +%{ + predicate(UseSVE == 0 && ((StrEqualsNode*)n)->encoding() == StrIntrinsicNode::LL); + match(Set result (StrEquals (Binary str1 str2) cnt)); + effect(USE_KILL str1, USE_KILL str2, USE_KILL cnt, TEMP str1_hi, TEMP str2_hi, KILL cr); format %{ "String Equals $str1,$str2,$cnt -> $result" %} ins_encode %{ // Count is in 8-bit bytes; non-Compact chars are 16 bits. __ string_equals($str1$$Register, $str2$$Register, - $result$$Register, $cnt$$Register); + $result$$Register, $cnt$$Register, + $str1_hi$$Register, $str2_hi$$Register); %} ins_pipe(pipe_class_memory); %} diff --git a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp index 79842aad05f1..d0741128bf0b 100644 --- a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp @@ -1129,6 +1129,43 @@ void C2_MacroAssembler::stringL_indexof_char(Register str1, Register cnt1, BIND(DONE); } +void C2_MacroAssembler::string_equals_sve(Register a1, Register a2, + Register result, Register cnt1, + FloatRegister ztmp1, FloatRegister ztmp2, + PRegister pg, PRegister pdata) { + Label LOOP, TAIL, END; + Register vec_len = rscratch1; + Register tmp_cnt1 = rscratch2; + sve_cntb(vec_len); + // Keep original cnt1 for the len <= VL tail decision. + // If length(cnt1) <= VL go to the tail + subs(tmp_cnt1, cnt1, vec_len); + br(Assembler::LE, TAIL); + sve_ptrue(pg, B); + bind(LOOP); + sve_ld1b(ztmp1, B, pg, Address(a1)); + sve_ld1b(ztmp2, B, pg, Address(a2)); + add(a1, a1, vec_len); + add(a2, a2, vec_len); + sve_cmp(Assembler::NE, pdata, B, pg, ztmp1, ztmp2); + br(Assembler::NE, END); + subs(tmp_cnt1, tmp_cnt1, vec_len); + br(Assembler::HI, LOOP); + // Final overlapped full-VL compare. + sve_ld1b(ztmp1, B, pg, Address(a1, tmp_cnt1)); + sve_ld1b(ztmp2, B, pg, Address(a2, tmp_cnt1)); + sve_cmp(Assembler::NE, pdata, B, pg, ztmp1, ztmp2); + b(END); + + bind(TAIL); + sve_whilelt(pg, B, zr, cnt1); + sve_ld1b(ztmp1, B, pg, Address(a1)); + sve_ld1b(ztmp2, B, pg, Address(a2)); + sve_cmp(Assembler::NE, pdata, B, pg, ztmp1, ztmp2); + bind(END); + cset(result, Assembler::EQ); +} + // Compare strings. void C2_MacroAssembler::string_compare(Register str1, Register str2, Register cnt1, Register cnt2, Register result, Register tmp1, Register tmp2, diff --git a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp index 108191dc87ad..dda6424a288c 100644 --- a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp @@ -90,6 +90,10 @@ void fast_lock(Register object, Register box, Register t1, Register t2, Register t3); void fast_unlock(Register object, Register box, Register t1, Register t2, Register t3); + void string_equals_sve(Register a1, Register a2, Register result, Register cnt1, + FloatRegister ztmp1, FloatRegister ztmp2, + PRegister pgtmp, PRegister ptmp); + void string_compare(Register str1, Register str2, Register cnt1, Register cnt2, Register result, Register tmp1, Register tmp2, FloatRegister vtmp1, diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp index ca359cb376d6..ca7299b04ea9 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp @@ -1,6 +1,7 @@ /* * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, 2024, Red Hat Inc. All rights reserved. + * Copyright 2026 Arm Limited and/or its affiliates. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -6348,22 +6349,43 @@ address MacroAssembler::arrays_equals(Register a1, Register a2, Register tmp3, } // Compare Strings - -// For Strings we're passed the address of the first characters in a1 -// and a2 and the length in cnt1. -// There are two implementations. For arrays >= 8 bytes, all -// comparisons (including the final one, which may overlap) are -// performed 8 bytes at a time. For strings < 8 bytes, we compare a -// halfword, then a short, and then a byte. +// +// Inputs: +// a1, a2 - byte addresses of the first elements +// cnt1 - byte length +// +// Invariants and memory contract: +// - cnt1 is the number of bytes to compare. +// - The 8 bytes immediately preceding a1/a2 are readable +// (Java object header guarantee). This allows a pre-read at +// (base + len - 8) even when len < 8. +// - No read is performed beyond (base + len - 1). +// +// Strategy: +// 1) Preload the final 8-byte window at (base + len - 8). +// This covers the last up to 8 bytes and serves as a fast-fail check. +// 2) For len <= 8, handle entirely in SMALL using shift/mask logic. +// 3) For medium sizes (9..23) and post-loop remainders, +// TAIL15 compares head and tail windows with overlap as needed. +// 4) For larger inputs (>= 24), MAINLOOP processes 16-byte blocks +// using LDP + CMP/CCMP to allow a single branch on inequality. +// Any remaining <16 bytes fall back to TAIL15. +// +// SMALL path: +// For lengths <= 8, the preloaded 8-byte window is shifted +// so that only the valid low-order bytes participate in comparison. void MacroAssembler::string_equals(Register a1, Register a2, - Register result, Register cnt1) + Register result, Register cnt1, + Register a1_hi, Register a2_hi) { - Label SAME, DONE, SHORT, NEXT_WORD; - Register tmp1 = rscratch1; - Register tmp2 = rscratch2; + Label MAINLOOP, TAIL15, SMALL, END, SMALL2; + Register a1_low = rscratch1; + Register a2_low = rscratch2; - assert_different_registers(a1, a2, result, cnt1, rscratch1, rscratch2); + assert_different_registers(a1, a2, cnt1, a1_hi, a2_hi, a1_low, a2_low); + assert(result != a1, "result must not alias a1"); + assert(result != a2, "result must not alias a2"); #ifndef PRODUCT { @@ -6373,62 +6395,71 @@ void MacroAssembler::string_equals(Register a1, Register a2, } #endif - mov(result, false); - - // Check for short strings, i.e. smaller than wordSize. - subs(cnt1, cnt1, wordSize); - br(Assembler::LT, SHORT); - // Main 8 byte comparison loop. - bind(NEXT_WORD); { - ldr(tmp1, Address(post(a1, wordSize))); - ldr(tmp2, Address(post(a2, wordSize))); - subs(cnt1, cnt1, wordSize); - eor(tmp1, tmp1, tmp2); - cbnz(tmp1, DONE); - } br(GT, NEXT_WORD); - // Last longword. In the case where length == 4 we compare the - // same longword twice, but that's still faster than another - // conditional branch. - // cnt1 could be 0, -1, -2, -3, -4 for chars; -4 only happens when - // length == 4. - ldr(tmp1, Address(a1, cnt1)); - ldr(tmp2, Address(a2, cnt1)); - eor(tmp2, tmp1, tmp2); - cbnz(tmp2, DONE); - b(SAME); - - bind(SHORT); - Label TAIL03, TAIL01; - - tbz(cnt1, 2, TAIL03); // 0-7 bytes left. - { - ldrw(tmp1, Address(post(a1, 4))); - ldrw(tmp2, Address(post(a2, 4))); - eorw(tmp1, tmp1, tmp2); - cbnzw(tmp1, DONE); - } - bind(TAIL03); - tbz(cnt1, 1, TAIL01); // 0-3 bytes left. - { - ldrh(tmp1, Address(post(a1, 2))); - ldrh(tmp2, Address(post(a2, 2))); - eorw(tmp1, tmp1, tmp2); - cbnzw(tmp1, DONE); - } - bind(TAIL01); - tbz(cnt1, 0, SAME); // 0-1 bytes left. - { - ldrb(tmp1, a1); - ldrb(tmp2, a2); - eorw(tmp1, tmp1, tmp2); - cbnzw(tmp1, DONE); - } - // Arrays are equal. - bind(SAME); - mov(result, true); + subs(cnt1, cnt1, 8); + ldr(a1_low, Address(a1, cnt1)); // Load last 8 bytes from a1 + ldr(a2_low, Address(a2, cnt1)); // Load last 8 bytes from a2 + br(Assembler::LE, SMALL); + subs(cnt1, cnt1, 16); + br(Assembler::LT, TAIL15); + cmp(a1_low, a2_low); + br(Assembler::NE, END); + // ---- MAINLOOP: process two 8B via ldp/ccmp ---- + bind(MAINLOOP); + ldp(a1_low, a1_hi, Address(post(a1,16))); // A1: low/high 8B + ldp(a2_low, a2_hi, Address(post(a2,16))); // A2: low/high 8B + cmp(a1_low, a2_low); + ccmp(a1_hi, a2_hi, /*nzcv=*/0, Assembler::EQ); + br(Assembler::NE, END); + subs(cnt1, cnt1, 16); + br(Assembler::HS, MAINLOOP); // while remaining >= 16 + + adds(zr, cnt1, 16); // If cnt1 == -16, skip tail handling. + br(Assembler::EQ, END); + + // ---- TAIL15: medium sizes and post-loop tail. + // Entered when (initial len < 24) or when MAINLOOP leaves a <16B tail. + // At entry, cnt1 is in [-15 .. -1] ---- + bind(TAIL15); + // cnt1 := remaining length - 8 ; if remaining lengths <= 8 goto SMALL2 + adds(cnt1, cnt1, 8); + br(Assembler::LE, SMALL2); + cmp(a1_low, a2_low); + + // We have more than 8 bytes unchecked and 8 bytes from end previously read + // One ldp can cover all remained bytes + ldp(a1_low, a1_hi, Address(a1)); // A1 high 8B + ldp(a2_low, a2_hi, Address(a2)); // A2 high 8B + ccmp(a1_hi, a2_hi, 0, Assembler::EQ); + ccmp(a1_low, a2_low, /*nzcv=*/0, Assembler::EQ); + b(END); + // Tail <= 16B case: compare head 8 bytes and tail 8 bytes (tail 8 bytes was preloaded). + bind(SMALL2); + ldr(a1_hi, Address(a1)); + ldr(a2_hi, Address(a2)); + cmp(a1_low, a2_low); + ccmp(a1_hi, a2_hi, /*nzcv=*/0, Assembler::EQ); + b(END); + // For lengths <= 8 we avoid 4/2/1-byte tail branches and extra loads. + // Compute shift = (8 - len) * 8 and right-shift the preloaded 8B window + // so that only the valid low-order len bytes remain for comparison. + // + // The load at (base + len - 8) produces an 8B window ending at the last + // string byte. When len < 8, the leading bytes in this window are + // outside the logical string. On little-endian AArch64, lower-address + // bytes occupy the least significant bits of the 64-bit word, so a + // logical right shift cleanly discards those unused prefix bytes. + // + // a2_hi is reused as a temporary register holding the shift amount. + bind(SMALL); + neg(a2_hi, cnt1, LSL, 3); + lsrv(a1_low, a1_low, a2_hi); + lsrv(a2_low, a2_low, a2_hi); + adds(zr, cnt1, 8); // Prepare flags for length==0 handling + ccmp(a1_low, a2_low, /*nzcv=*/4, Assembler::NE); + + bind(END); + cset(result, Assembler::EQ); - // That's it. - bind(DONE); BLOCK_COMMENT("} string_equals"); } diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp index 9940f4e7c3c2..c689466d3377 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp @@ -1598,7 +1598,7 @@ class MacroAssembler: public Assembler { vpowm == v13, "registers must match aarch64.ad"); \ } while (0) - void string_equals(Register a1, Register a2, Register result, Register cnt1); + void string_equals(Register a1, Register a2, Register result, Register cnt1, Register a1_hi, Register a2_hi); void fill_words(Register base, Register cnt, Register value); void fill_words(Register base, uint64_t cnt, Register value); diff --git a/test/hotspot/jtreg/compiler/intrinsics/string/TestStringIntrinsics2.java b/test/hotspot/jtreg/compiler/intrinsics/string/TestStringIntrinsics2.java index 5f316e9c0f18..9c6dfb285f9e 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/string/TestStringIntrinsics2.java +++ b/test/hotspot/jtreg/compiler/intrinsics/string/TestStringIntrinsics2.java @@ -1,6 +1,7 @@ /* * Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016 SAP SE. All rights reserved. + * Copyright 2026 Arm Limited and/or its affiliates. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -704,6 +705,66 @@ public static void test_asmStringEquals() { "TestOther.asmStringEquals(\"abcdefgh0123456abcdefgh0123456abcdefgh0123456\", " + "\"abcdefgh0123456abcdefgh0123456abcdefgh0123456\")"); } + + // Length-sensitive comparisons + { + String[] latin1 = { + "1234567", // 7 + "12345678", // 8 + "123456789", // 9 + "12345678901234567890123", // 23 + "123456789012345678901234", // 24 + "1234567890123456789012345" // 25 + }; + + String[] utf16 = { + "\u0101\u0102\u0103", // 6 bytes + "\u0101\u0102\u0103\u0104", // 8 bytes + "\u0101\u0102\u0103\u0104\u0105", // 10 bytes + "\u0101\u0102\u0103\u0104\u0105\u0106\u0107" + + "\u0108\u0109\u010a\u010b", // 22 bytes + "\u0101\u0102\u0103\u0104\u0105\u0106\u0107" + + "\u0108\u0109\u010a\u010b\u010c", // 24 bytes + "\u0101\u0102\u0103\u0104\u0105\u0106\u0107" + + "\u0108\u0109\u010a\u010b\u010c\u010d" // 26 bytes + }; + + for (String value : latin1) { + String copy = new String(value.toCharArray()); + + assertTrue(asmStringEquals(value, copy), "Latin1 equal, length = " + value.length()); + for (int i = 0; i < value.length(); i++) { + char[] different = value.toCharArray(); + different[i]++; + String differentString = new String(different); + + assertFalse(asmStringEquals(value, differentString), + "Latin1 mismatch, length = " + value.length() + ", index = " + i); + + assertFalse(asmStringEquals(differentString, value), + "TestOther.asmStringEquals(Latin1 reverse mismatch, length=" + + value.length() + ", index=" + i + ")"); + } + } + + for (String value : utf16) { + String copy = new String(value.toCharArray()); + + assertTrue(asmStringEquals(value, copy), "UTF16 equal, length = " + value.length()); + for (int i = 0; i < value.length(); i++) { + char[] different = value.toCharArray(); + different[i]++; + String differentString = new String(different); + + assertFalse(asmStringEquals(value, differentString), + "UTF16 mismatch, length = " + value.length() + ", index = " + i); + + assertFalse(asmStringEquals(differentString, value), + "TestOther.asmStringEquals(UTF16 reverse mismatch, length=" + + value.length() + ", index=" + i + ")"); + } + } + } } static String longLatin1 = "0123456789A".repeat(100); @@ -720,4 +781,4 @@ public static void test_indexOf_use_result_immediately() { assertTrue(indexOf_use_result_immediately(longLatin1, "A")); assertTrue(indexOf_use_result_immediately(longUTF, "\ubeef")); } -} +} \ No newline at end of file diff --git a/test/micro/org/openjdk/bench/java/lang/StringEquals.java b/test/micro/org/openjdk/bench/java/lang/StringEquals.java index b0db6a7037ee..11e092424ff9 100644 --- a/test/micro/org/openjdk/bench/java/lang/StringEquals.java +++ b/test/micro/org/openjdk/bench/java/lang/StringEquals.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright 2026 Arm Limited and/or its affiliates. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -43,6 +44,45 @@ public class StringEquals { public String test5 = new String(test4); // equal to test4, but not same public String test6 = new String("0123456780"); public String test7 = new String("0123\u01FE"); + // string with parameterizable size + public String test8; + // same chars as test8, but different object; forces the intrinsic to read + // the entire string to check equality + public String test9; + // same chars as test8, except at length + diff_pos; set diff_pos to the + // worst case for the intrinsic being tested (usually -1, but could be -9 + // if the intrinsic reads the last 8B first, or -length if the intrinsic + // reads the string backwards + public String test10; + + @Param({"30"}) // can be used at runtime to define a length sweep + public int size; + + @Param({"-1"}) // set to the worst location for the intrinsic under test + public int diff_pos; + + @Setup + public void setup() { + if (size > 0) { + test8 = "a".repeat(size); + // NOTE 1: can't do test9 = new String(test8) or they'll share byte + // arrays, which improves cache hit rate of the equal-string case + test9 = "a".repeat(size); + StringBuilder sb = new StringBuilder("a".repeat(size)); + sb.setCharAt(Math.max(test8.length() + diff_pos, 0), 'b'); + test10 = sb.toString(); + } + else { + // NOTE 2: can't use "a".repeat(0) or it returns the "" literal, + // which will early-exit from String.equals() + // NOTE 3: can't use no-arg String ctor or they'll share the byte + // array of the "" literal, which improves cache hit rate for + // intrinsics that read backwards into the object header + test8 = new String(new char [] {}); + test9 = new String(new char [] {}); + test10 = new String(new char [] {}); + } + } @Benchmark public boolean different() { @@ -54,6 +94,16 @@ public boolean equal() { return test.equals(test3); } + @Benchmark + public boolean differentParam() { + return test8.equals(test10); + } + + @Benchmark + public boolean equalParam() { + return test8.equals(test9); + } + @Benchmark public boolean almostEqual() { return test.equals(test6); From 8a8893e0e22fd3b4507b910eb720a6d659b3fbda Mon Sep 17 00:00:00 2001 From: Arno Zeller Date: Thu, 24 Sep 2026 08:33:27 +0000 Subject: [PATCH 010/103] 8392827: com/sun/jndi/dns/ConfigTests/PortUnreachable test threshold should scale with timeout factor Reviewed-by: clanger, jiefu --- .../sun/jndi/dns/ConfigTests/PortUnreachable.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/test/jdk/com/sun/jndi/dns/ConfigTests/PortUnreachable.java b/test/jdk/com/sun/jndi/dns/ConfigTests/PortUnreachable.java index eb917c2f25a8..28f5dbcb9a7a 100644 --- a/test/jdk/com/sun/jndi/dns/ConfigTests/PortUnreachable.java +++ b/test/jdk/com/sun/jndi/dns/ConfigTests/PortUnreachable.java @@ -25,6 +25,8 @@ import javax.naming.Context; import javax.naming.directory.InitialDirContext; +import static jdk.test.lib.Utils.adjustTimeout; + /* * @test * @bug 8200151 @@ -34,7 +36,7 @@ * dead DNS server or a flakey router. * On AIX, no ICMP Destination Unreachable is received, so skip test. * @requires os.family != "aix" - * @library ../lib/ + * @library ../lib/ /test/lib * @modules java.base/sun.security.util * @run main PortUnreachable */ @@ -44,11 +46,12 @@ public class PortUnreachable extends DNSTestBase { // Port 25 is the SMTP port, used here to simulate a dead DNS server. private static final int PORT = 25; - // Threshold in ms for elapsed time of request failed. Normally, it should - // be very quick, but consider to different platform and test machine - // performance, here we define 3000 ms as threshold which acceptable for - // this test. - private static final int THRESHOLD = 3000; + // Max elapsed time (ms) for a request that fails via ICMP Port Unreachable. + // Base is 1000 ms, scaled by adjustTimeout() for slow platforms/machines. + // Capped at 9s to stay well below the full DNS exponential backoff cycle + // (1 + 2 + 4 + 8 = 15s), ensuring the test verifies a quick fail rather + // than waiting through all retries. + private static final long THRESHOLD = Math.min(adjustTimeout(1000), 9000); private long startTime; From dc8a2960d2e9da996df36682ec870bcea7c49494 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20H=C3=A4ssig?= Date: Thu, 24 Sep 2026 08:44:33 +0000 Subject: [PATCH 011/103] 8373612: Codegen: exclude unsigned subword types where they are not expected Reviewed-by: qamai, vlivanov, dlong --- src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp index 30165bced647..1285b742649f 100644 --- a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp @@ -1447,7 +1447,7 @@ void C2_MacroAssembler::vgather8b_masked(BasicType elem_bt, XMMRegister dst, incq(mask_idx); } } else { - assert(elem_bt == T_BYTE, ""); + assert(elem_bt == T_BYTE, "only handles short and byte"); for (int i = 0; i < 8; i++) { // dst[i] = mask[i] ? src[idx_base[i]] : 0 Label skip_load; @@ -1472,7 +1472,7 @@ void C2_MacroAssembler::vgather8b(BasicType elem_bt, XMMRegister dst, pinsrw(dst, Address(base, rtmp, Address::times_2), i); } } else { - assert(elem_bt == T_BYTE, ""); + assert(elem_bt == T_BYTE, "only handles short and byte"); for (int i = 0; i < 8; i++) { // dst[i] = src[idx_base[i]] movl(rtmp, Address(idx_base, i * 4)); @@ -1509,7 +1509,7 @@ void C2_MacroAssembler::vgather_subword(BasicType elem_ty, XMMRegister dst, Register rtmp, Register mask_idx, Register length, int vector_len, int vlen_enc) { Label GATHER8_LOOP; - assert(is_subword_type(elem_ty), ""); + assert(is_signed_subword_type(elem_ty), "only handles short and byte"); movl(length, vector_len); vpxor(xtmp1, xtmp1, xtmp1, vlen_enc); // xtmp1 = {0, ...} vpxor(dst, dst, dst, vlen_enc); // dst = {0, ...} @@ -2513,7 +2513,7 @@ void C2_MacroAssembler::get_elem(BasicType typ, Register dst, XMMRegister src, i int esize = type2aelembytes(typ); int elem_per_lane = 16/esize; int eindex = elemindex % elem_per_lane; - assert(is_integral_type(typ),"required"); + assert(is_signed_subword_type(typ) || is_non_subword_integral_type(typ), "only handles signed integral types"); if (eindex == 0) { if (typ == T_LONG) { From 16e1f9115f615382e34ae0983efe2f97dda0e8af Mon Sep 17 00:00:00 2001 From: Ehsan Behrangi Date: Thu, 24 Sep 2026 10:27:33 +0000 Subject: [PATCH 012/103] 8385513: AArch64: Improve ArraysSupport.vectorizedHashCode performance for large arrays Reviewed-by: aph, ruben, epeter --- src/hotspot/cpu/aarch64/aarch64.ad | 24 +- .../cpu/aarch64/c2_MacroAssembler_aarch64.cpp | 16 +- .../cpu/aarch64/c2_MacroAssembler_aarch64.hpp | 8 +- .../cpu/aarch64/macroAssembler_aarch64.hpp | 34 +- .../cpu/aarch64/stubGenerator_aarch64.cpp | 476 +++++++++--------- test/jdk/java/util/Arrays/HashCode.java | 162 +++++- 6 files changed, 450 insertions(+), 270 deletions(-) diff --git a/src/hotspot/cpu/aarch64/aarch64.ad b/src/hotspot/cpu/aarch64/aarch64.ad index b424dff19665..4d0e769042a5 100644 --- a/src/hotspot/cpu/aarch64/aarch64.ad +++ b/src/hotspot/cpu/aarch64/aarch64.ad @@ -546,6 +546,9 @@ reg_class int_r3_reg(R3); // Singleton class for R4 int register reg_class int_r4_reg(R4); +// Singleton class for R5 int register +reg_class int_r5_reg(R5); + // Singleton class for R31 int register reg_class int_r31_reg(R31); @@ -4914,6 +4917,18 @@ operand iRegI_R4() %} +// Register R5 only +operand iRegI_R5() +%{ + constraint(ALLOC_IN_RC(int_r5_reg)); + match(RegI); + match(iRegINoSp); + op_cost(0); + format %{ %} + interface(REG_INTER); +%} + + // Pointer Register Operands // Narrow Pointer Register operand iRegN() @@ -16242,19 +16257,22 @@ instruct array_equalsC(iRegP_R1 ary1, iRegP_R2 ary2, iRegI_R0 result, %} instruct arrays_hashcode(iRegP_R1 ary, iRegI_R2 cnt, iRegI_R0 result, immI basic_type, + iRegI_R3 blocks, iRegI_R4 tail, iRegI_R5 sum, vRegD_V0 vtmp0, vRegD_V1 vtmp1, vRegD_V2 vtmp2, vRegD_V3 vtmp3, vRegD_V4 vtmp4, vRegD_V5 vtmp5, vRegD_V6 vtmp6, vRegD_V7 vtmp7, vRegD_V12 vtmp8, vRegD_V13 vtmp9, rFlagsReg cr) %{ match(Set result (VectorizedHashCode (Binary ary cnt) (Binary result basic_type))); - effect(TEMP vtmp0, TEMP vtmp1, TEMP vtmp2, TEMP vtmp3, TEMP vtmp4, TEMP vtmp5, TEMP vtmp6, + effect(TEMP blocks, TEMP tail, TEMP sum, + TEMP vtmp0, TEMP vtmp1, TEMP vtmp2, TEMP vtmp3, TEMP vtmp4, TEMP vtmp5, TEMP vtmp6, TEMP vtmp7, TEMP vtmp8, TEMP vtmp9, USE_KILL ary, USE_KILL cnt, USE basic_type, KILL cr); format %{ "Array HashCode array[] $ary,$cnt,$result,$basic_type -> $result // KILL all" %} ins_encode %{ address tpc = __ arrays_hashcode($ary$$Register, $cnt$$Register, $result$$Register, - $vtmp3$$FloatRegister, $vtmp2$$FloatRegister, - $vtmp1$$FloatRegister, $vtmp0$$FloatRegister, + $blocks$$Register, $tail$$Register, $sum$$Register, + $vtmp0$$FloatRegister, $vtmp1$$FloatRegister, + $vtmp2$$FloatRegister, $vtmp3$$FloatRegister, $vtmp4$$FloatRegister, $vtmp5$$FloatRegister, $vtmp6$$FloatRegister, $vtmp7$$FloatRegister, $vtmp8$$FloatRegister, $vtmp9$$FloatRegister, diff --git a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp index d0741128bf0b..a59e4ed6afea 100644 --- a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp @@ -72,11 +72,12 @@ void C2_MacroAssembler::entry_barrier() { // jdk.internal.util.ArraysSupport.vectorizedHashCode address C2_MacroAssembler::arrays_hashcode(Register ary, Register cnt, Register result, - FloatRegister vdata0, FloatRegister vdata1, - FloatRegister vdata2, FloatRegister vdata3, - FloatRegister vmul0, FloatRegister vmul1, - FloatRegister vmul2, FloatRegister vmul3, - FloatRegister vpow, FloatRegister vpowm, + Register blocks, Register tail, Register sum, + FloatRegister v_block0, FloatRegister v_block1, + FloatRegister v_block2, FloatRegister v_block3, + FloatRegister v_p0, FloatRegister v_p1, + FloatRegister v_p2, FloatRegister v_p3, + FloatRegister v_input1, FloatRegister v_input2, BasicType eltype) { ARRAYS_HASHCODE_REGISTERS; @@ -116,9 +117,8 @@ address C2_MacroAssembler::arrays_hashcode(Register ary, Register cnt, Register ShouldNotReachHere(); } - // large_arrays_hashcode(T_INT) performs worse than the scalar loop below when the Neon loop - // implemented by the stub executes just once. Call the stub only if at least two iterations will - // be executed. + // large_arrays_hashcode(T_INT) uses SIMD for >= 8 elements. + // For lower lengths we use an unrolled loop. const size_t large_threshold = eltype == T_INT ? vf * 2 : vf; cmpw(cnt, large_threshold); br(Assembler::HS, LARGE); diff --git a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp index dda6424a288c..082cdbe09c1b 100644 --- a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp @@ -80,10 +80,10 @@ void entry_barrier(); // jdk.internal.util.ArraysSupport.vectorizedHashCode - address arrays_hashcode(Register ary, Register cnt, Register result, FloatRegister vdata0, - FloatRegister vdata1, FloatRegister vdata2, FloatRegister vdata3, - FloatRegister vmul0, FloatRegister vmul1, FloatRegister vmul2, - FloatRegister vmul3, FloatRegister vpow, FloatRegister vpowm, + address arrays_hashcode(Register ary, Register cnt, Register result, Register blocks, + Register tail, Register sum, FloatRegister v_block0, FloatRegister v_block1, + FloatRegister v_block2, FloatRegister v_block3, FloatRegister v_p0, FloatRegister v_p1, + FloatRegister v_p2, FloatRegister v_p3, FloatRegister v_input1, FloatRegister v_input2, BasicType eltype); // Code used by cmpFastLock and cmpFastUnlock mach instructions in .ad file. diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp index c689466d3377..fedcdb2482cb 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp @@ -1581,21 +1581,25 @@ class MacroAssembler: public Assembler { Register tmp1, Register tmp2, Register tmp3, int elem_size); // Ensure that the inline code and the stub use the same registers. -#define ARRAYS_HASHCODE_REGISTERS \ - do { \ - assert(result == r0 && \ - ary == r1 && \ - cnt == r2 && \ - vdata0 == v3 && \ - vdata1 == v2 && \ - vdata2 == v1 && \ - vdata3 == v0 && \ - vmul0 == v4 && \ - vmul1 == v5 && \ - vmul2 == v6 && \ - vmul3 == v7 && \ - vpow == v12 && \ - vpowm == v13, "registers must match aarch64.ad"); \ +#define ARRAYS_HASHCODE_REGISTERS \ + do { \ + assert(result == r0 && \ + ary == r1 && \ + cnt == r2 && \ + blocks == r3 && \ + tail == r4 && \ + sum == r5 && \ + v_block0 == v0 && \ + v_block1 == v1 && \ + v_block2 == v2 && \ + v_block3 == v3 && \ + v_p0 == v4 && \ + v_p1 == v5 && \ + v_p2 == v6 && \ + v_p3 == v7 && \ + v_input1 == v12 && \ + v_input2 == v13, \ + "registers must match aarch64.ad"); \ } while (0) void string_equals(Register a1, Register a2, Register result, Register cnt1, Register a1_hi, Register a2_hi); diff --git a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp index ba9f7bf8cf80..e92b463b23e8 100644 --- a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp @@ -1,6 +1,7 @@ /* * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, 2025, Red Hat Inc. All rights reserved. + * Copyright 2026 Arm Limited and/or its affiliates. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -10325,8 +10326,29 @@ class StubGenerator: public StubCodeGenerator { // result = r0 - return value. Contains initial hashcode value on entry. // ary = r1 - array address // cnt = r2 - elements count - // Clobbers: v0-v13, rscratch1, rscratch2 - address generate_large_arrays_hashcode(BasicType eltype) { + // Clobbers (must match ad rule): + // r3 (blocks) : number of 16-element blocks = cnt / 16 + // r4 (tail) : remaining elements = cnt % 16 + // r5 (sum) : scalar temporary for horizontal-reduce results and + // Horner multipliers used by P16/P8/P7 accumulation. + // rscratch1 : temporary scratch + // rscratch2 : 31^16 mod 2^32; loaded only when blocks != 0 + // SIMD regs : v0-v7, v12-v13 + // + // Notes: + // - All counts are element counts (not byte counts). + // - v12/v13 are input halves; v0..v3 hold block accumulators used in the vector multiply/reduction. + // - The AD rule must list the same temps; the ARRAYS_HASHCODE_REGISTERS assert enforces that. + // - Algorithm uses Horner method: + // * P16: vector polynomial for each 16-element block + // * P8 : single 8-element block in tail (if present) + // * P7 : scalar unrolled loop for remaining <= 7 elements + // Performance: + // Reduce each block immediately instead of carrying vector accumulators across the entire loop. + // Delaying the reduction increases loop-carried vector + // dependencies and register pressure, and was measured to perform worse on AArch64. + // See: https://github.com/openjdk/jdk/pull/31674#discussion_r3497411227 + address generate_large_arrays_hashcode(BasicType eltype, Label& L_pow_table) { StubId stub_id; switch (eltype) { case T_BOOLEAN: @@ -10354,46 +10376,62 @@ class StubGenerator: public StubCodeGenerator { if (start != nullptr) { return start; } - const Register result = r0, ary = r1, cnt = r2; - const FloatRegister vdata0 = v3, vdata1 = v2, vdata2 = v1, vdata3 = v0; - const FloatRegister vmul0 = v4, vmul1 = v5, vmul2 = v6, vmul3 = v7; - const FloatRegister vpow = v12; // powers of 31: <31^3, ..., 31^0> - const FloatRegister vpowm = v13; - ARRAYS_HASHCODE_REGISTERS; + const Register result = r0; + const Register ary = r1; + const Register cnt = r2; - Label SMALL_LOOP, LARGE_LOOP_PREHEADER, LARGE_LOOP, TAIL, TAIL_SHORTCUT, BR_BASE; + const Register blocks = r3; + const Register tail = r4; + const Register sum = r5; - unsigned int vf; // vectorization factor - bool multiply_by_halves; - Assembler::SIMD_Arrangement load_arrangement; - switch (eltype) { - case T_BOOLEAN: - case T_BYTE: - load_arrangement = Assembler::T8B; - multiply_by_halves = true; - vf = 8; - break; - case T_CHAR: - case T_SHORT: - load_arrangement = Assembler::T8H; - multiply_by_halves = true; - vf = 8; - break; - case T_INT: - load_arrangement = Assembler::T4S; - multiply_by_halves = false; - vf = 4; - break; - default: - ShouldNotReachHere(); - } + const FloatRegister v_input1 = v12; + const FloatRegister v_input2 = v13; - // Unroll factor - const unsigned uf = 4; + const FloatRegister v_block0 = v0; + const FloatRegister v_block1 = v1; + const FloatRegister v_block2 = v2; + const FloatRegister v_block3 = v3; + + const FloatRegister v_p0 = v4; + const FloatRegister v_p1 = v5; + const FloatRegister v_p2 = v6; + const FloatRegister v_p3 = v7; + + ARRAYS_HASHCODE_REGISTERS; - // Effective vectorization factor - const unsigned evf = vf * uf; + Label L_loop, L_tail_setup, L_tail_lt8, L_done; + + const int elem_bytes = type2aelembytes(eltype); + const bool widen_signed = is_signed_subword_type(eltype); + + auto widen = [this](FloatRegister dst1, + FloatRegister dst2, + FloatRegister src, + Assembler::SIMD_Arrangement dst_arr, + Assembler::SIMD_Arrangement src_arr1, + Assembler::SIMD_Arrangement src_arr2, + bool is_signed) { + if (is_signed) { + __ sxtl(dst1, dst_arr, src, src_arr1); + __ sshll2(dst2, dst_arr, src, src_arr2, 0); + } else { + __ uxtl(dst1, dst_arr, src, src_arr1); + __ ushll2(dst2, dst_arr, src, src_arr2, 0); + } + }; + + auto widen_low = [this](FloatRegister dst, + FloatRegister src, + Assembler::SIMD_Arrangement dst_arr, + Assembler::SIMD_Arrangement src_arr, + bool is_signed) { + if (is_signed) { + __ sxtl(dst, dst_arr, src, src_arr); + } else { + __ uxtl(dst, dst_arr, src, src_arr); + } + }; __ align(CodeEntryAlignment); @@ -10402,231 +10440,171 @@ class StubGenerator: public StubCodeGenerator { address entry = __ pc(); __ enter(); - // Put 0-3'th powers of 31 into a single SIMD register together. The register will be used in - // the SMALL and LARGE LOOPS' epilogues. The initialization is hoisted here and the register's - // value shouldn't change throughout both loops. - __ movw(rscratch1, intpow(31U, 3)); - __ mov(vpow, Assembler::S, 0, rscratch1); - __ movw(rscratch1, intpow(31U, 2)); - __ mov(vpow, Assembler::S, 1, rscratch1); - __ movw(rscratch1, intpow(31U, 1)); - __ mov(vpow, Assembler::S, 2, rscratch1); - __ movw(rscratch1, intpow(31U, 0)); - __ mov(vpow, Assembler::S, 3, rscratch1); + // blocks = cnt >> 4 ; tail = cnt & 15 + __ lsr(blocks, cnt, 4); + __ andr(tail, cnt, 15); - __ mov(vmul0, Assembler::T16B, 0); - __ mov(vmul0, Assembler::S, 3, result); + // ----------------------------- + // Load pow table; if blocks != 0 load rscratch2 with 31^16. + // ----------------------------- + __ adr(rscratch1, L_pow_table); + __ ld1(v_p0, v_p1, v_p2, v_p3, Assembler::T4S, Address(rscratch1)); - __ andr(rscratch2, cnt, (uf - 1) * vf); - __ cbz(rscratch2, LARGE_LOOP_PREHEADER); + __ cbz(blocks, L_tail_setup); + // rscratch2 = 31^16 mod 2^32 = 0x50A9DE01 + __ movw(rscratch2, intpow(31U, 16)); - __ movw(rscratch1, intpow(31U, multiply_by_halves ? vf / 2 : vf)); - __ mov(vpowm, Assembler::S, 0, rscratch1); + // ----------------------------- + // Main loop over 16 elements to calculate P16 + // ----------------------------- - // SMALL LOOP - __ bind(SMALL_LOOP); + __ bind(L_loop); - __ ld1(vdata0, load_arrangement, Address(__ post(ary, vf * type2aelembytes(eltype)))); - __ mulvs(vmul0, Assembler::T4S, vmul0, vpowm, 0); - __ subsw(rscratch2, rscratch2, vf); + if (elem_bytes == 1) { + __ ld1(v_input1, Assembler::T16B, Address(__ post(ary, 16))); - if (load_arrangement == Assembler::T8B) { - // Extend 8B to 8H to be able to use vector multiply - // instructions - assert(load_arrangement == Assembler::T8B, "expected to extend 8B to 8H"); - if (is_signed_subword_type(eltype)) { - __ sxtl(vdata0, Assembler::T8H, vdata0, load_arrangement); - } else { - __ uxtl(vdata0, Assembler::T8H, vdata0, load_arrangement); - } - } + // byte -> half + widen(v_input2, v_input1, v_input1, Assembler::T8H, Assembler::T8B, Assembler::T16B, widen_signed); + // first half -> int + widen(v_block3, v_block0, v_input2, Assembler::T4S, Assembler::T4H, Assembler::T8H, widen_signed); + __ mulv(v_block3, Assembler::T4S, v_block3, v_p0); + __ mulv(v_block0, Assembler::T4S, v_block0, v_p1); + __ addv(v_block3, Assembler::T4S, v_block3, v_block0); - switch (load_arrangement) { - case Assembler::T4S: - __ addv(vmul0, load_arrangement, vmul0, vdata0); - break; - case Assembler::T8B: - case Assembler::T8H: - assert(is_subword_type(eltype), "subword type expected"); - if (is_signed_subword_type(eltype)) { - __ saddwv(vmul0, vmul0, Assembler::T4S, vdata0, Assembler::T4H); - } else { - __ uaddwv(vmul0, vmul0, Assembler::T4S, vdata0, Assembler::T4H); - } - break; - default: - __ should_not_reach_here(); - } + // second half -> int + widen(v_block0, v_block1, v_input1, Assembler::T4S, Assembler::T4H, Assembler::T8H, widen_signed); + __ mulv(v_block0, Assembler::T4S, v_block0, v_p2); + __ mulv(v_block1, Assembler::T4S, v_block1, v_p3); + __ addv(v_block0, Assembler::T4S, v_block0, v_block1); - // Process the upper half of a vector - if (load_arrangement == Assembler::T8B || load_arrangement == Assembler::T8H) { - __ mulvs(vmul0, Assembler::T4S, vmul0, vpowm, 0); - if (is_signed_subword_type(eltype)) { - __ saddwv2(vmul0, vmul0, Assembler::T4S, vdata0, Assembler::T8H); - } else { - __ uaddwv2(vmul0, vmul0, Assembler::T4S, vdata0, Assembler::T8H); - } - } + __ addv(v_block3, Assembler::T4S, v_block3, v_block0); - __ br(Assembler::HI, SMALL_LOOP); + __ addv(v_block3, Assembler::T4S, v_block3); + __ umov(sum, v_block3, Assembler::S, 0); + __ maddw(result, result, rscratch2, sum); - // SMALL LOOP'S EPILOQUE - __ lsr(rscratch2, cnt, exact_log2(evf)); - __ cbnz(rscratch2, LARGE_LOOP_PREHEADER); + } else if (elem_bytes == 2) { + __ ld1(v_input2, Assembler::T8H, Address(__ post(ary, 16))); + __ ld1(v_input1, Assembler::T8H, Address(__ post(ary, 16))); - __ mulv(vmul0, Assembler::T4S, vmul0, vpow); - __ addv(vmul0, Assembler::T4S, vmul0); - __ umov(result, vmul0, Assembler::S, 0); + widen(v_block3, v_block0, v_input2, Assembler::T4S, Assembler::T4H, Assembler::T8H, widen_signed); + __ mulv(v_block3, Assembler::T4S, v_block3, v_p0); + __ mulv(v_block0, Assembler::T4S, v_block0, v_p1); + __ addv(v_block3, Assembler::T4S, v_block3, v_block0); - // TAIL - __ bind(TAIL); + widen(v_block0, v_block1, v_input1, Assembler::T4S, Assembler::T4H, Assembler::T8H, widen_signed); + __ mulv(v_block0, Assembler::T4S, v_block0, v_p2); + __ mulv(v_block1, Assembler::T4S, v_block1, v_p3); + __ addv(v_block0, Assembler::T4S, v_block0, v_block1); - // The andr performs cnt % vf. The subtract shifted by 3 offsets past vf - 1 - (cnt % vf) pairs - // of load + madd insns i.e. it only executes cnt % vf load + madd pairs. - assert(is_power_of_2(vf), "can't use this value to calculate the jump target PC"); - __ andr(rscratch2, cnt, vf - 1); - __ bind(TAIL_SHORTCUT); - __ adr(rscratch1, BR_BASE); - // For Cortex-A53 offset is 4 because 2 nops are generated. - __ sub(rscratch1, rscratch1, rscratch2, ext::uxtw, VM_Version::supports_a53mac() ? 4 : 3); - __ movw(rscratch2, 0x1f); - __ br(rscratch1); + __ addv(v_block3, Assembler::T4S, v_block3, v_block0); - for (size_t i = 0; i < vf - 1; ++i) { - __ load(rscratch1, Address(__ post(ary, type2aelembytes(eltype))), - eltype); - __ maddw(result, result, rscratch2, rscratch1); - // maddw generates an extra nop for Cortex-A53 (see maddw definition in macroAssembler). - // Generate 2nd nop to have 4 instructions per iteration. - if (VM_Version::supports_a53mac()) { - __ nop(); - } + __ addv(v_block3, Assembler::T4S, v_block3); + __ umov(sum, v_block3, Assembler::S, 0); + __ maddw(result, result, rscratch2, sum); + + } else { + __ ld1(v_block0, v_block1, v_block2, v_block3, Assembler::T4S, Address(__ post(ary, 64))); + + __ mulv(v_block0, Assembler::T4S, v_block0, v_p0); + __ mulv(v_block1, Assembler::T4S, v_block1, v_p1); + __ mulv(v_block2, Assembler::T4S, v_block2, v_p2); + __ mulv(v_block3, Assembler::T4S, v_block3, v_p3); + + __ addv(v_block0, Assembler::T4S, v_block0, v_block1); + __ addv(v_block2, Assembler::T4S, v_block2, v_block3); + __ addv(v_block0, Assembler::T4S, v_block0, v_block2); + + __ addv(v_block0, Assembler::T4S, v_block0); + __ umov(sum, v_block0, Assembler::S, 0); + __ maddw(result, result, rscratch2, sum); } - __ bind(BR_BASE); - __ leave(); - __ ret(lr); + __ subs(blocks, blocks, 1); + __ br(Assembler::HI, L_loop); + + __ bind(L_tail_setup); + __ cbz(tail, L_done); + + // If (tail & 8) do P8, then do <8 computed-branch. + __ tbz(tail, 3, L_tail_lt8); + + // P8: result = result * 31^8 + Horner hash of next 8 elements + __ umov(sum, v_p1, Assembler::S, 3); // sum = 31^8 - // LARGE LOOP - __ bind(LARGE_LOOP_PREHEADER); + if (elem_bytes == 1) { + __ ld1(v_input1, Assembler::T8B, Address(__ post(ary, 8))); + // 8B -> 8H (low half only) + widen_low(v_input2, v_input1, Assembler::T8H, Assembler::T8B, widen_signed); + // 8H -> two 4S + widen(v_block0, v_block1, v_input2, Assembler::T4S, Assembler::T4H, Assembler::T8H, widen_signed); - __ lsr(rscratch2, cnt, exact_log2(evf)); + __ mulv(v_block0, Assembler::T4S, v_block0, v_p2); + __ mulv(v_block1, Assembler::T4S, v_block1, v_p3); + __ addv(v_block0, Assembler::T4S, v_block0, v_block1); + + __ addv(v_block0, Assembler::T4S, v_block0); + __ umov(rscratch1, v_block0, Assembler::S, 0); + __ maddw(result, result, sum, rscratch1); + + } else if (elem_bytes == 2) { + __ ld1(v_input2, Assembler::T8H, Address(__ post(ary, 16))); + + // 8H -> two 4S + widen(v_block0, v_block1, v_input2, Assembler::T4S, Assembler::T4H, Assembler::T8H, widen_signed); + + __ mulv(v_block0, Assembler::T4S, v_block0, v_p2); + __ mulv(v_block1, Assembler::T4S, v_block1, v_p3); + __ addv(v_block0, Assembler::T4S, v_block0, v_block1); + + __ addv(v_block0, Assembler::T4S, v_block0); + __ umov(rscratch1, v_block0, Assembler::S, 0); + __ maddw(result, result, sum, rscratch1); - if (multiply_by_halves) { - // 31^4 - multiplier between lower and upper parts of a register - __ movw(rscratch1, intpow(31U, vf / 2)); - __ mov(vpowm, Assembler::S, 1, rscratch1); - // 31^28 - remainder of the iteraion multiplier, 28 = 32 - 4 - __ movw(rscratch1, intpow(31U, evf - vf / 2)); - __ mov(vpowm, Assembler::S, 0, rscratch1); } else { - // 31^16 - __ movw(rscratch1, intpow(31U, evf)); - __ mov(vpowm, Assembler::S, 0, rscratch1); - } - - __ mov(vmul3, Assembler::T16B, 0); - __ mov(vmul2, Assembler::T16B, 0); - __ mov(vmul1, Assembler::T16B, 0); - - __ bind(LARGE_LOOP); - - __ mulvs(vmul3, Assembler::T4S, vmul3, vpowm, 0); - __ mulvs(vmul2, Assembler::T4S, vmul2, vpowm, 0); - __ mulvs(vmul1, Assembler::T4S, vmul1, vpowm, 0); - __ mulvs(vmul0, Assembler::T4S, vmul0, vpowm, 0); - - __ ld1(vdata3, vdata2, vdata1, vdata0, load_arrangement, - Address(__ post(ary, evf * type2aelembytes(eltype)))); - - if (load_arrangement == Assembler::T8B) { - // Extend 8B to 8H to be able to use vector multiply - // instructions - assert(load_arrangement == Assembler::T8B, "expected to extend 8B to 8H"); - if (is_signed_subword_type(eltype)) { - __ sxtl(vdata3, Assembler::T8H, vdata3, load_arrangement); - __ sxtl(vdata2, Assembler::T8H, vdata2, load_arrangement); - __ sxtl(vdata1, Assembler::T8H, vdata1, load_arrangement); - __ sxtl(vdata0, Assembler::T8H, vdata0, load_arrangement); - } else { - __ uxtl(vdata3, Assembler::T8H, vdata3, load_arrangement); - __ uxtl(vdata2, Assembler::T8H, vdata2, load_arrangement); - __ uxtl(vdata1, Assembler::T8H, vdata1, load_arrangement); - __ uxtl(vdata0, Assembler::T8H, vdata0, load_arrangement); - } - } + __ ld1(v_block0, Assembler::T4S, Address(__ post(ary, 16))); + __ ld1(v_block1, Assembler::T4S, Address(__ post(ary, 16))); - switch (load_arrangement) { - case Assembler::T4S: - __ addv(vmul3, load_arrangement, vmul3, vdata3); - __ addv(vmul2, load_arrangement, vmul2, vdata2); - __ addv(vmul1, load_arrangement, vmul1, vdata1); - __ addv(vmul0, load_arrangement, vmul0, vdata0); - break; - case Assembler::T8B: - case Assembler::T8H: - assert(is_subword_type(eltype), "subword type expected"); - if (is_signed_subword_type(eltype)) { - __ saddwv(vmul3, vmul3, Assembler::T4S, vdata3, Assembler::T4H); - __ saddwv(vmul2, vmul2, Assembler::T4S, vdata2, Assembler::T4H); - __ saddwv(vmul1, vmul1, Assembler::T4S, vdata1, Assembler::T4H); - __ saddwv(vmul0, vmul0, Assembler::T4S, vdata0, Assembler::T4H); - } else { - __ uaddwv(vmul3, vmul3, Assembler::T4S, vdata3, Assembler::T4H); - __ uaddwv(vmul2, vmul2, Assembler::T4S, vdata2, Assembler::T4H); - __ uaddwv(vmul1, vmul1, Assembler::T4S, vdata1, Assembler::T4H); - __ uaddwv(vmul0, vmul0, Assembler::T4S, vdata0, Assembler::T4H); - } - break; - default: - __ should_not_reach_here(); - } + __ mulv(v_block0, Assembler::T4S, v_block0, v_p2); + __ mulv(v_block1, Assembler::T4S, v_block1, v_p3); + __ addv(v_block0, Assembler::T4S, v_block0, v_block1); - // Process the upper half of a vector - if (load_arrangement == Assembler::T8B || load_arrangement == Assembler::T8H) { - __ mulvs(vmul3, Assembler::T4S, vmul3, vpowm, 1); - __ mulvs(vmul2, Assembler::T4S, vmul2, vpowm, 1); - __ mulvs(vmul1, Assembler::T4S, vmul1, vpowm, 1); - __ mulvs(vmul0, Assembler::T4S, vmul0, vpowm, 1); - if (is_signed_subword_type(eltype)) { - __ saddwv2(vmul3, vmul3, Assembler::T4S, vdata3, Assembler::T8H); - __ saddwv2(vmul2, vmul2, Assembler::T4S, vdata2, Assembler::T8H); - __ saddwv2(vmul1, vmul1, Assembler::T4S, vdata1, Assembler::T8H); - __ saddwv2(vmul0, vmul0, Assembler::T4S, vdata0, Assembler::T8H); - } else { - __ uaddwv2(vmul3, vmul3, Assembler::T4S, vdata3, Assembler::T8H); - __ uaddwv2(vmul2, vmul2, Assembler::T4S, vdata2, Assembler::T8H); - __ uaddwv2(vmul1, vmul1, Assembler::T4S, vdata1, Assembler::T8H); - __ uaddwv2(vmul0, vmul0, Assembler::T4S, vdata0, Assembler::T8H); - } + __ addv(v_block0, Assembler::T4S, v_block0); + __ umov(rscratch1, v_block0, Assembler::S, 0); + __ maddw(result, result, sum, rscratch1); } - __ subsw(rscratch2, rscratch2, 1); - __ br(Assembler::HI, LARGE_LOOP); + // tail -= 8 after P8 + __ subw(tail, tail, 8); + __ cbz(tail, L_done); - __ mulv(vmul3, Assembler::T4S, vmul3, vpow); - __ addv(vmul3, Assembler::T4S, vmul3); - __ umov(result, vmul3, Assembler::S, 0); + // ----------------------------- + // Tail < 8: HotSpot-style P7 scalar tail + // ----------------------------- + __ bind(L_tail_lt8); + // tail in [1..7] here - __ mov(rscratch2, intpow(31U, vf)); - - __ mulv(vmul2, Assembler::T4S, vmul2, vpow); - __ addv(vmul2, Assembler::T4S, vmul2); - __ umov(rscratch1, vmul2, Assembler::S, 0); - __ maddw(result, result, rscratch2, rscratch1); + __ adr(rscratch1, L_done); + // For Cortex-A53 offset is 4 because 2 nops are generated. + __ sub(rscratch1, rscratch1, tail, ext::uxtw, + VM_Version::supports_a53mac() ? 4 : 3); + __ movw(sum, 0x1f); // 31 + __ br(rscratch1); - __ mulv(vmul1, Assembler::T4S, vmul1, vpow); - __ addv(vmul1, Assembler::T4S, vmul1); - __ umov(rscratch1, vmul1, Assembler::S, 0); - __ maddw(result, result, rscratch2, rscratch1); + __ align(8); - __ mulv(vmul0, Assembler::T4S, vmul0, vpow); - __ addv(vmul0, Assembler::T4S, vmul0); - __ umov(rscratch1, vmul0, Assembler::S, 0); - __ maddw(result, result, rscratch2, rscratch1); + // Unrolled scalar Horner for up to 7 elements + for (int i = 0; i < 7; i++) { + __ load(rscratch1, Address(__ post(ary, elem_bytes)), eltype); + __ maddw(result, result, sum, rscratch1); + // maddw generates an extra nop for Cortex-A53 (see maddw definition). + // Generate 2nd nop to have 4 instructions per iteration. + if (VM_Version::supports_a53mac()) { + __ nop(); + } + } - __ andr(rscratch2, cnt, vf - 1); - __ cbnz(rscratch2, TAIL_SHORTCUT); + __ bind(L_done); __ leave(); __ ret(lr); @@ -13879,6 +13857,23 @@ class StubGenerator: public StubCodeGenerator { StubRoutines::aarch64::set_completed(); // Inidicate that arraycopy and zero_blocks stubs are generated } + // Generate the shared table of powers of 31 used by the large + // Arrays.hashCode stubs. The table is emitted once and referenced by + // each element-type-specific stub. + void generate_large_arrays_hashcode_pow_table(Label& L_pow_table) { + _masm->align(16); + _masm->bind(L_pow_table); + + uint32_t* pow_table = (uint32_t*) _masm->pc(); + _masm->code_section()->set_end(address(pow_table + 16)); + + // Fill the table in memory order as 31^15, 31^14, ..., 31^0. + uint32_t n = 1; + for (int i = 15; i >= 0; i--, n *= 31) { + pow_table[i] = n; + } + } + void generate_compiler_stubs() { #ifdef COMPILER2 @@ -13891,12 +13886,15 @@ class StubGenerator: public StubCodeGenerator { StubRoutines::aarch64::_large_array_equals = generate_large_array_equals(); } - // arrays_hascode stub for large arrays. - StubRoutines::aarch64::_large_arrays_hashcode_boolean = generate_large_arrays_hashcode(T_BOOLEAN); - StubRoutines::aarch64::_large_arrays_hashcode_byte = generate_large_arrays_hashcode(T_BYTE); - StubRoutines::aarch64::_large_arrays_hashcode_char = generate_large_arrays_hashcode(T_CHAR); - StubRoutines::aarch64::_large_arrays_hashcode_int = generate_large_arrays_hashcode(T_INT); - StubRoutines::aarch64::_large_arrays_hashcode_short = generate_large_arrays_hashcode(T_SHORT); + // arrays_hashcode stubs for large arrays. They all use the same 16-word table of powers of 31. + Label L_large_arrays_hashcode_pow_table; + StubRoutines::aarch64::_large_arrays_hashcode_boolean = generate_large_arrays_hashcode(T_BOOLEAN, L_large_arrays_hashcode_pow_table); + StubRoutines::aarch64::_large_arrays_hashcode_byte = generate_large_arrays_hashcode(T_BYTE, L_large_arrays_hashcode_pow_table); + StubRoutines::aarch64::_large_arrays_hashcode_char = generate_large_arrays_hashcode(T_CHAR, L_large_arrays_hashcode_pow_table); + StubRoutines::aarch64::_large_arrays_hashcode_int = generate_large_arrays_hashcode(T_INT, L_large_arrays_hashcode_pow_table); + StubRoutines::aarch64::_large_arrays_hashcode_short = generate_large_arrays_hashcode(T_SHORT, L_large_arrays_hashcode_pow_table); + + generate_large_arrays_hashcode_pow_table(L_large_arrays_hashcode_pow_table); // byte_array_inflate stub for large arrays. StubRoutines::aarch64::_large_byte_array_inflate = generate_large_byte_array_inflate(); diff --git a/test/jdk/java/util/Arrays/HashCode.java b/test/jdk/java/util/Arrays/HashCode.java index bceb43abf1a1..ff1c1edd0f7c 100644 --- a/test/jdk/java/util/Arrays/HashCode.java +++ b/test/jdk/java/util/Arrays/HashCode.java @@ -24,13 +24,18 @@ /** * @test * @summary Basic array hashCode functionality + * @modules java.base/jdk.internal.util * @library /test/lib * @run main/othervm --add-exports java.base/jdk.internal.util=ALL-UNNAMED * --add-opens java.base/jdk.internal.util=ALL-UNNAMED -Xcomp -Xbatch HashCode */ +import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.nio.ByteOrder; import java.util.Arrays; + +import jdk.internal.util.ArraysSupport; import jdk.test.lib.valueclass.AsValueClass; public class HashCode { @@ -56,12 +61,109 @@ static class Point { private static int[] expected = { 1, 63, 128, 536518979, -1174896354, -1357593156, 428276276}; private static int[] expectedUnsigned = { 1, 63, 128, 536518979, -1174896354, 584369596, -2025326028}; + private static int[] lengths = {0, 1, 7, 8, 9, 15, 16, 17, 24, 25}; + + private static byte[] paddedBytes = { + 99, 98, 97, 96, 95, + 0, 1, 2, 3, 4, 5, 6, 7, 127, -1, -2, -128, 55, -73, 99, -100, 11, 12, 13, + 77, 78, 79, 80, 81, 82, 83, 84 + }; + + private static char[] paddedChars = { + 999, 998, 997, 996, 995, + 0, 1, 2, 3, 4, 5, 6, 7, 127, 128, 255, 256, 511, 1024, 4096, 65535, 11, 12, 13, + 777, 778, 779, 780, 781, 782, 783, 784 + }; + + private static short[] paddedShorts = { + 999, 998, 997, 996, 995, + 0, 1, 2, 3, 4, 5, 6, 7, 127, -1, -2, -128, 255, -255, 1024, -1024, 11, 12, 13, + 777, 778, 779, 780, -781, 782, -783, 784 + }; + + private static int[] paddedInts = { + 999, 998, 997, 996, 995, + 0, 1, 2, 3, 4, 5, 6, 7, 127, -1, -2, -128, 255, -255, 65536, -65536, 11, 12, 13, + 777, 778, 779, 780, -781, 65537, -65537, 123456 + }; + + // Same logical content as paddedChars, but stored as UTF-16 code units in byte[] form. + // This exercises the T_CHAR + byte[] -> utf16hashCode(...) path. + private static byte[] paddedUtf16Bytes = toNativeUtf16Bytes(paddedChars); + + private static byte[] toNativeUtf16Bytes(char[] chars) { + byte[] bytes = new byte[chars.length * 2]; + boolean little = ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN; + for (int i = 0; i < chars.length; i++) { + char c = chars[i]; + if (little) { + bytes[2 * i] = (byte) (c & 0xFF); + bytes[2 * i + 1] = (byte) ((c >>> 8) & 0xFF); + } else { + bytes[2 * i] = (byte) ((c >>> 8) & 0xFF); + bytes[2 * i + 1] = (byte) (c & 0xFF); + } + } + return bytes; + } + + private static int getIntConstant(Class holder, String name) throws Exception { + Field f = holder.getDeclaredField(name); + f.setAccessible(true); + return f.getInt(null); + } + + private static int signedByteHash(byte[] a, int from, int len, int initial) { + int h = initial; + for (int i = 0; i < len; i++) { + h = 31 * h + a[from + i]; + } + return h; + } + + private static int unsignedByteHash(byte[] a, int from, int len, int initial) { + int h = initial; + for (int i = 0; i < len; i++) { + h = 31 * h + (a[from + i] & 0xFF); + } + return h; + } + + private static int charHash(char[] a, int from, int len, int initial) { + int h = initial; + for (int i = 0; i < len; i++) { + h = 31 * h + a[from + i]; + } + return h; + } + + private static int shortHash(short[] a, int from, int len, int initial) { + int h = initial; + for (int i = 0; i < len; i++) { + h = 31 * h + a[from + i]; + } + return h; + } + + private static int intHash(int[] a, int from, int len, int initial) { + int h = initial; + for (int i = 0; i < len; i++) { + h = 31 * h + a[from + i]; + } + return h; + } + public static void main(String[] args) throws Exception { // Deep introspection into range-based hash functions Class arraysSupport = Class.forName("jdk.internal.util.ArraysSupport"); Method vectorizedHashCode = arraysSupport.getDeclaredMethod("vectorizedHashCode", Object.class, int.class, int.class, int.class, int.class); vectorizedHashCode.setAccessible(true); + int T_BOOLEAN = getIntConstant(arraysSupport, "T_BOOLEAN"); + int T_BYTE = getIntConstant(arraysSupport, "T_BYTE"); + int T_CHAR = getIntConstant(arraysSupport, "T_CHAR"); + int T_SHORT = getIntConstant(arraysSupport, "T_SHORT"); + int T_INT = getIntConstant(arraysSupport, "T_INT"); for (int i = 0; i < tests.length; i++) { testBytes[i] = tests[i].getBytes("UTF-8"); @@ -90,7 +192,7 @@ public static void main(String[] args) throws Exception { + ", hashCode = " + hashCode + ", repetition = " + j); } - hashCode = (int) vectorizedHashCode.invoke(null, extraZeroes, 17, i, 1, /* ArraysSupport.T_BYTE */ 8); + hashCode = (int) vectorizedHashCode.invoke(null, extraZeroes, 17, i, 1, T_BYTE); if (hashCode != zeroResult) { throw new RuntimeException("byte[] subrange \"" + Arrays.toString(extraZeroes) + "\" at offset 17, limit " + i + ": " @@ -175,6 +277,64 @@ public static void main(String[] args) throws Exception { System.out.println(e.getMessage()); failed = true; } + try { + int initial = 1; + int from = 5; + + for (int len : lengths) { + int hashCode = ArraysSupport.hashCode(paddedBytes, from, len, initial); + int e = signedByteHash(paddedBytes, from, len, initial); + if (hashCode != e) { + throw new RuntimeException("T_BYTE subrange failed: len = " + len + + ", e = " + e + + ", hashCode = " + hashCode); + } + + hashCode = ArraysSupport.hashCodeOfUnsigned(paddedBytes, from, len, initial); + e = unsignedByteHash(paddedBytes, from, len, initial); + if (hashCode != e) { + throw new RuntimeException("T_BOOLEAN subrange failed: len = " + len + + ", e = " + e + + ", hashCode = " + hashCode); + } + + hashCode = ArraysSupport.hashCode(paddedChars, from, len, initial); + e = charHash(paddedChars, from, len, initial); + if (hashCode != e) { + throw new RuntimeException("T_CHAR subrange failed: len = " + len + + ", e = " + e + + ", hashCode = " + hashCode); + } + + hashCode = ArraysSupport.hashCodeOfUTF16(paddedUtf16Bytes, from, len, initial); + e = charHash(paddedChars, from, len, initial); + if (hashCode != e) { + throw new RuntimeException("T_CHAR byte[] UTF-16 subrange failed: len = " + len + + ", e = " + e + + ", hashCode = " + hashCode); + } + + hashCode = ArraysSupport.hashCode(paddedShorts, from, len, initial); + e = shortHash(paddedShorts, from, len, initial); + if (hashCode != e) { + throw new RuntimeException("T_SHORT subrange failed: len = " + len + + ", e = " + e + + ", hashCode = " + hashCode); + } + + hashCode = ArraysSupport.hashCode(paddedInts, from, len, initial); + e = intHash(paddedInts, from, len, initial); + if (hashCode != e) { + throw new RuntimeException("T_INT subrange failed: len = " + len + + ", e = " + e + + ", hashCode = " + hashCode); + } + } + System.out.println("ArraysSupport subrange tests passed"); + } catch (RuntimeException e) { + System.out.println(e.getMessage()); + failed = true; + } try { testValueClassArrayHashCode(); From 060bc741910cde314a596dac075aaeaba646a0f9 Mon Sep 17 00:00:00 2001 From: Evgeny Astigeevich Date: Thu, 24 Sep 2026 10:36:31 +0000 Subject: [PATCH 013/103] 8390662: HotCodeCollector mishandles overflowed HotCodeHeap Reviewed-by: crakoczy, kvn --- src/hotspot/share/code/codeCache.cpp | 47 ++++---- src/hotspot/share/code/nmethod.cpp | 26 +++-- src/hotspot/share/code/nmethod.hpp | 38 ++++++- src/hotspot/share/compiler/compileBroker.cpp | 2 + src/hotspot/share/prims/whitebox.cpp | 3 +- .../share/runtime/hotCodeCollector.cpp | 74 +++++++----- .../share/runtime/hotCodeCollector.hpp | 3 +- .../codecache/OverflowCodeCacheTest.java | 27 +++-- .../hotcode/HotCodeCollectorVirtualCalls.java | 105 ++++++++++++++++++ .../compiler/hotcode/TestFullHotCodeHeap.java | 99 +++++++++++++++++ test/lib/jdk/test/whitebox/code/BlobType.java | 11 +- 11 files changed, 349 insertions(+), 86 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/hotcode/HotCodeCollectorVirtualCalls.java create mode 100644 test/hotspot/jtreg/compiler/hotcode/TestFullHotCodeHeap.java diff --git a/src/hotspot/share/code/codeCache.cpp b/src/hotspot/share/code/codeCache.cpp index efe5d6549eb5..05785f694fff 100644 --- a/src/hotspot/share/code/codeCache.cpp +++ b/src/hotspot/share/code/codeCache.cpp @@ -594,6 +594,27 @@ CodeBlob* CodeCache::next_blob(CodeHeap* heap, CodeBlob* cb) { return (CodeBlob*)heap->next(cb); } +static int report_code_heap_full_event(CodeBlobType code_blob_type) { + CodeHeap* heap = CodeCache::get_code_heap(code_blob_type); + assert(heap != nullptr, "heap is null"); + int full_count = heap->report_full(); + EventCodeCacheFull event; + if (event.should_commit()) { + event.set_codeBlobType((u1)code_blob_type); + event.set_startAddress((u8)heap->low_boundary()); + event.set_commitedTopAddress((u8)heap->high()); + event.set_reservedTopAddress((u8)heap->high_boundary()); + event.set_entryCount(heap->blob_count()); + event.set_methodCount(heap->nmethod_count()); + event.set_adaptorCount(heap->adapter_count()); + event.set_unallocatedCapacity(heap->unallocated_capacity()); + event.set_fullCount(full_count); + event.set_codeCacheMaxCapacity(CodeCache::max_capacity()); + event.commit(); + } + return full_count; +} + /** * Do not seize the CodeCache lock here--if the caller has not * already done so, we are going to lose bigtime, since the code @@ -639,9 +660,6 @@ CodeBlob* CodeCache::allocate(uint size, CodeBlobType code_blob_type, bool handl type = CodeBlobType::MethodNonProfiled; } break; - case CodeBlobType::MethodHot: - type = CodeBlobType::MethodNonProfiled; - break; default: break; } @@ -656,6 +674,8 @@ CodeBlob* CodeCache::allocate(uint size, CodeBlobType code_blob_type, bool handl if (handle_alloc_failure) { MutexUnlocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); CompileBroker::handle_full_code_cache(orig_code_blob_type); + } else if (orig_code_blob_type == CodeBlobType::MethodHot) { + report_code_heap_full_event(orig_code_blob_type); } return nullptr; } else { @@ -1533,11 +1553,7 @@ void CodeCache::verify() { PRAGMA_DIAG_PUSH PRAGMA_FORMAT_NONLITERAL_IGNORED void CodeCache::report_codemem_full(CodeBlobType code_blob_type, bool print) { - // Get nmethod heap for the given CodeBlobType and build CodeCacheFull event - CodeHeap* heap = get_code_heap(code_blob_type); - assert(heap != nullptr, "heap is null"); - - int full_count = heap->report_full(); + int full_count = report_code_heap_full_event(code_blob_type); if ((full_count == 1) || print) { // Not yet reported for this heap, report @@ -1581,21 +1597,6 @@ void CodeCache::report_codemem_full(CodeBlobType code_blob_type, bool print) { } } } - - EventCodeCacheFull event; - if (event.should_commit()) { - event.set_codeBlobType((u1)code_blob_type); - event.set_startAddress((u8)heap->low_boundary()); - event.set_commitedTopAddress((u8)heap->high()); - event.set_reservedTopAddress((u8)heap->high_boundary()); - event.set_entryCount(heap->blob_count()); - event.set_methodCount(heap->nmethod_count()); - event.set_adaptorCount(heap->adapter_count()); - event.set_unallocatedCapacity(heap->unallocated_capacity()); - event.set_fullCount(heap->full_count()); - event.set_codeCacheMaxCapacity(CodeCache::max_capacity()); - event.commit(); - } } PRAGMA_DIAG_POP diff --git a/src/hotspot/share/code/nmethod.cpp b/src/hotspot/share/code/nmethod.cpp index a671faa72394..635853b549a5 100644 --- a/src/hotspot/share/code/nmethod.cpp +++ b/src/hotspot/share/code/nmethod.cpp @@ -40,6 +40,7 @@ #include "compiler/directivesParser.hpp" #include "compiler/disassembler.hpp" #include "compiler/oopMap.inline.hpp" +#include "cppstdlib/new.hpp" #include "gc/shared/barrierSet.hpp" #include "gc/shared/barrierSetNMethod.hpp" #include "gc/shared/classUnloadingContext.hpp" @@ -1494,7 +1495,13 @@ nmethod::nmethod(const nmethod &nm) : CodeBlob(nm._name, nm._kind, nm._size, nm. post_init(); } -nmethod* nmethod::relocate(CodeBlobType code_blob_type) { +static inline void set_relocation_result(nmethod::RelocationResult* relocation_result, nmethod::RelocationResult result) { + if (relocation_result != nullptr) { + *relocation_result = result; + } +} + +nmethod* nmethod::relocate(CodeBlobType code_blob_type, RelocationResult* relocation_result) { assert(NMethodRelocation, "must enable use of function"); // Locks required to be held by caller to ensure the nmethod @@ -1504,16 +1511,22 @@ nmethod* nmethod::relocate(CodeBlobType code_blob_type) { assert(CompiledICLocker::is_safe(this), "mt unsafe call"); if (!is_relocatable()) { + set_relocation_result(relocation_result, RelocationResult::FAILED_NOT_RELOCATABLE_NMETHOD); return nullptr; } run_nmethod_entry_barrier(); - nmethod* nm_copy = new (size(), code_blob_type) nmethod(*this); - if (nm_copy == nullptr) { + // Relocation is not compilation: on allocation failure it should not + // stop compilation. + void* blob = CodeCache::allocate(size(), code_blob_type, false /* handle_alloc_failure */); + if (blob == nullptr) { + set_relocation_result(relocation_result, RelocationResult::FAILED_NO_SPACE_IN_CODE_HEAP); return nullptr; } + nmethod* nm_copy = ::new (blob) nmethod(*this); + // To make dependency checking during class loading fast, record // the nmethod dependencies in the classes it is dependent on. // This allows the dependency checking code to simply walk the @@ -1554,12 +1567,13 @@ nmethod* nmethod::relocate(CodeBlobType code_blob_type) { nm_copy->log_relocated_nmethod(this); + set_relocation_result(relocation_result, RelocationResult::SUCCESS); return nm_copy; } } nm_copy->make_not_used(); - + set_relocation_result(relocation_result, RelocationResult::FAILED_INVALIDATED_NMETHOD); return nullptr; } @@ -1595,10 +1609,6 @@ void* nmethod::operator new(size_t size, int nmethod_size, int comp_level) throw return CodeCache::allocate(nmethod_size, CodeCache::get_code_blob_type(comp_level)); } -void* nmethod::operator new(size_t size, int nmethod_size, CodeBlobType code_blob_type) throw () { - return CodeCache::allocate(nmethod_size, code_blob_type); -} - void* nmethod::operator new(size_t size, int nmethod_size, bool allow_NonNMethod_space) throw () { // Try MethodNonProfiled and MethodProfiled. void* return_value = CodeCache::allocate(nmethod_size, CodeBlobType::MethodNonProfiled); diff --git a/src/hotspot/share/code/nmethod.hpp b/src/hotspot/share/code/nmethod.hpp index f301847f1f99..5e4a18d12450 100644 --- a/src/hotspot/share/code/nmethod.hpp +++ b/src/hotspot/share/code/nmethod.hpp @@ -361,7 +361,6 @@ class nmethod : public CodeBlob { // helper methods void* operator new(size_t size, int nmethod_size, int comp_level) throw(); - void* operator new(size_t size, int nmethod_size, CodeBlobType code_blob_type) throw(); // For method handle intrinsics: Try MethodNonProfiled, MethodProfiled and NonNMethod. // Attention: Only allow NonNMethod space for special nmethods which don't need to be @@ -578,11 +577,40 @@ class nmethod : public CodeBlob { CompLevel comp_level, Flags flags); + enum class RelocationResult : u1 { + SUCCESS, + FAILED_NO_SPACE_IN_CODE_HEAP, + FAILED_NOT_RELOCATABLE_NMETHOD, + FAILED_INVALIDATED_NMETHOD + }; + + static const char* relocation_result_to_string(RelocationResult relocation_result) { + switch (relocation_result) { + case RelocationResult::SUCCESS: + return "relocated"; + case RelocationResult::FAILED_NO_SPACE_IN_CODE_HEAP: + return "not enough space in the code heap"; + case RelocationResult::FAILED_NOT_RELOCATABLE_NMETHOD: + return "not relocatable nmethod"; + case RelocationResult::FAILED_INVALIDATED_NMETHOD: + return "nmethod invalidated during relocation"; + default: { + assert(false, "Unhandled relocation result"); + return "Unknown"; + } + } + } + // Relocate the nmethod to the code heap identified by code_blob_type. - // Returns nullptr if the code heap does not have enough space, the - // nmethod is unrelocatable, or the nmethod is invalidated during relocation, - // otherwise the relocated nmethod. The original nmethod will be marked not entrant. - nmethod* relocate(CodeBlobType code_blob_type); + // Returns nullptr if the code heap does not have enough space, + // the nmethod is unrelocatable, or the nmethod is invalidated during + // relocation, otherwise the relocated nmethod (relocation_result is set + // if provided). + // + // If relocation succeeds, the relocated nmethod is installed into + // the owner of the original nmethod and the original nmethod is made + // not entrant. + nmethod* relocate(CodeBlobType code_blob_type, RelocationResult* relocation_result = nullptr); static nmethod* new_native_nmethod(const methodHandle& method, int compile_id, diff --git a/src/hotspot/share/compiler/compileBroker.cpp b/src/hotspot/share/compiler/compileBroker.cpp index 3f1b5722f8fa..d97feeece770 100644 --- a/src/hotspot/share/compiler/compileBroker.cpp +++ b/src/hotspot/share/compiler/compileBroker.cpp @@ -2151,6 +2151,8 @@ void CompileBroker::invoke_compiler_on_method(CompileTask* task) { * since we currently handle a full code cache uniformly. */ void CompileBroker::handle_full_code_cache(CodeBlobType code_blob_type) { + assert(code_blob_type != CodeBlobType::MethodHot, "Full HotCodeHeap should not stop compilation. It is for relocated code."); + UseInterpreter = true; if (UseCompiler) { if (xtty != nullptr) { diff --git a/src/hotspot/share/prims/whitebox.cpp b/src/hotspot/share/prims/whitebox.cpp index 1e9c23b81b73..2d0213eceeed 100644 --- a/src/hotspot/share/prims/whitebox.cpp +++ b/src/hotspot/share/prims/whitebox.cpp @@ -1704,7 +1704,8 @@ CodeBlob* WhiteBox::allocate_code_blob(int size, CodeBlobType blob_type) { } { MutexLocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); - blob = (BufferBlob*) CodeCache::allocate(full_size, blob_type); + bool handle_alloc_failure = (blob_type != CodeBlobType::MethodHot); + blob = (BufferBlob*) CodeCache::allocate(full_size, blob_type, handle_alloc_failure); if (blob != nullptr) { ::new (blob) BufferBlob("WB::DummyBlob", CodeBlobKind::Buffer, full_size); } diff --git a/src/hotspot/share/runtime/hotCodeCollector.cpp b/src/hotspot/share/runtime/hotCodeCollector.cpp index 179b57d46788..bb848582c46d 100644 --- a/src/hotspot/share/runtime/hotCodeCollector.cpp +++ b/src/hotspot/share/runtime/hotCodeCollector.cpp @@ -26,6 +26,7 @@ #include "code/codeCache.hpp" #include "code/compiledIC.hpp" +#include "code/nmethod.hpp" #include "compiler/compilerDefinitions.inline.hpp" #include "logging/log.hpp" #include "memory/resourceArea.hpp" @@ -112,13 +113,17 @@ void HotCodeCollector::thread_entry(JavaThread* thread, TRAPS) { } void HotCodeCollector::do_grouping(Candidates& candidates) { + // Number of nmethods relocated (candidate + callees) int num_relocated = 0; + int num_skipped = 0; + int num_invalidated = 0; + + bool hot_code_heap_full = false; // Sort nmethods by increasing sample count so pop() returns the hottest candidates.sort(); - while (candidates.has_candidates()) { - + while (candidates.has_candidates() && !hot_code_heap_full) { double percent_from_hot = candidates.get_hot_sample_percent(); log_debug(hotcode)("Percentage of samples from hot code heap: %f", percent_from_hot); if (percent_from_hot >= HotCodeSamplePercent) { @@ -132,27 +137,51 @@ void HotCodeCollector::do_grouping(Candidates& candidates) { MutexLocker ml_CompiledIC_lock(CompiledIC_lock, Mutex::_no_safepoint_check_flag); MutexLocker ml_CodeCache_lock(CodeCache_lock, Mutex::_no_safepoint_check_flag); - num_relocated += do_relocation(candidate, 0); + switch (do_relocation(candidate, 0, &num_relocated)) { + case nmethod::RelocationResult::SUCCESS: + break; + case nmethod::RelocationResult::FAILED_NO_SPACE_IN_CODE_HEAP: { + CodeHeap* heap = CodeCache::get_code_heap(CodeBlobType::MethodHot); + if (heap->full_count() == 1) { + // Avoid multiple reporting the MethodHot heap being full + log_warning(codecache)("Allocation failed in MethodHot heap (%zu bytes free). " + "Stopping hot methods relocation.", + heap->unallocated_capacity()); + log_warning(codecache)("Try increasing the code heap size using -XX:HotCodeHeapSize="); + } + hot_code_heap_full = true; + break; + } + case nmethod::RelocationResult::FAILED_NOT_RELOCATABLE_NMETHOD: + num_skipped++; + break; + case nmethod::RelocationResult::FAILED_INVALIDATED_NMETHOD: + num_invalidated++; + break; + } } - log_info(hotcode)("Collection done. Relocated %d nmethods to the MethodHot heap", num_relocated); + log_info(hotcode)("Collection done. Relocated %d nmethods (candidates and callees) to the MethodHot heap. " + "Skipped %d candidates. %d candidates invalidated during relocation.", num_relocated, num_skipped, num_invalidated); } -int HotCodeCollector::do_relocation(void* candidate, uint call_level) { +nmethod::RelocationResult HotCodeCollector::do_relocation(void* candidate, uint call_level, int* num_relocated) { + assert(num_relocated != nullptr, "num_relocated must be provided"); + if (candidate == nullptr) { - return 0; + return nmethod::RelocationResult::FAILED_NOT_RELOCATABLE_NMETHOD; } // Verify that address still points to CodeBlob CodeBlob* blob = CodeCache::find_blob(candidate); if (blob == nullptr) { - return 0; + return nmethod::RelocationResult::FAILED_NOT_RELOCATABLE_NMETHOD; } // Verify that blob is nmethod nmethod* nm = blob->as_nmethod_or_null(); if (nm == nullptr || nm->method() == nullptr) { - return 0; + return nmethod::RelocationResult::FAILED_NOT_RELOCATABLE_NMETHOD; } // The candidate may have been recompiled or already relocated. @@ -161,34 +190,27 @@ int HotCodeCollector::do_relocation(void* candidate, uint call_level) { // Verify the nmethod is still valid for relocation if (nm == nullptr || !nm->is_in_use() || !nm->is_compiled_by_c2()) { - return 0; + return nmethod::RelocationResult::FAILED_NOT_RELOCATABLE_NMETHOD; } - // Verify code heap has space - if (CodeCache::get_code_heap(CodeBlobType::MethodHot)->unallocated_capacity() < (size_t)nm->size()) { - log_info(hotcode)("Not enough free space in MethodHot heap (%zd bytes) to relocate nm (%d bytes). Bailing out", - CodeCache::get_code_heap(CodeBlobType::MethodHot)->unallocated_capacity(), nm->size()); - return 0; - } - - // Number of nmethods relocated (candidate + callees) - int num_relocated = 0; - // Pointer to nmethod in hot heap nmethod* hot_nm = nullptr; if (CodeCache::get_code_blob_type(nm) != CodeBlobType::MethodHot) { CompiledICLocker ic_locker(nm); - hot_nm = nm->relocate(CodeBlobType::MethodHot); + + nmethod::RelocationResult relocation_result; + hot_nm = nm->relocate(CodeBlobType::MethodHot, &relocation_result); if (hot_nm != nullptr) { + assert(CodeCache::get_code_blob_type(hot_nm) == CodeBlobType::MethodHot, "Must be relocated to HotCodeHeap"); // Successfully relocated nmethod. Update counts and proceed to callee relocation. log_debug(hotcode)("Successful relocation: nmethod (%p), method (%s), call level (%d)", nm, hot_nm->method()->name_and_sig_as_C_string(), call_level); - num_relocated++; + (*num_relocated)++; } else { // Relocation failed so return and do not attempt to relocate callees - log_debug(hotcode)("Failed relocation: nmethod (%p), call level (%d)", nm, call_level); - return 0; + log_debug(hotcode)("Failed relocation: nmethod (%p), call level (%d), reason (%s)", nm, call_level, nmethod::relocation_result_to_string(relocation_result)); + return relocation_result; } } else { // Skip relocation since already in hot heap, but still relocate callees @@ -213,11 +235,13 @@ int HotCodeCollector::do_relocation(void* candidate, uint call_level) { address dest = ((CallRelocation*) reloc)->destination(); // Recursively relocate callees - num_relocated += do_relocation(dest, call_level + 1); + if (do_relocation(dest, call_level + 1, num_relocated) == nmethod::RelocationResult::FAILED_NO_SPACE_IN_CODE_HEAP) { + return nmethod::RelocationResult::FAILED_NO_SPACE_IN_CODE_HEAP; + } } } - return num_relocated; + return nmethod::RelocationResult::SUCCESS; } void HotCodeCollector::unregister_nmethod(nmethod* nm) { diff --git a/src/hotspot/share/runtime/hotCodeCollector.hpp b/src/hotspot/share/runtime/hotCodeCollector.hpp index dbefa3dc788c..0c4e853730e0 100644 --- a/src/hotspot/share/runtime/hotCodeCollector.hpp +++ b/src/hotspot/share/runtime/hotCodeCollector.hpp @@ -26,6 +26,7 @@ #ifndef SHARE_RUNTIME_HOTCODECOLLECTOR_HPP #define SHARE_RUNTIME_HOTCODECOLLECTOR_HPP +#include "code/nmethod.hpp" #include "runtime/javaThread.hpp" class Candidates; @@ -41,7 +42,7 @@ class HotCodeCollector : public JavaThread { static void do_grouping(Candidates& candidates); - static int do_relocation(void* candidate, uint call_level); + static nmethod::RelocationResult do_relocation(void* candidate, uint call_level, int* num_relocated); public: static void initialize(); diff --git a/test/hotspot/jtreg/compiler/codecache/OverflowCodeCacheTest.java b/test/hotspot/jtreg/compiler/codecache/OverflowCodeCacheTest.java index e0a45e4cba6e..b764bb85fae1 100644 --- a/test/hotspot/jtreg/compiler/codecache/OverflowCodeCacheTest.java +++ b/test/hotspot/jtreg/compiler/codecache/OverflowCodeCacheTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ -46,7 +46,7 @@ /* * @test OverflowCodeCacheTest - * @bug 8059550 8279356 8326205 + * @bug 8059550 8279356 8326205 8390662 * @requires vm.compiler2.enabled * @summary testing of code cache segments overflow * @library /test/lib @@ -106,12 +106,14 @@ private void test() { System.out.println("allocating till possible..."); ArrayList blobs = new ArrayList<>(); int compilationActivityMode = -1; + int compilationActivityModeBefore = -1; CodeCacheConstraints constraints = getCodeCacheConstraints(type); // Lock compilation to be able to better control code cache space WHITE_BOX.lockCompilation(); try { long addr; int size = (int) (getHeapSize() >> 7); + compilationActivityModeBefore = WHITE_BOX.getCompilationActivityMode(); while ((addr = WHITE_BOX.allocateCodeBlob(size, type.id)) != 0) { blobs.add(addr); @@ -154,6 +156,12 @@ private void test() { helper.method(0, 0, 0, null); } } + + if (type == BlobType.MethodHot) { + Asserts.assertEQ(compilationActivityMode, compilationActivityModeBefore, + "MethodHot overflow must not change compilation activity mode"); + } + // Only check this if compilation is disabled, otherwise the sweeper might have // freed enough nmethods to allow for re-enabling compilation. if (COMPILATION_DISABLED) { @@ -171,18 +179,7 @@ void check() {} } CodeCacheConstraints getCodeCacheConstraints(final BlobType type) { - if (Long.valueOf(0).equals(WHITE_BOX.getVMFlag("HotCodeHeapSize"))) { - return new CodeCacheConstraints(); - } else if (BlobType.MethodHot == type) { - // NonProfiledHeap is used when HotCodeHeap runs out of space. - return new CodeCacheConstraints() { - final int nonProfiledCount = WHITE_BOX.getCodeHeapEntries(BlobType.MethodNonProfiled.id).length; - @Override - void check() { - Asserts.assertLT(nonProfiledCount, WHITE_BOX.getCodeHeapEntries(BlobType.MethodNonProfiled.id).length); - } - }; - } else { + if (Boolean.TRUE.equals(WHITE_BOX.getBooleanVMFlag("HotCodeHeap")) && type != BlobType.MethodHot) { // HotCodeHeap should not be used when other heap runs out of space. return new CodeCacheConstraints() { final int hotCount = WHITE_BOX.getCodeHeapEntries(BlobType.MethodHot.id).length; @@ -192,5 +189,7 @@ void check() { } }; } + + return new CodeCacheConstraints(); } } diff --git a/test/hotspot/jtreg/compiler/hotcode/HotCodeCollectorVirtualCalls.java b/test/hotspot/jtreg/compiler/hotcode/HotCodeCollectorVirtualCalls.java new file mode 100644 index 000000000000..703728412819 --- /dev/null +++ b/test/hotspot/jtreg/compiler/hotcode/HotCodeCollectorVirtualCalls.java @@ -0,0 +1,105 @@ +/* + * Copyright Amazon.com Inc. 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 id=default + * @bug 8390662 + * @summary Check the HotCodeCollector can relocate nmethods containing + * virtual call sites. Inline cache access requires the IC lock + * under GCs with concurrent class unloading (ZGC, Shenandoah). + * @requires vm.compiler2.enabled & vm.opt.SegmentedCodeCache != false + * @run main/othervm -XX:+SegmentedCodeCache -XX:+UnlockExperimentalVMOptions -XX:+HotCodeHeap + * -XX:+NMethodRelocation -XX:HotCodeIntervalSeconds=0 -XX:HotCodeSampleSeconds=5 + * -XX:HotCodeStablePercent=-1 -XX:HotCodeSamplePercent=100 -XX:HotCodeStartupDelaySeconds=0 + * compiler.hotcode.HotCodeCollectorVirtualCalls + */ + +/* + * @test id=ZGC + * @bug 8390662 + * @requires vm.compiler2.enabled & vm.opt.SegmentedCodeCache != false & vm.gc.Z + * @run main/othervm -XX:+UseZGC + * -XX:+SegmentedCodeCache -XX:+UnlockExperimentalVMOptions -XX:+HotCodeHeap + * -XX:+NMethodRelocation -XX:HotCodeIntervalSeconds=0 -XX:HotCodeSampleSeconds=5 + * -XX:HotCodeStablePercent=-1 -XX:HotCodeSamplePercent=100 -XX:HotCodeStartupDelaySeconds=0 + * compiler.hotcode.HotCodeCollectorVirtualCalls + */ + +/* + * @test id=Shenandoah + * @bug 8390662 + * @requires vm.compiler2.enabled & vm.opt.SegmentedCodeCache != false & vm.gc.Shenandoah + * @run main/othervm -XX:+UseShenandoahGC + * -XX:+SegmentedCodeCache -XX:+UnlockExperimentalVMOptions -XX:+HotCodeHeap + * -XX:+NMethodRelocation -XX:HotCodeIntervalSeconds=0 -XX:HotCodeSampleSeconds=5 + * -XX:HotCodeStablePercent=-1 -XX:HotCodeSamplePercent=100 -XX:HotCodeStartupDelaySeconds=0 + * compiler.hotcode.HotCodeCollectorVirtualCalls + */ + +package compiler.hotcode; + +public class HotCodeCollectorVirtualCalls { + + private static final long RUN_MILLIS = 60_000; + + private abstract static class Base { + abstract int f(int x); + } + + private static class A extends Base { + int f(int x) { return x + 1; } + } + + private static class B extends Base { + int f(int x) { return x + 2; } + } + + private static class C extends Base { + int f(int x) { return x + 3; } + } + + private static class D extends Base { + int f(int x) { return x + 4; } + } + + // Four receiver classes make the call site megamorphic. + private static final Base[] RECEIVERS = { new A(), new B(), new C(), new D() }; + + private static int hot(int i) { + int s = 0; + for (int j = 0; j < 1000; j++) { + s += RECEIVERS[(i + j) & 3].f(j); + } + return s; + } + + public static void main(String[] args) { + long start = System.currentTimeMillis(); + long s = 0; + while (System.currentTimeMillis() - start < RUN_MILLIS) { + s += hot((int) s); + } + System.out.println("Checksum: " + s); + } +} diff --git a/test/hotspot/jtreg/compiler/hotcode/TestFullHotCodeHeap.java b/test/hotspot/jtreg/compiler/hotcode/TestFullHotCodeHeap.java new file mode 100644 index 000000000000..ec51b4be8e5f --- /dev/null +++ b/test/hotspot/jtreg/compiler/hotcode/TestFullHotCodeHeap.java @@ -0,0 +1,99 @@ +/* + * Copyright Amazon.com Inc. 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 8390662 + * @summary Test that compilation is not stopped when there is no space left in + * HotCodeHeap. Relocation is not compilation, so a failed + * relocation must not disable the compilers. + * @requires vm.compiler2.enabled & vm.opt.SegmentedCodeCache != false + * @library /test/lib / + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -Xbatch -XX:-TieredCompilation -XX:+SegmentedCodeCache + * -XX:+UnlockExperimentalVMOptions -XX:+HotCodeHeap -XX:+NMethodRelocation + * -XX:HotCodeHeapSize=256K -XX:HotCodeStartupDelaySeconds=86400 + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:CodeCacheMinBlockLength=1 + * -XX:-UseCodeCacheFlushing + * compiler.hotcode.TestFullHotCodeHeap + */ + +package compiler.hotcode; + +import java.lang.reflect.Method; + +import jdk.test.lib.Asserts; +import jdk.test.whitebox.WhiteBox; +import jdk.test.whitebox.code.BlobType; +import jdk.test.whitebox.code.NMethod; + +public class TestFullHotCodeHeap { + + private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); + + private static final int C2_LEVEL = 4; + private static final int BLOB_SIZE = 128; + + public static void main(String[] args) throws Exception { + Method method1 = getMethod("simple1"); + + simple1(10); + compileWithC2(method1); + NMethod nmethod = NMethod.get(method1, false); + Asserts.assertNotNull(nmethod, "`simple1` is not compiled"); + + // Fill HotCodeHeap until allocation fails. + while (WHITE_BOX.allocateCodeBlob(BLOB_SIZE, BlobType.MethodHot.id) != 0) {} + + // Not enough space is left in the hot code heap, so the relocation must fail + // and leave the nmethod where it is. + WHITE_BOX.relocateNMethodFromMethod(method1, BlobType.MethodHot.id); + NMethod relocated = NMethod.get(method1, false); + Asserts.assertNotNull(relocated, "`simple1` is not kept after the failed relocation"); + Asserts.assertEQ(nmethod.address, relocated.address, + "`simple1` must not be relocated to the full hot code heap"); + + simple2(11); + compileWithC2(getMethod("simple2")); + } + + private static Method getMethod(String name) throws Exception { + return TestFullHotCodeHeap.class.getMethod(name, int.class); + } + + private static void compileWithC2(Method method) { + WHITE_BOX.enqueueMethodForCompilation(method, C2_LEVEL); + Asserts.assertEQ(C2_LEVEL, WHITE_BOX.getMethodCompilationLevel(method), + "`" + method.getName() + "` is not compiled by C2"); + } + + public static int simple1(int n) { + return ((n % 2) == 0) ? 1 : n; + } + + public static int simple2(int n) { + return ((n % 2) == 0) ? 2 : n; + } +} diff --git a/test/lib/jdk/test/whitebox/code/BlobType.java b/test/lib/jdk/test/whitebox/code/BlobType.java index e2d57f79484d..0fd9ee0a3359 100644 --- a/test/lib/jdk/test/whitebox/code/BlobType.java +++ b/test/lib/jdk/test/whitebox/code/BlobType.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ -46,14 +46,7 @@ public boolean allowTypeWhenOverflow(BlobType type) { || type == BlobType.MethodNonProfiled; } }, - MethodHot(2, "CodeHeap 'hot nmethods'", "HotCodeHeapSize") { - @Override - public boolean allowTypeWhenOverflow(BlobType type) { - return super.allowTypeWhenOverflow(type) - || type == BlobType.MethodNonProfiled - || type == BlobType.MethodProfiled; - } - }, + MethodHot(2, "CodeHeap 'hot nmethods'", "HotCodeHeapSize"), // Non-nmethods like Buffers, Adapters and Runtime Stubs NonNMethod(3, "CodeHeap 'non-nmethods'", "NonNMethodCodeHeapSize") { @Override From ee58739fe2e735ffc35700a0fe8e4bd6a013fd41 Mon Sep 17 00:00:00 2001 From: Albert Mingkun Yang Date: Thu, 24 Sep 2026 11:02:27 +0000 Subject: [PATCH 014/103] 8392931: G1: Rename G1PreBarrierStub to G1PreBarrierStubC1 Reviewed-by: tschatzl --- .../aarch64/gc/g1/g1BarrierSetAssembler_aarch64.cpp | 2 +- .../aarch64/gc/g1/g1BarrierSetAssembler_aarch64.hpp | 6 +++--- .../cpu/arm/gc/g1/g1BarrierSetAssembler_arm.cpp | 4 ++-- .../cpu/arm/gc/g1/g1BarrierSetAssembler_arm.hpp | 6 +++--- .../cpu/ppc/gc/g1/g1BarrierSetAssembler_ppc.cpp | 2 +- .../cpu/ppc/gc/g1/g1BarrierSetAssembler_ppc.hpp | 6 +++--- .../cpu/riscv/gc/g1/g1BarrierSetAssembler_riscv.cpp | 2 +- .../cpu/riscv/gc/g1/g1BarrierSetAssembler_riscv.hpp | 6 +++--- .../cpu/s390/gc/g1/g1BarrierSetAssembler_s390.cpp | 6 +++--- .../cpu/s390/gc/g1/g1BarrierSetAssembler_s390.hpp | 6 +++--- .../cpu/x86/gc/g1/g1BarrierSetAssembler_x86.cpp | 2 +- .../cpu/x86/gc/g1/g1BarrierSetAssembler_x86.hpp | 6 +++--- src/hotspot/share/gc/g1/c1/g1BarrierSetC1.cpp | 8 ++++---- src/hotspot/share/gc/g1/c1/g1BarrierSetC1.hpp | 11 +++++------ 14 files changed, 36 insertions(+), 37 deletions(-) diff --git a/src/hotspot/cpu/aarch64/gc/g1/g1BarrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/g1/g1BarrierSetAssembler_aarch64.cpp index f94297074283..2a1987bffd24 100644 --- a/src/hotspot/cpu/aarch64/gc/g1/g1BarrierSetAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/g1/g1BarrierSetAssembler_aarch64.cpp @@ -445,7 +445,7 @@ void G1BarrierSetAssembler::oop_store_at(MacroAssembler* masm, DecoratorSet deco #undef __ #define __ ce->masm()-> -void G1BarrierSetAssembler::gen_pre_barrier_stub(LIR_Assembler* ce, G1PreBarrierStub* stub) { +void G1BarrierSetAssembler::gen_pre_barrier_stub(LIR_Assembler* ce, G1PreBarrierStubC1* stub) { G1BarrierSetC1* bs = (G1BarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1(); // At this point we know that marking is in progress. // If do_load() is true then we have to emit the diff --git a/src/hotspot/cpu/aarch64/gc/g1/g1BarrierSetAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/gc/g1/g1BarrierSetAssembler_aarch64.hpp index bbcbb6bff1a2..644727b7f649 100644 --- a/src/hotspot/cpu/aarch64/gc/g1/g1BarrierSetAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/gc/g1/g1BarrierSetAssembler_aarch64.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -31,7 +31,7 @@ class LIR_Assembler; class StubAssembler; -class G1PreBarrierStub; +class G1PreBarrierStubC1; class G1PreBarrierStubC2; class G1BarrierSetAssembler: public CardTableBarrierSetAssembler { @@ -62,7 +62,7 @@ class G1BarrierSetAssembler: public CardTableBarrierSetAssembler { public: #ifdef COMPILER1 - void gen_pre_barrier_stub(LIR_Assembler* ce, G1PreBarrierStub* stub); + void gen_pre_barrier_stub(LIR_Assembler* ce, G1PreBarrierStubC1* stub); void generate_c1_pre_barrier_runtime_stub(StubAssembler* sasm); diff --git a/src/hotspot/cpu/arm/gc/g1/g1BarrierSetAssembler_arm.cpp b/src/hotspot/cpu/arm/gc/g1/g1BarrierSetAssembler_arm.cpp index e8ac1f56bdab..c05c0d20ff7a 100644 --- a/src/hotspot/cpu/arm/gc/g1/g1BarrierSetAssembler_arm.cpp +++ b/src/hotspot/cpu/arm/gc/g1/g1BarrierSetAssembler_arm.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -375,7 +375,7 @@ void G1BarrierSetAssembler::oop_store_at(MacroAssembler* masm, DecoratorSet deco #undef __ #define __ ce->masm()-> -void G1BarrierSetAssembler::gen_pre_barrier_stub(LIR_Assembler* ce, G1PreBarrierStub* stub) { +void G1BarrierSetAssembler::gen_pre_barrier_stub(LIR_Assembler* ce, G1PreBarrierStubC1* stub) { G1BarrierSetC1* bs = (G1BarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1(); // At this point we know that marking is in progress. // If do_load() is true then we have to emit the diff --git a/src/hotspot/cpu/arm/gc/g1/g1BarrierSetAssembler_arm.hpp b/src/hotspot/cpu/arm/gc/g1/g1BarrierSetAssembler_arm.hpp index e3c8cf46600d..65f013b5192d 100644 --- a/src/hotspot/cpu/arm/gc/g1/g1BarrierSetAssembler_arm.hpp +++ b/src/hotspot/cpu/arm/gc/g1/g1BarrierSetAssembler_arm.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -31,7 +31,7 @@ class LIR_Assembler; class StubAssembler; -class G1PreBarrierStub; +class G1PreBarrierStubC1; class G1PreBarrierStubC2; class G1BarrierSetAssembler: public CardTableBarrierSetAssembler { @@ -63,7 +63,7 @@ class G1BarrierSetAssembler: public CardTableBarrierSetAssembler { #ifdef COMPILER1 public: - void gen_pre_barrier_stub(LIR_Assembler* ce, G1PreBarrierStub* stub); + void gen_pre_barrier_stub(LIR_Assembler* ce, G1PreBarrierStubC1* stub); void generate_c1_pre_barrier_runtime_stub(StubAssembler* sasm); diff --git a/src/hotspot/cpu/ppc/gc/g1/g1BarrierSetAssembler_ppc.cpp b/src/hotspot/cpu/ppc/gc/g1/g1BarrierSetAssembler_ppc.cpp index e27390203fbb..7f495ccc0a3f 100644 --- a/src/hotspot/cpu/ppc/gc/g1/g1BarrierSetAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/gc/g1/g1BarrierSetAssembler_ppc.cpp @@ -469,7 +469,7 @@ void G1BarrierSetAssembler::g1_write_barrier_post_c2(MacroAssembler* masm, #undef __ #define __ ce->masm()-> -void G1BarrierSetAssembler::gen_pre_barrier_stub(LIR_Assembler* ce, G1PreBarrierStub* stub) { +void G1BarrierSetAssembler::gen_pre_barrier_stub(LIR_Assembler* ce, G1PreBarrierStubC1* stub) { G1BarrierSetC1* bs = (G1BarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1(); // At this point we know that marking is in progress. // If do_load() is true then we have to emit the diff --git a/src/hotspot/cpu/ppc/gc/g1/g1BarrierSetAssembler_ppc.hpp b/src/hotspot/cpu/ppc/gc/g1/g1BarrierSetAssembler_ppc.hpp index a72dfb80ee4f..d1a590901efb 100644 --- a/src/hotspot/cpu/ppc/gc/g1/g1BarrierSetAssembler_ppc.hpp +++ b/src/hotspot/cpu/ppc/gc/g1/g1BarrierSetAssembler_ppc.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2018, 2021 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -36,7 +36,7 @@ class LIR_Assembler; class StubAssembler; -class G1PreBarrierStub; +class G1PreBarrierStubC1; class G1PreBarrierStubC2; class G1BarrierSetAssembler: public CardTableBarrierSetAssembler { @@ -80,7 +80,7 @@ class G1BarrierSetAssembler: public CardTableBarrierSetAssembler { bool decode_new_val); #endif #ifdef COMPILER1 - void gen_pre_barrier_stub(LIR_Assembler* ce, G1PreBarrierStub* stub); + void gen_pre_barrier_stub(LIR_Assembler* ce, G1PreBarrierStubC1* stub); void generate_c1_pre_barrier_runtime_stub(StubAssembler* sasm); diff --git a/src/hotspot/cpu/riscv/gc/g1/g1BarrierSetAssembler_riscv.cpp b/src/hotspot/cpu/riscv/gc/g1/g1BarrierSetAssembler_riscv.cpp index 4b54588cfbc4..19b744cf976b 100644 --- a/src/hotspot/cpu/riscv/gc/g1/g1BarrierSetAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/gc/g1/g1BarrierSetAssembler_riscv.cpp @@ -464,7 +464,7 @@ void G1BarrierSetAssembler::oop_store_at(MacroAssembler* masm, DecoratorSet deco #undef __ #define __ ce->masm()-> -void G1BarrierSetAssembler::gen_pre_barrier_stub(LIR_Assembler* ce, G1PreBarrierStub* stub) { +void G1BarrierSetAssembler::gen_pre_barrier_stub(LIR_Assembler* ce, G1PreBarrierStubC1* stub) { G1BarrierSetC1* bs = (G1BarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1(); // At this point we know that marking is in progress. diff --git a/src/hotspot/cpu/riscv/gc/g1/g1BarrierSetAssembler_riscv.hpp b/src/hotspot/cpu/riscv/gc/g1/g1BarrierSetAssembler_riscv.hpp index 2010e58cc89b..596f68ed3b69 100644 --- a/src/hotspot/cpu/riscv/gc/g1/g1BarrierSetAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/gc/g1/g1BarrierSetAssembler_riscv.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, 2024, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -34,7 +34,7 @@ class LIR_Assembler; #endif class StubAssembler; -class G1PreBarrierStub; +class G1PreBarrierStubC1; class G1PreBarrierStubC2; class G1BarrierSetAssembler: public CardTableBarrierSetAssembler { @@ -65,7 +65,7 @@ class G1BarrierSetAssembler: public CardTableBarrierSetAssembler { public: #ifdef COMPILER1 - void gen_pre_barrier_stub(LIR_Assembler* ce, G1PreBarrierStub* stub); + void gen_pre_barrier_stub(LIR_Assembler* ce, G1PreBarrierStubC1* stub); void generate_c1_pre_barrier_runtime_stub(StubAssembler* sasm); diff --git a/src/hotspot/cpu/s390/gc/g1/g1BarrierSetAssembler_s390.cpp b/src/hotspot/cpu/s390/gc/g1/g1BarrierSetAssembler_s390.cpp index ba69bfbc7731..c235c18720ef 100644 --- a/src/hotspot/cpu/s390/gc/g1/g1BarrierSetAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/gc/g1/g1BarrierSetAssembler_s390.cpp @@ -481,7 +481,7 @@ void G1BarrierSetAssembler::resolve_jobject(MacroAssembler* masm, Register value #undef __ #define __ ce->masm()-> -void G1BarrierSetAssembler::gen_pre_barrier_stub(LIR_Assembler* ce, G1PreBarrierStub* stub) { +void G1BarrierSetAssembler::gen_pre_barrier_stub(LIR_Assembler* ce, G1PreBarrierStubC1* stub) { G1BarrierSetC1* bs = (G1BarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1(); // At this point we know that marking is in progress. // If do_load() is true then we have to emit the @@ -547,7 +547,7 @@ void G1BarrierSetAssembler::generate_c1_pre_barrier_runtime_stub(StubAssembler* int satb_q_index_byte_offset = in_bytes(G1ThreadLocalData::satb_mark_queue_index_offset()); int satb_q_buf_byte_offset = in_bytes(G1ThreadLocalData::satb_mark_queue_buffer_offset()); - // Save tmp registers (see assertion in G1PreBarrierStub::emit_code()). + // Save tmp registers (see assertion in G1BarrierSetAssembler::gen_pre_barrier_stub). __ z_stg(tmp, 0*BytesPerWord + FrameMap::first_available_sp_in_frame, Z_SP); __ z_stg(tmp2, 1*BytesPerWord + FrameMap::first_available_sp_in_frame, Z_SP); @@ -575,7 +575,7 @@ void G1BarrierSetAssembler::generate_c1_pre_barrier_runtime_stub(StubAssembler* __ z_stg(tmp, satb_q_index_byte_offset, Z_thread); __ bind(marking_not_active); - // Restore tmp registers (see assertion in G1PreBarrierStub::emit_code()). + // Restore tmp registers (see assertion in G1PreBarrierStubC1::emit_code()). __ z_lg(tmp, 0*BytesPerWord + FrameMap::first_available_sp_in_frame, Z_SP); __ z_lg(tmp2, 1*BytesPerWord + FrameMap::first_available_sp_in_frame, Z_SP); __ z_br(Z_R14); diff --git a/src/hotspot/cpu/s390/gc/g1/g1BarrierSetAssembler_s390.hpp b/src/hotspot/cpu/s390/gc/g1/g1BarrierSetAssembler_s390.hpp index 00e0a1162076..45bf5ea897ad 100644 --- a/src/hotspot/cpu/s390/gc/g1/g1BarrierSetAssembler_s390.hpp +++ b/src/hotspot/cpu/s390/gc/g1/g1BarrierSetAssembler_s390.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2018, 2024 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -32,7 +32,7 @@ class LIR_Assembler; class StubAssembler; -class G1PreBarrierStub; +class G1PreBarrierStubC1; class G1PreBarrierStubC2; class G1BarrierSetAssembler: public CardTableBarrierSetAssembler { @@ -57,7 +57,7 @@ class G1BarrierSetAssembler: public CardTableBarrierSetAssembler { public: #ifdef COMPILER1 - void gen_pre_barrier_stub(LIR_Assembler* ce, G1PreBarrierStub* stub); + void gen_pre_barrier_stub(LIR_Assembler* ce, G1PreBarrierStubC1* stub); void generate_c1_pre_barrier_runtime_stub(StubAssembler* sasm); diff --git a/src/hotspot/cpu/x86/gc/g1/g1BarrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/g1/g1BarrierSetAssembler_x86.cpp index 3bdf407b41a1..3e246230d590 100644 --- a/src/hotspot/cpu/x86/gc/g1/g1BarrierSetAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/gc/g1/g1BarrierSetAssembler_x86.cpp @@ -462,7 +462,7 @@ void G1BarrierSetAssembler::oop_store_at(MacroAssembler* masm, DecoratorSet deco #undef __ #define __ ce->masm()-> -void G1BarrierSetAssembler::gen_pre_barrier_stub(LIR_Assembler* ce, G1PreBarrierStub* stub) { +void G1BarrierSetAssembler::gen_pre_barrier_stub(LIR_Assembler* ce, G1PreBarrierStubC1* stub) { G1BarrierSetC1* bs = (G1BarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1(); // At this point we know that marking is in progress. // If do_load() is true then we have to emit the diff --git a/src/hotspot/cpu/x86/gc/g1/g1BarrierSetAssembler_x86.hpp b/src/hotspot/cpu/x86/gc/g1/g1BarrierSetAssembler_x86.hpp index 0db96495beea..bcfb15ad5a56 100644 --- a/src/hotspot/cpu/x86/gc/g1/g1BarrierSetAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/gc/g1/g1BarrierSetAssembler_x86.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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,7 +30,7 @@ class LIR_Assembler; class StubAssembler; -class G1PreBarrierStub; +class G1PreBarrierStubC1; class G1BarrierStubC2; class G1PreBarrierStubC2; @@ -59,7 +59,7 @@ class G1BarrierSetAssembler: public CardTableBarrierSetAssembler { Register dst, Address src, Register tmp1); #ifdef COMPILER1 - void gen_pre_barrier_stub(LIR_Assembler* ce, G1PreBarrierStub* stub); + void gen_pre_barrier_stub(LIR_Assembler* ce, G1PreBarrierStubC1* stub); void generate_c1_pre_barrier_runtime_stub(StubAssembler* sasm); diff --git a/src/hotspot/share/gc/g1/c1/g1BarrierSetC1.cpp b/src/hotspot/share/gc/g1/c1/g1BarrierSetC1.cpp index 51c8a53b54a7..643931949b37 100644 --- a/src/hotspot/share/gc/g1/c1/g1BarrierSetC1.cpp +++ b/src/hotspot/share/gc/g1/c1/g1BarrierSetC1.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -40,7 +40,7 @@ #define __ gen->lir()-> #endif -void G1PreBarrierStub::emit_code(LIR_Assembler* ce) { +void G1PreBarrierStubC1::emit_code(LIR_Assembler* ce) { G1BarrierSetAssembler* bs = (G1BarrierSetAssembler*)BarrierSet::barrier_set()->barrier_set_assembler(); bs->gen_pre_barrier_stub(ce, this); } @@ -98,14 +98,14 @@ void G1BarrierSetC1::pre_barrier(LIRAccess& access, LIR_Opr addr_opr, assert(addr_opr->is_register(), "must be"); addr_opr = LIR_OprFact::address(new LIR_Address(addr_opr, T_OBJECT)); } - slow = new G1PreBarrierStub(addr_opr, pre_val, pre_val_patch_code, info); + slow = new G1PreBarrierStubC1(addr_opr, pre_val, pre_val_patch_code, info); } else { assert(addr_opr == LIR_OprFact::illegalOpr, "sanity"); assert(pre_val->is_register(), "must be"); assert(pre_val->type() == T_OBJECT, "must be an object"); assert(info == nullptr, "sanity"); - slow = new G1PreBarrierStub(pre_val); + slow = new G1PreBarrierStubC1(pre_val); } __ branch(lir_cond_notEqual, slow); diff --git a/src/hotspot/share/gc/g1/c1/g1BarrierSetC1.hpp b/src/hotspot/share/gc/g1/c1/g1BarrierSetC1.hpp index 76dcd7fa4ff3..30ada680d723 100644 --- a/src/hotspot/share/gc/g1/c1/g1BarrierSetC1.hpp +++ b/src/hotspot/share/gc/g1/c1/g1BarrierSetC1.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -29,8 +29,7 @@ #include "c1/c1_Compilation.hpp" #include "gc/shared/c1/cardTableBarrierSetC1.hpp" -class G1PreBarrierStub: public CodeStub { - friend class G1BarrierSetC1; +class G1PreBarrierStubC1: public CodeStub { private: bool _do_load; LIR_Opr _addr; @@ -42,7 +41,7 @@ class G1PreBarrierStub: public CodeStub { // Version that _does_ generate a load of the previous value from addr. // addr (the address of the field to be read) must be a LIR_Address // pre_val (a temporary register) must be a register; - G1PreBarrierStub(LIR_Opr addr, LIR_Opr pre_val, LIR_PatchCode patch_code, CodeEmitInfo* info) : + G1PreBarrierStubC1(LIR_Opr addr, LIR_Opr pre_val, LIR_PatchCode patch_code, CodeEmitInfo* info) : _do_load(true), _addr(addr), _pre_val(pre_val), _patch_code(patch_code), _info(info) { @@ -54,7 +53,7 @@ class G1PreBarrierStub: public CodeStub { // Version that _does not_ generate load of the previous value; the // previous value is assumed to have already been loaded into pre_val. - G1PreBarrierStub(LIR_Opr pre_val) : + G1PreBarrierStubC1(LIR_Opr pre_val) : _do_load(false), _addr(LIR_OprFact::illegalOpr), _pre_val(pre_val), _patch_code(lir_patch_none), _info(nullptr) { @@ -87,7 +86,7 @@ class G1PreBarrierStub: public CodeStub { } } #ifndef PRODUCT - virtual void print_name(outputStream* out) const { out->print("G1PreBarrierStub"); } + virtual void print_name(outputStream* out) const { out->print("G1PreBarrierStubC1"); } #endif // PRODUCT }; From b647a9dd5cfba4b5944afbb42fd8c39727e85df5 Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Thu, 24 Sep 2026 11:44:08 +0000 Subject: [PATCH 015/103] 8392401: WindowsLookAndFeel isClassicWindows can be removed after JDK-8390526 Reviewed-by: prr, aivanov, serb --- .../swing/plaf/windows/WindowsLookAndFeel.java | 15 --------------- .../java/swing/plaf/windows/WindowsMenuUI.java | 14 ++++---------- 2 files changed, 4 insertions(+), 25 deletions(-) diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsLookAndFeel.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsLookAndFeel.java index 5c2564504a1c..3d5b314f427f 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsLookAndFeel.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsLookAndFeel.java @@ -1864,21 +1864,6 @@ public void uninitialize() { WindowsDesktopProperty.flushUnreferencedProperties(); } - /** - * Gets the state of the flag which indicates if the old Windows - * look and feel should be rendered. This flag is used by the - * component UI delegates as a hint to determine which style the component - * should be rendered. - * - * @return true if Windows 95 and Windows NT 4 look and feel should - * be rendered - * @since 1.4 - */ - public static boolean isClassicWindows() { - // we could probably remove the whole method - return false; - } - /** *

* Invoked when the user attempts an invalid operation, diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuUI.java index 78028db7c002..fe195e9e4030 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuUI.java @@ -121,10 +121,7 @@ public static ComponentUI createUI(JComponent x) { @Override protected void installDefaults() { super.installDefaults(); - if (!WindowsLookAndFeel.isClassicWindows()) { - menuItem.setRolloverEnabled(true); - } - + menuItem.setRolloverEnabled(true); menuBarHeight = (Integer)UIManager.getInt("MenuBar.height"); Object obj = UIManager.get("MenuBar.rolloverEnabled"); @@ -167,10 +164,8 @@ protected void paintBackground(Graphics g, JMenuItem menuItem, Color bgColor) { JMenu menu = (JMenu)menuItem; ButtonModel model = menu.getModel(); - // Use superclass method for the old Windows LAF, - // for submenus, and for XP toplevel if selected or pressed - if (WindowsLookAndFeel.isClassicWindows() || - !menu.isTopLevelMenu() || + // Use superclass method for submenus, and for XP toplevel if selected or pressed + if (!menu.isTopLevelMenu() || (XPStyle.getXP() != null && (model.isArmed() || model.isSelected()))) { super.paintBackground(g, menu, bgColor); @@ -260,8 +255,7 @@ protected void paintText(Graphics g, JMenuItem menuItem, } } - if ((model.isSelected() && (WindowsLookAndFeel.isClassicWindows() || - !menu.isTopLevelMenu())) || + if ((model.isSelected() && !menu.isTopLevelMenu()) || (XPStyle.getXP() != null && (paintRollover || model.isArmed() || model.isSelected()))) { From 2ebc4732602911af84e363753a70efb912aa1527 Mon Sep 17 00:00:00 2001 From: Casper Norrbin Date: Thu, 24 Sep 2026 12:23:04 +0000 Subject: [PATCH 016/103] 8392375: Linux cgroup v2 CPU detection retains the wrong controller path when the mount root supplies the lowest quota Reviewed-by: sgehwolf, roland --- src/hotspot/os/linux/cgroupUtil_linux.cpp | 2 +- .../docker/TestCPUWithSubgroups.java | 25 ++++++++++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/hotspot/os/linux/cgroupUtil_linux.cpp b/src/hotspot/os/linux/cgroupUtil_linux.cpp index c92abc5fcefa..78eb51f1eeab 100644 --- a/src/hotspot/os/linux/cgroupUtil_linux.cpp +++ b/src/hotspot/os/linux/cgroupUtil_linux.cpp @@ -183,7 +183,7 @@ void CgroupUtil::adjust_controller(CgroupCpuController* cpu, double upper_bound) if (cpus != upper_bound && cpus < lowest_limit) { lowest_limit = cpus; os::free(limit_cg_path); // handles nullptr - limit_cg_path = os::strdup(cg_path); + limit_cg_path = os::strdup("/"); } assert(lowest_limit >= 0, "limit must be positive"); if (lowest_limit != orig_limit) { diff --git a/test/hotspot/jtreg/containers/docker/TestCPUWithSubgroups.java b/test/hotspot/jtreg/containers/docker/TestCPUWithSubgroups.java index 2e1112dad9b4..370e80b8f2c6 100644 --- a/test/hotspot/jtreg/containers/docker/TestCPUWithSubgroups.java +++ b/test/hotspot/jtreg/containers/docker/TestCPUWithSubgroups.java @@ -24,7 +24,7 @@ /* * @test * @key cgroups - * @summary Test that a stricter leaf CPU quota is not replaced by a looser parent quota + * @summary Test that the strictest CPU quota is used in a cgroup hierarchy * @requires container.support * @requires !vm.asan * @library /test/lib @@ -64,6 +64,9 @@ public static void main(String[] args) throws Exception { DockerTestUtils.buildJdkContainerImage(imageName); try { testCpuLimitSubgroup(provider); + if ("cgroupv2".equals(provider)) { + testCpuLimitAtMountRoot(); + } } finally { DockerTestUtils.removeDockerImage(imageName); } @@ -102,4 +105,24 @@ private static void testCpuLimitSubgroup(String cgroupVersion) throws Exception Common.run(opts) .shouldContain("active_processor_count: determined by OSContainer: 1"); } + + private static void testCpuLimitAtMountRoot() throws Exception { + Common.logNewTestCase("Cgroup V2 mount-root CPU limit"); + + DockerRunOptions opts = new DockerRunOptions(imageName, "sh", "-c"); + opts.javaOpts.clear(); + opts.appendTestJavaOptions = false; + opts.addDockerOpts("--privileged") + .addDockerOpts("--user", "root") + .addDockerOpts("--cgroupns=private", "--cpus=1"); + opts.addClassOptions( + "set -e; " + + "CG=/sys/fs/cgroup/jdk-cpu-hierarchy-test; mkdir -p $CG; " + + "echo $$ > $CG/cgroup.procs; " + + "echo '+cpu' > /sys/fs/cgroup/cgroup.subtree_control; " + + "/jdk/bin/java -Xlog:os+container=trace -Xlog:os=trace -version"); + + Common.run(opts) + .shouldContain("active_processor_count: determined by OSContainer: 1"); + } } From a944ec3aff9550fd498c37559a21510b8dd6ca39 Mon Sep 17 00:00:00 2001 From: Erik Gahlin Date: Thu, 24 Sep 2026 12:54:57 +0000 Subject: [PATCH 017/103] 8392857: JFR: jfr disassemble may fail incorrectly Reviewed-by: mgronlun --- .../share/classes/jdk/jfr/internal/tool/Disassemble.java | 4 ++-- test/jdk/jdk/jfr/tool/TestDisassemble.java | 9 ++++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Disassemble.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Disassemble.java index 25211f84bd4c..cf419db3a144 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Disassemble.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Disassemble.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, 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 @@ -149,7 +149,7 @@ public void execute(Deque options) throws UserSyntaxException, UserDataE } catch (IOException e) { throw new UserDataException("unexpected i/o error. " + e.getMessage()); } - if (maxsize == Integer.MAX_VALUE == sizes.size() <= maxChunks) { + if (maxsize == Integer.MAX_VALUE && sizes.size() <= maxChunks) { throw new UserDataException("number of chunks in recording (" + sizes.size() + ") doesn't exceed max chunks (" + maxChunks + ")"); } println(); diff --git a/test/jdk/jdk/jfr/tool/TestDisassemble.java b/test/jdk/jdk/jfr/tool/TestDisassemble.java index 68088fdc5bb9..514748101640 100644 --- a/test/jdk/jdk/jfr/tool/TestDisassemble.java +++ b/test/jdk/jdk/jfr/tool/TestDisassemble.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, 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 @@ -55,15 +55,18 @@ public static void main(String[] args) throws Throwable { Path recordingFileB = Paths.get("many-chunks-B-" + dateText + ".jfr"); Path recordingFileC = Paths.get("many-chunks-C-" + dateText + ".jfr"); Path recordingFileD = Paths.get("many-chunks-D-" + dateText + ".jfr"); + Path recordingFileE = Paths.get("many-chunks-E-" + dateText + ".jfr"); makeRecordingWithChunks(6, recordingFileA); Files.copy(recordingFileA, recordingFileB); Files.copy(recordingFileA, recordingFileC); Files.copy(recordingFileA, recordingFileD); + Files.copy(recordingFileA, recordingFileE); String fileAText = recordingFileA.toAbsolutePath().toString(); String fileBText = recordingFileB.toAbsolutePath().toString(); String fileCText = recordingFileC.toAbsolutePath().toString(); String fileDText = recordingFileD.toAbsolutePath().toString(); + String fileEText = recordingFileE.toAbsolutePath().toString(); OutputAnalyzer output = ExecuteHelper.jfr("disassemble"); output.shouldContain("missing file"); @@ -108,6 +111,10 @@ public static void main(String[] args) throws Throwable { for (long i = 0; i < Long.parseLong(chunks); i++) { verifyRecording(chunkFilePrefix + String.format("%0" + chunks.length() + "d", i) + ".jfr"); } + + // test JDK-8392857 + output = ExecuteHelper.jfr("disassemble", "--max-size", "10000", "--max-chunks", "3", fileEText); + output.shouldNotContain("number of chunks in recording"); } private static void verifyRecording(String name) throws IOException { From ae1a1399be3028b7ccdc6b3642a3019d8cd66345 Mon Sep 17 00:00:00 2001 From: Vladimir Ivanov Date: Thu, 24 Sep 2026 14:56:38 +0000 Subject: [PATCH 018/103] 8376219: C2: FastLock cannot be shared Reviewed-by: chagedorn, thartmann, mchevalier --- src/hotspot/share/adlc/formssel.cpp | 17 --- src/hotspot/share/adlc/formssel.hpp | 2 - src/hotspot/share/opto/callnode.cpp | 28 ++-- src/hotspot/share/opto/callnode.hpp | 35 +++-- src/hotspot/share/opto/escape.cpp | 3 +- src/hotspot/share/opto/graphKit.cpp | 31 ++--- src/hotspot/share/opto/graphKit.hpp | 8 +- src/hotspot/share/opto/locknode.cpp | 37 +---- src/hotspot/share/opto/locknode.hpp | 29 ++-- src/hotspot/share/opto/loopopts.cpp | 25 ++-- src/hotspot/share/opto/machnode.hpp | 8 -- src/hotspot/share/opto/macro.cpp | 33 +---- src/hotspot/share/opto/macro.hpp | 2 +- src/hotspot/share/opto/memnode.hpp | 4 +- src/hotspot/share/opto/parse.hpp | 4 +- src/hotspot/share/opto/parse1.cpp | 39 +++--- src/hotspot/share/opto/phaseX.cpp | 3 +- src/hotspot/share/opto/split_if.cpp | 128 +++++++++--------- .../loopopts/TestPeelingFastLock.java | 122 +++++++++++++++++ 19 files changed, 280 insertions(+), 278 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/loopopts/TestPeelingFastLock.java diff --git a/src/hotspot/share/adlc/formssel.cpp b/src/hotspot/share/adlc/formssel.cpp index 3108f963daa2..644dd237ed7f 100644 --- a/src/hotspot/share/adlc/formssel.cpp +++ b/src/hotspot/share/adlc/formssel.cpp @@ -318,13 +318,6 @@ bool InstructForm::is_ideal_if() const { return _matrule->is_ideal_if(); } -// Return 'true' if this instruction matches an ideal 'FastLock' node -bool InstructForm::is_ideal_fastlock() const { - if( _matrule == nullptr ) return false; - - return _matrule->is_ideal_fastlock(); -} - // Return 'true' if this instruction matches an ideal 'MemBarXXX' node bool InstructForm::is_ideal_membar() const { if( _matrule == nullptr ) return false; @@ -1118,9 +1111,6 @@ const char *InstructForm::mach_base_class(FormDict &globals) const { else if (is_ideal_goto()) { return "MachGotoNode"; } - else if (is_ideal_fastlock()) { - return "MachFastLockNode"; - } else if (is_ideal_nop()) { return "MachNopNode"; } @@ -4227,13 +4217,6 @@ bool MatchRule::is_ideal_if() const { !strcmp(_opType,"CountedLoopEnd"); } -bool MatchRule::is_ideal_fastlock() const { - if ( _opType && (strcmp(_opType,"Set") == 0) && _rChild ) { - return (strcmp(_rChild->_opType,"FastLock") == 0); - } - return false; -} - bool MatchRule::is_ideal_membar() const { if( !_opType ) return false; return diff --git a/src/hotspot/share/adlc/formssel.hpp b/src/hotspot/share/adlc/formssel.hpp index 7a87b7255981..3dd2f4bc503e 100644 --- a/src/hotspot/share/adlc/formssel.hpp +++ b/src/hotspot/share/adlc/formssel.hpp @@ -157,7 +157,6 @@ class InstructForm : public Form { virtual int is_ideal_copy() const; // node matches ideal 'Copy*' virtual bool is_ideal_negD() const; // node matches ideal 'NegD' virtual bool is_ideal_if() const; // node matches ideal 'If' - virtual bool is_ideal_fastlock() const; // node matches 'FastLock' virtual bool is_ideal_membar() const; // node matches ideal 'MemBarXXX' virtual bool is_ideal_loadPC() const; // node matches ideal 'LoadPC' virtual bool is_ideal_box() const; // node matches ideal 'Box' @@ -1051,7 +1050,6 @@ class MatchRule : public MatchNode { int is_ideal_copy() const; int is_expensive() const; // node matches ideal 'CosD' bool is_ideal_if() const; // node matches ideal 'If' - bool is_ideal_fastlock() const; // node matches ideal 'FastLock' bool is_ideal_jump() const; // node matches ideal 'Jump' bool is_ideal_membar() const; // node matches ideal 'MemBarXXX' bool is_ideal_loadPC() const; // node matches ideal 'LoadPC' diff --git a/src/hotspot/share/opto/callnode.cpp b/src/hotspot/share/opto/callnode.cpp index 3f1b97449101..5dc459c0ca48 100644 --- a/src/hotspot/share/opto/callnode.cpp +++ b/src/hotspot/share/opto/callnode.cpp @@ -1886,15 +1886,15 @@ void SafePointNode::grow_stack(JVMState* jvms, uint grow_by) { jvms->set_endoff(endoff + grow_by); } -void SafePointNode::push_monitor(const FastLockNode *lock) { +void SafePointNode::push_monitor(BoxLockNode* box, Node* obj) { // Add a LockNode, which points to both the original BoxLockNode (the // stack space for the monitor) and the Object being locked. const int MonitorEdges = 2; assert(JVMState::logMonitorEdges == exact_log2(MonitorEdges), "correct MonitorEdges"); assert(req() == jvms()->endoff(), "correct sizing"); int nextmon = jvms()->scloff(); - ins_req(nextmon, lock->box_node()); - ins_req(nextmon+1, lock->obj_node()); + ins_req(nextmon, box); + ins_req(nextmon+1, obj); jvms()->set_scloff(nextmon + MonitorEdges); jvms()->set_endoff(req()); } @@ -1914,13 +1914,13 @@ void SafePointNode::pop_monitor() { assert(jvms()->nof_monitors() == num_before_pop-1, ""); } -Node *SafePointNode::peek_monitor_box() const { +BoxLockNode* SafePointNode::peek_monitor_box() const { int mon = jvms()->nof_monitors() - 1; assert(mon >= 0, "must have a monitor"); return monitor_box(jvms(), mon); } -Node *SafePointNode::peek_monitor_obj() const { +Node* SafePointNode::peek_monitor_obj() const { int mon = jvms()->nof_monitors() - 1; assert(mon >= 0, "must have a monitor"); return monitor_obj(jvms(), mon); @@ -2273,17 +2273,8 @@ uint LockNode::size_of() const { return sizeof(*this); } // Locking and unlocking have a canonical form in ideal that looks // roughly like this: // -// -// | \\------+ -// | \ \ -// | BoxLock \ -// | | | \ -// | | \ \ -// | | FastLock -// | | / -// | | / -// | | | -// +// obj BoxLock +// \ / // Lock // | // Proj #0 @@ -2520,8 +2511,7 @@ void AbstractLockNode::dump_compact_spec(outputStream* st) const { #endif //============================================================================= -Node *LockNode::Ideal(PhaseGVN *phase, bool can_reshape) { - +Node* LockNode::Ideal(PhaseGVN* phase, bool can_reshape) { // perform any generic optimizations first (returns 'this' or null) Node *result = SafePointNode::Ideal(phase, can_reshape); if (result != nullptr) return result; @@ -2701,7 +2691,7 @@ bool LockNode::is_nested_lock_region(Compile * c) { // Loop over monitors for (int idx = 0; idx < num_mon; idx++) { Node* obj_node = sfn->monitor_obj(jvms, idx); - BoxLockNode* box_node = sfn->monitor_box(jvms, idx)->as_BoxLock(); + BoxLockNode* box_node = sfn->monitor_box(jvms, idx); if ((box_node->stack_slot() < stk_slot) && obj_node->eqv_uncast(obj)) { box->set_nested(); return true; diff --git a/src/hotspot/share/opto/callnode.hpp b/src/hotspot/share/opto/callnode.hpp index 055d32048ef4..349cef7fe625 100644 --- a/src/hotspot/share/opto/callnode.hpp +++ b/src/hotspot/share/opto/callnode.hpp @@ -54,7 +54,6 @@ class AllocateArrayNode; class AbstractLockNode; class LockNode; class UnlockNode; -class FastLockNode; //------------------------------StartNode-------------------------------------- // The method start node @@ -402,11 +401,11 @@ class SafePointNode : public MultiNode { verify_input(jvms, arg_idx); return in(jvms->argoff() + idx); } - Node* monitor_box(const JVMState* jvms, uint idx) const { + BoxLockNode* monitor_box(const JVMState* jvms, uint idx) const { assert(verify_jvms(jvms), "jvms must match"); uint mon_box_idx = jvms->monitor_box_offset(idx); assert(jvms->is_monitor_box(mon_box_idx), "not a monitor box offset"); - return in(mon_box_idx); + return in(mon_box_idx)->as_BoxLock(); } Node* monitor_obj(const JVMState* jvms, uint idx) const { assert(verify_jvms(jvms), "jvms must match"); @@ -432,10 +431,10 @@ class SafePointNode : public MultiNode { } void grow_stack(JVMState* jvms, uint grow_by); // Handle monitor stack - void push_monitor( const FastLockNode *lock ); - void pop_monitor (); - Node *peek_monitor_box() const; - Node *peek_monitor_obj() const; + void push_monitor(BoxLockNode* box, Node* obj); + void pop_monitor(); + BoxLockNode* peek_monitor_box() const; + Node* peek_monitor_obj() const; // Peek Operand Stacks, JVMS 2.6.2 Node* peek_operand(uint off = 0) const; @@ -1310,10 +1309,9 @@ class AbstractLockNode: public CallNode { #endif } virtual int Opcode() const = 0; - Node * obj_node() const {return in(TypeFunc::Parms + 0); } - Node * box_node() const {return in(TypeFunc::Parms + 1); } - Node * fastlock_node() const {return in(TypeFunc::Parms + 2); } - void set_box_node(Node* box) { set_req(TypeFunc::Parms + 1, box); } + Node* obj_node() const { return in(TypeFunc::Parms + 0); } + Node* box_node() const { return in(TypeFunc::Parms + 1); } + void set_box_node(Node* box) { set_req(TypeFunc::Parms + 1, box); } const Type *sub(const Type *t1, const Type *t2) const { return TypeInt::CC;} @@ -1350,10 +1348,9 @@ class AbstractLockNode: public CallNode { // High-level lock operation // // This is a subclass of CallNode because it is a macro node which gets expanded -// into a code sequence containing a call. This node takes 3 "parameters": -// 0 - object to lock -// 1 - a BoxLockNode -// 2 - a FastLockNode +// into a code sequence containing a call. This node takes two "parameters": +// 0 - object to lock +// 1 - a BoxLockNode // class LockNode : public AbstractLockNode { static const TypeFunc* _lock_type_Type; @@ -1366,12 +1363,12 @@ class LockNode : public AbstractLockNode { static void initialize_lock_Type() { assert(_lock_type_Type == nullptr, "should be called once"); - // create input type (domain) - const Type **fields = TypeTuple::fields(3); + int argcnt = 2; + + const Type** fields = TypeTuple::fields(argcnt); fields[TypeFunc::Parms+0] = TypeInstPtr::NOTNULL; // Object to be Locked fields[TypeFunc::Parms+1] = TypeRawPtr::BOTTOM; // Address of stack location for lock - fields[TypeFunc::Parms+2] = TypeInt::BOOL; // FastLock - const TypeTuple *domain = TypeTuple::make(TypeFunc::Parms+3,fields); + const TypeTuple* domain = TypeTuple::make(TypeFunc::Parms + argcnt, fields); // create result type (range) fields = TypeTuple::fields(0); diff --git a/src/hotspot/share/opto/escape.cpp b/src/hotspot/share/opto/escape.cpp index b80365187b7d..858bb6578022 100644 --- a/src/hotspot/share/opto/escape.cpp +++ b/src/hotspot/share/opto/escape.cpp @@ -5206,8 +5206,7 @@ void ConnectionGraph::split_unique_types(GrowableArray &alloc_worklist, (use->in(MemNode::Memory) == n)) { // They overwrite memory edge corresponding to destination array, memnode_worklist.push(use); - } else if (!(op == Op_CmpP || op == Op_Conv2B || - op == Op_CastP2X || op == Op_FastLock || + } else if (!(op == Op_CmpP || op == Op_Conv2B || op == Op_CastP2X || use->is_memory_access_intrinsic() || op == Op_SubTypeCheck || op == Op_ValueType || op == Op_FlatArrayCheck || op == Op_ReinterpretS2HF || diff --git a/src/hotspot/share/opto/graphKit.cpp b/src/hotspot/share/opto/graphKit.cpp index 4096a9c2841b..5d954f6a2b43 100644 --- a/src/hotspot/share/opto/graphKit.cpp +++ b/src/hotspot/share/opto/graphKit.cpp @@ -4114,31 +4114,27 @@ Node* GraphKit::insert_reachability_fence(Node* referent) { //------------------------------shared_lock------------------------------------ // Emit locking code. -FastLockNode* GraphKit::shared_lock(Node* obj) { +BoxLockNode* GraphKit::shared_lock(Node* obj) { // bci is either a monitorenter bc or InvocationEntryBci // %%% SynchronizationEntryBCI is redundant; use InvocationEntryBci in interfaces assert(SynchronizationEntryBCI == InvocationEntryBci, ""); - if (stopped()) // Dead monitor? - return nullptr; + if (stopped()) { return nullptr; } // Dead monitor? assert(dead_locals_are_killed(), "should kill locals before sync. point"); // Box the stack location - Node* box = new BoxLockNode(next_monitor()); - // Check for bailout after new BoxLockNode - if (failing()) { return nullptr; } - box = _gvn.transform(box); + BoxLockNode* box = new BoxLockNode(next_monitor()); + if (failing()) { return nullptr; } // check for bailout after new BoxLockNode + box = _gvn.transform(box)->as_BoxLock(); Node* mem = reset_memory(); - FastLockNode * flock = _gvn.transform(new FastLockNode(nullptr, obj, box) )->as_FastLock(); - // Add monitor to debug info for the slow path. If we block inside the // slow path and de-opt, we need the monitor hanging around - map()->push_monitor( flock ); + map()->push_monitor(box, obj); - const TypeFunc *tf = LockNode::lock_type(); - LockNode *lock = new LockNode(C, tf); + const TypeFunc* tf = LockNode::lock_type(); + LockNode* lock = new LockNode(C, tf); lock->init_req( TypeFunc::Control, control() ); lock->init_req( TypeFunc::Memory , mem ); @@ -4148,12 +4144,12 @@ FastLockNode* GraphKit::shared_lock(Node* obj) { lock->init_req(TypeFunc::Parms + 0, obj); lock->init_req(TypeFunc::Parms + 1, box); - lock->init_req(TypeFunc::Parms + 2, flock); + add_safepoint_edges(lock); - lock = _gvn.transform( lock )->as_Lock(); + lock = _gvn.transform(lock)->as_Lock(); - // lock has no side-effects, sets few values + // lock has no side effects, sets few values set_predefined_output_for_runtime_call(lock, mem, TypeRawPtr::BOTTOM); insert_mem_bar(Op_MemBarAcquireLock); @@ -4169,14 +4165,13 @@ FastLockNode* GraphKit::shared_lock(Node* obj) { increment_counter(lock->counter()->addr()); } #endif - - return flock; + return box; } //------------------------------shared_unlock---------------------------------- // Emit unlocking code. -void GraphKit::shared_unlock(Node* box, Node* obj) { +void GraphKit::shared_unlock(BoxLockNode* box, Node* obj) { // bci is either a monitorenter bc or InvocationEntryBci // %%% SynchronizationEntryBCI is redundant; use InvocationEntryBci in interfaces assert(SynchronizationEntryBCI == InvocationEntryBci, ""); diff --git a/src/hotspot/share/opto/graphKit.hpp b/src/hotspot/share/opto/graphKit.hpp index 7246364bd2cf..88cf7484446f 100644 --- a/src/hotspot/share/opto/graphKit.hpp +++ b/src/hotspot/share/opto/graphKit.hpp @@ -41,8 +41,6 @@ #include "runtime/deoptimization.hpp" class BarrierSetC2; -class FastLockNode; -class FastUnlockNode; class IdealKit; class LibraryCallKit; class Parse; @@ -525,7 +523,7 @@ class GraphKit : public Phase { Node* local(uint idx) const { map_not_null(); return _map->local( _map->_jvms, idx); } Node* stack(uint idx) const { map_not_null(); return _map->stack( _map->_jvms, idx); } Node* argument(uint idx) const { map_not_null(); return _map->argument( _map->_jvms, idx); } - Node* monitor_box(uint idx) const { map_not_null(); return _map->monitor_box(_map->_jvms, idx); } + BoxLockNode* monitor_box(uint idx) const { map_not_null(); return _map->monitor_box(_map->_jvms, idx); } Node* monitor_obj(uint idx) const { map_not_null(); return _map->monitor_obj(_map->_jvms, idx); } void set_control (Node* c) { map_not_null()->set_control(c); } @@ -827,8 +825,8 @@ class GraphKit : public Phase { Node* insert_mem_bar_volatile(int opcode, int alias_idx, Node* precedent = nullptr); Node* insert_reachability_fence(Node* referent); // Optional 'precedent' is appended as an extra edge, to force ordering. - FastLockNode* shared_lock(Node* obj); - void shared_unlock(Node* box, Node* obj); + BoxLockNode* shared_lock(Node* obj); + void shared_unlock(BoxLockNode* box, Node* obj); // helper functions for the fast path/slow path idioms Node* fast_and_slow(Node* in, const Type *result_type, Node* null_result, IfNode* fast_test, Node* fast_result, address slow_call, const TypeFunc *slow_call_type, Node* slow_arg, Klass* ex_klass, Node* slow_result); diff --git a/src/hotspot/share/opto/locknode.cpp b/src/hotspot/share/opto/locknode.cpp index a8f2478ea299..d8f1a48d26fb 100644 --- a/src/hotspot/share/opto/locknode.cpp +++ b/src/hotspot/share/opto/locknode.cpp @@ -157,22 +157,6 @@ bool BoxLockNode::is_simple_lock_region(LockNode** unique_lock, Node* obj, Node* } } } -#ifdef ASSERT - // Verify that FastLock and Safepoint reference only this lock region. - for (uint i = 0; i < this->outcnt(); i++) { - Node* n = this->raw_out(i); - if (n->is_FastLock()) { - FastLockNode* flock = n->as_FastLock(); - assert((flock->box_node() == this) && flock->obj_node()->eqv_uncast(obj),""); - } - // Don't check monitor info in safepoints since the referenced object could - // be different from the locked object. It could be Phi node of different - // cast nodes which point to this locked object. - // We assume that no other objects could be referenced in monitor info - // associated with this BoxLock node because all associated locks and - // unlocks are reference only this one object. - } -#endif if (unique_lock != nullptr && has_one_lock) { *unique_lock = lock; } @@ -180,16 +164,6 @@ bool BoxLockNode::is_simple_lock_region(LockNode** unique_lock, Node* obj, Node* } //============================================================================= -//-----------------------------hash-------------------------------------------- -uint FastLockNode::hash() const { return NO_HASH; } - -uint FastLockNode::size_of() const { return sizeof(*this); } - -//------------------------------cmp-------------------------------------------- -bool FastLockNode::cmp( const Node &n ) const { - return (&n == this); // Always fail except on self -} - const Type* FastLockNode::Value(PhaseGVN* phase) const { const Type* in1_t = phase->type(in(1)); if (in1_t == Type::TOP) { @@ -202,15 +176,6 @@ const Type* FastLockNode::Value(PhaseGVN* phase) const { return TypeInt::CC; } -//============================================================================= -//-----------------------------hash-------------------------------------------- -uint FastUnlockNode::hash() const { return NO_HASH; } - -//------------------------------cmp-------------------------------------------- -bool FastUnlockNode::cmp( const Node &n ) const { - return (&n == this); // Always fail except on self -} - //============================================================================= //------------------------------do_monitor_enter------------------------------- void Parse::do_monitor_enter() { @@ -230,7 +195,7 @@ void Parse::do_monitor_enter() { // the monitor object is not part of debug info expression stack pop(); - // Insert a FastLockNode which takes as arguments the current thread pointer, + // Insert a LockNode which takes as arguments the current thread pointer, // the obj pointer & the address of the stack slot pair used for the lock. shared_lock(obj); } diff --git a/src/hotspot/share/opto/locknode.hpp b/src/hotspot/share/opto/locknode.hpp index 9d32b94befd8..7f833c8a8f9a 100644 --- a/src/hotspot/share/opto/locknode.hpp +++ b/src/hotspot/share/opto/locknode.hpp @@ -131,22 +131,15 @@ class BoxLockNode : public Node { //------------------------------FastLockNode----------------------------------- class FastLockNode: public CmpNode { public: - FastLockNode(Node *ctrl, Node *oop, Node *box) : CmpNode(oop,box) { - init_req(0,ctrl); + FastLockNode(Node* ctrl, Node* oop, Node* box) : CmpNode(oop, box) { + assert(ctrl != nullptr, "required"); + init_req(0, ctrl); init_class_id(Class_FastLock); } - Node* obj_node() const { return in(1); } - Node* box_node() const { return in(2); } - void set_box_node(Node* box) { set_req(2, box); } - // FastLock and FastUnlockNode do not hash, we need one for each corresponding - // LockNode/UnLockNode to avoid creating Phi's. - virtual uint hash() const ; // { return NO_HASH; } - virtual uint size_of() const; - virtual bool cmp( const Node &n ) const ; // Always fail, except on self virtual int Opcode() const; virtual const Type* Value(PhaseGVN* phase) const; - const Type *sub(const Type *t1, const Type *t2) const { return TypeInt::CC;} + const Type* sub(const Type* t1, const Type* t2) const { return TypeInt::CC;} private: virtual bool depends_only_on_test_impl() const { return false; } @@ -156,21 +149,15 @@ class FastLockNode: public CmpNode { //------------------------------FastUnlockNode--------------------------------- class FastUnlockNode: public CmpNode { public: - FastUnlockNode(Node *ctrl, Node *oop, Node *box) : CmpNode(oop,box) { - init_req(0,ctrl); + FastUnlockNode(Node* ctrl, Node* oop, Node* box) : CmpNode(oop, box) { + assert(ctrl != nullptr, "required"); + init_req(0, ctrl); init_class_id(Class_FastUnlock); } - Node* obj_node() const { return in(1); } - Node* box_node() const { return in(2); } - - // FastLock and FastUnlockNode do not hash, we need one for each corresponding - // LockNode/UnLockNode to avoid creating Phi's. - virtual uint hash() const ; // { return NO_HASH; } - virtual bool cmp( const Node &n ) const ; // Always fail, except on self virtual int Opcode() const; virtual const Type* Value(PhaseGVN* phase) const { return TypeInt::CC; } - const Type *sub(const Type *t1, const Type *t2) const { return TypeInt::CC;} + const Type* sub(const Type* t1, const Type* t2) const { return TypeInt::CC; } private: virtual bool depends_only_on_test_impl() const { return false; } diff --git a/src/hotspot/share/opto/loopopts.cpp b/src/hotspot/share/opto/loopopts.cpp index 43539fc09f05..77ccf3b31bdd 100644 --- a/src/hotspot/share/opto/loopopts.cpp +++ b/src/hotspot/share/opto/loopopts.cpp @@ -1577,14 +1577,13 @@ bool PhaseIdealLoop::flat_array_element_type_check(Node *n) { // in the post-order, so it can dirty the I-DOM info and not use the dirtied // info. void PhaseIdealLoop::split_if_with_blocks_post(Node *n) { - if (flat_array_element_type_check(n)) { return; } // Cloning Cmp through Phi's involves the split-if transform. - // FastLock is not used by an If - if (n->is_Cmp() && !n->is_FastLock()) { + if (n->is_Cmp()) { + assert(!n->is_FastLock(), "should not be materialized, yet"); Node *n_ctrl = get_ctrl(n); // Determine if the Node has inputs from some local Phi. // Returns the block to clone thru. @@ -2992,18 +2991,18 @@ void PhaseIdealLoop::fix_body_edges(const Node_List &body, IdealLoopTree* loop, } // Correct edges to the new node for (uint j = 0; j < nnn->req(); j++) { - Node *n = nnn->in(j); - if (n != nullptr) { - IdealLoopTree *old_in_loop = get_loop(has_ctrl(n) ? get_ctrl(n) : n); - if (loop->is_member(old_in_loop)) { - if (old_new[n->_idx] != nullptr) { - nnn->set_req(j, old_new[n->_idx]); - } else { - assert(!body.contains(n), ""); - assert(partial, "node not cloned"); - } + Node* n = nnn->in(j); + if (n != nullptr) { + IdealLoopTree* old_in_loop = get_loop(ctrl_or_self(n)); + if (loop->is_member(old_in_loop)) { + if (old_new[n->_idx] != nullptr) { + nnn->set_req(j, old_new[n->_idx]); + } else { + assert(!body.contains(n), ""); + assert(partial, "node not cloned"); } } + } } _igvn.hash_find_insert(nnn); } diff --git a/src/hotspot/share/opto/machnode.hpp b/src/hotspot/share/opto/machnode.hpp index ff56670a8427..fda3f41db704 100644 --- a/src/hotspot/share/opto/machnode.hpp +++ b/src/hotspot/share/opto/machnode.hpp @@ -853,14 +853,6 @@ class MachGotoNode : public MachBranchNode { } }; -//------------------------------MachFastLockNode------------------------------------- -// Machine-specific versions of FastLockNodes -class MachFastLockNode : public MachNode { - virtual uint size_of() const { return sizeof(*this); } // Size is bigger -public: - MachFastLockNode() : MachNode() {} -}; - //------------------------------MachReturnNode-------------------------------- // Machine-specific versions of subroutine returns class MachReturnNode : public MachNode { diff --git a/src/hotspot/share/opto/macro.cpp b/src/hotspot/share/opto/macro.cpp index a64d43b148cf..a8ee9645f754 100644 --- a/src/hotspot/share/opto/macro.cpp +++ b/src/hotspot/share/opto/macro.cpp @@ -93,20 +93,16 @@ int PhaseMacroExpand::replace_input(Node *use, Node *oldref, Node *newref) { } -Node* PhaseMacroExpand::opt_bits_test(Node* ctrl, Node* region, int edge, Node* word) { - Node* cmp = word; +Node* PhaseMacroExpand::opt_bits_test(Node* ctrl, Node* region, int edge, CmpNode* cmp) { Node* bol = transform_later(new BoolNode(cmp, BoolTest::ne)); IfNode* iff = new IfNode( ctrl, bol, PROB_MIN, COUNT_UNKNOWN ); transform_later(iff); - // Fast path taken. - Node *fast_taken = transform_later(new IfFalseNode(iff)); + Node* fast_path = transform_later(new IfFalseNode(iff)); + Node* slow_path = transform_later(new IfTrueNode(iff)); - // Fast path not-taken, i.e. slow path - Node *slow_taken = transform_later(new IfTrueNode(iff)); - - region->init_req(edge, fast_taken); // Capture fast-control - return slow_taken; + region->init_req(edge, fast_path); // Capture fast-control + return slow_path; } //--------------------copy_predefined_input_for_runtime_call-------------------- @@ -2523,13 +2519,6 @@ void PhaseMacroExpand::mark_eliminated_box(Node* box, Node* obj) { next_edge = false; } } - if (u->is_FastLock() && u->as_FastLock()->obj_node()->eqv_uncast(obj)) { - FastLockNode* flock = u->as_FastLock(); - assert(flock->box_node() == oldbox, "sanity"); - _igvn.rehash_node_delayed(flock); - flock->set_box_node(newbox); - next_edge = false; - } // Replace old box in monitor debug info. if (u->is_SafePoint() && u->as_SafePoint()->jvms()) { @@ -2677,14 +2666,6 @@ bool PhaseMacroExpand::eliminate_locking_node(AbstractLockNode *alock) { Node* memproj = membar->proj_out(TypeFunc::Memory); _igvn.replace_node(ctrlproj, fallthroughproj); _igvn.replace_node(memproj, memproj_fallthrough); - - // Delete FastLock node also if this Lock node is unique user - // (a loop peeling may clone a Lock node). - Node* flock = alock->as_Lock()->fastlock_node(); - if (flock->outcnt() == 1) { - assert(flock->unique_out() == alock, "sanity"); - _igvn.replace_node(flock, top()); - } } // Search for MemBarReleaseLock node and delete it also. @@ -2713,7 +2694,6 @@ void PhaseMacroExpand::expand_lock_node(LockNode *lock) { Node* mem = lock->in(TypeFunc::Memory); Node* obj = lock->obj_node(); Node* box = lock->box_node(); - Node* flock = lock->fastlock_node(); assert(!box->as_BoxLock()->is_eliminated(), "sanity"); @@ -2724,9 +2704,10 @@ void PhaseMacroExpand::expand_lock_node(LockNode *lock) { region = new RegionNode(3); // create a Phi for the memory state - mem_phi = new PhiNode( region, Type::MEMORY, TypeRawPtr::BOTTOM); + mem_phi = new PhiNode(region, Type::MEMORY, TypeRawPtr::BOTTOM); // Optimize test; set region slot 2 + FastLockNode* flock = transform_later(new FastLockNode(ctrl, obj, box))->as_FastLock(); slow_path = opt_bits_test(ctrl, region, 2, flock); mem_phi->init_req(2, mem); diff --git a/src/hotspot/share/opto/macro.hpp b/src/hotspot/share/opto/macro.hpp index 86a3e7947079..89a9cda6293e 100644 --- a/src/hotspot/share/opto/macro.hpp +++ b/src/hotspot/share/opto/macro.hpp @@ -225,7 +225,7 @@ class PhaseMacroExpand : public Phase { int replace_input(Node *use, Node *oldref, Node *newref); void migrate_outs(Node *old, Node *target); - Node* opt_bits_test(Node* ctrl, Node* region, int edge, Node* word); + Node* opt_bits_test(Node* ctrl, Node* region, int edge, CmpNode* cmp); void copy_predefined_input_for_runtime_call(Node * ctrl, CallNode* oldcall, CallNode* call); CallNode* make_slow_call(CallNode *oldcall, const TypeFunc* slow_call_type, address slow_call, const char* leaf_name, Node* slow_path, Node* parm0, Node* parm1, diff --git a/src/hotspot/share/opto/memnode.hpp b/src/hotspot/share/opto/memnode.hpp index 437752beb896..95d36b86a535 100644 --- a/src/hotspot/share/opto/memnode.hpp +++ b/src/hotspot/share/opto/memnode.hpp @@ -1220,7 +1220,7 @@ class ClearArrayNode: public Node { //------------------------------MemBar----------------------------------------- // There are different flavors of Memory Barriers to match the Java Memory // Model. Monitor-enter and volatile-load act as Acquires: no following ref -// can be moved to before them. We insert a MemBar-Acquire after a FastLock or +// can be moved to before them. We insert a MemBar-Acquire after a lock or // volatile-load. Monitor-exit and volatile-store act as Release: no // preceding ref can be moved to after them. We insert a MemBar-Release // before a FastUnlock or volatile-store. All volatiles need to be @@ -1336,7 +1336,7 @@ class StoreFenceNode: public MemBarNode { // "Acquire" - no following ref can move before (but earlier refs can // follow, like an early Load stalled in cache). Requires multi-cpu -// visibility. Inserted after a FastLock. +// visibility. Inserted after a Lock. class MemBarAcquireLockNode: public MemBarNode { public: MemBarAcquireLockNode(Compile* C, int alias_idx, Node* precedent) diff --git a/src/hotspot/share/opto/parse.hpp b/src/hotspot/share/opto/parse.hpp index 283ee1cb300e..e7b4ed2f0b19 100644 --- a/src/hotspot/share/opto/parse.hpp +++ b/src/hotspot/share/opto/parse.hpp @@ -363,7 +363,9 @@ class Parse : public GraphKit { Block* _block; // block currently getting parsed ciBytecodeStream _iter; // stream of this method's bytecodes - const FastLockNode* _synch_lock; // FastLockNode for synchronized method + // Lock info for synchronized method + BoxLockNode* _sync_lock_box; + Node* _sync_lock_obj; #ifndef PRODUCT int _max_switch_depth; // Debugging SwitchRanges. diff --git a/src/hotspot/share/opto/parse1.cpp b/src/hotspot/share/opto/parse1.cpp index 4557f1e1702a..08d5fdb8e22c 100644 --- a/src/hotspot/share/opto/parse1.cpp +++ b/src/hotspot/share/opto/parse1.cpp @@ -258,26 +258,25 @@ void Parse::load_interpreter_state(Node* osr_buf) { osr_box->set_coarsened(); osr_box->set_unbalanced(); - Node* box = _gvn.transform(osr_box); + BoxLockNode* box = _gvn.transform(osr_box)->as_BoxLock(); // Object monitor cache and locked objects are interleaved in the // temp OSR buffer. We only copy the locked objects out here. - // Fetch the locked object from the OSR temp buffer and copy to our fastlock node. + // Fetch the locked object from the OSR temp buffer and keep it around. Node* lock_object = fetch_interpreter_state(index*2, Type::get_const_basic_type(T_OBJECT), monitors_addr); // Try and copy the object monitor cache to the BoxNode Node* object_monitor_cache = fetch_interpreter_state((index*2) + 1, Type::get_const_basic_type(T_ADDRESS), monitors_addr); store_to_memory(control(), box, object_monitor_cache, T_ADDRESS, MemNode::unordered); - // Build a bogus FastLockNode (no code will be generated) and push the - // monitor into our debug info. - const FastLockNode *flock = _gvn.transform(new FastLockNode( nullptr, lock_object, box ))->as_FastLock(); - map()->push_monitor(flock); + // Push the monitor info into our debug info. + map()->push_monitor(box, lock_object); // If the lock is our method synchronization lock, tuck it away in // _sync_lock for return and rethrow exit paths. if (index == 0 && method()->is_synchronized()) { - _synch_lock = flock; + _sync_lock_box = box; + _sync_lock_obj = lock_object; } } @@ -459,6 +458,8 @@ Parse::Parse(JVMState* caller, ciMethod* parse_method, float expected_uses) _alloc_with_final_or_stable = nullptr; _stress_rf_hook = (StressReachabilityFences ? new Node(1) : nullptr); _block = nullptr; + _sync_lock_box = nullptr; + _sync_lock_obj = nullptr; _first_return = true; _replaced_nodes_for_exceptions = false; _new_idx = C->unique(); @@ -1174,13 +1175,12 @@ void Parse::do_exits() { JVMState* caller = kit.jvms(); JVMState* ex_jvms = caller->clone_shallow(C); ex_jvms->bind_map(kit.clone_map()); - ex_jvms->set_bci( InvocationEntryBci); + ex_jvms->set_bci(InvocationEntryBci); kit.set_jvms(ex_jvms); if (do_synch) { // Add on the synchronized-method box/object combo - kit.map()->push_monitor(_synch_lock); - // Unlock! - kit.shared_unlock(_synch_lock->box_node(), _synch_lock->obj_node()); + kit.map()->push_monitor(_sync_lock_box, _sync_lock_obj); + kit.shared_unlock(_sync_lock_box, _sync_lock_obj); // Unlock! } if (C->env()->dtrace_method_probes()) { kit.make_dtrace_method_exit(method()); @@ -1383,13 +1383,11 @@ void Parse::do_method_entry() { // If the method is synchronized, we need to construct a lock node, attach // it to the Start node, and pin it there. if (method()->is_synchronized()) { - // Insert a FastLockNode right after the Start which takes as arguments + // Insert a LockNode right after the Start which takes as arguments // the current thread pointer, the "this" pointer & the address of the // stack slot pair used for the lock. The "this" pointer is a projection // off the start node, but the locking spot has to be constructed by - // creating a ConLNode of 0, and boxing it with a BoxLockNode. The BoxLockNode - // becomes the second argument to the FastLockNode call. The - // FastLockNode becomes the new control parent to pin it to the start. + // creating a ConLNode of 0, and boxing it with a BoxLockNode. // Setup Object Pointer Node *lock_obj = nullptr; @@ -1403,10 +1401,11 @@ void Parse::do_method_entry() { } // Clear out dead values from the debug info. kill_dead_locals(); - // Build the FastLockNode - _synch_lock = shared_lock(lock_obj); - // Check for bailout in shared_lock - if (failing()) { return; } + BoxLockNode* box = shared_lock(lock_obj); + if (failing()) { return; } // check for bailout in shared_lock + assert(box != nullptr, "missing"); + _sync_lock_box = box; + _sync_lock_obj = lock_obj; } // Feed profiling data for parameters to the type system so it can @@ -2553,7 +2552,7 @@ void Parse::return_current(Node* value) { // Do not set_parse_bci, so that return goo is credited to the return insn. set_bci(InvocationEntryBci); if (method()->is_synchronized()) { - shared_unlock(_synch_lock->box_node(), _synch_lock->obj_node()); + shared_unlock(_sync_lock_box, _sync_lock_obj); } if (C->env()->dtrace_method_probes()) { make_dtrace_method_exit(method()); diff --git a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp index 6f3cc9bdb0fb..76ba689dacb3 100644 --- a/src/hotspot/share/opto/phaseX.cpp +++ b/src/hotspot/share/opto/phaseX.cpp @@ -3466,7 +3466,6 @@ Node *PhaseCCP::transform_once( Node *n ) { switch( n->Opcode() ) { case Op_CallStaticJava: // Give post-parse call devirtualization a chance case Op_CallDynamicJava: - case Op_FastLock: // Revisit FastLocks for lock coarsening case Op_If: case Op_CountedLoopEnd: case Op_Region: @@ -3476,6 +3475,8 @@ Node *PhaseCCP::transform_once( Node *n ) { case Op_Opaque1: _worklist.push(n); break; + case Op_FastLock: + assert(false, "should not be materialized yet"); default: break; } diff --git a/src/hotspot/share/opto/split_if.cpp b/src/hotspot/share/opto/split_if.cpp index c8f25f92de38..84a53211d09d 100644 --- a/src/hotspot/share/opto/split_if.cpp +++ b/src/hotspot/share/opto/split_if.cpp @@ -298,7 +298,8 @@ void PhaseIdealLoop::clone_loadklass_nodes_at_cmp_index(const Node* n, Node* cmp } bool PhaseIdealLoop::clone_cmp_down(Node* n, const Node* blk1, const Node* blk2) { - if( n->is_Cmp() ) { + if (n->is_Cmp()) { + assert(!n->is_FastLock(), "should not be materialized yet"); assert(get_ctrl(n) == blk2 || get_ctrl(n) == blk1, "must be in block with IF"); // Check for simple Cmp/Bool/CMove which we can clone-up. Cmp/Bool/CMove // sequence can have no other users and it must all reside in the split-if @@ -321,87 +322,80 @@ bool PhaseIdealLoop::clone_cmp_down(Node* n, const Node* blk1, const Node* blk2) tty->print_cr(" Cloning down (Cmp): %d %s", n->_idx, n->Name()); } #endif - if (!n->is_FastLock()) { - // Clone down any block-local BoolNode uses of this CmpNode - for (DUIterator i = n->outs(); n->has_out(i); i++) { - Node* bol = n->out(i); - assert( bol->is_Bool(), "" ); - if (bol->outcnt() == 1) { - Node* use = bol->unique_out(); - if (use->is_OpaqueConstantBool() || use->is_OpaqueTemplateAssertionPredicate() || - use->is_OpaqueInitializedAssertionPredicate()) { - if (use->outcnt() == 1) { - Node* iff = use->unique_out(); - assert(iff->is_If(), "unexpected node type"); - Node *use_c = iff->in(0); - if (use_c == blk1 || use_c == blk2) { - continue; - } - } - } else { - // We might see an Opaque1 from a loop limit check here - assert(use->is_If() || use->is_CMove() || use->Opcode() == Op_Opaque1 || use->is_AllocateArray(), "unexpected node type"); - Node *use_c = (use->is_If() || use->is_AllocateArray()) ? use->in(0) : get_ctrl(use); + // Clone down any block-local BoolNode uses of this CmpNode + for (DUIterator i = n->outs(); n->has_out(i); i++) { + Node* bol = n->out(i); + assert(bol->is_Bool(), ""); + if (bol->outcnt() == 1) { + Node* use = bol->unique_out(); + if (use->is_OpaqueConstantBool() || use->is_OpaqueTemplateAssertionPredicate() || + use->is_OpaqueInitializedAssertionPredicate()) { + if (use->outcnt() == 1) { + Node* iff = use->unique_out(); + assert(iff->is_If(), "unexpected node type"); + Node* use_c = iff->in(0); if (use_c == blk1 || use_c == blk2) { - assert(use->is_CMove(), "unexpected node type"); continue; } } + } else { + // We might see an Opaque1 from a loop limit check here + assert(use->is_If() || use->is_CMove() || use->Opcode() == Op_Opaque1 || use->is_AllocateArray(), "unexpected node type"); + Node* use_c = (use->is_If() || use->is_AllocateArray()) ? use->in(0) : get_ctrl(use); + if (use_c == blk1 || use_c == blk2) { + assert(use->is_CMove(), "unexpected node type"); + continue; + } } - if (at_relevant_ctrl(bol, blk1, blk2)) { - // Recursively sink any BoolNode - for (DUIterator j = bol->outs(); bol->has_out(j); j++) { - Node* u = bol->out(j); - // Uses are either IfNodes, CMoves, OpaqueConstantBool or Opaque*AssertionPredicate - if (u->is_OpaqueConstantBool() || u->is_OpaqueTemplateAssertionPredicate() || - u->is_OpaqueInitializedAssertionPredicate()) { - assert(u->in(1) == bol, "bad input"); - for (DUIterator_Last kmin, k = u->last_outs(kmin); k >= kmin; --k) { - Node* iff = u->last_out(k); - assert(iff->is_If() || iff->is_CMove(), "unexpected node type"); - assert( iff->in(1) == u, "" ); - // Get control block of either the CMove or the If input - Node *iff_ctrl = iff->is_If() ? iff->in(0) : get_ctrl(iff); - Node *x1 = bol->clone(); - Node *x2 = u->clone(); - register_new_node(x1, iff_ctrl); - register_new_node(x2, iff_ctrl); - _igvn.replace_input_of(x2, 1, x1); - _igvn.replace_input_of(iff, 1, x2); - } - _igvn.remove_dead_node(u, PhaseIterGVN::NodeOrigin::Graph); - --j; - } else { - // We might see an Opaque1 from a loop limit check here - assert(u->is_If() || u->is_CMove() || u->Opcode() == Op_Opaque1 || u->is_AllocateArray(), "unexpected node type"); - assert(u->is_AllocateArray() || u->in(1) == bol, ""); - assert(!u->is_AllocateArray() || u->in(AllocateNode::ValidLengthTest) == bol, "wrong input to AllocateArray"); + } + if (at_relevant_ctrl(bol, blk1, blk2)) { + // Recursively sink any BoolNode + for (DUIterator j = bol->outs(); bol->has_out(j); j++) { + Node* u = bol->out(j); + // Uses are either IfNodes, CMoves, OpaqueConstantBool or Opaque*AssertionPredicate + if (u->is_OpaqueConstantBool() || u->is_OpaqueTemplateAssertionPredicate() || + u->is_OpaqueInitializedAssertionPredicate()) { + assert(u->in(1) == bol, "bad input"); + for (DUIterator_Last kmin, k = u->last_outs(kmin); k >= kmin; --k) { + Node* iff = u->last_out(k); + assert(iff->is_If() || iff->is_CMove(), "unexpected node type"); + assert(iff->in(1) == u, ""); // Get control block of either the CMove or the If input - Node *u_ctrl = (u->is_If() || u->is_AllocateArray()) ? u->in(0) : get_ctrl(u); - assert((u_ctrl != blk1 && u_ctrl != blk2) || u->is_CMove(), "won't converge"); - Node *x = bol->clone(); - register_new_node(x, u_ctrl); - _igvn.replace_input_of(u, u->is_AllocateArray() ? AllocateNode::ValidLengthTest : 1, x); - --j; + Node* iff_ctrl = iff->is_If() ? iff->in(0) : get_ctrl(iff); + Node* x1 = bol->clone(); + Node* x2 = u->clone(); + register_new_node(x1, iff_ctrl); + register_new_node(x2, iff_ctrl); + _igvn.replace_input_of(x2, 1, x1); + _igvn.replace_input_of(iff, 1, x2); } + _igvn.remove_dead_node(u, PhaseIterGVN::NodeOrigin::Graph); + --j; + } else { + // We might see an Opaque1 from a loop limit check here + assert(u->is_If() || u->is_CMove() || u->Opcode() == Op_Opaque1 || u->is_AllocateArray(), "unexpected node type"); + assert(u->is_AllocateArray() || u->in(1) == bol, ""); + assert(!u->is_AllocateArray() || u->in(AllocateNode::ValidLengthTest) == bol, "wrong input to AllocateArray"); + // Get control block of either the CMove or the If input + Node* u_ctrl = (u->is_If() || u->is_AllocateArray()) ? u->in(0) : get_ctrl(u); + assert((u_ctrl != blk1 && u_ctrl != blk2) || u->is_CMove(), "won't converge"); + Node* x = bol->clone(); + register_new_node(x, u_ctrl); + _igvn.replace_input_of(u, u->is_AllocateArray() ? AllocateNode::ValidLengthTest : 1, x); + --j; } - _igvn.remove_dead_node(bol, PhaseIterGVN::NodeOrigin::Graph); - --i; } + _igvn.remove_dead_node(bol, PhaseIterGVN::NodeOrigin::Graph); + --i; } } // Clone down this CmpNode for (DUIterator_Last jmin, j = n->last_outs(jmin); j >= jmin; --j) { Node* use = n->last_out(j); - uint pos = 1; - if (n->is_FastLock()) { - pos = TypeFunc::Parms + 2; - assert(use->is_Lock(), "FastLock only used by LockNode"); - } - assert(use->in(pos) == n, "" ); - Node *x = n->clone(); + Node* x = n->clone(); register_new_node(x, ctrl_or_self(use)); - _igvn.replace_input_of(use, pos, x); + assert(use->in(1) == n, "should match"); + _igvn.replace_input_of(use, 1, x); } _igvn.remove_dead_node(n, PhaseIterGVN::NodeOrigin::Graph); diff --git a/test/hotspot/jtreg/compiler/loopopts/TestPeelingFastLock.java b/test/hotspot/jtreg/compiler/loopopts/TestPeelingFastLock.java new file mode 100644 index 000000000000..d946fcd8cfd9 --- /dev/null +++ b/test/hotspot/jtreg/compiler/loopopts/TestPeelingFastLock.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2026, 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 8376219 + * @summary C2: Shared FastLock node + * + * @requires vm.compiler2.enabled + * + * @run main/othervm -Xbatch -XX:-TieredCompilation ${test.main.class} + */ + + +package compiler.loopopts; + +public class TestPeelingFastLock { + + static int iFldStatic = 10; + int iFld = 11; + + private static boolean gt(int lhs, int rhs) { + return lhs > rhs; + } + + int test() { + int a = 1; + int b = 2; + int c = 3; + int v = 4; + int w = 5; + int x = 6; + int y = 7; + int z = 8; + int iArr[] = new int[400]; + + double d = 1.5; + + int k = 0; + for (a = 9; a < 283; a += 2) { + for (int i = 8; i < 183; i++) { } + } + + for (int i = 12; i < 283; i++) { + for (int j = 0; j < 4; j++) { + for (int jj = 0; jj < 8; jj++) { + switch (j) { + case -1, -2, -3 : + break; + case 0 : + iFldStatic += i; + } + } + } + iFldStatic += i; + for (int j = 1; gt(93, j); j += 2) { + x += j - z; + c -= iFld; + k = 3; + while ((k -= 2) > 0) {} + switch (i % 8 + 52) { + case 52: + iArr[8] = 5; + for (int i20 = 1; i20 < 3; ++i20) { + x *= (int)d; + w += 5; + } + break; + case 53: + case 55: + synchronized(this) { + v *= iFldStatic; + v *= iFldStatic; + } + break; + case 56: + case 57: + try { + for (int count = 0; count < 32; count++) { + if (count < 10) { + iArr[5] = a; + } + } + iArr[5] = a; + v = (a / b); + } catch (ArithmeticException a_e) {} + break; + default: + iFldStatic += iFldStatic; + } + } + } + return y + k; + } + + public static void main(String[] strArr) { + TestPeelingFastLock t = new TestPeelingFastLock(); + for (int i = 0; i < 10; i++) { + t.test(); + } + } +} From 4be9a676785f41dc432d515f9217d1a65db1ae3c Mon Sep 17 00:00:00 2001 From: Patrick Fontanilla Date: Thu, 24 Sep 2026 16:06:46 +0000 Subject: [PATCH 019/103] 8392001: Shenandoah: Optimize HdrSeq memory footprint Reviewed-by: wkemper, kdnilsen --- .../gc/shenandoah/shenandoahControlThread.cpp | 3 + .../shenandoahGenerationalControlThread.cpp | 3 + .../share/gc/shenandoah/shenandoahHeap.cpp | 6 ++ .../share/gc/shenandoah/shenandoahHeap.hpp | 4 ++ .../gc/shenandoah/shenandoahNumberSeq.cpp | 56 +++++++++++++------ .../gc/shenandoah/shenandoahNumberSeq.hpp | 17 ++++-- .../shenandoah/test_shenandoahNumberSeq.cpp | 33 ++++++++++- 7 files changed, 96 insertions(+), 26 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp index 2cd1e1b1c39c..c36df9c209e8 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp @@ -242,6 +242,9 @@ void ShenandoahControlThread::run_service() { } } } + + // We're done writing GC stats, so print them here. + heap->print_gc_stats_at_exit(); } void ShenandoahControlThread::service_concurrent_normal_cycle(GCCause::Cause cause) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp index 7580d45ed3f2..3f9d789123b7 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp @@ -94,6 +94,9 @@ void ShenandoahGenerationalControlThread::run_service() { notify_gc_waiters(); notify_alloc_failure_waiters(); set_gc_mode(stopped); + + // We're done writing GC stats, so print them here. + _heap->print_gc_stats_at_exit(); } void ShenandoahGenerationalControlThread::stop_service() { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp index 15a928cc53a4..88bf122f7bd8 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp @@ -1698,6 +1698,12 @@ void ShenandoahHeap::gc_threads_do(ThreadClosure* tcl) const { } void ShenandoahHeap::print_tracing_info() const { + // Do nothing. The control thread prints out GC statistics once it's finished updating them. +} + +void ShenandoahHeap::print_gc_stats_at_exit() const { + assert(Thread::current() == control_thread(), "Only the control thread prints these stats"); + assert(control_thread()->should_terminate(), "Only during shutdown"); LogTarget(Info, gc, stats) lt; if (lt.is_enabled()) { ResourceMark rm; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp index 07d1f491dd6d..8e6b1aa97ba5 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp @@ -209,6 +209,10 @@ class ShenandoahHeap : public CollectedHeap { // Flushes cycle timings to global timings and prints the phase timings for the last completed cycle. void process_gc_stats() const; + // Prints GC statistics for the entire GC run. Called by the control thread once it has + // finished updating them. + void print_gc_stats_at_exit() const; + void prepare_for_verify() override; void verify(VerifyOption vo) override; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahNumberSeq.cpp b/src/hotspot/share/gc/shenandoah/shenandoahNumberSeq.cpp index 07b883653dc7..381861cd4f7a 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahNumberSeq.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahNumberSeq.cpp @@ -1,7 +1,7 @@ /* * Copyright (c) 2018, 2019, Red Hat, Inc. All rights reserved. * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, 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 @@ -27,8 +27,12 @@ #include "gc/shenandoah/shenandoahNumberSeq.hpp" #include "runtime/atomicAccess.hpp" +#include "utilities/globalDefinitions.hpp" -HdrSeq::HdrSeq() { +#include +#include + +HdrSeq::HdrSeq() : _minimum(DBL_MAX) { _hdr = NEW_C_HEAP_ARRAY(int*, MagBuckets, mtGC); for (int c = 0; c < MagBuckets; c++) { _hdr[c] = nullptr; @@ -51,26 +55,25 @@ void HdrSeq::add(double val) { val = 0; } + if (val < _minimum) { + _minimum = val; + } + NumberSeq::add(val); - double v = val; - int mag; - if (v > 0) { - mag = 0; - while (v >= 1) { - mag++; - v /= 10; - } - while (v < 0.1) { - mag--; - v *= 10; - } + // Normalize val and compute which bucket it should reside in. + int exponent; + double v; + if (val == 0) { + exponent = MagMinimum; + v = 0.5; } else { - mag = MagMinimum; + v = std::frexp(val, &exponent); } + int bucket = exponent - MagMinimum; - int bucket = -MagMinimum + mag; - int sub_bucket = (int) (v * ValBuckets); + // Rescale v from [0.5, 1) to [0, 1) to fit into the sub-buckets. + int sub_bucket = (int) ((v - 0.5) * 2.0 * ValBuckets); // Defensively saturate for product bits if (bucket < 0) { @@ -104,7 +107,19 @@ void HdrSeq::add(double val) { b[sub_bucket]++; } +double HdrSeq::minimum() const { + return num() == 0 ? 0 : _minimum; +} + double HdrSeq::percentile(double level) const { + if (level == 0) { + return minimum(); + } + + if (level == 100) { + return maximum(); + } + // target should be non-zero to find the first sample int target = MAX2(1, (int) (level * num() / 100)); int cnt = 0; @@ -113,7 +128,10 @@ double HdrSeq::percentile(double level) const { for (int val = 0; val < ValBuckets; val++) { cnt += _hdr[mag][val]; if (cnt >= target) { - return pow(10.0, MagMinimum + mag) * val / ValBuckets; + double value = std::ldexp(((double) val / ValBuckets) / 2.0 + 0.5, MagMinimum + mag); + // value < _minimum and value > _maximum can be possible due to precision loss when + // recomputing value. Clamping is done to fit value within the range. + return clamp(value, minimum(), maximum()); } } } @@ -154,6 +172,7 @@ void HdrSeq::add(const HdrSeq& other) { // dealing with decayed average/variance, which we do not // know how to compute yet. _last = other._last; + _minimum = MIN2(_minimum, other._minimum); _maximum = MAX2(_maximum, other._maximum); _sum += other._sum; _sum_of_squares += other._sum_of_squares; @@ -177,6 +196,7 @@ void HdrSeq::clear() { // Clear other fields too _last = 0; + _minimum = DBL_MAX; _maximum = 0; _sum = 0; _sum_of_squares = 0; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahNumberSeq.hpp b/src/hotspot/share/gc/shenandoah/shenandoahNumberSeq.hpp index 1a14f9301746..0d3c515680a6 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahNumberSeq.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahNumberSeq.hpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2018, 2019, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -32,18 +33,21 @@ // It does so by maintaining the double array, where first array defines // the magnitude of the value being stored, and the second array maintains // the low resolution histogram within that magnitude. For example, storing -// 4.352819 * 10^3 increments the bucket _hdr[3][435]. This allows for -// memory efficient storage of huge amount of samples. +// 0.012 seconds gives 0.768 * 2^-6 and that increments the bucket _hdr[26][34]. +// This allows for memory efficient storage of huge amount of samples. // -// Accepts positive numbers only. +// Accepts zero and values within the range of [2^-33, 2^23). class HdrSeq: public NumberSeq { private: enum PrivateConstants { - ValBuckets = 512, - MagBuckets = 24, - MagMinimum = -12 + ValBuckets = 64, + // Accounts for the maximum object count we can have from scanning dirty + // cards in a chunk and leaves some extra headroom. + MagBuckets = 56, + MagMinimum = -32 }; int** _hdr; + double _minimum; public: HdrSeq(); @@ -51,6 +55,7 @@ class HdrSeq: public NumberSeq { virtual void add(double val); void add(const HdrSeq& other); + double minimum() const; double percentile(double level) const; void clear(); }; diff --git a/test/hotspot/gtest/gc/shenandoah/test_shenandoahNumberSeq.cpp b/test/hotspot/gtest/gc/shenandoah/test_shenandoahNumberSeq.cpp index c7077271b5c8..a73400409992 100644 --- a/test/hotspot/gtest/gc/shenandoah/test_shenandoahNumberSeq.cpp +++ b/test/hotspot/gtest/gc/shenandoah/test_shenandoahNumberSeq.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -116,6 +116,7 @@ TEST_VM_F(BasicShenandoahNumberSeqTest, clear_test) { EXPECT_NE(test.num(), 0); EXPECT_NE(test.sum(), 0); + EXPECT_EQ(test.minimum(), 1); EXPECT_NE(test.maximum(), 0); EXPECT_NE(test.avg(), 0); EXPECT_EQ(test.sd(), 0); @@ -129,6 +130,7 @@ TEST_VM_F(BasicShenandoahNumberSeqTest, clear_test) { EXPECT_EQ(test.num(), 0); EXPECT_EQ(test.sum(), 0); + EXPECT_EQ(test.minimum(), 0); EXPECT_EQ(test.maximum(), 0); EXPECT_EQ(test.avg(), 0); EXPECT_EQ(test.sd(), 0); @@ -149,7 +151,7 @@ TEST_VM_F(ShenandoahNumberSeqMergeTest, merge_test) { merged.add(seq2); EXPECT_EQ(merged.num(), seq3.num()); - + EXPECT_EQ(merged.minimum(), seq3.minimum()); EXPECT_EQ(merged.maximum(), seq3.maximum()); EXPECT_EQ(merged.percentile(0), seq3.percentile(0)); for (int i = 0; i <= 100; i += 10) { @@ -162,3 +164,30 @@ TEST_VM_F(ShenandoahNumberSeqMergeTest, merge_test) { EXPECT_TRUE(isnan(merged.davg())); EXPECT_TRUE(isnan(merged.dvariance())); } + +TEST_VM(ShenandoahNumberSeq, percentile_within_bounds) { + HdrSeq seq; + const double min_value = 0.1; + const double max_value = 100; + + seq.add(min_value); + seq.add(1); + seq.add(2); + seq.add(3); + seq.add(max_value); + for (int i = 0; i <= 100; i += 10) { + EXPECT_GE(seq.percentile(i), min_value) << "at percentile " << i; + EXPECT_LE(seq.percentile(i), max_value) << "at percentile " << i; + } +} + +TEST_VM(ShenandoahNumberSeq, large_value) { + HdrSeq seq; + // Largest real input. 4 MiB chunk / 8 byte min object + const double max_dirty_scan_obj_cnt = 524288; + seq.add(1); + seq.add(max_dirty_scan_obj_cnt); + EXPECT_EQ(seq.minimum(), 1); + EXPECT_EQ(seq.maximum(), max_dirty_scan_obj_cnt); + EXPECT_EQ(seq.percentile(100), max_dirty_scan_obj_cnt); +} From 28b5b3856458419e08024b5002e84e7cfe66411d Mon Sep 17 00:00:00 2001 From: Justin Lu Date: Thu, 24 Sep 2026 18:17:01 +0000 Subject: [PATCH 020/103] 8392606: HijrahChronology computes minYearLength as 0 and skips the last configured year Reviewed-by: naoto --- .../classes/java/time/chrono/HijrahChronology.java | 9 ++++++--- .../java/time/chrono/TestUmmAlQuraChronology.java | 12 +++++++++--- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/java.base/share/classes/java/time/chrono/HijrahChronology.java b/src/java.base/share/classes/java/time/chrono/HijrahChronology.java index 881dfb15fc7e..3baf58783141 100644 --- a/src/java.base/share/classes/java/time/chrono/HijrahChronology.java +++ b/src/java.base/share/classes/java/time/chrono/HijrahChronology.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026, 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 @@ -530,7 +530,7 @@ public ValueRange range(ChronoField field) { checkCalendarInit(); return switch (field) { case DAY_OF_MONTH -> ValueRange.of(1, 1, getMinimumMonthLength(), getMaximumMonthLength()); - case DAY_OF_YEAR -> ValueRange.of(1, getMaximumDayOfYear()); + case DAY_OF_YEAR -> ValueRange.of(1, getSmallestMaximumDayOfYear(), getMaximumDayOfYear()); case ALIGNED_WEEK_OF_MONTH -> ValueRange.of(1, 5); case YEAR, YEAR_OF_ERA -> ValueRange.of(getMinimumYear(), getMaximumYear()); case ERA -> ValueRange.of(1, 1); @@ -902,8 +902,11 @@ private void loadCalendarData() { hijrahEpochMonthStartDays = createEpochMonths(minEpochDay, minYear, maxYear, years); maxEpochDay = hijrahEpochMonthStartDays[hijrahEpochMonthStartDays.length - 1]; + minYearLength = Integer.MAX_VALUE; + maxYearLength = Integer.MIN_VALUE; + // Compute the min and max year length in days. - for (int year = minYear; year < maxYear; year++) { + for (int year = minYear; year <= maxYear; year++) { int length = getYearLength(year); minYearLength = Math.min(minYearLength, length); maxYearLength = Math.max(maxYearLength, length); diff --git a/test/jdk/java/time/test/java/time/chrono/TestUmmAlQuraChronology.java b/test/jdk/java/time/test/java/time/chrono/TestUmmAlQuraChronology.java index 2dfd112596b7..3b7a96f5a05c 100644 --- a/test/jdk/java/time/test/java/time/chrono/TestUmmAlQuraChronology.java +++ b/test/jdk/java/time/test/java/time/chrono/TestUmmAlQuraChronology.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2026, 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 @@ -74,7 +74,7 @@ /** * Tests for the Umm alQura chronology and data. * Note: The dates used for testing are just a sample of calendar data. - * @bug 8067800 + * @bug 8067800 8392606 */ @TestInstance(TestInstance.Lifecycle.PER_CLASS) public class TestUmmAlQuraChronology { @@ -121,7 +121,7 @@ public void Test_UmmAlQuraVsISODates(HijrahDate hd, LocalDate ld) { assertEquals(ld.toEpochDay(), hd.toEpochDay(), "Umm alQura date and ISO date should have same epochDay"); } - // UmmAlQura chronology ranges for year, month and days for the HijrahChronology + // UmmAlQura chronology ranges for year, month, day, and day of year for the HijrahChronology @Test public void Test_UmmAlQuraChronoRange() { HijrahChronology chrono = HijrahChronology.INSTANCE; @@ -142,6 +142,12 @@ public void Test_UmmAlQuraChronoRange() { assertEquals(1, day.getLargestMinimum(), "Largest minimum day"); assertEquals(30, day.getMaximum(), "Largest day"); assertEquals(29, day.getSmallestMaximum(), "Smallest Maximum day"); + + ValueRange dayOfYear = chrono.range(DAY_OF_YEAR); + assertEquals(1, dayOfYear.getMinimum(), "Minimum day of year"); + assertEquals(1, dayOfYear.getLargestMinimum(), "Largest minimum day of year"); + assertEquals(355, dayOfYear.getMaximum(), "Largest day of year"); + assertEquals(354, dayOfYear.getSmallestMaximum(), "Smallest Maximum day of year"); } //----------------------------------------------------------------------- From 29bbe3b366df0fec1cd955b4ade30fc9902358b5 Mon Sep 17 00:00:00 2001 From: Naoto Sato Date: Thu, 24 Sep 2026 20:37:07 +0000 Subject: [PATCH 021/103] 8392538: Align the JDK with Microsoft's new timezone names for British Columbia/Alberta Reviewed-by: jlu --- src/java.base/share/data/tzdata/tzmappings.override | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/java.base/share/data/tzdata/tzmappings.override b/src/java.base/share/data/tzdata/tzmappings.override index 0f0e0c1372c8..8466e3585c3c 100644 --- a/src/java.base/share/data/tzdata/tzmappings.override +++ b/src/java.base/share/data/tzdata/tzmappings.override @@ -1,5 +1,5 @@ # -# Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2024, 2026, 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,7 +26,15 @@ # Extra definitions for Windows /lib/tzmappings file. These entries # replace the existing (Windows Zone Name):(REGION) entries, or are added # as new entries - +# # Example entries # Foo Standard Time:US:America/Los_Angeles: # Bar Standard Time:001:Asia/Tokyo: + +# tz2026b: British Columbia +# https://techcommunity.microsoft.com/blog/dstblog/interim-guidance-for-british-columbia-time-zone-changes-2026/4544988 +British Columbia Standard Time:001:America/Vancouver: + +# tz2026c: Alberta +# https://techcommunity.microsoft.com/blog/dstblog/interim-guidance-for-alberta-time-zone-changes-2026/4545015 +Alberta Standard Time:001:America/Edmonton: From a59eb61e195b8583d6b892fb54a397fdbec0d031 Mon Sep 17 00:00:00 2001 From: Liyuan Wang Date: Thu, 24 Sep 2026 20:50:07 +0000 Subject: [PATCH 022/103] 8392848: HijrahChronology.createEpochMonths reports minYear instead of the invalid year Reviewed-by: jlu --- .../java/time/chrono/HijrahChronology.java | 3 +- .../java/time/chrono/HijrahConfigCheck.java | 30 +++++++++---- .../java/time/chrono/HijrahConfigTest.java | 13 +++--- ...-Hijrah-invalid_islamic-invalid.properties | 43 +++++++++++++++++++ ...fig-Hijrah-valid_islamic-valid.properties} | 6 +-- 5 files changed, 78 insertions(+), 17 deletions(-) create mode 100644 test/jdk/java/time/nonjunit/java/time/chrono/hijrah-config-Hijrah-invalid_islamic-invalid.properties rename test/jdk/java/time/nonjunit/java/time/chrono/{hijrah-config-Hijrah-test_islamic-test.properties => hijrah-config-Hijrah-valid_islamic-valid.properties} (94%) diff --git a/src/java.base/share/classes/java/time/chrono/HijrahChronology.java b/src/java.base/share/classes/java/time/chrono/HijrahChronology.java index 3baf58783141..ccc3dd3d8582 100644 --- a/src/java.base/share/classes/java/time/chrono/HijrahChronology.java +++ b/src/java.base/share/classes/java/time/chrono/HijrahChronology.java @@ -948,7 +948,8 @@ private int[] createEpochMonths(int epochDay, int minYear, int maxYear, Map 32) { - throw new IllegalArgumentException("Invalid month length in year: " + minYear); + throw new IllegalArgumentException("Invalid month length in year: " + year + + ", month: " + (month + 1) + ", length: " + length); } epochDay += length; minMonthLength = Math.min(minMonthLength, length); diff --git a/test/jdk/java/time/nonjunit/java/time/chrono/HijrahConfigCheck.java b/test/jdk/java/time/nonjunit/java/time/chrono/HijrahConfigCheck.java index 822932ceba86..317ba15205b8 100644 --- a/test/jdk/java/time/nonjunit/java/time/chrono/HijrahConfigCheck.java +++ b/test/jdk/java/time/nonjunit/java/time/chrono/HijrahConfigCheck.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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 @@ -21,6 +21,7 @@ * questions. */ +import java.time.DateTimeException; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; @@ -30,32 +31,45 @@ import java.util.Locale; public class HijrahConfigCheck { - private static final String CALTYPE = "islamic-test"; + private static final String VALID_CALTYPE = "islamic-valid"; + private static final String INVALID_CALTYPE = "islamic-invalid"; public static void main(String... args) { // Availability test if (Chronology.getAvailableChronologies().stream() - .filter(c -> c.getCalendarType().equals(CALTYPE)) + .filter(c -> c.getCalendarType().equals(VALID_CALTYPE)) .count() != 1) { - throw new RuntimeException(CALTYPE + " chronology was not found, or " + + throw new RuntimeException(VALID_CALTYPE + " chronology was not found, or " + "appeared more than once in Chronology.getAvailableChronologies()"); } // Instantiation tests - Chronology c1 = Chronology.of(CALTYPE); - Chronology c2 = Chronology.ofLocale(Locale.forLanguageTag("und-u-ca-" + CALTYPE )); + Chronology c1 = Chronology.of(VALID_CALTYPE); + Chronology c2 = Chronology.ofLocale(Locale.forLanguageTag("und-u-ca-" + VALID_CALTYPE)); if (!c1.equals(c2)) { - throw new RuntimeException(CALTYPE + " chronologies differ. c1: " + c1 + + throw new RuntimeException(VALID_CALTYPE + " chronologies differ. c1: " + c1 + ", c2: " + c2); } // Date test - // 2020-01-10 is AH 1000-01-10 in islamic-test config + // 2020-01-10 is AH 1000-01-10 in islamic-valid config LocalDateTime iso = LocalDateTime.of(LocalDate.of(2020, 1, 10), LocalTime.MIN); ChronoLocalDateTime hijrah = c1.date(1000, 1, 10).atTime(LocalTime.MIN); if (!iso.toInstant(ZoneOffset.UTC).equals(hijrah.toInstant(ZoneOffset.UTC))) { throw new RuntimeException("test Hijrah date is incorrect. LocalDate: " + iso + ", test date: " + hijrah); } + + // Invalid configuration test + try { + Chronology.of(INVALID_CALTYPE).date(1448, 1, 1); + throw new RuntimeException("Invalid Hijrah configuration did not throw an exception"); + } catch (DateTimeException ex) { + Throwable cause = ex.getCause(); + if (!(cause instanceof IllegalArgumentException) || + !"Invalid month length in year: 1448, month: 1, length: 28".equals(cause.getMessage())) { + throw new RuntimeException("Unexpected exception for invalid Hijrah configuration", ex); + } + } } } diff --git a/test/jdk/java/time/nonjunit/java/time/chrono/HijrahConfigTest.java b/test/jdk/java/time/nonjunit/java/time/chrono/HijrahConfigTest.java index 6a63fc462268..f809a7ab8553 100644 --- a/test/jdk/java/time/nonjunit/java/time/chrono/HijrahConfigTest.java +++ b/test/jdk/java/time/nonjunit/java/time/chrono/HijrahConfigTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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 @@ -32,7 +32,7 @@ /* * @test * @summary Tests whether a custom Hijrah configuration properties file works correctly - * @bug 8187987 + * @bug 8187987 8392848 * @requires (vm.compMode != "Xcomp" & os.maxMemory >= 2g) * @library /tools/lib /test/lib * @modules java.base/jdk.internal.jimage @@ -43,7 +43,8 @@ */ public class HijrahConfigTest { - private static final String TEST_CONFIG = "hijrah-config-Hijrah-test_islamic-test.properties"; + private static final String VALID_TEST_CONFIG = "hijrah-config-Hijrah-valid_islamic-valid.properties"; + private static final String INVALID_TEST_CONFIG = "hijrah-config-Hijrah-invalid_islamic-invalid.properties"; public static void main(String[] args) throws Exception { Helper helper = Helper.newHelper(); @@ -62,8 +63,10 @@ public static void main(String[] args) throws Exception { // Install the test hijrah configuration properties Path confPath = outputPath.resolve("conf").resolve("chronology"); Files.createDirectory(confPath); - Files.copy(Path.of(System.getProperty("test.src"), TEST_CONFIG), - confPath.resolve(TEST_CONFIG)); + Files.copy(Path.of(System.getProperty("test.src"), VALID_TEST_CONFIG), + confPath.resolve(VALID_TEST_CONFIG)); + Files.copy(Path.of(System.getProperty("test.src"), INVALID_TEST_CONFIG), + confPath.resolve(INVALID_TEST_CONFIG)); // Run tests Path launcher = outputPath.resolve("bin").resolve("java"); diff --git a/test/jdk/java/time/nonjunit/java/time/chrono/hijrah-config-Hijrah-invalid_islamic-invalid.properties b/test/jdk/java/time/nonjunit/java/time/chrono/hijrah-config-Hijrah-invalid_islamic-invalid.properties new file mode 100644 index 000000000000..1624f9432dbb --- /dev/null +++ b/test/jdk/java/time/nonjunit/java/time/chrono/hijrah-config-Hijrah-invalid_islamic-invalid.properties @@ -0,0 +1,43 @@ +# Copyright (c) 2026, 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. + +# A test configuration properties file for a custom Hijrah chronology. +# The data here are solely hypothetical and do not represent any of +# the actual Hijrah variants. + +# Test-only custom chronology for JDK-8392848 +# Year 1448 deliberately contains the invalid month length 28. + +# Version of this definition +version=0.1 + +# Java chronology ID +id=Hijrah-invalid + +# Standard calendar type specification +type=islamic-invalid + +# defines the corresponding ISO date to the earliest Hijrah date +iso-start=2025-01-01 + +# 1 2 3 4 5 6 7 8 9 10 11 12 +1447=30 29 30 29 30 29 30 29 30 29 30 29 +1448=28 29 30 29 30 29 30 29 30 29 30 29 diff --git a/test/jdk/java/time/nonjunit/java/time/chrono/hijrah-config-Hijrah-test_islamic-test.properties b/test/jdk/java/time/nonjunit/java/time/chrono/hijrah-config-Hijrah-valid_islamic-valid.properties similarity index 94% rename from test/jdk/java/time/nonjunit/java/time/chrono/hijrah-config-Hijrah-test_islamic-test.properties rename to test/jdk/java/time/nonjunit/java/time/chrono/hijrah-config-Hijrah-valid_islamic-valid.properties index 4995b11da36f..339f31bbca75 100644 --- a/test/jdk/java/time/nonjunit/java/time/chrono/hijrah-config-Hijrah-test_islamic-test.properties +++ b/test/jdk/java/time/nonjunit/java/time/chrono/hijrah-config-Hijrah-valid_islamic-valid.properties @@ -1,4 +1,4 @@ -# Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2020, 2026, 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 @@ -27,10 +27,10 @@ version=0.1 # Java chronology ID -id=Hijrah-test +id=Hijrah-valid # Standard calendar type specification -type=islamic-test +type=islamic-valid # defines the corresponding ISO date to the earliest Hijrah date iso-start=2020-01-01 From 054eb6e47e68360a2b84a4a00db25cef468d5b62 Mon Sep 17 00:00:00 2001 From: Marc Chevalier Date: Thu, 24 Sep 2026 21:57:43 +0000 Subject: [PATCH 023/103] 8389983: compiler/valhalla/valuetypes/TestValueConstruction.java subtests time out Reviewed-by: mhaessig, chagedorn --- .../valuetypes/TestValueConstruction.java | 18 ++++++++++++++++-- test/jtreg-ext/requires/VMProps.java | 3 +++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestValueConstruction.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestValueConstruction.java index d89b933efc27..ef4d6b2815a9 100644 --- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestValueConstruction.java +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestValueConstruction.java @@ -35,12 +35,13 @@ import jdk.test.whitebox.WhiteBox; import test.java.lang.invoke.lib.InstructionHelper; -/** +/* * @test id=Xbatch * @summary Test construction of value objects. * @key randomness * @library /testlibrary /test/lib /compiler/whitebox /test/jdk/java/lang/invoke/common / * @enablePreview + * @requires (vm.opt.final.PerMethodTrapLimit >= 100 & vm.opt.final.PerMethodSpecTrapLimit >= 5000) | vm.opt.final.StressIncrementalInlining == false * @build jdk.test.whitebox.WhiteBox test.java.lang.invoke.lib.InstructionHelper * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm/timeout=400 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch @@ -53,6 +54,7 @@ * @key randomness * @library /testlibrary /test/lib /compiler/whitebox /test/jdk/java/lang/invoke/common / * @enablePreview + * @requires (vm.opt.final.PerMethodTrapLimit >= 100 & vm.opt.final.PerMethodSpecTrapLimit >= 5000) | vm.opt.final.StressIncrementalInlining == false * @build jdk.test.whitebox.WhiteBox test.java.lang.invoke.lib.InstructionHelper * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm/timeout=400 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch -XX:+IgnoreUnrecognizedVMOptions -XX:+DeoptimizeALot @@ -65,6 +67,7 @@ * @key randomness * @library /testlibrary /test/lib /compiler/whitebox /test/jdk/java/lang/invoke/common / * @enablePreview + * @requires (vm.opt.final.PerMethodTrapLimit >= 100 & vm.opt.final.PerMethodSpecTrapLimit >= 5000) | vm.opt.final.StressIncrementalInlining == false * @build jdk.test.whitebox.WhiteBox test.java.lang.invoke.lib.InstructionHelper * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm/timeout=400 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI @@ -73,12 +76,13 @@ * compiler.valhalla.valuetypes.TestValueConstruction */ -/** +/* * @test id=DontInlineHelper * @summary Test construction of value objects. * @key randomness * @library /testlibrary /test/lib /compiler/whitebox /test/jdk/java/lang/invoke/common / * @enablePreview + * @requires (vm.opt.final.PerMethodTrapLimit >= 100 & vm.opt.final.PerMethodSpecTrapLimit >= 5000) | vm.opt.final.StressIncrementalInlining == false * @build jdk.test.whitebox.WhiteBox test.java.lang.invoke.lib.InstructionHelper * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm/timeout=400 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch @@ -92,6 +96,7 @@ * @key randomness * @library /testlibrary /test/lib /compiler/whitebox /test/jdk/java/lang/invoke/common / * @enablePreview + * @requires (vm.opt.final.PerMethodTrapLimit >= 100 & vm.opt.final.PerMethodSpecTrapLimit >= 5000) | vm.opt.final.StressIncrementalInlining == false * @build jdk.test.whitebox.WhiteBox test.java.lang.invoke.lib.InstructionHelper * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm/timeout=400 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI @@ -105,6 +110,7 @@ * @key randomness * @library /testlibrary /test/lib /compiler/whitebox /test/jdk/java/lang/invoke/common / * @enablePreview + * @requires (vm.opt.final.PerMethodTrapLimit >= 100 & vm.opt.final.PerMethodSpecTrapLimit >= 5000) | vm.opt.final.StressIncrementalInlining == false * @build jdk.test.whitebox.WhiteBox test.java.lang.invoke.lib.InstructionHelper * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm/timeout=400 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI @@ -118,6 +124,7 @@ * @key randomness * @library /testlibrary /test/lib /compiler/whitebox /test/jdk/java/lang/invoke/common / * @enablePreview + * @requires (vm.opt.final.PerMethodTrapLimit >= 100 & vm.opt.final.PerMethodSpecTrapLimit >= 5000) | vm.opt.final.StressIncrementalInlining == false * @build jdk.test.whitebox.WhiteBox test.java.lang.invoke.lib.InstructionHelper * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm/timeout=400 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+IgnoreUnrecognizedVMOptions @@ -131,6 +138,7 @@ * @key randomness * @library /testlibrary /test/lib /compiler/whitebox /test/jdk/java/lang/invoke/common / * @enablePreview + * @requires (vm.opt.final.PerMethodTrapLimit >= 100 & vm.opt.final.PerMethodSpecTrapLimit >= 5000) | vm.opt.final.StressIncrementalInlining == false * @build jdk.test.whitebox.WhiteBox test.java.lang.invoke.lib.InstructionHelper * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm/timeout=400 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI @@ -144,6 +152,7 @@ * @key randomness * @library /testlibrary /test/lib /compiler/whitebox /test/jdk/java/lang/invoke/common / * @enablePreview + * @requires vm.opt.final.PerMethodTrapLimit >= 100 & vm.opt.final.PerMethodSpecTrapLimit >= 5000 * @build jdk.test.whitebox.WhiteBox test.java.lang.invoke.lib.InstructionHelper * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm/timeout=400 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch @@ -158,6 +167,7 @@ * @key randomness * @library /testlibrary /test/lib /compiler/whitebox /test/jdk/java/lang/invoke/common / * @enablePreview + * @requires vm.opt.final.PerMethodTrapLimit >= 100 & vm.opt.final.PerMethodSpecTrapLimit >= 5000 * @build jdk.test.whitebox.WhiteBox test.java.lang.invoke.lib.InstructionHelper * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm/timeout=400 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI @@ -171,6 +181,7 @@ * @key randomness * @library /testlibrary /test/lib /compiler/whitebox /test/jdk/java/lang/invoke/common / * @enablePreview + * @requires vm.opt.final.PerMethodTrapLimit >= 100 & vm.opt.final.PerMethodSpecTrapLimit >= 5000 * @build jdk.test.whitebox.WhiteBox test.java.lang.invoke.lib.InstructionHelper * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm/timeout=400 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI @@ -185,6 +196,7 @@ * @key randomness * @library /testlibrary /test/lib /compiler/whitebox /test/jdk/java/lang/invoke/common / * @enablePreview + * @requires vm.opt.final.PerMethodTrapLimit >= 100 & vm.opt.final.PerMethodSpecTrapLimit >= 5000 * @build jdk.test.whitebox.WhiteBox test.java.lang.invoke.lib.InstructionHelper * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm/timeout=400 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI @@ -199,6 +211,7 @@ * @key randomness * @library /testlibrary /test/lib /compiler/whitebox /test/jdk/java/lang/invoke/common / * @enablePreview + * @requires vm.opt.final.PerMethodTrapLimit >= 100 & vm.opt.final.PerMethodSpecTrapLimit >= 5000 * @build jdk.test.whitebox.WhiteBox test.java.lang.invoke.lib.InstructionHelper * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm/timeout=400 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI @@ -214,6 +227,7 @@ * @key randomness * @library /testlibrary /test/lib /compiler/whitebox /test/jdk/java/lang/invoke/common / * @enablePreview + * @requires vm.opt.final.PerMethodTrapLimit >= 100 & vm.opt.final.PerMethodSpecTrapLimit >= 5000 * @build jdk.test.whitebox.WhiteBox test.java.lang.invoke.lib.InstructionHelper * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm/timeout=400 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI diff --git a/test/jtreg-ext/requires/VMProps.java b/test/jtreg-ext/requires/VMProps.java index 7c8cb9b4b556..a3ce27f484be 100644 --- a/test/jtreg-ext/requires/VMProps.java +++ b/test/jtreg-ext/requires/VMProps.java @@ -328,6 +328,7 @@ protected void vmOptFinalFlags(SafeMap map) { vmOptFinalFlag(map, "ClassUnloadingWithConcurrentMark"); vmOptFinalFlag(map, "CriticalJNINatives"); vmOptFinalFlag(map, "EliminateAllocations"); + vmOptFinalFlag(map, "StressIncrementalInlining"); vmOptFinalFlag(map, "TieredCompilation"); vmOptFinalFlag(map, "UnlockExperimentalVMOptions"); vmOptFinalFlag(map, "UseAdaptiveSizePolicy"); @@ -355,6 +356,8 @@ private void vmOptFinalIntxFlag(SafeMap map, String flagName) { */ protected void vmOptFinalIntxFlags(SafeMap map) { vmOptFinalIntxFlag(map, "MaxVectorSize"); + vmOptFinalIntxFlag(map, "PerMethodSpecTrapLimit"); + vmOptFinalIntxFlag(map, "PerMethodTrapLimit"); } /** From 08e1ae8896252b1bd661747439fa6e4b065548a4 Mon Sep 17 00:00:00 2001 From: Erik Gahlin Date: Fri, 25 Sep 2026 06:09:57 +0000 Subject: [PATCH 024/103] 8392930: JFR: onClose action should only be run once Reviewed-by: mgronlun --- .../consumer/AbstractEventStream.java | 6 +- .../consumer/EventDirectoryStream.java | 6 +- .../internal/consumer/EventFileStream.java | 6 +- .../jfr/internal/consumer/ParserState.java | 10 ++- .../api/consumer/filestream/TestOnClose.java | 63 +++++++++++++++++++ .../consumer/recordingstream/TestOnClose.java | 14 +++++ 6 files changed, 95 insertions(+), 10 deletions(-) create mode 100644 test/jdk/jdk/jfr/api/consumer/filestream/TestOnClose.java diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/AbstractEventStream.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/AbstractEventStream.java index 141fd58ca808..e5f599ef7e46 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/AbstractEventStream.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/AbstractEventStream.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, 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 @@ -209,8 +209,8 @@ public final void awaitTermination(Duration timeout) throws InterruptedException protected abstract boolean isRecordingStream(); - protected final void closeParser() { - parserState.close(); + protected final boolean closeParser() { + return parserState.close(); } protected final boolean isClosed() { diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/EventDirectoryStream.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/EventDirectoryStream.java index c8db9aef169e..b4c1a90cdd11 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/EventDirectoryStream.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/EventDirectoryStream.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, 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 @@ -78,7 +78,9 @@ public EventDirectoryStream( @Override public void close() { - closeParser(); + if (!closeParser()) { + return; + } dispatcher().runCloseActions(); repositoryFiles.close(); if (currentParser != null) { diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/EventFileStream.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/EventFileStream.java index 82712fcbedc4..0c72d04fee4d 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/EventFileStream.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/EventFileStream.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, 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,7 +62,9 @@ public void startAsync() { @Override public void close() { - closeParser(); + if (!closeParser()) { + return; + } dispatcher().runCloseActions(); try { input.close(); diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/ParserState.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/ParserState.java index a03cddfff9ea..55134171fd17 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/ParserState.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/ParserState.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ -32,7 +32,11 @@ public boolean isClosed() { return closed; } - public void close() { - closed = true; + public synchronized boolean close() { + if (!closed) { + closed = true; + return true; + } + return false; } } diff --git a/test/jdk/jdk/jfr/api/consumer/filestream/TestOnClose.java b/test/jdk/jdk/jfr/api/consumer/filestream/TestOnClose.java new file mode 100644 index 000000000000..fc346199c0ed --- /dev/null +++ b/test/jdk/jdk/jfr/api/consumer/filestream/TestOnClose.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2026, 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. + */ + +package jdk.jfr.api.consumer.filestream; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.concurrent.atomic.AtomicLong; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.EventStream; + +/** + * @test + * @summary Test EventStream::onClose(...) + * @requires vm.flagless + * @requires vm.hasJFR + * @library /test/lib + * @run main/othervm ${test.main.class} + */ +public class TestOnClose { + + public static void main(String... args) throws Exception { + testCloseOnce(); + } + + private static void testCloseOnce() throws IOException { + Path file = Path.of("recording.jfr"); + try (Recording r = new Recording()) { + r.start(); + r.dump(file); + } + AtomicLong counter = new AtomicLong(0); + try (EventStream es = EventStream.openFile(file)) { + es.onClose(counter::incrementAndGet); + es.close(); + // try-with-resources closes stream a second time + } + if (counter.get() != 1) { + throw new AssertionError("Expected close action to be run once"); + } + } +} diff --git a/test/jdk/jdk/jfr/api/consumer/recordingstream/TestOnClose.java b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestOnClose.java index 3da0c62b8c8a..e4c29aa1c91c 100644 --- a/test/jdk/jdk/jfr/api/consumer/recordingstream/TestOnClose.java +++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestOnClose.java @@ -25,6 +25,7 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; import jdk.jfr.Event; import jdk.jfr.consumer.RecordingStream; @@ -46,6 +47,19 @@ public static void main(String... args) throws Exception { testOnCloseNull(); testOnClosedUnstarted(); testOnClosedStarted(); + testOnCloseOnce(); + } + + private static void testOnCloseOnce() { + AtomicLong counter = new AtomicLong(0); + try (RecordingStream rs = new RecordingStream()) { + rs.onClose(counter::incrementAndGet); + rs.close(); + // try-with-resources closes stream a second time + } + if (counter.get() != 1) { + throw new AssertionError("Expected close action to be run once"); + } } private static void testOnCloseNull() { From a965427a2a6a4806513b8ff6400cb20f753bde67 Mon Sep 17 00:00:00 2001 From: Erik Gahlin Date: Fri, 25 Sep 2026 06:27:53 +0000 Subject: [PATCH 025/103] 8392893: JFR: getInstant and getDuration may return incorrect values for unsigned byte values Reviewed-by: mgronlun --- .../classes/jdk/jfr/consumer/RecordedObject.java | 6 +++--- .../jdk/jfr/api/consumer/TestRecordedObject.java | 13 +++++++++++-- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedObject.java b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedObject.java index a229709ecaa1..250bb767a02f 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedObject.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedObject.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, 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 @@ -727,7 +727,7 @@ public final Duration getDuration(String name) { case Byte b -> getDuration(b, name); case UnsignedValue(Integer i) -> getDuration(Integer.toUnsignedLong(i), name); case UnsignedValue(Short s) -> getDuration(Short.toUnsignedLong(s), name); - case UnsignedValue(Byte b) -> getDuration(Short.toUnsignedLong(b), name); + case UnsignedValue(Byte b) -> getDuration(Byte.toUnsignedLong(b), name); case null, default -> throw newIllegalArgumentException(name, "java.time.Duration"); }; } @@ -789,7 +789,7 @@ public final Instant getInstant(String name) { case Byte b -> getInstant(b, name); case UnsignedValue(Integer i) -> getInstant(Integer.toUnsignedLong(i), name); case UnsignedValue(Short s) -> getInstant(Short.toUnsignedLong(s), name); - case UnsignedValue(Byte b) -> getInstant(Short.toUnsignedLong(b), name); + case UnsignedValue(Byte b) -> getInstant(Byte.toUnsignedLong(b), name); case null, default -> throw newIllegalArgumentException(name, "java.time.Instant"); }; } diff --git a/test/jdk/jdk/jfr/api/consumer/TestRecordedObject.java b/test/jdk/jdk/jfr/api/consumer/TestRecordedObject.java index 772a0ca64195..6669e7c4e60d 100644 --- a/test/jdk/jdk/jfr/api/consumer/TestRecordedObject.java +++ b/test/jdk/jdk/jfr/api/consumer/TestRecordedObject.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -119,7 +119,12 @@ static final class EventWithValues extends Event { float unsignedFloat = Float.MIN_VALUE; // unsigned should be ignored @Unsigned double unsignedDouble = Double.MIN_VALUE; // unsigned should be ignored - + @Unsigned + @Timespan(Timespan.NANOSECONDS) + byte unsignedByteDuration = Byte.MIN_VALUE; + @Unsigned + @Timestamp(Timestamp.MILLISECONDS_SINCE_EPOCH) + byte unsignedByteTimestamp = Byte.MIN_VALUE; } private final static Set ALL = createAll(); @@ -188,8 +193,12 @@ private static void testTimeUnits(RecordedObject event) { Asserts.assertEquals(event.getDuration("durationSeconds"), DURATION_VALUE); Asserts.assertEquals(event.getDuration("foreverMillis"), ChronoUnit.FOREVER.getDuration()); Asserts.assertEquals(event.getDuration("foreverNanoseconds"), ChronoUnit.FOREVER.getDuration()); + Duration unsignedByteDuration = Duration.ofNanos(Byte.toUnsignedLong(Byte.MIN_VALUE)); + Asserts.assertEquals(event.getDuration("unsignedByteDuration"), unsignedByteDuration); Asserts.assertEquals(event.getInstant("instantMillis").toEpochMilli(), 1000L); + long unsignedByteTimestamp = Byte.toUnsignedLong(Byte.MIN_VALUE); + Asserts.assertEquals(event.getInstant("unsignedByteTimestamp").toEpochMilli(), unsignedByteTimestamp); if (!event.getInstant("instantTicks").isBefore(INSTANT_VALUE)) { throw new AssertionError("Expected start time of JVM to before call to Instant.now()"); } From 6ccd07b524c702d00f27b1fbccaeef292a403816 Mon Sep 17 00:00:00 2001 From: Tobias Hartmann Date: Fri, 25 Sep 2026 06:36:57 +0000 Subject: [PATCH 026/103] 8392935: Obsolete OptoBundling Reviewed-by: chagedorn, mhaessig --- src/hotspot/cpu/aarch64/c2_globals_aarch64.hpp | 1 - src/hotspot/cpu/arm/c2_globals_arm.hpp | 1 - src/hotspot/cpu/ppc/c2_globals_ppc.hpp | 1 - src/hotspot/cpu/riscv/c2_globals_riscv.hpp | 1 - src/hotspot/cpu/s390/c2_globals_s390.hpp | 1 - src/hotspot/cpu/x86/c2_globals_x86.hpp | 1 - src/hotspot/share/opto/c2_globals.hpp | 3 --- src/hotspot/share/runtime/arguments.cpp | 1 + 8 files changed, 1 insertion(+), 9 deletions(-) diff --git a/src/hotspot/cpu/aarch64/c2_globals_aarch64.hpp b/src/hotspot/cpu/aarch64/c2_globals_aarch64.hpp index 192461d1a61a..7bb516fb0fff 100644 --- a/src/hotspot/cpu/aarch64/c2_globals_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/c2_globals_aarch64.hpp @@ -61,7 +61,6 @@ define_pd_global(intx, RegisterCostAreaRatio, 16000); define_pd_global(bool, OptoPeephole, false); define_pd_global(bool, UseCISCSpill, false); define_pd_global(bool, OptoScheduling, false); -define_pd_global(bool, OptoBundling, false); define_pd_global(bool, OptoRegScheduling, false); define_pd_global(bool, SuperWordLoopUnrollAnalysis, true); define_pd_global(uint, SuperWordStoreToLoadForwardingFailureDetection, 8); diff --git a/src/hotspot/cpu/arm/c2_globals_arm.hpp b/src/hotspot/cpu/arm/c2_globals_arm.hpp index 34da47792ae8..cccabb6ff334 100644 --- a/src/hotspot/cpu/arm/c2_globals_arm.hpp +++ b/src/hotspot/cpu/arm/c2_globals_arm.hpp @@ -59,7 +59,6 @@ define_pd_global(intx, MinJumpTableSize, 16); // scheduler sick. define_pd_global(bool, OptoPeephole, false); define_pd_global(bool, UseCISCSpill, false); -define_pd_global(bool, OptoBundling, false); define_pd_global(bool, OptoScheduling, true); define_pd_global(bool, OptoRegScheduling, false); define_pd_global(bool, SuperWordLoopUnrollAnalysis, false); diff --git a/src/hotspot/cpu/ppc/c2_globals_ppc.hpp b/src/hotspot/cpu/ppc/c2_globals_ppc.hpp index e4942fa1850a..16af4672ff28 100644 --- a/src/hotspot/cpu/ppc/c2_globals_ppc.hpp +++ b/src/hotspot/cpu/ppc/c2_globals_ppc.hpp @@ -55,7 +55,6 @@ define_pd_global(intx, LoopPercentProfileLimit, 10); // scheduler sick. define_pd_global(bool, OptoPeephole, false); define_pd_global(bool, UseCISCSpill, false); -define_pd_global(bool, OptoBundling, false); define_pd_global(bool, OptoRegScheduling, false); define_pd_global(bool, SuperWordLoopUnrollAnalysis, true); define_pd_global(uint, SuperWordStoreToLoadForwardingFailureDetection, 16); diff --git a/src/hotspot/cpu/riscv/c2_globals_riscv.hpp b/src/hotspot/cpu/riscv/c2_globals_riscv.hpp index 73ef97939ed2..62ee04875253 100644 --- a/src/hotspot/cpu/riscv/c2_globals_riscv.hpp +++ b/src/hotspot/cpu/riscv/c2_globals_riscv.hpp @@ -61,7 +61,6 @@ define_pd_global(intx, RegisterCostAreaRatio, 16000); define_pd_global(bool, OptoPeephole, false); define_pd_global(bool, UseCISCSpill, false); define_pd_global(bool, OptoScheduling, true); -define_pd_global(bool, OptoBundling, false); define_pd_global(bool, OptoRegScheduling, false); define_pd_global(bool, SuperWordLoopUnrollAnalysis, true); define_pd_global(uint, SuperWordStoreToLoadForwardingFailureDetection, 16); diff --git a/src/hotspot/cpu/s390/c2_globals_s390.hpp b/src/hotspot/cpu/s390/c2_globals_s390.hpp index eee3a8588c3c..4c72b10b1a2a 100644 --- a/src/hotspot/cpu/s390/c2_globals_s390.hpp +++ b/src/hotspot/cpu/s390/c2_globals_s390.hpp @@ -55,7 +55,6 @@ define_pd_global(intx, MinJumpTableSize, 18); // scheduler sick. define_pd_global(bool, OptoPeephole, false); define_pd_global(bool, UseCISCSpill, true); -define_pd_global(bool, OptoBundling, false); define_pd_global(bool, OptoScheduling, false); define_pd_global(bool, OptoRegScheduling, false); define_pd_global(bool, SuperWordLoopUnrollAnalysis, true); diff --git a/src/hotspot/cpu/x86/c2_globals_x86.hpp b/src/hotspot/cpu/x86/c2_globals_x86.hpp index 11d8c03d0cae..2ea2c7c39b7d 100644 --- a/src/hotspot/cpu/x86/c2_globals_x86.hpp +++ b/src/hotspot/cpu/x86/c2_globals_x86.hpp @@ -58,7 +58,6 @@ define_pd_global(intx, RegisterCostAreaRatio, 16000); define_pd_global(bool, OptoPeephole, true); define_pd_global(bool, UseCISCSpill, true); define_pd_global(bool, OptoScheduling, false); -define_pd_global(bool, OptoBundling, false); define_pd_global(bool, OptoRegScheduling, true); define_pd_global(bool, SuperWordLoopUnrollAnalysis, true); define_pd_global(uint, SuperWordStoreToLoadForwardingFailureDetection, 16); diff --git a/src/hotspot/share/opto/c2_globals.hpp b/src/hotspot/share/opto/c2_globals.hpp index b4be75836a9a..3cc4049c2325 100644 --- a/src/hotspot/share/opto/c2_globals.hpp +++ b/src/hotspot/share/opto/c2_globals.hpp @@ -443,9 +443,6 @@ product(bool, MergeStores, true, DIAGNOSTIC, \ "Optimize stores by combining values into larger store") \ \ - product_pd(bool, OptoBundling, \ - "Generate nops to fill i-cache lines") \ - \ product_pd(intx, ConditionalMoveLimit, \ "Limit of ops to make speculative when using CMOVE") \ range(0, max_jint) \ diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index 0a571f18b99a..74750495db1b 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -544,6 +544,7 @@ static SpecialFlag const special_jvm_flags[] = { { "UseCompressedClassPointers", JDK_Version::jdk(25), JDK_Version::jdk(27), JDK_Version::undefined() }, #endif { "AlwaysCompileLoopMethods", JDK_Version::jdk(27), JDK_Version::jdk(28), JDK_Version::jdk(29) }, + { "OptoBundling", JDK_Version::undefined(), JDK_Version::jdk(28), JDK_Version::jdk(29) }, #ifdef ASSERT { "DummyObsoleteTestFlag", JDK_Version::undefined(), JDK_Version::jdk(18), JDK_Version::undefined() }, From 6403a5fb9b028a386a7b60decc944f7058acbba0 Mon Sep 17 00:00:00 2001 From: Suchismith Roy Date: Fri, 25 Sep 2026 06:42:27 +0000 Subject: [PATCH 027/103] 8343530: Cleanup adlc.hpp includes Reviewed-by: mbaesken, amitkumar, kvn --- src/hotspot/share/adlc/adlc.hpp | 28 ++-------------------------- 1 file changed, 2 insertions(+), 26 deletions(-) diff --git a/src/hotspot/share/adlc/adlc.hpp b/src/hotspot/share/adlc/adlc.hpp index fbc941f4c1b4..1ba9ea91e7f1 100644 --- a/src/hotspot/share/adlc/adlc.hpp +++ b/src/hotspot/share/adlc/adlc.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2026, 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 @@ -33,42 +33,18 @@ // standard library constants #include #include +#include #include #include #include -#include -/* Make sure that we have the intptr_t and uintptr_t definitions */ #ifdef _WIN32 -using namespace std; #define strdup _strdup -#ifndef _INTPTR_T_DEFINED -#ifdef _WIN64 -typedef __int64 intptr_t; -#else -#error "Unsupported platform" -#endif -#define _INTPTR_T_DEFINED -#endif - -#ifndef _UINTPTR_T_DEFINED -#ifdef _WIN64 -typedef unsigned __int64 uintptr_t; -#else -#error "Unsupported platform" -#endif -#define _UINTPTR_T_DEFINED -#endif - #endif // _WIN32 -#if defined(LINUX) || defined(_ALLBSD_SOURCE) - #include -#endif // LINUX || _ALLBSD_SOURCE - // Macros #define uint32 unsigned int #define uint unsigned int From f9a09768a4d2e5443f9de2f0e78690964305510f Mon Sep 17 00:00:00 2001 From: Alan Bateman Date: Fri, 25 Sep 2026 07:14:07 +0000 Subject: [PATCH 028/103] 8392974: (ch) Channels.newInputStream available returns negative value if position > size Reviewed-by: jpai --- .../native/libnio/ch/UnixFileDispatcherImpl.c | 8 +- .../java/nio/channels/Channels/Available.java | 269 ++++++++++++++++++ 2 files changed, 275 insertions(+), 2 deletions(-) create mode 100644 test/jdk/java/nio/channels/Channels/Available.java diff --git a/src/java.base/unix/native/libnio/ch/UnixFileDispatcherImpl.c b/src/java.base/unix/native/libnio/ch/UnixFileDispatcherImpl.c index 08321b602e5b..d0703da4e497 100644 --- a/src/java.base/unix/native/libnio/ch/UnixFileDispatcherImpl.c +++ b/src/java.base/unix/native/libnio/ch/UnixFileDispatcherImpl.c @@ -213,8 +213,12 @@ Java_sun_nio_ch_UnixFileDispatcherImpl_available0(JNIEnv *env, jobject this, job } jlong available = size - position; - return available > java_lang_Integer_MAX_VALUE ? - java_lang_Integer_MAX_VALUE : (jint)available; + if (available > java_lang_Integer_MAX_VALUE) { + available = java_lang_Integer_MAX_VALUE; + } else if (available < 0) { + available = 0; + } + return (jint)available; } JNIEXPORT jboolean JNICALL diff --git a/test/jdk/java/nio/channels/Channels/Available.java b/test/jdk/java/nio/channels/Channels/Available.java new file mode 100644 index 000000000000..6f5c19d783a4 --- /dev/null +++ b/test/jdk/java/nio/channels/Channels/Available.java @@ -0,0 +1,269 @@ +/* + * Copyright (c) 2026, 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 6884800 8392974 + * @summary Test Channels.newInputStream available method + * @run junit ${test.main.class} + */ + +import java.nio.ByteBuffer; +import java.nio.channels.Channels; +import java.nio.channels.FileChannel; +import java.nio.channels.ReadableByteChannel; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.nio.channels.SeekableByteChannel; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.nio.file.Files; +import java.nio.file.Path; +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import static org.junit.jupiter.api.Assertions.*; + +class Available { + + /** + * Test InputStream.available when input stream reads from a SeekableByteChannel. + */ + void testSeekableByteChannel(SeekableByteChannel ch) throws Exception { + InputStream in = Channels.newInputStream(ch); + long size = ch.size(); + long[] positions = new long[] { + 0L, + size - 1L, + size, + size + 1L, + Integer.MAX_VALUE - size - 1L, + Integer.MAX_VALUE - size, + Integer.MAX_VALUE - size + 1L, + Integer.MAX_VALUE - 1L, + Integer.MAX_VALUE, + Integer.MAX_VALUE + 1L, + Long.MAX_VALUE - size - 1L, + Long.MAX_VALUE - size, + Long.MAX_VALUE - size + 1L, + Long.MAX_VALUE - 1L, + Long.MAX_VALUE + }; + for (long pos : positions) { + if (pos >= 0) { + ch.position(pos); + int expectedAvailable = Math.clamp(size - pos, 0, Integer.MAX_VALUE); + int available = in.available(); + System.err.format(" position = %d, available = %d%n", pos, available); + assertEquals(expectedAvailable, available); + } + } + } + + /** + * Test InputStream.available when input stream reads from a FileChannel. + */ + @ParameterizedTest + @ValueSource(ints = { 0, 100 }) + void testFileChannel(int size) throws Exception { + Path file = Files.createTempFile(Path.of("."), "blah", "dat"); + try { + Files.write(file, new byte[size]); + try (FileChannel fc = FileChannel.open(file)) { + testSeekableByteChannel(fc); + } + } finally { + Files.delete(file); + } + } + + /** + * Test InputStream.available when input stream reads from a SeekableByteChannel. + */ + @ParameterizedTest + @ValueSource(ints = { 0, 100 }) + void testSeekableByteChannel(int size) throws Exception { + var ch = new SeekableByteChannel() { + long position; + @Override + public int read(ByteBuffer dst) throws IOException { + throw new IOException(); + } + @Override + public int write(ByteBuffer src) throws IOException { + throw new IOException(); + } + @Override + public long position() { + return position; + } + @Override + public SeekableByteChannel position(long newPosition) { + if (newPosition < 0) + throw new IllegalArgumentException(); + position = newPosition; + return this; + } + @Override + public long size() { + return size; + } + @Override + public SeekableByteChannel truncate(long size) throws IOException { + throw new IOException(); + } + @Override + public boolean isOpen() { + return true; + } + @Override + public void close() throws IOException { + throw new IOException(); + } + }; + testSeekableByteChannel(ch); + } + + /** + * Test InputStream.available when input stream reads from a ReadableByteChannel. + */ + @Test + void testReadableByteChannel() throws Exception { + var ch = new ReadableByteChannel() { + @Override + public int read(ByteBuffer dst) throws IOException { + throw new IOException(); + } + @Override + public boolean isOpen() { + return true; + } + @Override + public void close() throws IOException { + throw new IOException(); + } + }; + InputStream in = Channels.newInputStream(ch); + assertEquals(0, in.available()); + } + + /** + * Test InputStream.available when input stream reads from a socket. + * @param in input stream that reads from a socket + * @param peer output stream of connected peer + */ + void testSocketInputStream(InputStream in, OutputStream peer) throws Exception { + assertEquals(0, in.available()); + + for (int i = 0; i < 5; i++) { + final int count = 10; + peer.write("X".repeat(count).getBytes("UTF-8")); + + // available should return count when all bytes available to read + while (in.available() < count) { + Thread.sleep(10); + } + assertEquals(count, in.available()); + + // read one byte until all bytes are read + int rem = count; + while (rem > 0) { + int aByte = in.read(); + assertEquals('X', aByte); + rem--; + assertEquals(rem, in.available()); + } + } + + // available should return 0 when all bytes are read and peer has closed connection + peer.close(); + assertEquals(0, in.available()); + } + + /** + * Test InputStream.available for input stream that reads from a socket channel. + */ + @Test + void testSocketChannel() throws Exception { + try (var connection = new Connection()) { + InputStream in = Channels.newInputStream(connection.channel1()); + OutputStream out = Channels.newOutputStream(connection.channel2()); + testSocketInputStream(in, out); + } + } + + /** + * Test InputStream.available for input stream that reads from a socket associated + * a socket channel. + */ + @Test + void testSocketChannelAdaptor() throws Exception { + try (var connection = new Connection()) { + Socket s1 = connection.channel1().socket(); + Socket s2 = connection.channel2().socket(); + testSocketInputStream(s1.getInputStream(), s2.getOutputStream()); + } + } + + /** + * A loopback connection. + */ + private static class Connection implements Closeable { + private final SocketChannel sc1; + private final SocketChannel sc2; + Connection() throws IOException { + var lh = InetAddress.getLoopbackAddress(); + try (var listener = ServerSocketChannel.open()) { + listener.bind(new InetSocketAddress(lh, 0)); + SocketChannel sc1 = SocketChannel.open(); + SocketChannel sc2 = null; + try { + sc1.socket().connect(listener.getLocalAddress()); + sc2 = listener.accept(); + } catch (IOException ioe) { + sc1.close(); + throw ioe; + } + this.sc1 = sc1; + this.sc2 = sc2; + } + } + SocketChannel channel1() { + return sc1; + } + SocketChannel channel2() { + return sc2; + } + @Override + public void close() throws IOException { + sc1.close(); + sc2.close(); + } + } +} From 034d2c7475d4e5bee925fc14eb144dfbddf73c76 Mon Sep 17 00:00:00 2001 From: Erik Gahlin Date: Fri, 25 Sep 2026 07:14:33 +0000 Subject: [PATCH 029/103] 8392750: JFR: Harden the query tokenizer Reviewed-by: mgronlun --- .../jdk/jfr/internal/query/QueryParser.java | 28 +++---- .../jdk/jfr/internal/query/ViewFile.java | 10 +-- .../jdk/jfr/internal/util/Tokenizer.java | 74 ++++++++++++++----- 3 files changed, 77 insertions(+), 35 deletions(-) diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/query/QueryParser.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/query/QueryParser.java index 3c7d1e58b350..1eacc2332865 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/query/QueryParser.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/query/QueryParser.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, 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 @@ -56,7 +56,7 @@ public List column() throws ParseException { } List texts = new ArrayList<>(); texts.add(text()); - while (tokenizer.accept(",")) { + while (tokenizer.accept(',')) { texts.add(text()); } return texts; @@ -66,7 +66,7 @@ public List format() throws ParseException { if (tokenizer.accept("FORMAT")) { List formatters = new ArrayList<>(); formatters.add(formatter()); - while (tokenizer.accept(",")) { + while (tokenizer.accept(',')) { formatters.add(formatter()); } return formatters; @@ -77,7 +77,7 @@ public List format() throws ParseException { private Formatter formatter() throws ParseException { List properties = new ArrayList<>(); properties.add(property()); - while (tokenizer.accept(";")) { + while (tokenizer.accept(';')) { properties.add(property()); } return new Formatter(properties); @@ -85,7 +85,7 @@ private Formatter formatter() throws ParseException { public List select() throws ParseException { tokenizer.expect("SELECT"); - if (tokenizer.accept("*")) { + if (tokenizer.accept('*')) { return List.of(); } List expressions = new ArrayList<>(); @@ -93,7 +93,7 @@ public List select() throws ParseException { throw new ParseException("Missing fields in SELECT statement", position()); } expressions.add(expression()); - while (tokenizer.accept(",")) { + while (tokenizer.accept(',')) { Expression exp = expression(); if (exp.name().equalsIgnoreCase("FROM")) { throw new ParseException("Missing field name in SELECT statement, or qualify field with event type if name is called '" + exp.name() + "'", position()); @@ -113,12 +113,14 @@ private Expression expression() throws ParseException { } private Expression aggregator() throws ParseException { + int position = tokenizer.getPosition(); for (Aggregator function : Aggregator.values()) { - if (tokenizer.accept(function.name, "(")) { + if (tokenizer.accept(function.name) && tokenizer.accept('(')) { String eventField = eventField(); - tokenizer.expect(")"); + tokenizer.expect(')'); return new Expression(eventField, alias(), function); } + tokenizer.setPosition(position); } return null; } @@ -135,7 +137,7 @@ public List from() throws ParseException { tokenizer.expect("FROM"); List sources = new ArrayList<>(); sources.add(source()); - while (tokenizer.accept(",")) { + while (tokenizer.accept(',')) { sources.add(source()); } return sources; @@ -170,10 +172,10 @@ public List where() throws ParseException { private Condition condition() throws ParseException { String field = eventField(); - if (tokenizer.acceptAny("<", ">", "<>", ">=", "<=", "==", "BETWEEN", "LIKE", "IN")) { + if (tokenizer.accept('<') || tokenizer.accept('>') || tokenizer.acceptAny("BETWEEN", "LIKE", "IN")) { throw new ParseException("The only operator allowed in WHERE clause is '='", position()); } - tokenizer.expect("="); + tokenizer.expect('='); String value = text(); return new Condition(field, value); } @@ -186,7 +188,7 @@ public List groupBy() throws ParseException { tokenizer.expect("BY"); List groupers = new ArrayList<>(); groupers.add(grouper()); - while (tokenizer.accept(",")) { + while (tokenizer.accept(',')) { groupers.add(grouper()); } return groupers; @@ -203,7 +205,7 @@ public List orderBy() throws ParseException { tokenizer.expect("BY"); List fields = new ArrayList<>(); fields.add(orderer()); - while (tokenizer.accept(",")) { + while (tokenizer.accept(',')) { fields.add(orderer()); } return fields; diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/query/ViewFile.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/query/ViewFile.java index 96eb1fbdea8b..3440874b4836 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/query/ViewFile.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/query/ViewFile.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, 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 @@ -96,19 +96,19 @@ private List parse(String text) throws ParseException { List views = new ArrayList<>(); try (Tokenizer tokenizer = new Tokenizer(text, '[', ']', ';')) { while (tokenizer.hasNext()) { - while (tokenizer.accept(";")) { + while (tokenizer.accept(';')) { tokenizer.skipLine(); } - if (tokenizer.accept("[")) { + if (tokenizer.accept('[')) { String fullName = tokenizer.next(); - tokenizer.expect("]"); + tokenizer.expect(']'); views.add(createView(fullName)); } if (views.isEmpty()) { throw new ParseException("Expected view file to begin with a section", tokenizer.getPosition()); } String key = tokenizer.next(); - tokenizer.expect("="); + tokenizer.expect('='); String value = tokenizer.next(); views.getLast().properties().put(key, value); } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/util/Tokenizer.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/util/Tokenizer.java index 460635be61ab..f9a87b996a19 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/util/Tokenizer.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/util/Tokenizer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, 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 @@ -42,16 +42,31 @@ public Tokenizer(String text, char... separators) { this.separators = separators; } + /** + * If the next non-whitespace character matches, it is consumed and + * {@code true} returned, {@code false} otherwise. + */ + public boolean accept(char c) { + skipWhitespace(); + if (index < text.length()) { + if (Character.toLowerCase(text.charAt(index)) == Character.toLowerCase(c)) { + index++; + return true; + } + } + return false; + } + /** * If the next token matches a string, it is consumed and {@code true} returned, * {@code false} otherwise. */ - public boolean accept(String match) { + public boolean accept(String token) { skipWhitespace(); int t = 0; - while (index + t < text.length() && t < match.length()) { + while (index + t < text.length() && t < token.length()) { char c = Character.toLowerCase(text.charAt(index + t)); - char d = Character.toLowerCase(match.charAt(t)); + char d = Character.toLowerCase(token.charAt(t)); if (d != c) { return false; } @@ -60,9 +75,17 @@ public boolean accept(String match) { break; } } - if (t == match.length()) { - index += match.length(); - return true; + if (t == token.length()) { + int position = index + t; + if (position >= text.length()) { + index = position; + return true; + } + char c = text.charAt(position); + if (isSeparator(c)|| Character.isWhitespace(c)) { + index = position; + return true; + } } return false; } @@ -70,10 +93,10 @@ public boolean accept(String match) { /** * Similar to accept(String), but requires several tokens to match. */ - public boolean accept(String... matches) { + public boolean accept(String... tokens) { int position = index; - for (String s : matches) { - if (!accept(s)) { + for (String token : tokens) { + if (!accept(token)) { index = position; return false; @@ -88,9 +111,9 @@ public boolean accept(String... matches) { * @param matches * @return */ - public boolean acceptAny(String... matches) { - for (String match : matches) { - if (accept(match)) { + public boolean acceptAny(String... tokens) { + for (String token : tokens) { + if (accept(token)) { return true; } } @@ -115,7 +138,16 @@ public boolean hasNext() { /** * Throws exception if the next token doesn't match. */ - public void expect(String expected) throws ParseException { + public void expect(String token) throws ParseException { + if (!accept(token)) { + throw new ParseException("Expected " + token, index); + } + } + + /** + * Throws exception if the next non-whitspace character doesn't match. + */ + public void expect(char expected) throws ParseException { if (!accept(expected)) { throw new ParseException("Expected " + expected, index); } @@ -139,6 +171,7 @@ public char peekChar() throws ParseException { public String next() throws ParseException { skipWhitespace(); StringBuilder sb = new StringBuilder(); + int start = index; while (index < text.length()) { char c = text.charAt(index); if (isQuoteCharacter(c)) { @@ -148,9 +181,9 @@ public String next() throws ParseException { index = p; } else { if (isSeparator(c)) { - if (sb.isEmpty()) { + if (start == index) { index++; - return String.valueOf(c); // Inte helt optimalt + return String.valueOf(c); } else { return sb.toString(); } @@ -162,7 +195,7 @@ public String next() throws ParseException { } index++; } - if (sb.isEmpty()) { + if (start == index) { throw new ParseException("Unexpected EOF reached", index); } return sb.toString(); @@ -187,6 +220,13 @@ public int getPosition() { return index; } + /** + * Sets the current position in the text. + */ + public void setPosition(int index) { + this.index = index; + } + private void skipWhitespace() { while (index < text.length()) { char c = text.charAt(index); From e2f4490dc717e21e666cb2c3679de46f37b8e927 Mon Sep 17 00:00:00 2001 From: Serguei Spitsyn Date: Fri, 25 Sep 2026 07:46:46 +0000 Subject: [PATCH 030/103] 8392964: JVMTI value-object tag equality ignores inherited fields 8386824: JVMTI equal_fields compares value object floats mishandling NaNs Co-authored-by: Axel Boldt-Christmas Reviewed-by: aboldtch, fparain --- src/hotspot/share/prims/jvmtiTagMapTable.cpp | 10 +- .../SetTag/ValueTagSubstitutabilityTest.java | 154 ++++++++++++++++++ .../libValueTagSubstitutabilityTest.cpp | 61 +++++++ 3 files changed, 220 insertions(+), 5 deletions(-) create mode 100644 test/hotspot/jtreg/serviceability/jvmti/valhalla/SetTag/ValueTagSubstitutabilityTest.java create mode 100644 test/hotspot/jtreg/serviceability/jvmti/valhalla/SetTag/libValueTagSubstitutabilityTest.cpp diff --git a/src/hotspot/share/prims/jvmtiTagMapTable.cpp b/src/hotspot/share/prims/jvmtiTagMapTable.cpp index 9bdf5a575f1a..31cb9890d74b 100644 --- a/src/hotspot/share/prims/jvmtiTagMapTable.cpp +++ b/src/hotspot/share/prims/jvmtiTagMapTable.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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 @@ -53,9 +53,9 @@ static bool equal_fields(char type, oop obj1, int offset1, oop obj2, int offset2 case JVM_SIGNATURE_CHAR: return obj1->char_field(offset1) == obj2->char_field(offset2); case JVM_SIGNATURE_FLOAT: - return obj1->float_field(offset1) == obj2->float_field(offset2); + return *obj1->field_addr(offset1) == *obj2->field_addr(offset2); case JVM_SIGNATURE_DOUBLE: - return obj1->double_field(offset1) == obj2->double_field(offset2); + return *obj1->field_addr(offset1) == *obj2->field_addr(offset2); case JVM_SIGNATURE_BYTE: return obj1->byte_field(offset1) == obj2->byte_field(offset2); case JVM_SIGNATURE_SHORT: @@ -79,7 +79,7 @@ static bool is_null_flat_field(oop obj, int offset, ValueKlass* klass) { // For flattened objects offset is the offset in the holder object, 'klass' is value object class. // The object must be prechecked for non-null values. static bool equal_value_objects(oop obj1, int offset1, oop obj2, int offset2, ValueKlass* klass) { - for (JavaFieldStream fld(klass); !fld.done(); fld.next()) { + for (HierarchicalFieldStream fld(klass); !fld.done(); fld.next()) { // ignore static fields if (fld.access_flags().is_static()) { continue; @@ -87,7 +87,7 @@ static bool equal_value_objects(oop obj1, int offset1, oop obj2, int offset2, Va int field_offset1 = offset1 + fld.offset() - (offset1 > 0 ? klass->payload_offset() : 0); int field_offset2 = offset2 + fld.offset() - (offset2 > 0 ? klass->payload_offset() : 0); if (fld.is_flat()) { // flat value field - InstanceKlass* holder_klass = fld.field_holder(); + InstanceKlass* holder_klass = fld.field_descriptor().field_holder(); ValueKlass* field_klass = holder_klass->get_value_type_field_klass(fld.index()); if (!fld.is_null_free_value_type()) { bool field1_is_null = is_null_flat_field(obj1, field_offset1, field_klass); diff --git a/test/hotspot/jtreg/serviceability/jvmti/valhalla/SetTag/ValueTagSubstitutabilityTest.java b/test/hotspot/jtreg/serviceability/jvmti/valhalla/SetTag/ValueTagSubstitutabilityTest.java new file mode 100644 index 000000000000..ed4aa0fb297d --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/valhalla/SetTag/ValueTagSubstitutabilityTest.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2026, 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 + * @summary Regression test for JVMTIs tag map value class instance substitutability test + * @bug 8386824 8392964 + * @requires vm.jvmti + * @enablePreview + * @run main/othervm/native -agentlib:ValueTagSubstitutabilityTest ValueTagSubstitutabilityTest + */ + +public class ValueTagSubstitutabilityTest { + private static void log(String msg) { + System.out.println(msg); + System.out.flush(); + } + + static value record FloatValue(float value) {} + static value record DoubleValue(double value) {} + + static abstract value class Base { + private int base; + + Base(int base) { + this.base = base; + } + } + + static abstract value class Middle extends Base { + private int middle; + + Middle(int base, int middle) { + this.middle = middle; + super(base); + } + } + + static value class Leaf extends Middle { + private int leaf; + + Leaf(int base, int middle, int leaf) { + this.leaf = leaf; + super(base, middle); + } + } + + private static native void setTag(Object object, long tag); + private static native long getTag(Object object); + + private static int failures; + + private static void checkTag(String description, Object object, long expected) { + final long actual = getTag(object); + if (actual != expected) { + log("checkTag: Failed: " + description + ": expected tag " + expected + ", got " + actual); + failures++; + } + } + + private static void checkPair(String description, Object first, Object second, boolean substitutable) { + log("checkPair: Testing " + description); + if ((first == second) != substitutable) { + throw new RuntimeException(description + ": unexpected Java substitutability"); + } + + try { + // First tagged with 1. If they are substitutable, lookup on second + // expects to see this tag, otherwise it should not be tagged / 0. + setTag(first, 1); + checkTag(description + " / original", first, 1); + checkTag(description + " / lookup", second, substitutable ? 1 : 0); + + // Second tagged with 2. If they are substitutable, the first tag 1 + // should be overwritten, and lookup on first should produce 2, + // otherwise first should keep its tag 1. + setTag(second, 2); + checkTag(description + " / first after update", first, substitutable ? 2 : 1); + checkTag(description + " / second after update", second, 2); + + // This propery should work for removals as well. + setTag(second, 0); + checkTag(description + " / first after removal", first, substitutable ? 0 : 1); + checkTag(description + " / second after removal", second, 0); + } finally { + setTag(first, 0); + setTag(second, 0); + } + } + + private static void testFloat() { + checkPair("equal finite floats", new FloatValue(1.0f), new FloatValue(1.0f), true); + checkPair("different finite floats", new FloatValue(1.0f), new FloatValue(2.0f), false); + checkPair("signed float zeros", new FloatValue(+0.0f), new FloatValue(-0.0f), false); + checkPair("identical float NaNs", new FloatValue(Float.NaN), new FloatValue(Float.NaN), true); + + // Use quiet NaNs so merely loading a signaling NaN cannot change its bits. + final float nan1 = Float.intBitsToFloat(0x7fc00001); + final float nan2 = Float.intBitsToFloat(0x7fc00002); + checkPair("identical float NaN payloads", new FloatValue(nan1), new FloatValue(nan1), true); + checkPair("different float NaN payloads", new FloatValue(nan1), new FloatValue(nan2), false); + } + + private static void testDouble() { + checkPair("equal finite doubles", new DoubleValue(1.0), new DoubleValue(1.0), true); + checkPair("different finite doubles", new DoubleValue(1.0), new DoubleValue(2.0), false); + checkPair("signed double zeros", new DoubleValue(+0.0), new DoubleValue(-0.0), false); + checkPair("identical double NaNs", new DoubleValue(Double.NaN), new DoubleValue(Double.NaN), true); + + final double nan1 = Double.longBitsToDouble(0x7ff8000000000001L); + final double nan2 = Double.longBitsToDouble(0x7ff8000000000002L); + checkPair("identical double NaN payloads", new DoubleValue(nan1), new DoubleValue(nan1), true); + checkPair("different double NaN payloads", new DoubleValue(nan1), new DoubleValue(nan2), false); + } + + private static void testInheritance() { + checkPair("equal inherited fields", new Leaf(1, 2, 3), new Leaf(1, 2, 3), true); + checkPair("different leaf field", new Leaf(1, 2, 3), new Leaf(1, 2, 4), false); + checkPair("different middle field", new Leaf(1, 2, 3), new Leaf(1, 4, 3), false); + checkPair("different base field", new Leaf(1, 2, 3), new Leaf(4, 2, 3), false); + } + + public static void main(String[] args) { + System.loadLibrary("ValueTagSubstitutabilityTest"); + + testFloat(); + testDouble(); + testInheritance(); + + if (failures != 0) { + throw new RuntimeException(failures + " tag checks failed"); + } + } +} diff --git a/test/hotspot/jtreg/serviceability/jvmti/valhalla/SetTag/libValueTagSubstitutabilityTest.cpp b/test/hotspot/jtreg/serviceability/jvmti/valhalla/SetTag/libValueTagSubstitutabilityTest.cpp new file mode 100644 index 000000000000..ba5885975772 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/valhalla/SetTag/libValueTagSubstitutabilityTest.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2026, 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. + */ + +#include +#include "jvmti_common.hpp" + +extern "C" { + +static jvmtiEnv *jvmti = nullptr; + +JNIEXPORT jint JNICALL +Agent_OnLoad(JavaVM *vm, char *options, void *reserved) { + if (vm->GetEnv(reinterpret_cast(&jvmti), JVMTI_VERSION) != JNI_OK || !jvmti) { + LOG("Could not initialize JVMTI\n"); + abort(); + } + jvmtiCapabilities capabilities; + memset(&capabilities, 0, sizeof(capabilities)); + capabilities.can_tag_objects = 1; + + jvmtiError err = jvmti->AddCapabilities(&capabilities); + check_jvmti_error(err, "Error in AddCapabilities"); + return JNI_OK; +} + +JNIEXPORT void JNICALL +Java_ValueTagSubstitutabilityTest_setTag(JNIEnv* jni, jclass clazz, jobject object, jlong tag) { + jvmtiError err = jvmti->SetTag(object, tag); + check_jvmti_status(jni, err, "Error in SetTag"); +} + +JNIEXPORT jlong JNICALL +Java_ValueTagSubstitutabilityTest_getTag(JNIEnv* jni, jclass clazz, jobject object) { + jlong tag; + jvmtiError err = jvmti->GetTag(object, &tag); + check_jvmti_status(jni, err, "Error in GetTag"); + return tag; +} + +} // extern "C" + From 225ab139c0662112e6715778aedbe19e4268f326 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Fri, 25 Sep 2026 07:48:51 +0000 Subject: [PATCH 031/103] 8392625: G1: Adjust naming of region counts in region iteration to num_*_regions Reviewed-by: ayang --- src/hotspot/share/gc/g1/g1CollectedHeap.cpp | 8 ++++---- src/hotspot/share/gc/g1/g1CollectedHeap.hpp | 8 ++++---- src/hotspot/share/gc/g1/g1CollectionSet.cpp | 8 ++++---- src/hotspot/share/gc/g1/g1CollectionSet.hpp | 8 ++++---- src/hotspot/share/gc/g1/g1HeapRegionManager.cpp | 9 ++------- 5 files changed, 18 insertions(+), 23 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index ada94ab68082..9f59d1c65c75 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -2246,15 +2246,15 @@ void G1CollectedHeap::collection_set_iterate_increment_from(G1HeapRegionClosure void G1CollectedHeap::par_iterate_regions_array(G1HeapRegionClosure* cl, G1HeapRegionClaimer* hr_claimer, const uint regions[], - size_t length, + size_t num_regions, uint worker_id) const { assert_at_safepoint(); - if (length == 0) { + if (num_regions == 0) { return; } uint total_workers = workers()->active_workers(); - size_t start_pos = (worker_id * length) / total_workers; + size_t start_pos = (worker_id * num_regions) / total_workers; size_t cur_pos = start_pos; do { @@ -2266,7 +2266,7 @@ void G1CollectedHeap::par_iterate_regions_array(G1HeapRegionClosure* cl, } cur_pos++; - if (cur_pos == length) { + if (cur_pos == num_regions) { cur_pos = 0; } } while (cur_pos != start_pos); diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp index 31a947ae5ea9..b1bdeebdfabe 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp @@ -1148,13 +1148,13 @@ class G1CollectedHeap : public CollectedHeap { collection_set_iterate_increment_from(blk, nullptr, worker_id); } void collection_set_iterate_increment_from(G1HeapRegionClosure *blk, G1HeapRegionClaimer* hr_claimer, uint worker_id); - // Iterate over the array of region indexes, uint regions[length], applying - // the given G1HeapRegionClosure on each region. The worker_id will determine where - // to start the iteration to allow for more efficient parallel iteration. + // Iterate over the array of region indices applying the given G1HeapRegionClosure + // on each region. The worker_id will determine where to start the iteration to + // allow for more efficient parallel iteration. void par_iterate_regions_array(G1HeapRegionClosure* cl, G1HeapRegionClaimer* hr_claimer, const uint regions[], - size_t length, + size_t num_regions, uint worker_id) const; // Returns the G1HeapRegion that contains addr. addr must not be null. diff --git a/src/hotspot/share/gc/g1/g1CollectionSet.cpp b/src/hotspot/share/gc/g1/g1CollectionSet.cpp index 48fb04df358e..5314f7449cc7 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSet.cpp +++ b/src/hotspot/share/gc/g1/g1CollectionSet.cpp @@ -171,9 +171,9 @@ void G1CollectionSet::clear() { } void G1CollectionSet::iterate(G1HeapRegionClosure* cl) const { - uint len = _num_regions.load_acquire(); + uint num_regions = _num_regions.load_acquire(); - for (uint i = 0; i < len; i++) { + for (uint i = 0; i < num_regions; i++) { G1HeapRegion* r = _g1h->region_at(_regions[i]); bool result = cl->do_heap_region(r); if (result) { @@ -206,12 +206,12 @@ void G1CollectionSet::iterate_incremental_part_from(G1HeapRegionClosure* cl, void G1CollectionSet::iterate_part_from(G1HeapRegionClosure* cl, G1HeapRegionClaimer* hr_claimer, uint offset, - uint length, + uint num_regions, uint worker_id) const { _g1h->par_iterate_regions_array(cl, hr_claimer, &_regions[offset], - length, + num_regions, worker_id); } diff --git a/src/hotspot/share/gc/g1/g1CollectionSet.hpp b/src/hotspot/share/gc/g1/g1CollectionSet.hpp index 31816d3c3192..d3eb4edd08d9 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSet.hpp +++ b/src/hotspot/share/gc/g1/g1CollectionSet.hpp @@ -220,13 +220,13 @@ class G1CollectionSet { // and retained collection set candidates. void finalize_old_part(double time_remaining_ms); - // Iterate the part of the collection set given by the offset and length applying the given - // G1HeapRegionClosure. The worker_id will determine where in the part to start the iteration - // to allow for more efficient parallel iteration. + // Iterate over the collection set range [offset, offset + num_regions) applying the given + // G1HeapRegionClosure on each region. The worker_id will determine where in the range to + // start the iteration to allow for more efficient parallel iteration. void iterate_part_from(G1HeapRegionClosure* cl, G1HeapRegionClaimer* hr_claimer, uint offset, - uint length, + uint num_regions, uint worker_id) const; // Adds the given group to the optional groups list (_optional_groups) diff --git a/src/hotspot/share/gc/g1/g1HeapRegionManager.cpp b/src/hotspot/share/gc/g1/g1HeapRegionManager.cpp index 1b49b3697c4d..b637e7e83b41 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegionManager.cpp +++ b/src/hotspot/share/gc/g1/g1HeapRegionManager.cpp @@ -502,9 +502,7 @@ G1HeapRegion* G1HeapRegionManager::next_region_in_heap(const G1HeapRegion* r) co } void G1HeapRegionManager::iterate(G1HeapRegionClosure* blk) const { - uint len = max_num_regions(); - - for (uint i = 0; i < len; i++) { + for (uint i = 0; i < max_num_regions(); i++) { if (!is_available(i)) { continue; } @@ -517,9 +515,7 @@ void G1HeapRegionManager::iterate(G1HeapRegionClosure* blk) const { } void G1HeapRegionManager::iterate(G1HeapRegionIndexClosure* blk) const { - uint len = max_num_regions(); - - for (uint i = 0; i < len; i++) { + for (uint i = 0; i < max_num_regions(); i++) { if (!is_available(i)) { continue; } @@ -685,7 +681,6 @@ void G1HeapRegionManager::verify() { i, HR_FORMAT_PARAMS(hr), p2i(prev_end)); guarantee(hr->hrm_index() == i, "invariant: i: %u hrm_index(): %u", i, hr->hrm_index()); - // Asserts will fire if i is >= _length HeapWord* addr = hr->bottom(); guarantee(addr_to_region(addr) == hr, "sanity"); // We cannot check whether the region is part of a particular set: at the time From f4554155d3ad81a85f384ba479be1aa6cb52847a Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Fri, 25 Sep 2026 07:49:22 +0000 Subject: [PATCH 032/103] 8392840: G1: Uncommit task crashes with region size > 128M Reviewed-by: ayang, shade --- .../share/gc/g1/g1UncommitRegionTask.cpp | 6 +-- .../share/gc/g1/g1UncommitRegionTask.hpp | 7 ++- .../gc/g1/TestShrinkWithLargeRegions.java | 52 +++++++++++++++++++ 3 files changed, 60 insertions(+), 5 deletions(-) create mode 100644 test/hotspot/jtreg/gc/g1/TestShrinkWithLargeRegions.java diff --git a/src/hotspot/share/gc/g1/g1UncommitRegionTask.cpp b/src/hotspot/share/gc/g1/g1UncommitRegionTask.cpp index a93c3ef275cc..989a1292c085 100644 --- a/src/hotspot/share/gc/g1/g1UncommitRegionTask.cpp +++ b/src/hotspot/share/gc/g1/g1UncommitRegionTask.cpp @@ -104,9 +104,9 @@ void G1UncommitRegionTask::clear_summary() { void G1UncommitRegionTask::execute() { assert(_active, "Must be active"); - // Translate the size limit into a number of regions. This cannot be a - // compile time constant because G1HeapRegionSize is set ergonomically. - static const uint max_num_regions_to_uncommit = (uint)(UncommitSizeLimit / G1HeapRegionSize); + // Translate the size limit into a number of regions, taking the minimum + // region count into account. + static const uint max_num_regions_to_uncommit = MAX2(MaxUncommitSize / (uint)G1HeapRegionSize, MinNumRegionsToUncommit); // Prevent from running during a GC pause. SuspendibleThreadSetJoiner sts; diff --git a/src/hotspot/share/gc/g1/g1UncommitRegionTask.hpp b/src/hotspot/share/gc/g1/g1UncommitRegionTask.hpp index 2de91a3c5149..4f121e0046cb 100644 --- a/src/hotspot/share/gc/g1/g1UncommitRegionTask.hpp +++ b/src/hotspot/share/gc/g1/g1UncommitRegionTask.hpp @@ -30,10 +30,13 @@ #include "utilities/ticks.hpp" class G1UncommitRegionTask : public G1ServiceTask { - // Each execution of the uncommit task is limited to uncommit at most 128M. + // Each execution of the uncommit task is limited to uncommit at most 128M, + // subject to the minimum region requirement. // This limit is small enough to ensure that the duration of each invocation // is short, while still making reasonable progress. - static const uint UncommitSizeLimit = 128 * M; + static const uint MaxUncommitSize = 128 * M; + // Minimum number of regions to uncommit. + static const uint MinNumRegionsToUncommit = 1; // The delay between two uncommit task executions. static const uint UncommitTaskDelayMs = 10; diff --git a/test/hotspot/jtreg/gc/g1/TestShrinkWithLargeRegions.java b/test/hotspot/jtreg/gc/g1/TestShrinkWithLargeRegions.java new file mode 100644 index 000000000000..05f66fb3af38 --- /dev/null +++ b/test/hotspot/jtreg/gc/g1/TestShrinkWithLargeRegions.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2026, 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. + */ + +package gc.g1; + +/* + * @test TestShrinkWithLargeRegions.java + * @bug 8392840 + * @requires vm.gc.G1 + * @requires vm.bits != "32" + * @summary Shrinking the heap fails with an assert/guarantee in the uncommit task in presence of large regions. + * @run main/othervm -XX:+UseG1GC -XX:G1HeapRegionSize=256m -XX:MinHeapSize=512m -XX:InitialHeapSize=4g -XX:MaxHeapSize=4g -XX:MaxHeapFreeRatio=1 -Xlog:gc+heap=trace,gc+ergo+heap=debug,gc+task=debug gc.g1.TestShrinkWithLargeRegions + */ + +/* + * @test TestShrinkWithLargeRegions.java + * @bug 8392840 + * @requires vm.gc.G1 + * @requires vm.bits != "32" + * @summary Shrinking the heap fails with a assert/guarantee in the uncommit task in presence of large regions. + * @run main/othervm -XX:+UseG1GC -XX:G1HeapRegionSize=512m -XX:MinHeapSize=512m -XX:InitialHeapSize=4g -XX:MaxHeapSize=4g -XX:MaxHeapFreeRatio=1 -Xlog:gc+heap=debug,gc+ergo+heap=debug,gc+task=debug gc.g1.TestShrinkWithLargeRegions + */ + +import java.lang.Thread; + +public class TestShrinkWithLargeRegions { + public static void main(String[] args) throws Exception { + System.gc(); + // Let the uncommit thread do its work. + Thread.sleep(1000); + } +} From 91aac483e04c2fe9d89c6f1da77a7e8053015c8b Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Fri, 25 Sep 2026 07:50:25 +0000 Subject: [PATCH 033/103] 8392820: G1: Card table clearing hangs in STW pause Reviewed-by: ayang, shade --- src/hotspot/share/gc/g1/g1RemSet.cpp | 2 +- .../jtreg/gc/g1/TestCardClearHang.java | 74 +++++++++++++++++++ 2 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 test/hotspot/jtreg/gc/g1/TestCardClearHang.java diff --git a/src/hotspot/share/gc/g1/g1RemSet.cpp b/src/hotspot/share/gc/g1/g1RemSet.cpp index 03b7ea2612cb..48998e3afd79 100644 --- a/src/hotspot/share/gc/g1/g1RemSet.cpp +++ b/src/hotspot/share/gc/g1/g1RemSet.cpp @@ -213,7 +213,7 @@ class G1ClearCardTableTask : public G1AbstractSubTask { } void do_work(uint worker_id) override { - const uint num_regions_per_worker = num_cards_per_worker / (uint)G1HeapRegion::CardsPerRegion; + const uint num_regions_per_worker = MAX2(num_cards_per_worker / (uint)G1HeapRegion::CardsPerRegion, 1); uint cur = _cur_dirty_regions.load_relaxed(); while (cur < _regions->num_regions()) { diff --git a/test/hotspot/jtreg/gc/g1/TestCardClearHang.java b/test/hotspot/jtreg/gc/g1/TestCardClearHang.java new file mode 100644 index 000000000000..76e504549748 --- /dev/null +++ b/test/hotspot/jtreg/gc/g1/TestCardClearHang.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2026, 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. + */ + +package gc.g1; + +/* + * @test id=256-128 + * @bug 8392820 + * @requires vm.gc.G1 + * @requires vm.bits != "32" + * @summary Make sure that G1 does not hang with some combinations of -XX:G1HeapRegionSize and -XX:GCCardSizeInBytes + * @library /test/lib + * @modules java.base/jdk.internal.misc + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseG1GC -Xmx2g -XX:G1HeapRegionSize=256m -XX:GCCardSizeInBytes=128 gc.g1.TestCardClearHang + */ + +/* + * @test id=512-128 + * @bug 8392820 + * @requires vm.gc.G1 + * @requires vm.bits != "32" + * @summary Make sure that G1 does not hang with some combinations of -XX:G1HeapRegionSize and -XX:GCCardSizeInBytes + * @library /test/lib + * @modules java.base/jdk.internal.misc + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseG1GC -Xmx2g -XX:G1HeapRegionSize=512m -XX:GCCardSizeInBytes=128 gc.g1.TestCardClearHang + */ + +/* + * @test id=512-256 + * @bug 8392820 + * @requires vm.gc.G1 + * @requires vm.bits != "32" + * @summary Make sure that G1 does not hang with some combinations of -XX:G1HeapRegionSize and -XX:GCCardSizeInBytes + * @library /test/lib + * @modules java.base/jdk.internal.misc + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseG1GC -Xmx2g -XX:G1HeapRegionSize=512m -XX:GCCardSizeInBytes=256 gc.g1.TestCardClearHang + */ + +import jdk.test.whitebox.WhiteBox; + +public class TestCardClearHang { + + public static void main(String[] args) throws InterruptedException { + WhiteBox wb = WhiteBox.getWhiteBox(); + // Run a young gc. + wb.youngGC(); + } +} From 94ec97d1076347a97d5f1e3f86269c1aceca5aed Mon Sep 17 00:00:00 2001 From: Martin Doerr Date: Fri, 25 Sep 2026 08:30:04 +0000 Subject: [PATCH 034/103] 8392945: [Valhalla PPC64, s390] Fix acmp profiling and fix and enable Valhalla tests Reviewed-by: rrich, dbriemann --- src/hotspot/cpu/ppc/templateTable_ppc_64.cpp | 2 +- .../valuetypes/GraphShouldBeSchedulable.java | 1 - .../valuetypes/RepairStackWithBigFrame.java | 1 - .../valhalla/valuetypes/TestAcmpFastPath.java | 14 -------------- .../valuetypes/TestAcmpFastPathPerf.java | 1 - .../valuetypes/TestArrayNullMarkers.java | 11 ----------- .../valhalla/valuetypes/TestArrays.java | 7 ------- .../valuetypes/TestBasicFunctionality.java | 7 ------- .../compiler/valhalla/valuetypes/TestC1.java | 1 - .../valuetypes/TestCallingConvention.java | 7 ------- .../valuetypes/TestCallingConventionC1.java | 1 - .../valuetypes/TestFieldNullMarkers.java | 14 -------------- .../valuetypes/TestGetfieldChains.java | 1 - .../valuetypes/TestHashcodeFastPath.java | 18 ------------------ .../valhalla/valuetypes/TestIntrinsics.java | 11 ++--------- .../valhalla/valuetypes/TestJNICalls.java | 7 ------- .../valhalla/valuetypes/TestLWorld.java | 7 ------- .../valuetypes/TestLWorldProfiling.java | 1 - .../TestLateInliningIncorrectArgs.java | 1 - .../TestLoadingDefaultRefinedArrayKlass.java | 1 - .../valhalla/valuetypes/TestMethodHandles.java | 7 ------- .../valuetypes/TestNullableArrays.java | 7 ------- .../valuetypes/TestNullableValueTypes.java | 7 ------- .../valuetypes/TestOnStackReplacement.java | 7 ------- .../valuetypes/TestOptimizePtrCompare.java | 1 - ...alarizedCCReceiverOnlyEntryAllocBuffer.java | 1 - .../valuetypes/TestUnloadedValueTypeField.java | 1 - .../valhalla/valuetypes/TestValueClasses.java | 7 ------- .../bootstrap/TestBootClassloader.java | 1 - 29 files changed, 3 insertions(+), 150 deletions(-) diff --git a/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp b/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp index c0b634c8186d..74f0d76504f0 100644 --- a/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp +++ b/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp @@ -1866,7 +1866,7 @@ void TemplateTable::if_acmp(Condition cc) { __ pop_ptr(Rfirst); - __ profile_acmp(Rsecond, Rfirst, R11_scratch1, R12_scratch2); + __ profile_acmp(Rfirst, Rsecond, R11_scratch1, R12_scratch2); const int is_value_type_mask = markWord::value_type_pattern; if (Arguments::is_valhalla_enabled()) { diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/GraphShouldBeSchedulable.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/GraphShouldBeSchedulable.java index 71c14e5b6b05..6116654a316c 100644 --- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/GraphShouldBeSchedulable.java +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/GraphShouldBeSchedulable.java @@ -33,7 +33,6 @@ * a load of the initial value of the array may find an object with an imprecise type (initVal1 in this case, that is * an Object), potentially leading to wrong alias indices. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") * @enablePreview * @modules java.base/jdk.internal.value * @run main/othervm -XX:+UnlockDiagnosticVMOptions diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/RepairStackWithBigFrame.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/RepairStackWithBigFrame.java index 000a2cb4e04d..525762c105cf 100644 --- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/RepairStackWithBigFrame.java +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/RepairStackWithBigFrame.java @@ -33,7 +33,6 @@ * increment and rfp at the same time, since it only has a 7 bit offset. * We use two ldr with 9-bit offsets instead. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.vm.annotation * @run main/othervm diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestAcmpFastPath.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestAcmpFastPath.java index f2a929fb76bc..33f988a3662d 100644 --- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestAcmpFastPath.java +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestAcmpFastPath.java @@ -25,7 +25,6 @@ * @test * @summary Test acmp fast path with value classes * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -36,7 +35,6 @@ * @test * @summary Test acmp fast path with value classes * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -47,7 +45,6 @@ * @test * @summary Test acmp fast path with value classes * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -58,7 +55,6 @@ * @test * @summary Test acmp fast path with value classes * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -69,7 +65,6 @@ * @test * @summary Test acmp fast path with value classes * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -80,7 +75,6 @@ * @test * @summary Test acmp fast path with value classes * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -91,7 +85,6 @@ * @test * @summary Test acmp fast path with value classes * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -102,7 +95,6 @@ * @test * @summary Test acmp fast path with value classes * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -113,7 +105,6 @@ * @test * @summary Test acmp fast path with value classes * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -124,7 +115,6 @@ * @test * @summary Test acmp fast path with value classes * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -135,7 +125,6 @@ * @test * @summary Test acmp fast path with value classes * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -146,7 +135,6 @@ * @test * @summary Test acmp fast path with value classes * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -157,7 +145,6 @@ * @test * @summary Test acmp fast path with value classes * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -168,7 +155,6 @@ * @test * @summary Test acmp fast path with value classes * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestAcmpFastPathPerf.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestAcmpFastPathPerf.java index 3bd539baf8b6..213cdcb50769 100644 --- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestAcmpFastPathPerf.java +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestAcmpFastPathPerf.java @@ -25,7 +25,6 @@ * @test * @summary Test acmp fast path with value classes * @requires vm.flagless - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @run main/othervm/timeout=30 -Xbatch * -XX:CompileCommand=CompileOnly,${test.main.class}::test diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestArrayNullMarkers.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestArrayNullMarkers.java index a0a5c115798f..4d0251b2b24a 100644 --- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestArrayNullMarkers.java +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestArrayNullMarkers.java @@ -34,7 +34,6 @@ * @test id=default * @summary Test support for null markers in (flat) arrays. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -48,7 +47,6 @@ * @test id=no-flattening * @summary Test support for null markers in (flat) arrays. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -64,7 +62,6 @@ * @test id=nAVF * @summary Test support for null markers in (flat) arrays. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -80,7 +77,6 @@ * @test id=AVF * @summary Test support for null markers in (flat) arrays. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -96,7 +92,6 @@ * @test id=AVF-nAVF * @summary Test support for null markers in (flat) arrays. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -112,7 +107,6 @@ * @test id=NVF * @summary Test support for null markers in (flat) arrays. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -128,7 +122,6 @@ * @test id=NVF-nAVF * @summary Test support for null markers in (flat) arrays. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -144,7 +137,6 @@ * @test id=NVF-AVF * @summary Test support for null markers in (flat) arrays. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -160,7 +152,6 @@ * @test id=all-flattening * @summary Test support for null markers in (flat) arrays. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -176,7 +167,6 @@ * @test id=all-flattening-di * @summary Test support for null markers in (flat) arrays. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -193,7 +183,6 @@ * @test id=all-flattening-restrict-profiling * @summary Test support for null markers in (flat) arrays. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestArrays.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestArrays.java index 116f5b0fc82c..a5ea1ba863ed 100644 --- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestArrays.java +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestArrays.java @@ -64,7 +64,6 @@ * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @run driver ${test.main.class} 0 */ @@ -76,7 +75,6 @@ * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @run driver ${test.main.class} 1 */ @@ -88,7 +86,6 @@ * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @run driver ${test.main.class} 2 */ @@ -100,7 +97,6 @@ * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @run driver ${test.main.class} 3 */ @@ -112,7 +108,6 @@ * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @run driver ${test.main.class} 4 */ @@ -124,7 +119,6 @@ * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @run driver ${test.main.class} 5 */ @@ -136,7 +130,6 @@ * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @run driver ${test.main.class} 6 */ diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestBasicFunctionality.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestBasicFunctionality.java index 32705a2b9600..0c8c449eaa07 100644 --- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestBasicFunctionality.java +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestBasicFunctionality.java @@ -49,7 +49,6 @@ * @key randomness * @bug 8327695 * @summary Test the basic value class implementation in C2. - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @library /test/lib / * @enablePreview * @modules java.base/jdk.internal.value @@ -62,7 +61,6 @@ * @key randomness * @bug 8327695 * @summary Test the basic value class implementation in C2. - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @library /test/lib / * @enablePreview * @modules java.base/jdk.internal.value @@ -75,7 +73,6 @@ * @key randomness * @bug 8327695 * @summary Test the basic value class implementation in C2. - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @library /test/lib / * @enablePreview * @modules java.base/jdk.internal.value @@ -88,7 +85,6 @@ * @key randomness * @bug 8327695 * @summary Test the basic value class implementation in C2. - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @library /test/lib / * @enablePreview * @modules java.base/jdk.internal.value @@ -101,7 +97,6 @@ * @key randomness * @bug 8327695 * @summary Test the basic value class implementation in C2. - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @library /test/lib / * @enablePreview * @modules java.base/jdk.internal.value @@ -114,7 +109,6 @@ * @key randomness * @bug 8327695 * @summary Test the basic value class implementation in C2. - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @library /test/lib / * @enablePreview * @modules java.base/jdk.internal.value @@ -127,7 +121,6 @@ * @key randomness * @bug 8327695 * @summary Test the basic value class implementation in C2. - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @library /test/lib / * @enablePreview * @modules java.base/jdk.internal.value diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestC1.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestC1.java index b490affcfea2..98fa9ac5b4f0 100644 --- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestC1.java +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestC1.java @@ -41,7 +41,6 @@ * @key randomness * @summary Various tests that are specific to C1. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestCallingConvention.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestCallingConvention.java index 3bbe3d65292b..655793c1ba6b 100644 --- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestCallingConvention.java +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestCallingConvention.java @@ -51,7 +51,6 @@ * @key randomness * @summary Test value class calling convention optimizations. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -63,7 +62,6 @@ * @key randomness * @summary Test value class calling convention optimizations. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -75,7 +73,6 @@ * @key randomness * @summary Test value class calling convention optimizations. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -87,7 +84,6 @@ * @key randomness * @summary Test value class calling convention optimizations. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -99,7 +95,6 @@ * @key randomness * @summary Test value class calling convention optimizations. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -111,7 +106,6 @@ * @key randomness * @summary Test value class calling convention optimizations. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -123,7 +117,6 @@ * @key randomness * @summary Test value class calling convention optimizations. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestCallingConventionC1.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestCallingConventionC1.java index 294ddaf3348a..f9e3b05d771d 100644 --- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestCallingConventionC1.java +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestCallingConventionC1.java @@ -38,7 +38,6 @@ * @key randomness * @summary Test calls from {C1} to {C2, Interpreter}, and vice versa. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestFieldNullMarkers.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestFieldNullMarkers.java index 061463eb8eb3..568712885606 100644 --- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestFieldNullMarkers.java +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestFieldNullMarkers.java @@ -34,7 +34,6 @@ * @key randomness * @summary Test support for null markers in flat fields. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @requires (vm.opt.PreloadClasses == null | vm.opt.PreloadClasses == "true") * @enablePreview * @modules java.base/jdk.internal.value @@ -50,7 +49,6 @@ * @key randomness * @summary Test support for null markers in flat fields. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @requires (vm.opt.PreloadClasses == null | vm.opt.PreloadClasses == "true") * @enablePreview * @modules java.base/jdk.internal.value @@ -70,7 +68,6 @@ * @key randomness * @summary Test support for null markers in flat fields. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @requires (vm.opt.PreloadClasses == null | vm.opt.PreloadClasses == "true") * @enablePreview * @modules java.base/jdk.internal.value @@ -90,7 +87,6 @@ * @key randomness * @summary Test support for null markers in flat fields. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @requires (vm.opt.PreloadClasses == null | vm.opt.PreloadClasses == "true") * @enablePreview * @modules java.base/jdk.internal.value @@ -110,7 +106,6 @@ * @key randomness * @summary Test support for null markers in flat fields. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @requires (vm.opt.PreloadClasses == null | vm.opt.PreloadClasses == "true") * @enablePreview * @modules java.base/jdk.internal.value @@ -130,7 +125,6 @@ * @key randomness * @summary Test support for null markers in flat fields. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @requires (vm.opt.PreloadClasses == null | vm.opt.PreloadClasses == "true") * @enablePreview * @modules java.base/jdk.internal.value @@ -150,7 +144,6 @@ * @key randomness * @summary Test support for null markers in flat fields. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @requires (vm.opt.PreloadClasses == null | vm.opt.PreloadClasses == "true") * @enablePreview * @modules java.base/jdk.internal.value @@ -170,7 +163,6 @@ * @key randomness * @summary Test support for null markers in flat fields. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @requires (vm.opt.PreloadClasses == null | vm.opt.PreloadClasses == "true") * @enablePreview * @modules java.base/jdk.internal.value @@ -190,7 +182,6 @@ * @key randomness * @summary Test support for null markers in flat fields. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @requires (vm.opt.PreloadClasses == null | vm.opt.PreloadClasses == "true") * @enablePreview * @modules java.base/jdk.internal.value @@ -210,7 +201,6 @@ * @key randomness * @summary Test support for null markers in flat fields. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @requires (vm.opt.PreloadClasses == null | vm.opt.PreloadClasses == "true") * @enablePreview * @modules java.base/jdk.internal.value @@ -231,7 +221,6 @@ * @key randomness * @summary Test support for null markers in flat fields. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @requires (vm.opt.PreloadClasses == null | vm.opt.PreloadClasses == "true") * @enablePreview * @modules java.base/jdk.internal.value @@ -252,7 +241,6 @@ * @key randomness * @summary Test support for null markers in flat fields. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @requires (vm.opt.PreloadClasses == null | vm.opt.PreloadClasses == "true") * @enablePreview * @modules java.base/jdk.internal.value @@ -273,7 +261,6 @@ * @key randomness * @summary Test support for null markers in flat fields. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @requires (vm.opt.PreloadClasses == null | vm.opt.PreloadClasses == "true") * @enablePreview * @modules java.base/jdk.internal.value @@ -294,7 +281,6 @@ * @key randomness * @summary Test support for null markers in flat fields. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @requires (vm.opt.PreloadClasses == null | vm.opt.PreloadClasses == "true") * @enablePreview * @modules java.base/jdk.internal.value diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestGetfieldChains.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestGetfieldChains.java index f48469b743e3..7b3b31dc87fc 100644 --- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestGetfieldChains.java +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestGetfieldChains.java @@ -39,7 +39,6 @@ * @key randomness * @summary Verify that chains of getfields on flat fields are correctly optimized. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestHashcodeFastPath.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestHashcodeFastPath.java index 7871b767560e..cbaec7c65b6a 100644 --- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestHashcodeFastPath.java +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestHashcodeFastPath.java @@ -25,7 +25,6 @@ * @test id=0-fast * @summary Test hashcode fast path with value classes * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -36,7 +35,6 @@ * @test id=1-fast * @summary Test hashcode fast path with value classes * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -47,7 +45,6 @@ * @test id=2-fast * @summary Test hashcode fast path with value classes * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -58,7 +55,6 @@ * @test id=3-fast * @summary Test hashcode fast path with value classes * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -69,7 +65,6 @@ * @test id=4-fast * @summary Test hashcode fast path with value classes * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -80,7 +75,6 @@ * @test id=5-fast * @summary Test hashcode fast path with value classes * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -91,7 +85,6 @@ * @test id=6-fast * @summary Test hashcode fast path with value classes * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -102,7 +95,6 @@ * @test id=0-nofast * @summary Test hashcode fast path with value classes * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -113,7 +105,6 @@ * @test id=1-nofast * @summary Test hashcode fast path with value classes * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -124,7 +115,6 @@ * @test id=2-nofast * @summary Test hashcode fast path with value classes * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -135,7 +125,6 @@ * @test id=3-nofast * @summary Test hashcode fast path with value classes * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -146,7 +135,6 @@ * @test id=4-nofast * @summary Test hashcode fast path with value classes * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -157,7 +145,6 @@ * @test id=5-nofast * @summary Test hashcode fast path with value classes * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -168,7 +155,6 @@ * @test id=6-nofast * @summary Test hashcode fast path with value classes * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -179,7 +165,6 @@ * @test id=0-fast-nointrinsics * @summary Test hashcode fast path with value classes * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -190,7 +175,6 @@ * @test id=0-nofast-nointrinsics * @summary Test hashcode fast path with value classes * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -201,7 +185,6 @@ * @test id=1-fast-nointrinsics * @summary Test hashcode fast path with value classes * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -212,7 +195,6 @@ * @test id=1-nofast-nointrinsics * @summary Test hashcode fast path with value classes * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestIntrinsics.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestIntrinsics.java index 55c202ce1210..ff0fb3d0d8d9 100644 --- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestIntrinsics.java +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestIntrinsics.java @@ -49,7 +49,6 @@ * @key randomness * @summary Test intrinsic support for value classes. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.misc * java.base/jdk.internal.value @@ -62,7 +61,6 @@ * @key randomness * @summary Test intrinsic support for value classes. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.misc * java.base/jdk.internal.value @@ -75,7 +73,6 @@ * @key randomness * @summary Test intrinsic support for value classes. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.misc * java.base/jdk.internal.value @@ -88,7 +85,6 @@ * @key randomness * @summary Test intrinsic support for value classes. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.misc * java.base/jdk.internal.value @@ -101,7 +97,6 @@ * @key randomness * @summary Test intrinsic support for value classes. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.misc * java.base/jdk.internal.value @@ -114,7 +109,6 @@ * @key randomness * @summary Test intrinsic support for value classes. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.misc * java.base/jdk.internal.value @@ -127,7 +121,6 @@ * @key randomness * @summary Test intrinsic support for value classes. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.misc * java.base/jdk.internal.value @@ -634,9 +627,9 @@ public void test29_verifier() { } } else { if (X_OFFSET < Y_OFFSET) { - Asserts.assertEQ(((int)res), v.x >>> 8); + Asserts.assertEQ(((int)res) >> 8, (v.x << 8) >> 8); } else { - Asserts.assertEQ(res, v.y >>> 8); + Asserts.assertEQ(res >> 8, (v.y << 8) >> 8); } } } diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestJNICalls.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestJNICalls.java index 7c69d9f7e799..0b1888438aa3 100644 --- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestJNICalls.java +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestJNICalls.java @@ -34,7 +34,6 @@ * @key randomness * @summary Test calling native methods with value class arguments from compiled code. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -46,7 +45,6 @@ * @key randomness * @summary Test calling native methods with value class arguments from compiled code. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -58,7 +56,6 @@ * @key randomness * @summary Test calling native methods with value class arguments from compiled code. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -70,7 +67,6 @@ * @key randomness * @summary Test calling native methods with value class arguments from compiled code. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -82,7 +78,6 @@ * @key randomness * @summary Test calling native methods with value class arguments from compiled code. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -94,7 +89,6 @@ * @key randomness * @summary Test calling native methods with value class arguments from compiled code. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -106,7 +100,6 @@ * @key randomness * @summary Test calling native methods with value class arguments from compiled code. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestLWorld.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestLWorld.java index b858dea5c58f..88ecba77713b 100644 --- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestLWorld.java +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestLWorld.java @@ -67,7 +67,6 @@ * @key randomness * @summary Test value types in LWorld. * @library /test/lib /test/jdk/java/lang/invoke/common / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @requires (vm.opt.PreloadClasses == null | vm.opt.PreloadClasses == "true") * @enablePreview * @modules java.base/jdk.internal.value @@ -81,7 +80,6 @@ * @key randomness * @summary Test value types in LWorld. * @library /test/lib /test/jdk/java/lang/invoke/common / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -94,7 +92,6 @@ * @key randomness * @summary Test value types in LWorld. * @library /test/lib /test/jdk/java/lang/invoke/common / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -107,7 +104,6 @@ * @key randomness * @summary Test value types in LWorld. * @library /test/lib /test/jdk/java/lang/invoke/common / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -120,7 +116,6 @@ * @key randomness * @summary Test value types in LWorld. * @library /test/lib /test/jdk/java/lang/invoke/common / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -133,7 +128,6 @@ * @key randomness * @summary Test value types in LWorld. * @library /test/lib /test/jdk/java/lang/invoke/common / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @requires (vm.opt.PreloadClasses == null | vm.opt.PreloadClasses == "true") * @enablePreview * @modules java.base/jdk.internal.value @@ -147,7 +141,6 @@ * @key randomness * @summary Test value types in LWorld. * @library /test/lib /test/jdk/java/lang/invoke/common / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @requires (vm.opt.PreloadClasses == null | vm.opt.PreloadClasses == "true") * @enablePreview * @modules java.base/jdk.internal.value diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestLWorldProfiling.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestLWorldProfiling.java index 0f23146e8b56..24e512976660 100644 --- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestLWorldProfiling.java +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestLWorldProfiling.java @@ -41,7 +41,6 @@ * @key randomness * @summary Test value class specific type profiling. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestLateInliningIncorrectArgs.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestLateInliningIncorrectArgs.java index 98ed34f972a3..fffd7b531026 100644 --- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestLateInliningIncorrectArgs.java +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestLateInliningIncorrectArgs.java @@ -24,7 +24,6 @@ /** * @test * @enablePreview - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @run main/othervm -XX:-BackgroundCompilation -XX:+IgnoreUnrecognizedVMOptions -XX:+AlwaysIncrementalInline ${test.main.class} */ diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestLoadingDefaultRefinedArrayKlass.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestLoadingDefaultRefinedArrayKlass.java index ec4e96997354..98894dc2272d 100644 --- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestLoadingDefaultRefinedArrayKlass.java +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestLoadingDefaultRefinedArrayKlass.java @@ -35,7 +35,6 @@ * @test * @summary Make sure that the correct default refined klass is loaded by intrinsics. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestMethodHandles.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestMethodHandles.java index 35b745a3e21d..fcdf771ae336 100644 --- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestMethodHandles.java +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestMethodHandles.java @@ -48,7 +48,6 @@ * @key randomness * @summary Test method handle support for value types * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @requires vm.opt.AbortVMOnCompilationFailure != true * @requires vm.compMode != "Xcomp" * @enablePreview @@ -62,7 +61,6 @@ * @key randomness * @summary Test method handle support for value types * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @requires vm.opt.AbortVMOnCompilationFailure != true * @requires vm.compMode != "Xcomp" * @enablePreview @@ -76,7 +74,6 @@ * @key randomness * @summary Test method handle support for value types * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @requires vm.opt.AbortVMOnCompilationFailure != true * @requires vm.compMode != "Xcomp" * @enablePreview @@ -90,7 +87,6 @@ * @key randomness * @summary Test method handle support for value types * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @requires vm.opt.AbortVMOnCompilationFailure != true * @requires vm.compMode != "Xcomp" * @enablePreview @@ -104,7 +100,6 @@ * @key randomness * @summary Test method handle support for value types * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @requires vm.opt.AbortVMOnCompilationFailure != true * @requires vm.compMode != "Xcomp" * @enablePreview @@ -118,7 +113,6 @@ * @key randomness * @summary Test method handle support for value types * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @requires vm.opt.AbortVMOnCompilationFailure != true * @requires vm.compMode != "Xcomp" * @enablePreview @@ -132,7 +126,6 @@ * @key randomness * @summary Test method handle support for value types * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @requires vm.opt.AbortVMOnCompilationFailure != true * @requires vm.compMode != "Xcomp" * @enablePreview diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestNullableArrays.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestNullableArrays.java index f7580f9c69bb..0e6e8de6b0b7 100644 --- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestNullableArrays.java +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestNullableArrays.java @@ -51,7 +51,6 @@ * @key randomness * @summary Test nullable value class arrays. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -63,7 +62,6 @@ * @key randomness * @summary Test nullable value class arrays. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -75,7 +73,6 @@ * @key randomness * @summary Test nullable value class arrays. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -87,7 +84,6 @@ * @key randomness * @summary Test nullable value class arrays. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -99,7 +95,6 @@ * @key randomness * @summary Test nullable value class arrays. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -111,7 +106,6 @@ * @key randomness * @summary Test nullable value class arrays. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -123,7 +117,6 @@ * @key randomness * @summary Test nullable value class arrays. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestNullableValueTypes.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestNullableValueTypes.java index 81140f8eee56..7948bac145e9 100644 --- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestNullableValueTypes.java +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestNullableValueTypes.java @@ -54,7 +54,6 @@ * @key randomness * @summary Test correct handling of nullable value classes. * @library /test/lib /test/jdk/java/lang/invoke/common / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @requires (vm.opt.PreloadClasses == null | vm.opt.PreloadClasses == "true") * @enablePreview * @modules java.base/jdk.internal.value @@ -68,7 +67,6 @@ * @key randomness * @summary Test correct handling of nullable value classes. * @library /test/lib /test/jdk/java/lang/invoke/common / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @requires (vm.opt.PreloadClasses == null | vm.opt.PreloadClasses == "true") * @enablePreview * @modules java.base/jdk.internal.value @@ -82,7 +80,6 @@ * @key randomness * @summary Test correct handling of nullable value classes. * @library /test/lib /test/jdk/java/lang/invoke/common / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @requires (vm.opt.PreloadClasses == null | vm.opt.PreloadClasses == "true") * @enablePreview * @modules java.base/jdk.internal.value @@ -96,7 +93,6 @@ * @key randomness * @summary Test correct handling of nullable value classes. * @library /test/lib /test/jdk/java/lang/invoke/common / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @requires (vm.opt.PreloadClasses == null | vm.opt.PreloadClasses == "true") * @enablePreview * @modules java.base/jdk.internal.value @@ -110,7 +106,6 @@ * @key randomness * @summary Test correct handling of nullable value classes. * @library /test/lib /test/jdk/java/lang/invoke/common / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @requires (vm.opt.PreloadClasses == null | vm.opt.PreloadClasses == "true") * @enablePreview * @modules java.base/jdk.internal.value @@ -124,7 +119,6 @@ * @key randomness * @summary Test correct handling of nullable value classes. * @library /test/lib /test/jdk/java/lang/invoke/common / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @requires (vm.opt.PreloadClasses == null | vm.opt.PreloadClasses == "true") * @enablePreview * @modules java.base/jdk.internal.value @@ -138,7 +132,6 @@ * @key randomness * @summary Test correct handling of nullable value classes. * @library /test/lib /test/jdk/java/lang/invoke/common / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @requires (vm.opt.PreloadClasses == null | vm.opt.PreloadClasses == "true") * @enablePreview * @modules java.base/jdk.internal.value diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestOnStackReplacement.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestOnStackReplacement.java index c02a3d4c9c9e..0e98e6993f5d 100644 --- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestOnStackReplacement.java +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestOnStackReplacement.java @@ -42,7 +42,6 @@ * @key randomness * @summary Test on stack replacement (OSR) with value classes. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -54,7 +53,6 @@ * @key randomness * @summary Test on stack replacement (OSR) with value classes. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -66,7 +64,6 @@ * @key randomness * @summary Test on stack replacement (OSR) with value classes. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -78,7 +75,6 @@ * @key randomness * @summary Test on stack replacement (OSR) with value classes. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -90,7 +86,6 @@ * @key randomness * @summary Test on stack replacement (OSR) with value classes. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -102,7 +97,6 @@ * @key randomness * @summary Test on stack replacement (OSR) with value classes. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -114,7 +108,6 @@ * @key randomness * @summary Test on stack replacement (OSR) with value classes. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestOptimizePtrCompare.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestOptimizePtrCompare.java index b4d71ded8e9e..1879b8ae16f5 100644 --- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestOptimizePtrCompare.java +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestOptimizePtrCompare.java @@ -27,7 +27,6 @@ * @summary [lworld] incorrect execution due to EA pointer comparison optimization at scalarized call * @library /test/lib / * @enablePreview - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") * @run driver ${test.main.class} */ diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestScalarizedCCReceiverOnlyEntryAllocBuffer.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestScalarizedCCReceiverOnlyEntryAllocBuffer.java index a535f8c5ad21..660674e60966 100644 --- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestScalarizedCCReceiverOnlyEntryAllocBuffer.java +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestScalarizedCCReceiverOnlyEntryAllocBuffer.java @@ -26,7 +26,6 @@ * @bug 8381563 * @summary [lworld] SharedRuntime::allocate_value_types hits "buffer not of expected class" assert * @enablePreview - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") * @run main/othervm -XX:-BackgroundCompilation * -XX:CompileOnly=compiler.valhalla.valuetypes.TestScalarizedCCReceiverOnlyEntryAllocBuffer::test1 * ${test.main.class} diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestUnloadedValueTypeField.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestUnloadedValueTypeField.java index 7522dd208cd0..d6063dd7591b 100644 --- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestUnloadedValueTypeField.java +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestUnloadedValueTypeField.java @@ -36,7 +36,6 @@ * @key randomness * @summary Test the handling of fields of unloaded value classes. * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestValueClasses.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestValueClasses.java index db75cac2322d..126611733c39 100644 --- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestValueClasses.java +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestValueClasses.java @@ -42,7 +42,6 @@ * @key randomness * @summary Test correct handling of value classes. * @library /test/lib /test/jdk/java/lang/invoke/common / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -54,7 +53,6 @@ * @key randomness * @summary Test correct handling of value classes. * @library /test/lib /test/jdk/java/lang/invoke/common / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -66,7 +64,6 @@ * @key randomness * @summary Test correct handling of value classes. * @library /test/lib /test/jdk/java/lang/invoke/common / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -78,7 +75,6 @@ * @key randomness * @summary Test correct handling of value classes. * @library /test/lib /test/jdk/java/lang/invoke/common / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -90,7 +86,6 @@ * @key randomness * @summary Test correct handling of value classes. * @library /test/lib /test/jdk/java/lang/invoke/common / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -102,7 +97,6 @@ * @key randomness * @summary Test correct handling of value classes. * @library /test/lib /test/jdk/java/lang/invoke/common / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation @@ -114,7 +108,6 @@ * @key randomness * @summary Test correct handling of value classes. * @library /test/lib /test/jdk/java/lang/invoke/common / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/bootstrap/TestBootClassloader.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/bootstrap/TestBootClassloader.java index c07737288ed0..93330a5f0b45 100644 --- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/bootstrap/TestBootClassloader.java +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/bootstrap/TestBootClassloader.java @@ -37,7 +37,6 @@ * @summary Test that field flattening works as expected if value classes of * holder and field were loaded by different class loaders (bootstrap + app). * @library /test/lib / - * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation From f6af1a5b924b66c5baf110f40c5b52517384a9f0 Mon Sep 17 00:00:00 2001 From: Prasanta Sadhukhan Date: Fri, 25 Sep 2026 08:57:06 +0000 Subject: [PATCH 035/103] 8169476: ExtendedKeyCodeTest: Wrong extended key code on Windows Reviewed-by: prr, jdv, kizune --- test/jdk/ProblemList.txt | 1 - .../ExtendedKeyCode/ExtendedKeyCodeTest.java | 35 +++++++++++++++---- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt index a6c413b612a1..460c8c1f8e9c 100644 --- a/test/jdk/ProblemList.txt +++ b/test/jdk/ProblemList.txt @@ -165,7 +165,6 @@ java/awt/FullScreen/BufferStrategyExceptionTest/BufferStrategyExceptionTest.java java/awt/Focus/8013611/JDK8013611.java 8175366 windows-all,macosx-all java/awt/Focus/6981400/Test1.java 8029675 windows-all,macosx-all java/awt/Focus/6981400/Test3.java 8173264 generic-all -java/awt/event/KeyEvent/ExtendedKeyCode/ExtendedKeyCodeTest.java 8169476 windows-all,macosx-all java/awt/event/KeyEvent/KeyChar/KeyCharTest.java 8169474,8224055 macosx-all,windows-all java/awt/event/KeyEvent/KeyTyped/CtrlASCII.java 8298910 linux-all diff --git a/test/jdk/java/awt/event/KeyEvent/ExtendedKeyCode/ExtendedKeyCodeTest.java b/test/jdk/java/awt/event/KeyEvent/ExtendedKeyCode/ExtendedKeyCodeTest.java index fae72f45bfab..e43929d43602 100644 --- a/test/jdk/java/awt/event/KeyEvent/ExtendedKeyCode/ExtendedKeyCodeTest.java +++ b/test/jdk/java/awt/event/KeyEvent/ExtendedKeyCode/ExtendedKeyCodeTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2026, 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 @@ -25,24 +25,28 @@ import java.awt.Robot; import java.awt.event.KeyEvent; import java.awt.event.KeyAdapter; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; /* * @test * @key headful * @bug 8007156 8025126 * @summary Tests that ExtendedKeyCode is set for key events - * @library /lib/client - * @build ExtendedRobot * @run main ExtendedKeyCodeTest */ public class ExtendedKeyCodeTest { private static volatile boolean setExtendedKeyCode = true; private static volatile int eventsCount = 0; + private static final CountDownLatch frameFocused = new CountDownLatch(1); public static void main(String[] args) throws Exception { - ExtendedRobot robot = new ExtendedRobot(); + Robot robot = new Robot(); robot.setAutoDelay(50); + robot.setAutoWaitForIdle(true); Frame frame = new Frame(); frame.setSize(300, 300); @@ -63,13 +67,22 @@ public void keyReleased(KeyEvent e) { == KeyEvent.getExtendedKeyCodeForChar(e.getKeyChar())); } }); + frame.addWindowFocusListener(new WindowAdapter() { + @Override + public void windowGainedFocus(WindowEvent e) { + frameFocused.countDown(); + } + }); frame.setVisible(true); - robot.waitForIdle(); + if (!frameFocused.await(5, TimeUnit.SECONDS)) { + throw new RuntimeException("Frame was not opened"); + } robot.keyPress(KeyEvent.VK_D); robot.keyRelease(KeyEvent.VK_D); robot.waitForIdle(); + robot.delay(100); frame.dispose(); @@ -89,13 +102,23 @@ public void keyPressed(KeyEvent e) { setExtendedKeyCode = e.getExtendedKeyCode() == KeyEvent.VK_LEFT; } }); + CountDownLatch secondFrameFocused = new CountDownLatch(1); + frame.addWindowFocusListener(new WindowAdapter() { + @Override + public void windowGainedFocus(WindowEvent e) { + secondFrameFocused.countDown(); + } + }); frame.setVisible(true); - robot.waitForIdle(); + if (!secondFrameFocused.await(5, TimeUnit.SECONDS)) { + throw new RuntimeException("Second frame was not opened"); + } robot.keyPress(KeyEvent.VK_LEFT); robot.keyRelease(KeyEvent.VK_LEFT); robot.waitForIdle(); + robot.delay(100); frame.dispose(); if (!setExtendedKeyCode) { From 14766469765199548e0ece003e0fbfabf5843e7e Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 25 Sep 2026 09:10:46 +0000 Subject: [PATCH 036/103] 8392739: JvmtiEnvBase::set_native_method_prefixes leaks one prefix string on its error paths Reviewed-by: dholmes, sspitsyn --- src/hotspot/share/prims/jvmtiEnvBase.cpp | 4 +- .../RejectedPrefixesNoLeak.java | 105 ++++++++++++++++++ .../libRejectedPrefixesNoLeak.cpp | 83 ++++++++++++++ 3 files changed, 190 insertions(+), 2 deletions(-) create mode 100644 test/hotspot/jtreg/serviceability/jvmti/SetNativeMethodPrefix/RejectedPrefixesNoLeak.java create mode 100644 test/hotspot/jtreg/serviceability/jvmti/SetNativeMethodPrefix/libRejectedPrefixesNoLeak.cpp diff --git a/src/hotspot/share/prims/jvmtiEnvBase.cpp b/src/hotspot/share/prims/jvmtiEnvBase.cpp index b431c8f56101..b50972020636 100644 --- a/src/hotspot/share/prims/jvmtiEnvBase.cpp +++ b/src/hotspot/share/prims/jvmtiEnvBase.cpp @@ -415,7 +415,7 @@ JvmtiEnvBase::set_native_method_prefixes(jint prefix_count, char** prefixes) { for (int i = 0; i < prefix_count; i++) { char* prefix = prefixes[i]; if (prefix == nullptr) { - for (int j = 0; j < (i-1); j++) { + for (int j = 0; j < i; j++) { os::free(new_prefixes[j]); } os::free(new_prefixes); @@ -423,7 +423,7 @@ JvmtiEnvBase::set_native_method_prefixes(jint prefix_count, char** prefixes) { } prefix = os::strdup(prefixes[i], mtServiceability); if (prefix == nullptr) { - for (int j = 0; j < (i-1); j++) { + for (int j = 0; j < i; j++) { os::free(new_prefixes[j]); } os::free(new_prefixes); diff --git a/test/hotspot/jtreg/serviceability/jvmti/SetNativeMethodPrefix/RejectedPrefixesNoLeak.java b/test/hotspot/jtreg/serviceability/jvmti/SetNativeMethodPrefix/RejectedPrefixesNoLeak.java new file mode 100644 index 000000000000..9fa444916405 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/SetNativeMethodPrefix/RejectedPrefixesNoLeak.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2026, 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 8392739 + * @summary A rejected prefixes array must not leave the prefixes that were + * already copied behind. + * @requires vm.jvmti + * @library /test/lib + * @run main/othervm/native -agentlib:RejectedPrefixesNoLeak + * -XX:NativeMemoryTracking=summary RejectedPrefixesNoLeak + */ + +/* + * Regression test for the off-by-one cleanup loops in + * JvmtiEnvBase::set_native_method_prefixes() (jvmtiEnvBase.cpp). + * + * The function copies the caller's prefixes one by one with os::strdup(). When + * it finds a null entry it frees what it has copied so far and returns + * JVMTI_ERROR_NULL_POINTER, but the cleanup loop ran to i - 1 while the array + * was already populated up to i, so the most recently copied prefix stayed + * allocated. Every rejected call leaked one string. + * + * The agent here calls SetNativeMethodPrefixes() with { , nullptr }, + * so the first prefix is copied and the second one is rejected. Repeating that + * with a large prefix makes the leak big enough to read off NMT's Serviceability + * accounting, which is where os::strdup() charges the copies: an unfixed VM + * grows by iterations * prefix length, a fixed one does not grow at all. + */ + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import jdk.test.lib.dcmd.PidJcmdExecutor; + +public class RejectedPrefixesNoLeak { + + private static final int JVMTI_ERROR_NULL_POINTER = 100; + + // One megabyte per copied prefix, so the leak stands out against the noise + // of whatever else is charged to Serviceability while the test runs. + private static final int PREFIX_LENGTH = 1024 * 1024; + private static final int ITERATIONS = 64; + + // An unfixed VM leaks 64MB here, a fixed one stays flat, so anything above + // a few megabytes of growth is the bug and not measurement noise. + private static final long GROWTH_LIMIT_KB = 16 * 1024; + + private static final Pattern SERVICEABILITY_COMMITTED = + Pattern.compile("Serviceability \\(reserved=\\d+KB, committed=(\\d+)KB\\)"); + + // Calls SetNativeMethodPrefixes() with a rejected array the given number of + // times and returns the error code of the last call. + private static native int rejectPrefixes(int iterations, int prefixLength); + + private static long serviceabilityCommittedKB() { + String summary = new PidJcmdExecutor().execute("VM.native_memory summary scale=KB").getStdout(); + Matcher matcher = SERVICEABILITY_COMMITTED.matcher(summary); + if (!matcher.find()) { + throw new RuntimeException("No Serviceability entry in the NMT summary:\n" + summary); + } + return Long.parseLong(matcher.group(1)); + } + + public static void main(String[] args) { + long before = serviceabilityCommittedKB(); + + int error = rejectPrefixes(ITERATIONS, PREFIX_LENGTH); + if (error != JVMTI_ERROR_NULL_POINTER) { + throw new RuntimeException("SetNativeMethodPrefixes returned " + error + + ", expected JVMTI_ERROR_NULL_POINTER"); + } + + long growth = serviceabilityCommittedKB() - before; + System.out.println("Serviceability committed grew by " + growth + "KB"); + + if (growth > GROWTH_LIMIT_KB) { + throw new RuntimeException(ITERATIONS + " rejected SetNativeMethodPrefixes calls with a " + + (PREFIX_LENGTH / 1024) + "KB prefix grew Serviceability by " + growth + + "KB, expected at most " + GROWTH_LIMIT_KB + "KB"); + } + System.out.println("Test PASSED"); + } +} diff --git a/test/hotspot/jtreg/serviceability/jvmti/SetNativeMethodPrefix/libRejectedPrefixesNoLeak.cpp b/test/hotspot/jtreg/serviceability/jvmti/SetNativeMethodPrefix/libRejectedPrefixesNoLeak.cpp new file mode 100644 index 000000000000..b198521c0444 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/SetNativeMethodPrefix/libRejectedPrefixesNoLeak.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2026, 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. + */ + +#include +#include +#include "jvmti.h" +#include "jvmti_common.hpp" + +extern "C" { + +static jvmtiEnv* jvmti = nullptr; + +// Implements RejectedPrefixesNoLeak.rejectPrefixes(). Each call passes a +// two-element array whose second element is null, so the VM copies the first +// prefix, rejects the second one and cleans up. Returns the error code of the +// last call, which the caller checks. +JNIEXPORT jint JNICALL +Java_RejectedPrefixesNoLeak_rejectPrefixes(JNIEnv* jni, jclass cls, jint iterations, jint prefix_length) { + char* prefix = (char*)malloc((size_t)prefix_length + 1); + if (prefix == nullptr) { + LOG("Could not allocate a %d byte prefix\n", (int)prefix_length); + return JVMTI_ERROR_OUT_OF_MEMORY; + } + memset(prefix, 'a', (size_t)prefix_length); + prefix[prefix_length] = '\0'; + + // The null entry is what makes the VM reject the array, after it has already + // copied the prefix in front of it. + char* prefixes[2] = { prefix, nullptr }; + + jvmtiError err = JVMTI_ERROR_NONE; + for (jint i = 0; i < iterations; i++) { + err = jvmti->SetNativeMethodPrefixes(2, prefixes); + if (err != JVMTI_ERROR_NULL_POINTER) { + LOG("SetNativeMethodPrefixes returned %d on iteration %d\n", err, (int)i); + break; + } + } + + free(prefix); + return (jint)err; +} + +JNIEXPORT jint JNICALL +Agent_OnLoad(JavaVM* jvm, char* options, void* reserved) { + if (jvm->GetEnv((void**)&jvmti, JVMTI_VERSION) != JNI_OK || jvmti == nullptr) { + LOG("Could not initialize JVMTI env\n"); + return JNI_ERR; + } + + jvmtiCapabilities caps; + memset(&caps, 0, sizeof(caps)); + caps.can_set_native_method_prefix = 1; + + jvmtiError err = jvmti->AddCapabilities(&caps); + if (err != JVMTI_ERROR_NONE) { + LOG("AddCapabilities failed with %d\n", err); + return JNI_ERR; + } + return JNI_OK; +} + +} // extern "C" From 907e43b11c24e2befe015ceb30e2777993c3960f Mon Sep 17 00:00:00 2001 From: Frederic Parain Date: Fri, 25 Sep 2026 13:42:06 +0000 Subject: [PATCH 037/103] 8392214: [Valhalla] C2 dereferences unloaded array metadata after OOME Reviewed-by: vlivanov, thartmann --- src/hotspot/share/opto/graphKit.cpp | 6 ++ .../TestUnloadedArrayKlassAfterOOME.java | 98 +++++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100644 test/hotspot/jtreg/compiler/valhalla/valuetypes/TestUnloadedArrayKlassAfterOOME.java diff --git a/src/hotspot/share/opto/graphKit.cpp b/src/hotspot/share/opto/graphKit.cpp index 5d954f6a2b43..fd9f7090afd0 100644 --- a/src/hotspot/share/opto/graphKit.cpp +++ b/src/hotspot/share/opto/graphKit.cpp @@ -1974,6 +1974,9 @@ Node* GraphKit::cast_to_flat_array(Node* array, ciValueKlass* elem_vk) { } ciArrayKlass* array_klass = ciObjArrayKlass::make(elem_vk, false); + if (!array_klass->is_loaded()) { + return top(); + } const TypeAryPtr* arytype = TypeOopPtr::make_from_klass(array_klass)->isa_aryptr(); arytype = arytype->cast_to_flat(true)->cast_to_null_free(is_null_free); return _gvn.transform(new CheckCastPPNode(control(), array, arytype, ConstraintCastNode::DependencyType::NonFloatingNarrowing)); @@ -1982,6 +1985,9 @@ Node* GraphKit::cast_to_flat_array(Node* array, ciValueKlass* elem_vk) { Node* GraphKit::cast_to_flat_array_exact(Node* array, ciValueKlass* elem_vk, bool is_null_free, bool is_atomic) { assert(is_null_free || is_atomic, "nullable arrays must be atomic"); ciArrayKlass* array_klass = ciObjArrayKlass::make(elem_vk, true, is_null_free, is_atomic); + if (!array_klass->is_loaded()) { + return top(); + } const TypeAryPtr* arytype = TypeOopPtr::make_from_klass(array_klass)->isa_aryptr(); assert(arytype->klass_is_exact(), "inconsistency"); assert(arytype->is_flat(), "inconsistency"); diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestUnloadedArrayKlassAfterOOME.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestUnloadedArrayKlassAfterOOME.java new file mode 100644 index 000000000000..889f40897ed1 --- /dev/null +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestUnloadedArrayKlassAfterOOME.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2026, 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 + * @summary Checks flat array code with unloaded array klass because of OOME + * @bug 8392214 + * @library /test/lib + * @requires vm.compiler2.enabled + * @requires vm.opt.AbortVMOnCompilationFailure != true + * @enablePreview + * @run driver ${test.main.class} + */ + +package compiler.valhalla.valuetypes; + +import java.util.ArrayList; +import java.util.List; + +import jdk.test.lib.Asserts; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +public class TestUnloadedArrayKlassAfterOOME { + + public static void main(String[] args) throws Exception { + List options = new ArrayList(); + options.add("-XX:+PrintCompilation"); + options.add("-Xcomp"); + options.add("-XX:-TieredCompilation"); + options.add("--enable-preview"); + options.add("-Xmx12m"); + options.add("-XX:CompileCommand=compileonly," + getTestClass() + "::test"); + options.add(getTestClass()); + + OutputAnalyzer oa = ProcessTools.executeTestJava(options); + + oa.shouldHaveExitValue(0); + // The OOM condition cannot be reliably reproduced with all GCs, + // so no strong assertion, just some information printed in the log + if (oa.contains("TestMain::test (5 bytes) COMPILE SKIPPED: out of memory (retry at different tier)")) { + IO.println("Compilation skipped because of OOM, good!"); + } else { + IO.println("No skipped compilation detected, test unconclusive"); + } + } + + // Test class that is invoked by the sub process + public static String getTestClass() { + return TestMain.class.getName(); + } + + public static class TestMain { + static value class Value { + int value = 0; + } + + static Object[] keepAlive; + + static void test(Object[] array, Value value) { + array[0] = value; + } + + public static void main(String[] args) { + Object[] array = new Object[1]; + Value value = new Value(); + try { + // Fill up the heap + while (true) { + keepAlive = new Object[] { keepAlive, new byte[1024] }; + } + } catch (OutOfMemoryError expected) { + // Ignore + } + test(array, value); + } + } +} From d63696bdd02413aacca0437e320258376a2d2511 Mon Sep 17 00:00:00 2001 From: Serguei Spitsyn Date: Fri, 25 Sep 2026 16:59:38 +0000 Subject: [PATCH 038/103] 8390472: Capability can_support_virtual_threads must be rejected with -XX:-DoJVMTIVirtualThreadTransitions Reviewed-by: cjplummer, lmesnik --- src/hotspot/share/prims/jvmtiManageCapabilities.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/hotspot/share/prims/jvmtiManageCapabilities.cpp b/src/hotspot/share/prims/jvmtiManageCapabilities.cpp index e54099d6ad75..1703cd889a82 100644 --- a/src/hotspot/share/prims/jvmtiManageCapabilities.cpp +++ b/src/hotspot/share/prims/jvmtiManageCapabilities.cpp @@ -103,7 +103,7 @@ jvmtiCapabilities JvmtiManageCapabilities::init_always_capabilities() { jc.can_generate_object_free_events = 1; jc.can_generate_resource_exhaustion_heap_events = 1; jc.can_generate_resource_exhaustion_threads_events = 1; - jc.can_support_virtual_threads = 1; + jc.can_support_virtual_threads = DoJVMTIVirtualThreadTransitions; jc.can_support_value_objects = 1; return jc; } @@ -276,7 +276,8 @@ jvmtiError JvmtiManageCapabilities::add_capabilities(const jvmtiCapabilities *cu exclude(&always_solo_remaining_capabilities, desired, &always_solo_remaining_capabilities); exclude(&onload_solo_remaining_capabilities, desired, &onload_solo_remaining_capabilities); - if (desired->can_support_virtual_threads != 0 && current->can_support_virtual_threads == 0) { + if (DoJVMTIVirtualThreadTransitions && + desired->can_support_virtual_threads != 0 && current->can_support_virtual_threads == 0) { _can_support_virtual_threads_count++; } if (desired->can_support_value_objects != 0 && current->can_support_value_objects == 0) { From 3de6ff76dc94464aa35983671f5d01ea11990e06 Mon Sep 17 00:00:00 2001 From: Phil Race Date: Fri, 25 Sep 2026 17:34:36 +0000 Subject: [PATCH 039/103] 8390320: SampleModel.setDataElements fails to throw NPE for null arguments with zero dimensions Reviewed-by: kizune, psadhukhan --- .../java/awt/image/BandedSampleModel.java | 26 +++- .../java/awt/image/ComponentSampleModel.java | 24 +++ .../classes/java/awt/image/SampleModel.java | 77 ++++++++++ .../java/awt/image/SampleModelNullTests.java | 140 ++++++++++++++++++ 4 files changed, 266 insertions(+), 1 deletion(-) create mode 100644 test/jdk/java/awt/image/SampleModelNullTests.java diff --git a/src/java.desktop/share/classes/java/awt/image/BandedSampleModel.java b/src/java.desktop/share/classes/java/awt/image/BandedSampleModel.java index 155e0c722575..7ee1c6fd92a0 100644 --- a/src/java.desktop/share/classes/java/awt/image/BandedSampleModel.java +++ b/src/java.desktop/share/classes/java/awt/image/BandedSampleModel.java @@ -441,6 +441,11 @@ public int[] getPixel(int x, int y, int[] iArray, DataBuffer data) { */ public int[] getPixels(int x, int y, int w, int h, int[] iArray, DataBuffer data) { + + if (data == null) { + throw new NullPointerException("DataBuffer must not be null"); + } + int x1 = x + w; int y1 = y + h; @@ -543,6 +548,10 @@ public int[] getSamples(int x, int y, int w, int h, int b, throw new ArrayIndexOutOfBoundsException ("Coordinate out of bounds!"); } + if (data == null) { + throw new NullPointerException("DataBuffer must not be null"); + } + int[] samples; if (iArray != null) { samples = iArray; @@ -689,7 +698,7 @@ public void setPixel(int x, int y, int[] iArray, DataBuffer data) { * {@inheritDoc} * @throws NullPointerException if {@code iArray} or {@code data} is {@code null}. * @throws ArrayIndexOutOfBoundsException if the coordinates are - * not in bounds, or {@code w} or {@code h} is negative. + * not in bounds, or {@code w} or {@code h} is negative, * or if {@code iArray} is too small to hold the input. */ public void setPixels(int x, int y, int w, int h, @@ -704,6 +713,13 @@ public void setPixels(int x, int y, int w, int h, ("Coordinate out of bounds!"); } + if (iArray == null) { + throw new NullPointerException("Array must not be null"); + } + if (data == null) { + throw new NullPointerException("DataBuffer must not be null"); + } + for (int k = 0; k < numBands; k++) { int lineOffset = y*scanlineStride + x + bandOffsets[k]; int srcOffset = k; @@ -787,6 +803,14 @@ public void setSamples(int x, int y, int w, int h, int b, throw new ArrayIndexOutOfBoundsException ("Coordinate out of bounds!"); } + + if (iArray == null) { + throw new NullPointerException("Array must not be null"); + } + if (data == null) { + throw new NullPointerException("DataBuffer must not be null"); + } + int lineOffset = y*scanlineStride + x + bandOffsets[b]; int srcOffset = 0; int bank = bankIndices[b]; diff --git a/src/java.desktop/share/classes/java/awt/image/ComponentSampleModel.java b/src/java.desktop/share/classes/java/awt/image/ComponentSampleModel.java index ea0837400c54..a2ebf612e226 100644 --- a/src/java.desktop/share/classes/java/awt/image/ComponentSampleModel.java +++ b/src/java.desktop/share/classes/java/awt/image/ComponentSampleModel.java @@ -758,6 +758,11 @@ public int[] getPixels(int x, int y, int w, int h, throw new ArrayIndexOutOfBoundsException ("Coordinate out of bounds!"); } + + if (data == null) { + throw new NullPointerException("DataBuffer must not be null"); + } + int[] pixels; if (iArray != null) { pixels = iArray; @@ -850,6 +855,10 @@ public int[] getSamples(int x, int y, int w, int h, int b, throw new ArrayIndexOutOfBoundsException ("Coordinate out of bounds!"); } + if (data == null) { + throw new NullPointerException("DataBuffer must not be null"); + } + int[] samples; if (iArray != null) { samples = iArray; @@ -1014,6 +1023,13 @@ public void setPixels(int x, int y, int w, int h, ("Coordinate out of bounds!"); } + if (iArray == null) { + throw new NullPointerException("Array must not be null"); + } + if (data == null) { + throw new NullPointerException("DataBuffer must not be null"); + } + int lineOffset = y*scanlineStride + x*pixelStride; int srcOffset = 0; @@ -1099,6 +1115,14 @@ public void setSamples(int x, int y, int w, int h, int b, throw new ArrayIndexOutOfBoundsException ("Coordinate out of bounds!"); } + + if (iArray == null) { + throw new NullPointerException("Array must not be null"); + } + if (data == null) { + throw new NullPointerException("DataBuffer must not be null"); + } + int lineOffset = y*scanlineStride + x*pixelStride + bandOffsets[b]; int srcOffset = 0; diff --git a/src/java.desktop/share/classes/java/awt/image/SampleModel.java b/src/java.desktop/share/classes/java/awt/image/SampleModel.java index 0c4c81d5fd11..073d2bdd32fa 100644 --- a/src/java.desktop/share/classes/java/awt/image/SampleModel.java +++ b/src/java.desktop/share/classes/java/awt/image/SampleModel.java @@ -369,6 +369,10 @@ public Object getDataElements(int x, int y, int w, int h, throw new ArrayIndexOutOfBoundsException("Invalid coordinates."); } + if (data == null) { + throw new NullPointerException("DataBuffer must not be null"); + } + switch(type) { case DataBuffer.TYPE_BYTE: @@ -599,6 +603,13 @@ public void setDataElements(int x, int y, int w, int h, throw new ArrayIndexOutOfBoundsException("Invalid coordinates."); } + if (obj == null) { + throw new NullPointerException("Array must not be null"); + } + if (data == null) { + throw new NullPointerException("DataBuffer must not be null"); + } + switch(type) { case DataBuffer.TYPE_BYTE: @@ -780,6 +791,10 @@ public int[] getPixels(int x, int y, int w, int h, throw new ArrayIndexOutOfBoundsException("Invalid coordinates."); } + if (data == null) { + throw new NullPointerException("DataBuffer must not be null"); + } + if (iArray != null) pixels = iArray; else @@ -829,6 +844,10 @@ public float[] getPixels(int x, int y, int w, int h, throw new ArrayIndexOutOfBoundsException("Invalid coordinates."); } + if (data == null) { + throw new NullPointerException("DataBuffer must not be null"); + } + if (fArray != null) pixels = fArray; else @@ -877,6 +896,10 @@ public double[] getPixels(int x, int y, int w, int h, throw new ArrayIndexOutOfBoundsException("Invalid coordinates."); } + if (data == null) { + throw new NullPointerException("DataBuffer must not be null"); + } + if (dArray != null) pixels = dArray; else @@ -993,6 +1016,10 @@ public int[] getSamples(int x, int y, int w, int h, int b, throw new ArrayIndexOutOfBoundsException("Invalid coordinates."); } + if (data == null) { + throw new NullPointerException("DataBuffer must not be null"); + } + if (iArray != null) pixels = iArray; else @@ -1042,6 +1069,10 @@ public float[] getSamples(int x, int y, int w, int h, throw new ArrayIndexOutOfBoundsException("Invalid coordinates"); } + if (data == null) { + throw new NullPointerException("DataBuffer must not be null"); + } + if (fArray != null) pixels = fArray; else @@ -1091,6 +1122,10 @@ public double[] getSamples(int x, int y, int w, int h, throw new ArrayIndexOutOfBoundsException("Invalid coordinates"); } + if (data == null) { + throw new NullPointerException("DataBuffer must not be null"); + } + if (dArray != null) pixels = dArray; else @@ -1194,6 +1229,13 @@ public void setPixels(int x, int y, int w, int h, throw new ArrayIndexOutOfBoundsException("Invalid coordinates."); } + if (iArray == null) { + throw new NullPointerException("Array must not be null"); + } + if (data == null) { + throw new NullPointerException("DataBuffer must not be null"); + } + for (int i=y; i model.setDataElements(0, 0, w, h, null_binput, null)); + expectNPE(test, true, () -> model.setDataElements(0, 0, w, h, null_binput, data)); + expectNPE(test, true, () -> model.setDataElements(0, 0, w, h, binput, null)); + expectNPE(test, false, () -> model.setDataElements(0, 0, w, h, binput, data)); + expectNPE(test, true, () -> model.getDataElements(0, 0, w, h, binput, null)); + expectNPE(test, false, () -> model.getDataElements(0, 0, w, h, null_binput, data)); + + test = "Int samples for " + model; + expectNPE(test, true, () -> model.setSamples(0, 0, w, h, 0, null_iinput, null)); + expectNPE(test, true, () -> model.setSamples(0, 0, w, h, 0, null_iinput, data)); + expectNPE(test, true, () -> model.setSamples(0, 0, w, h, 0, iinput, null)); + expectNPE(test, false, () -> model.setSamples(0, 0, w, h, 0, iinput, data)); + expectNPE(test, true, () -> model.getSamples(0, 0, w, h, 0, iinput, null)); + expectNPE(test, false, () -> model.getSamples(0, 0, w, h, 0, null_iinput, data)); + + test = "Float samples for " + model; + expectNPE(test, true, () -> model.setSamples(0, 0, w, h, 0, null_finput, null)); + expectNPE(test, true, () -> model.setSamples(0, 0, w, h, 0, null_finput, data)); + expectNPE(test, true, () -> model.setSamples(0, 0, w, h, 0, finput, null)); + expectNPE(test, false, () -> model.setSamples(0, 0, w, h, 0, finput, data)); + expectNPE(test, true, () -> model.getSamples(0, 0, w, h, 0, finput, null)); + expectNPE(test, false, () -> model.getSamples(0, 0, w, h, 0, null_finput, data)); + + test = "Double samples for " + model; + expectNPE(test, true, () -> model.setSamples(0, 0, w, h, 0, null_dinput, null)); + expectNPE(test, true, () -> model.setSamples(0, 0, w, h, 0, null_dinput, data)); + expectNPE(test, true, () -> model.setSamples(0, 0, w, h, 0, dinput, null)); + expectNPE(test, false, () -> model.setSamples(0, 0, w, h, 0, dinput, data)); + expectNPE(test, true, () -> model.getSamples(0, 0, w, h, 0, dinput, null)); + expectNPE(test, false, () -> model.getSamples(0, 0, w, h, 0, null_dinput, data)); + + test = "Int pixels for " + model; + expectNPE(test, true, () -> model.setPixels(0, 0, w, h, null_iinput, null)); + expectNPE(test, true, () -> model.setPixels(0, 0, w, h, null_iinput, data)); + expectNPE(test, true, () -> model.setPixels(0, 0, w, h, iinput, null)); + expectNPE(test, false, () -> model.setPixels(0, 0, w, h, iinput, data)); + expectNPE(test, true, () -> model.getPixels(0, 0, w, h, iinput, null)); + expectNPE(test, false, () -> model.getPixels(0, 0, w, h, null_iinput, data)); + + test = "Float pixels for " + model; + expectNPE(test, true, () -> model.setPixels(0, 0, w, h, null_finput, null)); + expectNPE(test, true, () -> model.setPixels(0, 0, w, h, null_finput, data)); + expectNPE(test, true, () -> model.setPixels(0, 0, w, h, finput, null)); + expectNPE(test, false, () -> model.setPixels(0, 0, w, h, finput, data)); + expectNPE(test, true, () -> model.getPixels(0, 0, w, h, finput, null)); + expectNPE(test, false, () -> model.getPixels(0, 0, w, h, null_finput, data)); + + test = "Double pixels for " + model; + expectNPE(test, true, () -> model.setPixels(0, 0, w, h, null_dinput, null)); + expectNPE(test, true, () -> model.setPixels(0, 0, w, h, null_dinput, data)); + expectNPE(test, true, () -> model.setPixels(0, 0, w, h, dinput, null)); + expectNPE(test, false, () -> model.setPixels(0, 0, w, h, dinput, data)); + expectNPE(test, true, () -> model.getPixels(0, 0, w, h, dinput, null)); + expectNPE(test, false, () -> model.getPixels(0, 0, w, h, null_dinput, data)); + + } + } +} From bf2e6ce918aa525b8a114b82dc056dd3c33703ce Mon Sep 17 00:00:00 2001 From: Justin Lu Date: Fri, 25 Sep 2026 19:18:29 +0000 Subject: [PATCH 040/103] 8392400: CompactNumberFormat.format(BigDecimal) formats inconsistently compared to format(double) Reviewed-by: naoto --- .../java/text/CompactNumberFormat.java | 54 ++++++++++++------- .../CompactNumberFormat/TestCNFRounding.java | 42 ++++++++++++++- 2 files changed, 75 insertions(+), 21 deletions(-) diff --git a/src/java.base/share/classes/java/text/CompactNumberFormat.java b/src/java.base/share/classes/java/text/CompactNumberFormat.java index 74c1eabe9705..1df338bcad72 100644 --- a/src/java.base/share/classes/java/text/CompactNumberFormat.java +++ b/src/java.base/share/classes/java/text/CompactNumberFormat.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -657,8 +657,8 @@ private StringBuf format(double number, StringBuf result, int compactDataIndex = selectCompactPattern((long) roundedNumber); if (compactDataIndex != -1) { long divisor = (Long) divisors.get(compactDataIndex); - double val = getNumberValue(number, divisor); - if (checkIncrement(val, compactDataIndex, divisor)) { + double val = getRoundedQuotient(number, divisor, isNegative); + if (shouldPromotePattern(val, compactDataIndex, divisor)) { divisor = (Long) divisors.get(++compactDataIndex); } roundedNumber = roundedNumber / divisor; @@ -739,8 +739,8 @@ private StringBuf format(long number, StringBuf result, FieldDelegate delegate) int compactDataIndex = selectCompactPattern(number); if (compactDataIndex != -1) { long divisor = (Long) divisors.get(compactDataIndex); - double val = getNumberValue(number, divisor); - if (checkIncrement(val, compactDataIndex, divisor)) { + double val = getRoundedQuotient(number, divisor, isNegative); + if (shouldPromotePattern(val, compactDataIndex, divisor)) { divisor = (Long) divisors.get(++compactDataIndex); } var noFraction = number % divisor == 0; @@ -835,11 +835,15 @@ private StringBuf format(BigDecimal number, StringBuf result, if (compactDataIndex != -1) { Number divisor = divisors.get(compactDataIndex); - double val = getNumberValue(number.doubleValue(), divisor.doubleValue()); - if (checkIncrement(val, compactDataIndex, divisor.doubleValue())) { + double val = getRoundedQuotient(number.doubleValue(), divisor.doubleValue(), isNegative); + if (shouldPromotePattern(val, compactDataIndex, divisor.doubleValue())) { divisor = divisors.get(++compactDataIndex); } - number = number.divide(new BigDecimal(divisor.toString()), getRoundingMode()); + + // Perform this division exactly, so that the quotient is not inaccurately rounded. + // For example, 7,800 / 1,000 should become 7.8 and not 8. It is safe to perform exact + // division because the divisor is a power of ten, thus the result is always terminating. + number = number.divide(new BigDecimal(divisor.toString())); decimalFormat.setDigitList(number, isNegative, getMaximumFractionDigits()); val = decimalFormat.getDigitList().getDouble(); String prefix = getAffix(false, true, isNegative, compactDataIndex, val); @@ -906,8 +910,8 @@ private StringBuf format(BigInteger number, StringBuf result, int compactDataIndex = selectCompactPattern(number); if (compactDataIndex != -1) { Number divisor = divisors.get(compactDataIndex); - double val = getNumberValue(number.doubleValue(), divisor.doubleValue()); - if (checkIncrement(val, compactDataIndex, divisor.doubleValue())) { + double val = getRoundedQuotient(number.doubleValue(), divisor.doubleValue(), isNegative); + if (shouldPromotePattern(val, compactDataIndex, divisor.doubleValue())) { divisor = divisors.get(++compactDataIndex); } var noFraction = number.mod(new BigInteger(divisor.toString())) @@ -917,10 +921,10 @@ private StringBuf format(BigInteger number, StringBuf result, decimalFormat.setDigitList(number, isNegative, 0); } else { // To avoid truncation of fractional part store the value in - // BigDecimal and follow BigDecimal path instead of - // BigInteger path + // BigDecimal and follow BigDecimal path instead of BigInteger path. + // This division can be performed exactly, because the divisor is a power of ten. BigDecimal nDecimal = new BigDecimal(number) - .divide(new BigDecimal(divisor.toString()), getRoundingMode()); + .divide(new BigDecimal(divisor.toString())); decimalFormat.setDigitList(nDecimal, isNegative, getMaximumFractionDigits()); } val = decimalFormat.getDigitList().getDouble(); @@ -2544,15 +2548,27 @@ Patterns expandAffix() { } } - private double getNumberValue(double number, double divisor) { - var num = BigDecimal.valueOf(number) - .divide(BigDecimal.valueOf(divisor), roundingMode); - return getMaximumFractionDigits() > 0 ? num.doubleValue() : num.intValue(); + /** + * Returns a double which indicates if promotion of the compact pattern should occur. + * The returned value is the result of dividing the number by the divisor associated with + * the initial compact pattern. The quotient is rounded to maximum fraction digits, + * and is fed to shouldPromotePattern to determine if the pattern needs to be promoted. + */ + private double getRoundedQuotient(double number, double divisor, boolean isNegative) { + // Division should be aware of the maximum fraction digits permitted. + // For example, with HALF_UP and 2 maximum fraction digits: + // - 999_951 / 1000 -> 999.95 -> stay at K + // - 999_999 / 1000 -> 1000.0 -> promote K to M + // Ensure that sign is taken into account, otherwise rounding under CEILING/FLOOR + // would be incorrect when the dividend is negative. + BigDecimal signedDividend = isNegative ? BigDecimal.valueOf(-number) : BigDecimal.valueOf(number); + var signedQuotient = signedDividend.divide(BigDecimal.valueOf(divisor), getMaximumFractionDigits(), roundingMode); + return signedQuotient.abs().doubleValue(); } // Checks whether the val is incremented by the BigDecimal division in - // getNumberValue(), and affects the compact number index. - private boolean checkIncrement(double val, int index, double divisor) { + // getRoundedQuotient, and affects the compact number index. + private boolean shouldPromotePattern(double val, int index, double divisor) { if (index < compactPatterns.length - 1 && !"".equals(compactPatterns[index])) { // ignore empty pattern var nextDiv = divisors.get(index + 1).doubleValue(); diff --git a/test/jdk/java/text/Format/CompactNumberFormat/TestCNFRounding.java b/test/jdk/java/text/Format/CompactNumberFormat/TestCNFRounding.java index 0ba9587a3443..473f393c50a4 100644 --- a/test/jdk/java/text/Format/CompactNumberFormat/TestCNFRounding.java +++ b/test/jdk/java/text/Format/CompactNumberFormat/TestCNFRounding.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -23,7 +23,7 @@ /* * @test - * @bug 8177552 + * @bug 8177552 8392400 * @summary Checks the rounding of formatted number in compact number formatting * @run junit/othervm TestCNFRounding */ @@ -31,8 +31,12 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.FieldSource; import org.junit.jupiter.params.provider.MethodSource; +import java.math.BigDecimal; +import java.math.BigInteger; import java.math.RoundingMode; import java.text.NumberFormat; import java.util.List; @@ -53,6 +57,28 @@ public class TestCNFRounding { RoundingMode.CEILING, RoundingMode.FLOOR); + private static final List BIG_NUMBER_ROUNDING_ARGS = List.of( + // Basic BigDecimal input + Arguments.of(BigDecimal.valueOf(21_534_567.20), "21.53M", 2, RoundingMode.HALF_UP), + Arguments.of(BigDecimal.valueOf(-21_534_567.20), "-21.53M", 2, RoundingMode.HALF_UP), + // RoundingMode UP/DOWN + Arguments.of(BigDecimal.valueOf(21_534_567.20), "21.54M", 2, RoundingMode.UP), + Arguments.of(BigDecimal.valueOf(21_534_567.20), "21.53M", 2, RoundingMode.DOWN), + // Values that border between two patterns depending on maximum fraction digits allowed + Arguments.of(BigDecimal.valueOf(999_951), "999.95K", 2, RoundingMode.HALF_UP), + Arguments.of(BigDecimal.valueOf(-999_951), "-999.95K", 2, RoundingMode.HALF_UP), + Arguments.of(BigDecimal.valueOf(999_951), "1M", 1, RoundingMode.HALF_UP), + Arguments.of(BigDecimal.valueOf(-999_951), "-1M", 1, RoundingMode.HALF_UP), + // Negative CEILING/FLOOR values + Arguments.of(BigDecimal.valueOf(-999_999), "-999.99K", 2, RoundingMode.CEILING), + Arguments.of(BigDecimal.valueOf(-999_999), "-1M", 2, RoundingMode.FLOOR), + // BigInteger path that forces BigDecimal division. + // Need to supply a BI whose underlying value exceeds what 64 bit long supports and whose + // division result would produce a fraction. LONG.MAX/MIN_VALUE +- 1 works. + Arguments.of(new BigInteger("9223372036854775808"), "9223372.04T", 2, RoundingMode.HALF_UP), + Arguments.of(new BigInteger("-9223372036854775809"), "-9223372.04T", 2, RoundingMode.HALF_UP) + ); + Object[][] roundingData() { return new Object[][]{ // Number, half_even, half_up, half_down, up, down, ceiling, floor @@ -165,6 +191,18 @@ void testRounding2Fract(Object number, String[] expected) { } } + // 8392400 exposes that the minimum fractional digits is applied to the result of + // the division operation which is calculated when applying the compact pattern. + // This causes premature rounding, and produces an incorrect value. + @ParameterizedTest + @FieldSource("BIG_NUMBER_ROUNDING_ARGS") + void testBigNumberRounding(Number num, String expected, int maxFrac, RoundingMode rm) { + var fmt = NumberFormat.getCompactNumberInstance(Locale.US, NumberFormat.Style.SHORT); + fmt.setMaximumFractionDigits(maxFrac); + fmt.setRoundingMode(rm); + assertEquals(expected, fmt.format(num)); + } + private void testRoundingMode(Object number, String expected, int fraction, RoundingMode rounding) { NumberFormat fmt = NumberFormat From 30ec3785d8181eb36593ab0871dcf1707af3cae3 Mon Sep 17 00:00:00 2001 From: Wei He Date: Sat, 26 Sep 2026 09:54:08 +0000 Subject: [PATCH 041/103] 8392016: RISC-V: C2: Add Zawrs-assisted retry for contended monitor acquisition Reviewed-by: gli, fyang, ayang --- src/hotspot/cpu/riscv/assembler_riscv.hpp | 1 + .../cpu/riscv/c2_MacroAssembler_riscv.cpp | 29 +++++++++++++++++-- src/hotspot/cpu/riscv/globals_riscv.hpp | 1 + src/hotspot/cpu/riscv/vm_version_riscv.hpp | 3 ++ .../os_cpu/linux_riscv/riscv_hwprobe.cpp | 6 ++++ .../linux_riscv/vm_version_linux_riscv.cpp | 3 +- 6 files changed, 40 insertions(+), 3 deletions(-) diff --git a/src/hotspot/cpu/riscv/assembler_riscv.hpp b/src/hotspot/cpu/riscv/assembler_riscv.hpp index 422519bcbc7e..3483fc42861f 100644 --- a/src/hotspot/cpu/riscv/assembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/assembler_riscv.hpp @@ -1113,6 +1113,7 @@ class Assembler : public AbstractAssembler { INSN(ecall, 0b1110011, 0b000, 0b000000000000); INSN(_ebreak, 0b1110011, 0b000, 0b000000000001); + INSN(wrs_sto, 0b1110011, 0b000, 0b000000011101); #undef INSN diff --git a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp index df70834f3acb..440ad6f54839 100644 --- a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp @@ -209,8 +209,33 @@ void C2_MacroAssembler::fast_lock(Register obj, Register box, /*acquire*/ Assembler::aq, /*release*/ Assembler::relaxed, /*result*/ tmp3_owner); beqz(tmp3_owner, monitor_locked); - // Check if recursive. - bne(tmp3_owner, tid, slow_path); + // Normally, a contended monitor enters the runtime slow path here. + // If `Zawrs` is enabled, wait briefly for the current owner to release + // the monitor and retry the fast-path acquisition once, which can + // potentially avoid a runtime call if the owner releases the monitor soon. + if (UseZawrs) { + Label recursive; + beq(tmp3_owner, tid, recursive); + // Defer non-thread owner markers to the runtime rather than waiting on them. + mv(t0, (uint64_t)ThreadIdentifier::initial()); + bltu(tmp3_owner, t0, slow_path); + + Label retry; + // Reload the owner to establish a reservation, so WRS.STO can + // observe an update to it. + lr_d(tmp3_owner, tmp2_owner_addr, Assembler::relaxed); + beqz(tmp3_owner, retry); + bltu(tmp3_owner, t0, slow_path); + wrs_sto(); + bind(retry); + cmpxchg(/*addr*/ tmp2_owner_addr, /*expected*/ zr, /*new*/ tid, Assembler::int64, + /*acquire*/ Assembler::aq, /*release*/ Assembler::relaxed, /*result*/ tmp3_owner); + beqz(tmp3_owner, monitor_locked); + bne(tmp3_owner, tid, slow_path); + bind(recursive); + } else { + bne(tmp3_owner, tid, slow_path); + } // Recursive. increment(recursions_address, 1, tmp2, tmp3); diff --git a/src/hotspot/cpu/riscv/globals_riscv.hpp b/src/hotspot/cpu/riscv/globals_riscv.hpp index c0063a075798..cb2be5987623 100644 --- a/src/hotspot/cpu/riscv/globals_riscv.hpp +++ b/src/hotspot/cpu/riscv/globals_riscv.hpp @@ -113,6 +113,7 @@ define_pd_global(bool, ValueTypeReturnedAsFields, false); product(bool, UseZacas, false, EXPERIMENTAL, "Use Zacas instructions") \ product(bool, UseZabha, false, EXPERIMENTAL, "Use Zabha instructions") \ product(bool, UseZalasr, false, EXPERIMENTAL, "Use Zalasr instructions") \ + product(bool, UseZawrs, false, EXPERIMENTAL, "Use Zawrs instructions") \ product(bool, UseZcb, false, DIAGNOSTIC, "Use Zcb instructions") \ product(bool, UseZic64b, false, EXPERIMENTAL, "Use Zic64b instructions") \ product(bool, UseZicbom, false, EXPERIMENTAL, "Use Zicbom instructions") \ diff --git a/src/hotspot/cpu/riscv/vm_version_riscv.hpp b/src/hotspot/cpu/riscv/vm_version_riscv.hpp index a64e5010e10f..5c6e1b30943d 100644 --- a/src/hotspot/cpu/riscv/vm_version_riscv.hpp +++ b/src/hotspot/cpu/riscv/vm_version_riscv.hpp @@ -255,6 +255,8 @@ class VM_Version : public Abstract_VM_Version { decl(Zabha , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZabha)) \ /* Load-acquire and store-release instructions */ \ decl(Zalasr , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZalasr)) \ + /* Wait-on-Reservation-Set instructions */ \ + decl(Zawrs , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZawrs)) \ /* Zba Address generation instructions */ \ decl(Zba , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZba)) \ /* Zbb Basic bit-manipulation */ \ @@ -453,6 +455,7 @@ class VM_Version : public Abstract_VM_Version { #define RV_USE_RVA23U64 \ RV_ENABLE_EXTENSION(UseRVC) \ RV_ENABLE_EXTENSION(UseRVV) \ + RV_ENABLE_EXTENSION(UseZawrs) \ RV_ENABLE_EXTENSION(UseZba) \ RV_ENABLE_EXTENSION(UseZbb) \ RV_ENABLE_EXTENSION(UseZbs) \ diff --git a/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp b/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp index 7201d9adade4..36b029d45769 100644 --- a/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp +++ b/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp @@ -81,6 +81,7 @@ #define RISCV_HWPROBE_EXT_ZACAS (1ULL << 34) #define RISCV_HWPROBE_EXT_ZICOND (1ULL << 35) #define RISCV_HWPROBE_EXT_ZCB (1ULL << 44) +#define RISCV_HWPROBE_EXT_ZAWRS (1ULL << 48) #define RISCV_HWPROBE_EXT_ZABHA (1ULL << 58) #define RISCV_HWPROBE_EXT_ZALASR (1ULL << 59) @@ -274,6 +275,11 @@ void RiscvHwprobe::add_features_from_query_result() { if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZVKG)) { VM_Version::ext_Zvkg.enable_feature(); } +#ifndef PRODUCT + if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZAWRS)) { + VM_Version::ext_Zawrs.enable_feature(); + } +#endif // ====== non-extensions ====== // diff --git a/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp b/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp index d7f477a98c97..bace57b9d32e 100644 --- a/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp +++ b/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp @@ -341,8 +341,9 @@ void VM_Version::xuantie_features() { } ext_v.enable_feature(); - ext_Zacas.enable_feature(); ext_Zabha.enable_feature(); + ext_Zacas.enable_feature(); + ext_Zawrs.enable_feature(); ext_Zba.enable_feature(); ext_Zbb.enable_feature(); ext_Zbc.enable_feature(); From 1587398892ce98284c9c994b14696a96cd43e1ed Mon Sep 17 00:00:00 2001 From: Ozan Cetin Date: Sat, 26 Sep 2026 14:24:16 +0000 Subject: [PATCH 042/103] 8389895: RISC-V: Fuse LoadN with DecodeN to eliminate redundant register moves Reviewed-by: fyang, gcao --- src/hotspot/cpu/riscv/riscv.ad | 22 ++++++- .../c2/riscv64/TestLoadN2PUnscaled.java | 66 +++++++++++++++++++ .../compiler/lib/ir_framework/IRNode.java | 5 ++ 3 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/c2/riscv64/TestLoadN2PUnscaled.java diff --git a/src/hotspot/cpu/riscv/riscv.ad b/src/hotspot/cpu/riscv/riscv.ad index 33fe9bf5102e..74f0e3687053 100644 --- a/src/hotspot/cpu/riscv/riscv.ad +++ b/src/hotspot/cpu/riscv/riscv.ad @@ -5090,6 +5090,24 @@ instruct loadN_volatile(iRegNNoSp dst, indirect mem) ins_pipe(pipe_serial); %} +// Fuse DecodeN and LoadN: when compressed oops are zero-based and unscaled, +// decoding is a no-op zero-extension, which lwu already performs. +instruct loadN2P_unscaled(iRegPNoSp dst, memory mem) +%{ + predicate(CompressedOops::shift() == 0 && n->in(1)->as_Load()->is_unordered() && + n->in(1)->as_Load()->barrier_data() == 0); + match(Set dst (DecodeN (LoadN mem))); + + ins_cost(LOAD_COST); + format %{ "lwu $dst, $mem\t# compressed ptr decode (zero-based unscaled)" %} + + ins_encode %{ + __ lwu(as_Register($dst$$reg), Address(as_Register($mem$$base), $mem$$disp)); + %} + + ins_pipe(iload_reg_mem); +%} + // Load Klass Pointer instruct loadKlass(iRegPNoSp dst, memory mem) %{ @@ -9651,7 +9669,7 @@ instruct decodeHeapOop(iRegPNoSp dst, iRegN src) %{ n->bottom_type()->is_ptr()->ptr() != TypePtr::Constant); match(Set dst (DecodeN src)); - ins_cost(0); + ins_cost(ALU_COST); format %{ "decode_heap_oop $dst, $src\t#@decodeHeapOop" %} ins_encode %{ Register s = $src$$Register; @@ -9666,7 +9684,7 @@ instruct decodeHeapOop_not_null(iRegPNoSp dst, iRegN src) %{ n->bottom_type()->is_ptr()->ptr() == TypePtr::Constant); match(Set dst (DecodeN src)); - ins_cost(0); + ins_cost(ALU_COST); format %{ "decode_heap_oop_not_null $dst, $src\t#@decodeHeapOop_not_null" %} ins_encode %{ Register s = $src$$Register; diff --git a/test/hotspot/jtreg/compiler/c2/riscv64/TestLoadN2PUnscaled.java b/test/hotspot/jtreg/compiler/c2/riscv64/TestLoadN2PUnscaled.java new file mode 100644 index 000000000000..07939e9e75a0 --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/riscv64/TestLoadN2PUnscaled.java @@ -0,0 +1,66 @@ +/* + * Copyright Amazon.com Inc. 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 8389895 + * @summary LoadN followed by DecodeN is matched into a single lwu when + * compressed oops are unscaled. + * @library /test/lib / + * @requires os.arch == "riscv64" & vm.compiler2.enabled + * @run driver ${test.main.class} + */ + +package compiler.c2.riscv64; + +import compiler.lib.ir_framework.*; + +public class TestLoadN2PUnscaled { + + static class Link { + Link next; + } + + Link link = new Link(); + + public static void main(String[] args) { + TestFramework framework = new TestFramework(); + framework.addScenarios( + // A small heap is placed below 4GB, so compressed oops are + // unscaled and decoding is a no-op zero-extension. + new Scenario(0, "-XX:+UseCompressedOops", "-Xmx128m"), + // A heap that ends above 4GB needs a shift to decode, so the + // load and the decode must not be fused. + new Scenario(1, "-XX:+UseCompressedOops", "-Xmx5g")); + framework.start(); + } + + @Test + @IR(counts = {IRNode.RISCV_LOAD_N2P_UNSCALED, "1"}, + applyIf = {"MaxHeapSize", "< 1073741824"}) + @IR(failOn = IRNode.RISCV_LOAD_N2P_UNSCALED, + applyIf = {"MaxHeapSize", "> 4294967296"}) + public Link test() { + return link; + } +} diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java index bab9b7594897..c0bb586f16ef 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java @@ -2482,6 +2482,11 @@ public static void anyStoreOfNodes(String irNodePlaceholder, String fieldHolder) machOnlyNameRegex(RISCV_VAND_NOTL_VX, "vand_notL_vx"); } + public static final String RISCV_LOAD_N2P_UNSCALED = PREFIX + "RISCV_LOAD_N2P_UNSCALED" + POSTFIX; + static { + machOnlyNameRegex(RISCV_LOAD_N2P_UNSCALED, "loadN2P_unscaled"); + } + public static final String RISCV_VAND_NOTI_VX_MASKED = PREFIX + "RISCV_VAND_NOTI_VX_MASKED" + POSTFIX; static { machOnlyNameRegex(RISCV_VAND_NOTI_VX_MASKED, "vand_notI_vx_masked"); From d244fceff3e48fae405e4da1d055db55344c7c7a Mon Sep 17 00:00:00 2001 From: David Holmes Date: Sun, 27 Sep 2026 22:27:59 +0000 Subject: [PATCH 043/103] 8361926: Thread dump has incorrect locking information when waiting on a monitor Reviewed-by: alanb, sspitsyn --- src/hotspot/share/services/threadService.cpp | 41 +++++++++++++---- .../HotSpotDiagnosticMXBean/DumpThreads.java | 46 +++++++++++-------- 2 files changed, 60 insertions(+), 27 deletions(-) diff --git a/src/hotspot/share/services/threadService.cpp b/src/hotspot/share/services/threadService.cpp index 860b7c49dd98..e0c53bc3a9fa 100644 --- a/src/hotspot/share/services/threadService.cpp +++ b/src/hotspot/share/services/threadService.cpp @@ -1210,19 +1210,31 @@ class GetThreadSnapshotHandshakeClosure: public HandshakeClosure { void detect_locks(javaVFrame* jvf, int depth) { Thread* current = Thread::current(); + ObjectMonitor* waiting_monitor = _java_thread == nullptr ? + java_lang_VirtualThread::current_waiting_monitor(_thread_h()) : + jvf->thread()->current_waiting_monitor(); + + // Note we may have a waiting_monitor for internal waits i.e. class initialization waiting, and we + // don't want to report that as-if Object::wait was called. So we check the frame information directly. if (depth == 0 && _blocker.is_empty()) { // If this is the first frame and it is java.lang.Object.wait(...) // then print out the receiver. if (jvf->method()->name() == vmSymbols::wait_name() && jvf->method()->method_holder()->name() == vmSymbols::java_lang_Object()) { OopHandle lock_object; - StackValueCollection* locs = jvf->locals(); - if (!locs->is_empty()) { - StackValue* sv = locs->at(0); - if (sv->type() == T_OBJECT) { - Handle o = locs->at(0)->get_obj(); - lock_object = OopHandle(oop_storage(), o()); + if (waiting_monitor == nullptr) { + // We have started the native part of wait0() but have not yet set the current + // waiting monitor, so we need to extract the receiver directly from the frame. + StackValueCollection* locs = jvf->locals(); + if (!locs->is_empty()) { + StackValue* sv = locs->at(0); + if (sv->type() == T_OBJECT) { + Handle o = locs->at(0)->get_obj(); + lock_object = OopHandle(oop_storage(), o()); + } } + } else { + lock_object = OopHandle(oop_storage(), waiting_monitor->object()); } _blocker = Blocker(Blocker::WAITING_ON, lock_object); } @@ -1249,7 +1261,7 @@ class GetThreadSnapshotHandshakeClosure: public HandshakeClosure { // the monitor is associated with an object, i.e., it is locked if (depth == 0 && _blocker.is_empty()) { - ObjectMonitor* pending_moninor = java_lang_VirtualThread::is_instance(_thread_h()) + ObjectMonitor* pending_monitor = java_lang_VirtualThread::is_instance(_thread_h()) ? java_lang_VirtualThread::current_pending_monitor(_thread_h()) : jvf->thread()->current_pending_monitor(); @@ -1261,7 +1273,7 @@ class GetThreadSnapshotHandshakeClosure: public HandshakeClosure { if (// if the monitor is null we must be in the process of locking mon == nullptr || // we have marked ourself as pending on this monitor - mon == pending_moninor || + mon == pending_monitor || // we are not the owner of this monitor (_java_thread != nullptr && !mon->is_entered(_java_thread))) { _blocker = Blocker(Blocker::WAITING_TO_LOCK, OopHandle(oop_storage(), monitor->owner())); @@ -1269,7 +1281,18 @@ class GetThreadSnapshotHandshakeClosure: public HandshakeClosure { } } } - _locks->push(OwnedLock(depth, OwnedLock::LOCKED, OopHandle(oop_storage(), monitor->owner()))); + // Don't report the monitor as owned by this thread if it is doing wait() and has released it. + // Check the actual owning thread as we can dump the stack before the actual release happens. + // If waiting_monitor is null then we definitely haven't released it. If there is no java_thread + // then we have an unmounted vthread but it could still be the owner. + if (_blocker._type == Blocker::WAITING_ON && _blocker._obj.resolve() == monitor->owner() && + waiting_monitor != nullptr && + (_java_thread != nullptr ? !waiting_monitor->is_entered(_java_thread) + : waiting_monitor->owner() != ObjectMonitor::owner_id_from(_thread_h()))) { + // skip + } else { + _locks->push(OwnedLock(depth, OwnedLock::LOCKED, OopHandle(oop_storage(), monitor->owner()))); + } } } } diff --git a/test/jdk/com/sun/management/HotSpotDiagnosticMXBean/DumpThreads.java b/test/jdk/com/sun/management/HotSpotDiagnosticMXBean/DumpThreads.java index 3878513d3f2e..4206d2f8ebe8 100644 --- a/test/jdk/com/sun/management/HotSpotDiagnosticMXBean/DumpThreads.java +++ b/test/jdk/com/sun/management/HotSpotDiagnosticMXBean/DumpThreads.java @@ -334,28 +334,38 @@ void testWaitingThread(ThreadFactory factory, boolean pinned) throws Exception { await(thread, Thread.State.WAITING); long tid = thread.threadId(); - // thread dump in plain text should include thread - List lines = dumpThreadsToPlainText(); - ThreadFields fields = findThread(tid, lines); - assertNotNull(fields, "thread not found"); - assertEquals("WAITING", fields.state()); + // Must take the lock ourselves to ensure the wait() has actually + // released it. + List lines; + ThreadDump.ThreadInfo ti; + synchronized(lock) { + // thread dump in plain text should include thread + lines = dumpThreadsToPlainText(); + ThreadFields fields = findThread(tid, lines); + assertNotNull(fields, "thread not found"); + assertEquals("WAITING", fields.state()); - // thread dump in JSON format should include thread in root container - ThreadDump threadDump = dumpThreadsToJson(); - ThreadDump.ThreadInfo ti = threadDump.rootThreadContainer() - .findThread(thread.threadId()) - .orElse(null); - assertNotNull(ti, "thread not found"); - assertEquals(ti.isVirtual(), thread.isVirtual()); - assertEquals("WAITING", ti.state()); - if (pinned) { - long carrierTid = ti.carrier().orElse(-1L); - assertNotEquals(-1L, carrierTid, "carrier not found"); - assertForkJoinWorkerThread(carrierTid); + // thread dump in JSON format should include thread in root container + ThreadDump threadDump = dumpThreadsToJson(); + ti = threadDump.rootThreadContainer() + .findThread(thread.threadId()) + .orElse(null); + assertNotNull(ti, "thread not found"); + assertEquals(ti.isVirtual(), thread.isVirtual()); + assertEquals("WAITING", ti.state()); + assertFalse(ti.ownedMonitors().values().stream() + .flatMap(List::stream) + .anyMatch(lockAsString::equals), + "Waiting thread should not own the monitor"); + if (pinned) { + long carrierTid = ti.carrier().orElse(-1L); + assertNotEquals(-1L, carrierTid, "carrier not found"); + assertForkJoinWorkerThread(carrierTid); + } } // Compiled native frames have no locals. If Object.wait0 has been compiled - // then we don't have the object that the thread is waiting on + // then we may not have the object that the thread is waiting on. Method wait0 = Object.class.getDeclaredMethod("wait0", long.class); boolean expectWaitingOn = !WhiteBox.getWhiteBox().isMethodCompiled(wait0); if (expectWaitingOn) { From dd7c9c17570f2217e9b28de60de7b26e0587c5b4 Mon Sep 17 00:00:00 2001 From: Yasumasa Suenaga Date: Sun, 27 Sep 2026 23:09:21 +0000 Subject: [PATCH 044/103] 8392120: Add a warning facility for SA Reviewed-by: dholmes, cjplummer --- .../linux/native/libsaproc/aarch64Dwarf.cpp | 6 +++--- .../linux/native/libsaproc/dwarf.cpp | 10 +++++----- .../linux/native/libsaproc/libproc_impl.c | 10 +++++++++- .../linux/native/libsaproc/libproc_impl.h | 1 + .../linux/native/libsaproc/ps_proc.c | 14 +++++++------- .../macosx/native/libsaproc/libproc_impl.c | 12 ++++++++++-- .../macosx/native/libsaproc/libproc_impl.h | 3 ++- .../macosx/native/libsaproc/ps_core.c | 4 ++-- 8 files changed, 39 insertions(+), 21 deletions(-) diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/aarch64Dwarf.cpp b/src/jdk.hotspot.agent/linux/native/libsaproc/aarch64Dwarf.cpp index e357dffddc2a..c0f1ebeff2d2 100644 --- a/src/jdk.hotspot.agent/linux/native/libsaproc/aarch64Dwarf.cpp +++ b/src/jdk.hotspot.agent/linux/native/libsaproc/aarch64Dwarf.cpp @@ -34,7 +34,7 @@ bool AARCH64DwarfParser::process_arch_specific_dwarf_instructions(const unsigned } else if (_sign_state == RA_SIGNED_SP) { _sign_state = RA_NOT_SIGNED; } else { - print_error("DWARF: DW_CFA_AARCH64_negate_ra_state: illegal state (%d)\n", _sign_state); + print_warning("DWARF: DW_CFA_AARCH64_negate_ra_state: illegal state (%d)\n", _sign_state); return false; } break; @@ -44,7 +44,7 @@ bool AARCH64DwarfParser::process_arch_specific_dwarf_instructions(const unsigned } else if (_sign_state == RA_SIGNED_SP_PC) { _sign_state = RA_NOT_SIGNED; } else { - print_error("DWARF: DW_CFA_AARCH64_negate_ra_state_with_pc: illegal state (%d)\n", _sign_state); + print_warning("DWARF: DW_CFA_AARCH64_negate_ra_state_with_pc: illegal state (%d)\n", _sign_state); return false; } break; @@ -64,7 +64,7 @@ void AARCH64DwarfParser::remember_arch_specific_state() { void AARCH64DwarfParser::restore_arch_specific_state() { if (remember_state.empty()) { - print_error("DWARF Error: DW_CFA_restore_state for AArch64 is empty.\n"); + print_warning("DWARF: DW_CFA_restore_state for AArch64 is empty.\n"); return; } _sign_state = remember_state.top(); diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf.cpp b/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf.cpp index 751cb8872c7c..ab1a031bbe23 100644 --- a/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf.cpp +++ b/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf.cpp @@ -115,11 +115,11 @@ bool DwarfParser::process_cie(unsigned char *start_of_entry, uint32_t id) { if (*augmentation_string == 'R') { _fde_ptr_encoding = *_buf++; } else if (*augmentation_string == 'P') { - print_debug("DWARF Warning: Ignore augmentation: P\n"); + print_warning("DWARF: Ignore augmentation: P\n"); unsigned char enc = *_buf++; // first argument (encoding) get_decoded_value(enc); // skip second argument (personality routine handler) } else if (*augmentation_string == 'L') { - print_debug("DWARF Warning: Ignore augmentation: L\n"); + print_warning("DWARF: Ignore augmentation: L\n"); _buf++; // skip 1 arguments } augmentation_string++; @@ -225,7 +225,7 @@ bool DwarfParser::parse_dwarf_instructions(uintptr_t begin, uintptr_t pc, const break; case 0x0b: // DW_CFA_restore_state if (remember_state.empty()) { - print_debug("DWARF Error: DW_CFA_restore_state with empty stack.\n"); + print_warning("DWARF: DW_CFA_restore_state with empty stack.\n"); return false; } _state = remember_state.top(); @@ -233,7 +233,7 @@ bool DwarfParser::parse_dwarf_instructions(uintptr_t begin, uintptr_t pc, const restore_arch_specific_state(); break; case 0x0f: // DW_CFA_def_cfa_expression - print_debug("DWARF: DW_CFA_def_cfa_expression is not yet supported.\n"); + print_warning("DWARF: DW_CFA_def_cfa_expression is not yet supported.\n"); return false; case 0xc0: {// DW_CFA_restore enum DWARF_Register reg = static_cast(opa); @@ -242,7 +242,7 @@ bool DwarfParser::parse_dwarf_instructions(uintptr_t begin, uintptr_t pc, const } default: if (!process_arch_specific_dwarf_instructions(op)) { - print_error("DWARF: Unknown opcode: 0x%x\n", op); + print_warning("DWARF: Unknown opcode: 0x%x\n", op); return false; } } diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.c b/src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.c index cc8f956bb0c1..06bc4153aba3 100644 --- a/src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.c +++ b/src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.c @@ -101,10 +101,18 @@ void print_debug(const char* format,...) { } } +void print_warning(const char* format,...) { + va_list alist; + va_start(alist, format); + fputs("libsaproc WARNING: ", stdout); + vfprintf(stdout, format, alist); + va_end(alist); +} + void print_error(const char* format,...) { va_list alist; va_start(alist, format); - fputs("ERROR: ", stderr); + fputs("libsaproc ERROR: ", stderr); vfprintf(stderr, format, alist); va_end(alist); } diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.h b/src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.h index 23d7daea9729..ee75fa07a5cd 100644 --- a/src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.h +++ b/src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.h @@ -127,6 +127,7 @@ extern "C" { int pathmap_open(const char* name); void print_debug(const char* format,...); +void print_warning(const char* format,...); void print_error(const char* format,...); bool is_debug(); diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/ps_proc.c b/src/jdk.hotspot.agent/linux/native/libsaproc/ps_proc.c index 4ab1302c9d22..399a71e70425 100644 --- a/src/jdk.hotspot.agent/linux/native/libsaproc/ps_proc.c +++ b/src/jdk.hotspot.agent/linux/native/libsaproc/ps_proc.c @@ -88,7 +88,7 @@ static bool process_read_data(struct ps_prochandle* ph, uintptr_t addr, char *bu errno = 0; rslt = ptrace(PTRACE_PEEKDATA, ph->pid, aligned_addr, 0); if (errno) { - print_debug("ptrace(PTRACE_PEEKDATA, ..) failed for %d bytes @ %lx\n", size, addr); + print_warning("ptrace(PTRACE_PEEKDATA, ..) failed for %d bytes @ %lx\n", size, addr); return false; } for (; aligned_addr != addr; aligned_addr++, ptr++); @@ -104,7 +104,7 @@ static bool process_read_data(struct ps_prochandle* ph, uintptr_t addr, char *bu errno = 0; rslt = ptrace(PTRACE_PEEKDATA, ph->pid, aligned_addr, 0); if (errno) { - print_debug("ptrace(PTRACE_PEEKDATA, ..) failed for %d bytes @ %lx\n", size, addr); + print_warning("ptrace(PTRACE_PEEKDATA, ..) failed for %d bytes @ %lx\n", size, addr); return false; } *(long *)buf = rslt; @@ -117,7 +117,7 @@ static bool process_read_data(struct ps_prochandle* ph, uintptr_t addr, char *bu errno = 0; rslt = ptrace(PTRACE_PEEKDATA, ph->pid, aligned_addr, 0); if (errno) { - print_debug("ptrace(PTRACE_PEEKDATA, ..) failed for %d bytes @ %lx\n", size, addr); + print_warning("ptrace(PTRACE_PEEKDATA, ..) failed for %d bytes @ %lx\n", size, addr); return false; } for (; aligned_addr != end_addr; aligned_addr++) @@ -150,19 +150,19 @@ static bool process_get_lwp_regs(struct ps_prochandle* ph, pid_t pid, struct use iov.iov_base = user; iov.iov_len = sizeof(*user); if (ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, (void*) &iov) < 0) { - print_debug("ptrace(PTRACE_GETREGSET, ...) failed for lwp %d\n", pid); + print_warning("ptrace(PTRACE_GETREGSET, ...) failed for lwp %d\n", pid); return false; } return true; #elif defined(PTRACE_GETREGS_REQ) if (ptrace(PTRACE_GETREGS_REQ, pid, NULL, user) < 0) { - print_debug("ptrace(PTRACE_GETREGS, ...) failed for lwp(%d) errno(%d) \"%s\"\n", pid, + print_warning("ptrace(PTRACE_GETREGS, ...) failed for lwp(%d) errno(%d) \"%s\"\n", pid, errno, strerror(errno)); return false; } return true; #else - print_debug("ptrace(PTRACE_GETREGS, ...) not supported\n"); + print_warning("ptrace(PTRACE_GETREGS, ...) not supported\n"); return false; #endif @@ -171,7 +171,7 @@ static bool process_get_lwp_regs(struct ps_prochandle* ph, pid_t pid, struct use static bool ptrace_continue(pid_t pid, int signal) { // pass the signal to the process so we don't swallow it if (ptrace(PTRACE_CONT, pid, NULL, signal) < 0) { - print_debug("ptrace(PTRACE_CONT, ..) failed for %d\n", pid); + print_warning("ptrace(PTRACE_CONT, ..) failed for %d\n", pid); return false; } return true; diff --git a/src/jdk.hotspot.agent/macosx/native/libsaproc/libproc_impl.c b/src/jdk.hotspot.agent/macosx/native/libsaproc/libproc_impl.c index 8bcd09d40ced..fa56e620a0a6 100644 --- a/src/jdk.hotspot.agent/macosx/native/libsaproc/libproc_impl.c +++ b/src/jdk.hotspot.agent/macosx/native/libsaproc/libproc_impl.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -95,10 +95,18 @@ void print_debug(const char* format,...) { } } +void print_warning(const char* format,...) { + va_list alist; + va_start(alist, format); + fputs("libsaproc WARNING: ", stdout); + vfprintf(stdout, format, alist); + va_end(alist); +} + void print_error(const char* format,...) { va_list alist; va_start(alist, format); - fputs("ERROR: ", stderr); + fputs("libsaproc ERROR: ", stderr); vfprintf(stderr, format, alist); va_end(alist); } diff --git a/src/jdk.hotspot.agent/macosx/native/libsaproc/libproc_impl.h b/src/jdk.hotspot.agent/macosx/native/libsaproc/libproc_impl.h index edd3d666f7f6..ec10f80713bd 100644 --- a/src/jdk.hotspot.agent/macosx/native/libsaproc/libproc_impl.h +++ b/src/jdk.hotspot.agent/macosx/native/libsaproc/libproc_impl.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2021, Azul Systems, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -218,6 +218,7 @@ struct ps_prochandle { int pathmap_open(const char* name); void print_debug(const char* format,...); +void print_warning(const char* format,...); void print_error(const char* format,...); bool is_debug(); diff --git a/src/jdk.hotspot.agent/macosx/native/libsaproc/ps_core.c b/src/jdk.hotspot.agent/macosx/native/libsaproc/ps_core.c index 20fe2ffb17ef..14a71816062b 100644 --- a/src/jdk.hotspot.agent/macosx/native/libsaproc/ps_core.c +++ b/src/jdk.hotspot.agent/macosx/native/libsaproc/ps_core.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2021, Azul Systems, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -650,7 +650,7 @@ static bool read_shared_lib_info(struct ps_prochandle* ph) { fpos += lcmd.cmdsize; // next command position // make sure still within seg size. if (fpos - lcmd.cmdsize - iter->offset > iter->memsz) { - print_debug("Warning: out of segment limit: %ld \n", fpos - lcmd.cmdsize - iter->offset); + print_warning("out of segment limit: %ld \n", fpos - lcmd.cmdsize - iter->offset); break; // no need to iterate all commands } if (lcmd.cmd == LC_ID_DYLIB) { From e868d83c46a3742d69fa8c9cf08966cb20a2f3ce Mon Sep 17 00:00:00 2001 From: Albert Mingkun Yang Date: Mon, 28 Sep 2026 00:29:46 +0000 Subject: [PATCH 045/103] 8392741: Parallel: Balance Full GC forwarding across regions Reviewed-by: tschatzl, gli --- .../share/gc/parallel/psParallelCompact.cpp | 31 ++++--------------- 1 file changed, 6 insertions(+), 25 deletions(-) diff --git a/src/hotspot/share/gc/parallel/psParallelCompact.cpp b/src/hotspot/share/gc/parallel/psParallelCompact.cpp index ca91d59ea1f2..8913c90a4bd3 100644 --- a/src/hotspot/share/gc/parallel/psParallelCompact.cpp +++ b/src/hotspot/share/gc/parallel/psParallelCompact.cpp @@ -1283,25 +1283,6 @@ void PSParallelCompact::adjust_pointers() { ParallelScavengeHeap::heap()->workers().run_task(&task); } -// Split [start, end) evenly for a number of workers and return the -// range for worker_id. -static void split_regions_for_worker(size_t start, size_t end, - uint worker_id, uint num_workers, - size_t* worker_start, size_t* worker_end) { - assert(start < end, "precondition"); - assert(num_workers > 0, "precondition"); - assert(worker_id < num_workers, "precondition"); - - size_t num_regions = end - start; - size_t num_regions_per_worker = num_regions / num_workers; - size_t remainder = num_regions % num_workers; - // The first few workers will get one extra. - *worker_start = start + worker_id * num_regions_per_worker - + MIN2(checked_cast(worker_id), remainder); - *worker_end = *worker_start + num_regions_per_worker - + (worker_id < remainder ? 1 : 0); -} - static bool safe_to_read_header(size_t words) { precond(words > 0); @@ -1389,12 +1370,12 @@ void PSParallelCompact::forward_to_new_addr() { size_t dense_prefix_region = _summary_data.addr_to_region_idx(dense_prefix_addr); size_t top_region = _summary_data.addr_to_region_idx(_summary_data.region_align_up(top)); - size_t start_region; - size_t end_region; - split_regions_for_worker(dense_prefix_region, top_region, - worker_id, _num_workers, - &start_region, &end_region); - for (size_t cur_region = start_region; cur_region < end_region; ++cur_region) { + assert(dense_prefix_region < top_region, "inv"); + + // Distribute adjacent regions across workers to balance forwarding work + // when live objects are clustered in the heap. + size_t start_region = dense_prefix_region + worker_id; + for (size_t cur_region = start_region; cur_region < top_region; cur_region += _num_workers) { RegionData* region_ptr = _summary_data.region(cur_region); size_t partial_obj_size = region_ptr->partial_obj_size(); From 21e4dadc427cf77760e9f34c5b61129bd413e5bb Mon Sep 17 00:00:00 2001 From: Yasumasa Suenaga Date: Mon, 28 Sep 2026 04:27:05 +0000 Subject: [PATCH 046/103] 8392132: Test serviceability/sa/TestJhsdbJstackMixedWithXComp.java#xcomp failed: Could not find PC in DWARF Reviewed-by: cjplummer --- .../linux/native/libsaproc/DwarfParser.cpp | 8 ++-- .../hotspot/debugger/linux/DwarfCFrame.java | 8 ++-- .../debugger/linux/DwarfException.java | 41 +++++++++++++++++++ .../classes/sun/jvm/hotspot/tools/PStack.java | 8 +++- 4 files changed, 57 insertions(+), 8 deletions(-) create mode 100644 src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/DwarfException.java diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/DwarfParser.cpp b/src/jdk.hotspot.agent/linux/native/libsaproc/DwarfParser.cpp index cc03f3fc8326..c29972a4ed73 100644 --- a/src/jdk.hotspot.agent/linux/native/libsaproc/DwarfParser.cpp +++ b/src/jdk.hotspot.agent/linux/native/libsaproc/DwarfParser.cpp @@ -82,7 +82,7 @@ JNIEXPORT jlong JNICALL Java_sun_jvm_hotspot_debugger_linux_DwarfParser_createDw (JNIEnv *env, jclass this_cls, jlong lib) { DwarfParser *parser = new DwarfParser(reinterpret_cast(lib)); if (!parser->is_parseable()) { - jclass ex_cls = env->FindClass("sun/jvm/hotspot/debugger/DebuggerException"); + jclass ex_cls = env->FindClass("sun/jvm/hotspot/debugger/linux/DwarfException"); if (!env->ExceptionCheck()) { env->ThrowNew(ex_cls, "DWARF not found"); } @@ -127,9 +127,11 @@ JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_linux_DwarfParser_processDw (JNIEnv *env, jobject this_obj, jlong pc) { DwarfParser *parser = reinterpret_cast(get_dwarf_context(env, this_obj)); if (!parser->process_dwarf(pc)) { - jclass ex_cls = env->FindClass("sun/jvm/hotspot/debugger/DebuggerException"); + jclass ex_cls = env->FindClass("sun/jvm/hotspot/debugger/linux/DwarfException"); if (!env->ExceptionCheck()) { - env->ThrowNew(ex_cls, "Could not find PC in DWARF"); + char buf[100]; + snprintf(buf, sizeof(buf), "Could not find PC in DWARF: 0x%lx", pc); + env->ThrowNew(ex_cls, buf); } return; } diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/DwarfCFrame.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/DwarfCFrame.java index f27f187b5dc4..8ef57f508f04 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/DwarfCFrame.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/DwarfCFrame.java @@ -49,7 +49,7 @@ public class DwarfCFrame extends BasicCFrame { /** * @return DwarfParser instance for the PC, null if native library relates to the pc not found. - * @throws DebuggerException if DWARF processing is failed. + * @throws DwarfException if DWARF processing is failed. * For example: pc is not covered in this DWARF, Common Information Entry (CIE) has * language personality routine and/or Language Data Area (LSDA). */ @@ -60,13 +60,13 @@ protected static DwarfParser createDwarfParser(LinuxDebugger linuxDbg, Address p : new DwarfParser(libptr); try { dwarf.processDwarf(pc); - } catch (DebuggerException e) { - // DebuggerException might be thrown from unwinding signal trampoline + } catch (DwarfException e) { + // DwarfException might be thrown from unwinding signal trampoline // (e.g. __restore_rt on AMD64) because it might have DW_CFA_def_cfa_expression // DWARF instruction. // However SA can ignore the case safely because it does not rely on DWARF, // would restore register values from the stack directly. - // Thus DebuggerException would be rethrown if the pc is not in signal trampoline. + // Thus DwarfException would be rethrown if the pc is not in signal trampoline. if (!linuxDbg.isSignalTrampoline(pc)) { throw e; } diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/DwarfException.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/DwarfException.java new file mode 100644 index 000000000000..0f644fefa187 --- /dev/null +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/DwarfException.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, NTT DATA. + * 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. + * + */ + +package sun.jvm.hotspot.debugger.linux; + +import sun.jvm.hotspot.debugger.DebuggerException; + + +public class DwarfException extends DebuggerException { + + public DwarfException() { + super(); + } + + public DwarfException(String message) { + super(message); + } + +} diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/PStack.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/PStack.java index 29d2954efdc0..e21be21dd6d8 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/PStack.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/PStack.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -31,6 +31,7 @@ import sun.jvm.hotspot.interpreter.*; import sun.jvm.hotspot.debugger.*; import sun.jvm.hotspot.debugger.cdbg.*; +import sun.jvm.hotspot.debugger.linux.*; import sun.jvm.hotspot.debugger.remote.*; import sun.jvm.hotspot.oops.*; import sun.jvm.hotspot.runtime.*; @@ -194,6 +195,11 @@ public void run(PrintStream out, Debugger dbg) { } f = f.sender(th, senderSP, senderFP, senderPC); } + } catch (DwarfException dex) { + // Linux: DwarfException will be thrown if DWARF processing fails. + // Stack unwinding should be aborted. However, we can continue for other threads. + // See JDK-8392132 and the review thread on GitHub for details. + IO.println(dex.toString()); } catch (Exception exp) { exp.printStackTrace(); // continue, may be we can do a better job for other threads From 7351533b4b6e08d7be3dacea43483cf0d84a9dd5 Mon Sep 17 00:00:00 2001 From: Tobias Hartmann Date: Mon, 28 Sep 2026 05:01:26 +0000 Subject: [PATCH 047/103] 8392758: C2 compilation hits assert during boxing elimination due to dead loop Reviewed-by: vlivanov, dlong, qamai --- src/hotspot/share/opto/callGenerator.cpp | 2 +- src/hotspot/share/opto/loopnode.cpp | 10 ++- src/hotspot/share/opto/node.cpp | 10 ++- src/hotspot/share/opto/node.hpp | 2 + .../valuetypes/TestPrimitiveUnboxing.java | 69 +++++++++++++++++++ 5 files changed, 84 insertions(+), 9 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/valhalla/valuetypes/TestPrimitiveUnboxing.java diff --git a/src/hotspot/share/opto/callGenerator.cpp b/src/hotspot/share/opto/callGenerator.cpp index ff46314e7fa1..dc1ee79297a0 100644 --- a/src/hotspot/share/opto/callGenerator.cpp +++ b/src/hotspot/share/opto/callGenerator.cpp @@ -213,7 +213,7 @@ JVMState* DirectCallGenerator::generate(JVMState* jvms) { kit.set_arguments_for_java_call(call); kit.set_edges_for_java_call(call, false, _separate_io_proj); Node* ret = kit.set_results_for_java_call(call, _separate_io_proj); - if (!call->is_boxing_method()) { + if (!call->is_boxing_or_unboxing_call()) { mark_projs_not_dead_loop_safe(ret); } kit.push_node(method()->return_type()->basic_type(), ret); diff --git a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp index a2c40ab5f4b7..65d47dac0bf6 100644 --- a/src/hotspot/share/opto/loopnode.cpp +++ b/src/hotspot/share/opto/loopnode.cpp @@ -4382,10 +4382,9 @@ void IdealLoopTree::allpaths_check_safepts(VectorSet &visited, Node_List &stack) visited.set(_head->_idx); while (stack.size() > 0) { Node* n = stack.pop(); - if (n->is_Call() && n->as_Call()->guaranteed_safepoint() - && !(n->is_CallStaticJava() && n->as_CallStaticJava()->is_boxing_method())) { + if (n->is_Call() && n->as_Call()->guaranteed_safepoint() && !n->is_boxing_or_unboxing_call()) { // Terminate this path: guaranteed safepoint found. - // Boxing CallStaticJava calls are excluded as they may lack a safepoint on the fast path. This is + // Boxing and unboxing calls are excluded as they may lack a safepoint on the fast path. This is // not done via CallStaticJavaNode::guaranteed_safepoint() as that also controls PcDesc emission. // In the future, guaranteed_safepoint() should be reworked to correctly handle boxing methods // to avoid this additional check. @@ -4486,12 +4485,11 @@ void IdealLoopTree::check_safepts(VectorSet &visited, Node_List &stack) { if (!_irreducible) { // Scan the dom-path nodes from tail to head for (Node* n = tail(); n != _head; n = _phase->idom(n)) { - // Boxing CallStaticJava calls are excluded as they may lack a safepoint on the fast path. This is + // Boxing and unboxing calls are excluded as they may lack a safepoint on the fast path. This is // not done via CallStaticJavaNode::guaranteed_safepoint() as that also controls PcDesc emission. // In the future, guaranteed_safepoint() should be reworked to correctly handle boxing methods // to avoid this additional check. - if (n->is_Call() && n->as_Call()->guaranteed_safepoint() - && !(n->is_CallStaticJava() && n->as_CallStaticJava()->is_boxing_method())) { + if (n->is_Call() && n->as_Call()->guaranteed_safepoint() && !n->is_boxing_or_unboxing_call()) { has_call = true; _has_sfpt = 1; // Then no need for a safept! break; diff --git a/src/hotspot/share/opto/node.cpp b/src/hotspot/share/opto/node.cpp index 7b7b58c8b9c3..6a75f8c40561 100644 --- a/src/hotspot/share/opto/node.cpp +++ b/src/hotspot/share/opto/node.cpp @@ -3007,8 +3007,9 @@ bool Node::is_dead_loop_safe() const { if (in(0)->is_Allocate()) { return false; } - // MemNode::can_see_stored_value() peeks through the boxing call - if (in(0)->is_CallStaticJava() && in(0)->as_CallStaticJava()->is_boxing_method()) { + // MemNode::can_see_stored_value() peeks through boxing calls and + // ProjNode::Identity() peeks through boxing and unboxing calls. + if (in(0)->is_boxing_or_unboxing_call()) { return false; } return true; @@ -3019,6 +3020,11 @@ bool Node::is_dead_loop_safe() const { bool Node::is_div_or_mod(BasicType bt) const { return Opcode() == Op_Div(bt) || Opcode() == Op_Mod(bt) || Opcode() == Op_UDiv(bt) || Opcode() == Op_UMod(bt); } +bool Node::is_boxing_or_unboxing_call() const { + return is_CallStaticJava() && (as_CallStaticJava()->is_boxing_method() || + as_CallStaticJava()->is_unboxing_method()); +} + // `maybe_pure_function` is assumed to be the input of `this`. This is a bit redundant, // but we already have and need maybe_pure_function in all the call sites, so // it makes it obvious that the `maybe_pure_function` is the same node as in the caller, diff --git a/src/hotspot/share/opto/node.hpp b/src/hotspot/share/opto/node.hpp index 257c54cac63d..e6500e98760e 100644 --- a/src/hotspot/share/opto/node.hpp +++ b/src/hotspot/share/opto/node.hpp @@ -1464,6 +1464,8 @@ class Node { bool is_div_or_mod(BasicType bt) const; + bool is_boxing_or_unboxing_call() const; + //----------------- Printing, etc #ifndef PRODUCT public: diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestPrimitiveUnboxing.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestPrimitiveUnboxing.java new file mode 100644 index 000000000000..d9a3bb6c1f80 --- /dev/null +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestPrimitiveUnboxing.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2026, 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 8392758 + * @summary C2 must handle boxing and unboxing call projections consistently + * @library /test/lib + * @run main/othervm -Xcomp -XX:CompileCommand=compileonly,${test.main.class}::test* + * -XX:CompileCommand=dontinline,java.lang.Integer::intValue + * -XX:CompileCommand=delayinline,java.lang.Long::longValue ${test.main.class} + * @run main ${test.main.class} + */ + +import jdk.test.lib.Asserts; + +public class TestPrimitiveUnboxing { + static final Integer THE_ANSWER = 42; + + // Test that unboxing methods are marked as dead loop safe + static void testDeadLoop() { + int j = 0; + // Nested loops keep the dead parts of the graph alive for long enough + do { + for (int i = 0; i < 1; i++) { + // This always throws + int res = 42/0; + } + } while (j < 0); + + // Unreachable + while (j < 1) { + // We assert here when intValue is removed by macro expansion because + // the dead loop entry created an I/O phi -> call -> proj data loop. + THE_ANSWER.intValue(); + } + } + + // Test that unboxing methods are not re-marked as not dead loop safe + static long testLateInline(Long value) { + return value.longValue(); + } + + public static void main(String[] args) { + Asserts.assertThrows(ArithmeticException.class, TestPrimitiveUnboxing::testDeadLoop); + Asserts.assertEQ(testLateInline(42L), 42L); + } +} + From fffd0a53131784d571bd1d536778ebb7015b898c Mon Sep 17 00:00:00 2001 From: Roland Westrelin Date: Mon, 28 Sep 2026 07:11:56 +0000 Subject: [PATCH 048/103] 8389623: [Valhalla] C2 fails to scalarize value objects through a narrow loop Phi after delayed inlining Reviewed-by: thartmann, chagedorn --- src/hotspot/share/opto/cfgnode.cpp | 42 +++-- src/hotspot/share/opto/cfgnode.hpp | 3 +- src/hotspot/share/opto/compile.cpp | 20 +++ src/hotspot/share/opto/macro.cpp | 14 +- src/hotspot/share/opto/node.cpp | 8 +- src/hotspot/share/opto/node.hpp | 2 +- src/hotspot/share/opto/phaseX.cpp | 11 ++ src/hotspot/share/opto/type.hpp | 4 + src/hotspot/share/opto/valuetypenode.hpp | 8 + ...TestLateInliningPreventsScalarization.java | 89 +++++++++++ .../TestPushInlineTypeThruNarrowPhi.java | 146 ++++++++++++++++++ 11 files changed, 334 insertions(+), 13 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/valhalla/valuetypes/TestLateInliningPreventsScalarization.java create mode 100644 test/hotspot/jtreg/compiler/valhalla/valuetypes/TestPushInlineTypeThruNarrowPhi.java diff --git a/src/hotspot/share/opto/cfgnode.cpp b/src/hotspot/share/opto/cfgnode.cpp index 76d80855cacc..6799e83dd572 100644 --- a/src/hotspot/share/opto/cfgnode.cpp +++ b/src/hotspot/share/opto/cfgnode.cpp @@ -2977,7 +2977,7 @@ class PushValueTypeDown { _nodes_from_phi.push(in); } } - } else if (n->is_ConstraintCast()) { + } else if (should_skip_over(n)) { Node* in = n->in(1); if (in != nullptr) { _nodes_from_phi.push(in); @@ -2991,6 +2991,7 @@ class PushValueTypeDown { for (int i = _nodes_from_phi.size() - 1; i >= 0; i--) { Node* n = _nodes_from_phi.at(i); if (!n->is_ValueType()) { + assert(!n->is_Phi() || n->bottom_type()->make_oopptr() != nullptr, "broken graph"); _nodes_from_phi.remove(i); } } @@ -3005,7 +3006,7 @@ class PushValueTypeDown { _nodes_from_phi.push(in); } } - } else if (n->is_ConstraintCast()) { + } else if (should_skip_over(n)) { Node* in = n->in(1); if (in != nullptr) { _nodes_from_phi.push(in); @@ -3076,7 +3077,7 @@ class PushValueTypeDown { } } } - } else if (n->is_ConstraintCast()) { + } else if (should_skip_over(n)) { Node* in = n->in(1); Node* in_clone = get_clone(in); assert(in_clone != nullptr, "must be cloned"); @@ -3102,12 +3103,15 @@ class PushValueTypeDown { uint vts_to_skip = 0; uint before_phis = 0; uint before_casts = 0; + uint before_encode_decode = 0; for (uint i = 0; i < _nodes_from_phi.size(); ++i) { Node *n = _nodes_from_phi.at(i); if (n->is_Phi()) { before_phis++; } else if (n->is_ConstraintCast()) { before_casts++; + } else if (n->is_DecodeN() || n->is_EncodeP()) { + before_encode_decode++; } else if (n->is_ValueType()) { Node* buf = n->as_ValueType()->get_oop(); if (buf != nullptr && i >= _init_nodes) { @@ -3127,7 +3131,7 @@ class PushValueTypeDown { after.push(in); } } - } else if (n->is_ConstraintCast()) { + } else if (should_skip_over(n)) { Node* in = n->in(1); if (in != nullptr) { after.push(in); @@ -3142,6 +3146,7 @@ class PushValueTypeDown { } uint after_phis = 0; uint after_casts = 0; + uint after_encode_decode = 0; uint init_nodes = after.size(); for (uint i = 0; i < after.size(); ++i) { Node* n = after.at(i); @@ -3159,6 +3164,12 @@ class PushValueTypeDown { if (in != nullptr) { after.push(in); } + } else if (n->is_DecodeN() || n->is_EncodeP()) { + after_encode_decode++; + Node* in = n->in(1); + if (in != nullptr) { + after.push(in); + } } else if (n->is_ValueType()) { assert(i < init_nodes, ""); Node* buf = n->as_ValueType()->get_oop(); @@ -3169,10 +3180,15 @@ class PushValueTypeDown { } assert(after.size() + vts_to_skip == _nodes_from_phi.size(), ""); assert(before_casts == after_casts, "no cast should have been dropped"); - assert(before_phis == after_phis, "no phi should"); + assert(before_phis == after_phis, "no phi should have been dropped"); + assert(before_encode_decode == after_encode_decode, "no DecodeN/EncodeP should have been dropped"); } #endif + bool should_skip_over(Node* n) { + return n->is_ConstraintCast() || n->is_EncodeP() || n->is_DecodeN(); + } + Node* do_transform(PhiNode* phi) { assert(_value_klass != nullptr, "must be"); ValueTypeNode* vt = ValueTypeNode::make_null(*_phase, _value_klass, /* transform = */ false)->clone_with_phis( @@ -3187,8 +3203,10 @@ class PushValueTypeDown { if (n == nullptr) { continue; } - while (n->is_ConstraintCast()) { - casts.push(n); + while (should_skip_over(n)) { + if (n->is_ConstraintCast()) { + casts.push(n); + } n = n->in(1); } if (_phase->type(n)->is_zero_type()) { @@ -3202,6 +3220,7 @@ class PushValueTypeDown { n = _phase->transform(do_transform(n->as_Phi())); } } + assert(n->is_top() || n->is_ValueType(), "Only InlineType or top at this point."); while (casts.size() != 0) { // Push the cast(s) through the ValueTypeNode Node *cast = casts.pop()->clone(); @@ -3223,8 +3242,10 @@ class PushValueTypeDown { vt->merge_with(_phase, n->as_ValueType(), i, transform); } // else nothing to do: phis above vt created by clone_with_phis are initialized to top already. } + if (phi == _root_phi && _root_phi->bottom_type()->isa_narrowoop()) { + return new EncodePNode(_phase->transform(vt), _root_phi->bottom_type()); + } return vt; - } PhiNode* _root_phi; @@ -3252,7 +3273,7 @@ class PushValueTypeDown { for (uint next = 0; next < _nodes_from_phi.size(); next++) { Node* n = _nodes_from_phi.at(next); if (n->is_Phi()) { - assert(n->bottom_type()->isa_ptr(), "broken graph"); + assert(n->bottom_type()->make_ptr() != nullptr, "broken graph"); if (n != _root_phi && !_can_reshape) { return false; } @@ -3265,6 +3286,9 @@ class PushValueTypeDown { } continue; } + if (n->is_EncodeP() || n->is_DecodeN()) { + continue; + } const Type* type = _phase->type(n); if (n->is_ValueType()) { if (_value_klass == nullptr) { diff --git a/src/hotspot/share/opto/cfgnode.hpp b/src/hotspot/share/opto/cfgnode.hpp index 8a920a865746..79245185d777 100644 --- a/src/hotspot/share/opto/cfgnode.hpp +++ b/src/hotspot/share/opto/cfgnode.hpp @@ -261,7 +261,8 @@ class PhiNode : public TypeNode { } bool can_be_value_type() const { - return Arguments::is_valhalla_enabled() && _type->isa_instptr() && _type->is_instptr()->can_be_value_type(); + const Type* type = _type->make_oopptr(); + return Arguments::is_valhalla_enabled() && type != nullptr && type->isa_instptr() && type->is_instptr()->can_be_value_type(); } Node* try_push_value_types_down(PhaseGVN* phase, bool can_reshape); diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index 550fe32e6814..42285e5e1763 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -2083,6 +2083,26 @@ bool Compile::clear_argument_if_only_used_as_buffer_at_calls(Node* result_cast, } void Compile::process_value_types(PhaseIterGVN &igvn, bool remove) { +#ifdef ASSERT + { + ResourceMark rm; + Unique_Node_List wq; + wq.push(C->root()); + for (uint i = 0; i < wq.size(); ++i) { + Node* n = wq.at(i); + if (n->is_Phi()) { + assert(!n->as_Phi()->can_push_value_types_down(&igvn), "should have been processed by igvn"); + } + for (uint j = 0; j < n->req(); j++) { + Node* in = n->in(j); + if (in != nullptr) { + wq.push(in); + } + } + } + } +#endif + // Make sure that the return value does not keep an otherwise unused allocation alive if (tf()->returns_value_type_as_fields()) { Node* ret = nullptr; diff --git a/src/hotspot/share/opto/macro.cpp b/src/hotspot/share/opto/macro.cpp index a8ee9645f754..6285f4810a4e 100644 --- a/src/hotspot/share/opto/macro.cpp +++ b/src/hotspot/share/opto/macro.cpp @@ -859,7 +859,12 @@ bool PhaseMacroExpand::can_eliminate_allocation(PhaseIterGVN* igvn, AllocateNode DEBUG_ONLY(disq_node = use;) NOT_PRODUCT(fail_eliminate = "Object is passed as argument";) can_eliminate = false; + } else if (sfpt->is_StoreFlat() && sfpt->as_StoreFlat()->has_non_debug_use(res) && sfpt->as_StoreFlat()->value() != res) { + DEBUG_ONLY(disq_node = use;) + NOT_PRODUCT(fail_eliminate = "Object is stored with StoreFlat";) + can_eliminate = false; } + Node* sfptMem = sfpt->memory(); if (sfptMem == nullptr || sfptMem->is_top()) { DEBUG_ONLY(disq_node = use;) @@ -1412,7 +1417,14 @@ void PhaseMacroExpand::process_users_of_allocation(CallNode *alloc, bool value_t // Process users for (DUIterator_Fast kmax, k = use->fast_outs(kmax); k < kmax; k++) { Node* u = use->fast_out(k); - if (!u->is_ValueType() && !u->is_StoreFlat()) { + // If u is a SafePoint either: + // - "use" is a debug info input an is removed by Compile::process_inline_types() + // - "use" is the value stored by StoreFlat and is removed when StoreFlat is expanded + assert(!u->is_SafePoint() || + (u->is_Call() && !u->as_Call()->has_non_debug_use(use)) || + (u->is_LoadFlat() && !u->as_LoadFlat()->has_non_debug_use(use)) || + (u->is_StoreFlat() && (!u->as_StoreFlat()->has_non_debug_use(use) || u->as_StoreFlat()->value() == use)), "unexpected InlineType use at safepoint"); + if (!u->is_ValueType() && !u->is_SafePoint()) { worklist.push(u); } } diff --git a/src/hotspot/share/opto/node.cpp b/src/hotspot/share/opto/node.cpp index 6a75f8c40561..d16edc38e239 100644 --- a/src/hotspot/share/opto/node.cpp +++ b/src/hotspot/share/opto/node.cpp @@ -886,7 +886,7 @@ void Node::ins_req( uint idx, Node *n ) { } //-----------------------------find_edge--------------------------------------- -int Node::find_edge(Node* n) { +int Node::find_edge(const Node* n) const { for (uint i = 0; i < len(); i++) { if (_in[i] == n) return i; } @@ -3064,6 +3064,12 @@ bool Node::has_non_debug_uses() const { if (u->is_Call() && u->as_Call()->has_non_debug_use(this)) { return true; } + if (u->is_StoreFlat() && u->as_StoreFlat()->has_non_debug_use(this)) { + return true; + } + if (u->is_LoadFlat() && u->as_LoadFlat()->has_non_debug_use(this)) { + return true; + } // Non-call safepoints have only debug uses. } else if (u->is_ReachabilityFence()) { // Reachability fence is treated as debug use. diff --git a/src/hotspot/share/opto/node.hpp b/src/hotspot/share/opto/node.hpp index e6500e98760e..18efd08c82bc 100644 --- a/src/hotspot/share/opto/node.hpp +++ b/src/hotspot/share/opto/node.hpp @@ -496,7 +496,7 @@ class Node { Compile::current()->record_modified_node(this); } // Find first occurrence of n among my edges: - int find_edge(Node* n); + int find_edge(const Node* n) const; int find_prec_edge(Node* n) { for (uint i = req(); i < len(); i++) { if (_in[i] == n) return i; diff --git a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp index 76ba689dacb3..32f71e889b52 100644 --- a/src/hotspot/share/opto/phaseX.cpp +++ b/src/hotspot/share/opto/phaseX.cpp @@ -2919,6 +2919,17 @@ void PhaseIterGVN::add_users_of_use_to_worklist(Node* n, Node* use, Unique_Node_ const int add_op = (use_op == Op_SubI) ? Op_AddI : Op_AddL; add_users_to_worklist_if(worklist, use, [=](Node* u) { return u->Opcode() == add_op; }); } + // (Phi (ValueType ValueType)) is transformed into (ValueType (Phi ) (Phi )...) + // and is applied if there are EncodeP/DecodeN or casts between a Phi and InlineType nodes + if (use->is_Phi() || use->is_EncodeP() || use->is_DecodeN() || use->is_ConstraintCast()) { + auto is_boundary = [](Node* n){ return !n->is_EncodeP() && !n->is_DecodeN() && !n->is_ConstraintCast(); }; + auto push_phi_to_worklist = [&worklist](Node* n){ + if (n->is_Phi()) { + worklist.push(n); + } + }; + use->visit_uses(push_phi_to_worklist, is_boundary); + } } /** diff --git a/src/hotspot/share/opto/type.hpp b/src/hotspot/share/opto/type.hpp index 09079419d27f..4c8ae02a0b3e 100644 --- a/src/hotspot/share/opto/type.hpp +++ b/src/hotspot/share/opto/type.hpp @@ -2188,6 +2188,10 @@ class TypeNarrowPtr : public Type { return _ptrtype->is_known_instance(); } + virtual bool maybe_null() const { + return _ptrtype->maybe_null(); + } + #ifndef PRODUCT virtual void dump2( Dict &d, uint depth, outputStream *st ) const; #endif diff --git a/src/hotspot/share/opto/valuetypenode.hpp b/src/hotspot/share/opto/valuetypenode.hpp index 4d8963dbf10c..5bc2010e7da8 100644 --- a/src/hotspot/share/opto/valuetypenode.hpp +++ b/src/hotspot/share/opto/valuetypenode.hpp @@ -201,6 +201,10 @@ class LoadFlatNode final : public SafePointNode { bool is_mismatched() const; + bool has_non_debug_use(const Node* n) const { + return find_edge(n) <= TypeFunc::Parms + 1; + } + private: LoadFlatNode(ciValueKlass* vk, const TypeTuple* type, bool null_free, DecoratorSet decorators) : SafePointNode(TypeFunc::Parms + 2, nullptr, TypePtr::BOTTOM), _vk(vk), _type(type), _null_free(null_free), _decorators(decorators) { @@ -242,6 +246,10 @@ class StoreFlatNode final : public SafePointNode { bool is_mismatched() const; + bool has_non_debug_use(const Node* n) const { + return find_edge(n) <= TypeFunc::Parms + 2; + } + private: StoreFlatNode(bool null_free, DecoratorSet decorators) : SafePointNode(TypeFunc::Parms + 3, nullptr, TypePtr::BOTTOM), _null_free(null_free), _decorators(decorators) { diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestLateInliningPreventsScalarization.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestLateInliningPreventsScalarization.java new file mode 100644 index 000000000000..cef459d24b14 --- /dev/null +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestLateInliningPreventsScalarization.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2026, 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. + */ + +package compiler.valhalla.inlinetypes; + +import compiler.lib.ir_framework.Check; +import compiler.lib.ir_framework.IR; +import compiler.lib.ir_framework.IRNode; +import compiler.lib.ir_framework.Test; +import compiler.lib.ir_framework.TestFramework; +import jdk.internal.vm.annotation.LooselyConsistentValue; +import jdk.test.lib.Asserts; + +/* + * @test + * @bug 8389623 + * @summary Test scalar replacement when delayed inlining hides exact types. + * @library /test/lib / + * @enablePreview + * @modules java.base/jdk.internal.vm.annotation + * @run driver ${test.main.class} + * @run driver ${test.main.class} delayinline + */ +public class TestLateInliningPreventsScalarization { + static value class MyValue2 { + int x; + + MyValue2(int x) { + this.x = x; + } + } + + @LooselyConsistentValue + static value class MyValue1 { + MyValue2 v; + + MyValue1(MyValue2 v) { + this.v = v; + } + } + + @Test + @IR(failOn = {IRNode.LOAD_OF_CLASS, "MyValue2"}) + static int test() { + MyValue1 val = new MyValue1(new MyValue2(1)); + for (int i = 0; i < 10; ++i) { + if ((i & 1) == 0) { + val = new MyValue1(new MyValue2(i)); + } + } + return val.v.x; + } + + @Check(test = "test") + static void checkTest(int result) { + Asserts.assertEquals(result, 8); + } + + public static void main(String[] args) { + TestFramework framework = new TestFramework(); + framework.addFlags("--enable-preview", "--add-exports", "java.base/jdk.internal.vm.annotation=ALL-UNNAMED"); + if (args.length > 0) { + Asserts.assertEquals(args[0], "delayinline"); + framework.addFlags("-XX:CompileCommand=delayinline,*MyValue1::"); + } + framework.start(); + } +} + diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestPushInlineTypeThruNarrowPhi.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestPushInlineTypeThruNarrowPhi.java new file mode 100644 index 000000000000..cda17869d15a --- /dev/null +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestPushInlineTypeThruNarrowPhi.java @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2026 IBM Corporation. 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 8389623 + * @enablePreview + * @modules java.base/jdk.internal.vm.annotation + * @run main/othervm -XX:-BackgroundCompilation + * -XX:CompileCommand=delayinline,${test.main.class}::lateInlined1 + * -XX:CompileCommand=delayinline,${test.main.class}::lateInlined2 + * -XX:CompileCommand=delayinline,${test.main.class}::lateInlined3 ${test.main.class} + * @run main/othervm -XX:-BackgroundCompilation + * -XX:CompileCommand=delayinline,${test.main.class}::lateInlined1 + * -XX:CompileCommand=delayinline,${test.main.class}::lateInlined2 + * -XX:CompileCommand=delayinline,${test.main.class}::lateInlined3 + * -XX:+UnlockDiagnosticVMOptions -XX:-UseFieldFlattening ${test.main.class} + */ + +package compiler.valhalla.inlinetypes; + +import jdk.internal.vm.annotation.NullRestricted; + +public class TestPushInlineTypeThruNarrowPhi { + static value class MyValue1 { + int x; + + MyValue1(int x) { + this.x = x; + } + } + + static value class MyValue2 { + long x; + + MyValue2(long x) { + this.x = x; + } + } + + static value class MyValue3 { + @NullRestricted + MyValue2 v; + + MyValue3(MyValue2 v) { + this.v = v; + } + } + + static MyValue1 fieldV1 = new MyValue1(42); + static MyValue2 fieldV2 = new MyValue2(42); + @NullRestricted + static MyValue3 fieldV3 = new MyValue3(fieldV2); + @NullRestricted + static MyValue3 fieldV4 = new MyValue3(fieldV2); + + static int field; + + public static void main(String[] args) { + for (int i = 0; i < 20_000; i++) { + test1(true); + test1(false); + test2(true, fieldV3); + test2(false, fieldV3); + inlined1(true, 0, fieldV3); + inlined1(true, 42, fieldV3); + } + } + + static MyValue1 test1(boolean flag1) { + MyValue1 res = null; + if (flag1) { + lateInlined1(); + res = fieldV1; + } else { + lateInlined1(); + res = fieldV1; + } + return res; + } + + static void lateInlined1() { + fieldV1 = new MyValue1(42); + } + + static MyValue3 test2(boolean flag1, MyValue3 v3) { + int flag2 = 42; + MyValue3 v = new MyValue3(new MyValue2(42)); + for (int i = 1; i < 4; i *= 2) { + v = inlined1(flag1, flag2, v3); + flag2 = lateInlined3(); + } + return v; + } + + static MyValue3 inlined1(boolean flag1, int flag2, MyValue3 v3) { + MyValue3 v; + lateInlined2(flag2, v3, flag1); + if (flag1) { + v = fieldV3; + } else { + v = fieldV4; + } + return v; + } + + static void lateInlined2(int flag2, MyValue3 v3, boolean flag3) { + MyValue3 v; + if (flag2 == 42) { + if (flag3) { + v = new MyValue3(new MyValue2(42)); + } else { + v = new MyValue3(new MyValue2(42)); + } + field = 42; + } else { + v = fieldV4; + } + fieldV3 = v; + fieldV4 = new MyValue3(new MyValue2(42)); + } + + static int lateInlined3() { + return 42; + } +} From 2370d3ee9f598df1b52c3a83d90120aefd5eda6d Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Mon, 28 Sep 2026 07:52:47 +0000 Subject: [PATCH 049/103] 8393012: G1: Move G1HeapRegionRemSet::iterate_for_merge to G1CardSetGroup Reviewed-by: ayang --- src/hotspot/share/gc/g1/g1CardSetGroup.hpp | 5 ++ .../share/gc/g1/g1CardSetGroup.inline.hpp | 61 +++++++++++++++++ .../share/gc/g1/g1CollectionSet.inline.hpp | 6 +- .../share/gc/g1/g1HeapRegionRemSet.hpp | 9 --- .../share/gc/g1/g1HeapRegionRemSet.inline.hpp | 65 ------------------- src/hotspot/share/gc/g1/g1RemSet.cpp | 4 +- 6 files changed, 71 insertions(+), 79 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1CardSetGroup.hpp b/src/hotspot/share/gc/g1/g1CardSetGroup.hpp index 35e8bd588fba..38a615fe36a3 100644 --- a/src/hotspot/share/gc/g1/g1CardSetGroup.hpp +++ b/src/hotspot/share/gc/g1/g1CardSetGroup.hpp @@ -133,6 +133,11 @@ class G1CardSetGroup : public CHeapObj{ assert(_group_id != InvalidId, "group must have an assigned id"); return _group_id; } + + // Iterate the cards in this card set group for merging them into the card table. + // The passed closure must be a CardOrRangeVisitor. + template + inline void iterate_for_merge(CardOrRangeVisitor& cl); }; using G1CardSetGroupListIterator = GrowableArrayIterator; diff --git a/src/hotspot/share/gc/g1/g1CardSetGroup.inline.hpp b/src/hotspot/share/gc/g1/g1CardSetGroup.inline.hpp index c23b82b5d134..f3a00839344c 100644 --- a/src/hotspot/share/gc/g1/g1CardSetGroup.inline.hpp +++ b/src/hotspot/share/gc/g1/g1CardSetGroup.inline.hpp @@ -27,6 +27,67 @@ #include "gc/g1/g1CardSetGroup.hpp" +#include "gc/g1/g1CardSet.inline.hpp" + +template +class G1ContainerCardsOrRanges { + Closure& _cl; + uint _region_idx; + uint _offset; + +public: + G1ContainerCardsOrRanges(Closure& cl, uint region_idx, uint offset) : _cl(cl), _region_idx(region_idx), _offset(offset) { } + + bool start_iterate(uint tag) { + return _cl.start_iterate(tag, _region_idx); + } + + void operator()(uint card_idx) { + _cl.do_card(card_idx + _offset); + } + + void operator()(uint card_idx, uint length) { + _cl.do_card_range(card_idx + _offset, length); + } +}; + +template +class G1CardSetGroupMergeCardClosure : public G1CardSet::ContainerPtrClosure { + G1CardSet* _card_set; + Closure& _cl; + uint _log_card_regions_per_region; + uint _card_regions_per_region_mask; + uint _log_card_region_size; + +public: + G1CardSetGroupMergeCardClosure(G1CardSet* card_set, + Closure& cl, + uint log_card_regions_per_region, + uint log_card_region_size) : + _card_set(card_set), + _cl(cl), + _log_card_regions_per_region(log_card_regions_per_region), + _card_regions_per_region_mask((1 << log_card_regions_per_region) - 1), + _log_card_region_size(log_card_region_size) { + } + + void do_containerptr(uint card_region_idx, size_t num_occupied, G1CardSet::ContainerPtr container) override { + G1ContainerCardsOrRanges cl(_cl, + card_region_idx >> _log_card_regions_per_region, + (card_region_idx & _card_regions_per_region_mask) << _log_card_region_size); + _card_set->iterate_cards_or_ranges_in_container(container, cl); + } +}; + +template +inline void G1CardSetGroup::iterate_for_merge(CardOrRangeVisitor& cl) { + G1CardSetGroupMergeCardClosure cl2(card_set(), + cl, + card_set()->config()->log2_card_regions_per_heap_region(), + card_set()->config()->log2_cards_per_card_region()); + card_set()->iterate_containers(&cl2, true /* at_safepoint */); +} + template void G1CardSetGroupList::iterate(Func&& f) const { for (G1CardSetGroup* group : _groups) { diff --git a/src/hotspot/share/gc/g1/g1CollectionSet.inline.hpp b/src/hotspot/share/gc/g1/g1CollectionSet.inline.hpp index 9fa035516c08..4a64cdcbebe8 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSet.inline.hpp +++ b/src/hotspot/share/gc/g1/g1CollectionSet.inline.hpp @@ -27,13 +27,13 @@ #include "gc/g1/g1CollectionSet.hpp" -#include "gc/g1/g1HeapRegionRemSet.hpp" +#include "gc/g1/g1CardSetGroup.inline.hpp" template inline void G1CollectionSet::merge_collection_set_card_set_groups(CardOrRangeVisitor& cl, uint worker_id, uint num_workers) { uint offset = _selected_groups_inc_part_start; if (offset == 0) { - G1HeapRegionRemSet::iterate_for_merge(_g1h->young_regions_card_set_group()->card_set(), cl); + _g1h->young_regions_card_set_group()->iterate_for_merge(cl); } const uint next_selected_group_increment = num_selected_groups_in_increment(); @@ -44,7 +44,7 @@ inline void G1CollectionSet::merge_collection_set_card_set_groups(CardOrRangeVis uint start_pos = (uint)((uint64_t)worker_id * next_selected_group_increment / num_workers); uint cur_pos = start_pos; do { - G1HeapRegionRemSet::iterate_for_merge(_selected_groups.at(offset + cur_pos)->card_set(), cl); + _selected_groups.at(offset + cur_pos)->iterate_for_merge(cl); cur_pos++; if (cur_pos == next_selected_group_increment) { cur_pos = 0; diff --git a/src/hotspot/share/gc/g1/g1HeapRegionRemSet.hpp b/src/hotspot/share/gc/g1/g1HeapRegionRemSet.hpp index 1b666fc747e5..0dd36406df72 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegionRemSet.hpp +++ b/src/hotspot/share/gc/g1/g1HeapRegionRemSet.hpp @@ -95,15 +95,6 @@ class G1HeapRegionRemSet : public CHeapObj { return (code_roots_length() == 0) && card_set()->occupancy_less_or_equal_to(occ); } - // Iterate the cards in this remembered set for merging them into the card table. - // The passed closure must be a CardOrRangeVisitor; we use a template parameter - // to pass it in to facilitate inlining as much as possible. - template - inline void iterate_for_merge(CardOrRangeVisitor& cl); - - template - inline static void iterate_for_merge(G1CardSet* card_set, CardOrRangeVisitor& cl); - size_t occupied() { assert(has_card_set_group(), "pre-condition"); return card_set()->occupied(); diff --git a/src/hotspot/share/gc/g1/g1HeapRegionRemSet.inline.hpp b/src/hotspot/share/gc/g1/g1HeapRegionRemSet.inline.hpp index e03ad4dc4cde..f7937c62d214 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegionRemSet.inline.hpp +++ b/src/hotspot/share/gc/g1/g1HeapRegionRemSet.inline.hpp @@ -51,71 +51,6 @@ void G1HeapRegionRemSet::set_state_complete() { _state = Complete; } -template -class G1ContainerCardsOrRanges { - Closure& _cl; - uint _region_idx; - uint _offset; - -public: - G1ContainerCardsOrRanges(Closure& cl, uint region_idx, uint offset) : _cl(cl), _region_idx(region_idx), _offset(offset) { } - - bool start_iterate(uint tag) { - return _cl.start_iterate(tag, _region_idx); - } - - void operator()(uint card_idx) { - _cl.do_card(card_idx + _offset); - } - - void operator()(uint card_idx, uint length) { - _cl.do_card_range(card_idx + _offset, length); - } -}; - -template class CardOrRanges> -class G1HeapRegionRemSetMergeCardClosure : public G1CardSet::ContainerPtrClosure { - G1CardSet* _card_set; - Closure& _cl; - uint _log_card_regions_per_region; - uint _card_regions_per_region_mask; - uint _log_card_region_size; - -public: - - G1HeapRegionRemSetMergeCardClosure(G1CardSet* card_set, - Closure& cl, - uint log_card_regions_per_region, - uint log_card_region_size) : - _card_set(card_set), - _cl(cl), - _log_card_regions_per_region(log_card_regions_per_region), - _card_regions_per_region_mask((1 << log_card_regions_per_region) - 1), - _log_card_region_size(log_card_region_size) { - } - - void do_containerptr(uint card_region_idx, size_t num_occupied, G1CardSet::ContainerPtr container) override { - CardOrRanges cl(_cl, - card_region_idx >> _log_card_regions_per_region, - (card_region_idx & _card_regions_per_region_mask) << _log_card_region_size); - _card_set->iterate_cards_or_ranges_in_container(container, cl); - } -}; - -template -inline void G1HeapRegionRemSet::iterate_for_merge(CardOrRangeVisitor& cl) { - iterate_for_merge(card_set(), cl); -} - -template -void G1HeapRegionRemSet::iterate_for_merge(G1CardSet* card_set, CardOrRangeVisitor& cl) { - G1HeapRegionRemSetMergeCardClosure cl2(card_set, - cl, - card_set->config()->log2_card_regions_per_heap_region(), - card_set->config()->log2_cards_per_card_region()); - card_set->iterate_containers(&cl2, true /* at_safepoint */); -} - uintptr_t G1HeapRegionRemSet::to_card(OopOrNarrowOopStar from) const { return pointer_delta(from, _heap_base_address, 1) >> CardTable::card_shift(); } diff --git a/src/hotspot/share/gc/g1/g1RemSet.cpp b/src/hotspot/share/gc/g1/g1RemSet.cpp index 48998e3afd79..fad3e98d5047 100644 --- a/src/hotspot/share/gc/g1/g1RemSet.cpp +++ b/src/hotspot/share/gc/g1/g1RemSet.cpp @@ -26,7 +26,7 @@ #include "gc/g1/g1BatchedTask.hpp" #include "gc/g1/g1BlockOffsetTable.inline.hpp" #include "gc/g1/g1CardSet.inline.hpp" -#include "gc/g1/g1CardSetGroup.hpp" +#include "gc/g1/g1CardSetGroup.inline.hpp" #include "gc/g1/g1CardTable.inline.hpp" #include "gc/g1/g1CardTableClaimTable.inline.hpp" #include "gc/g1/g1CardTableEntryClosure.hpp" @@ -1071,7 +1071,7 @@ class G1MergeHeapRootsTask : public WorkerTask { "Found a not-small remembered set here. This is inconsistent with previous assumptions."); if (!r->rem_set()->is_empty()) { - r->rem_set()->iterate_for_merge(_cl); + r->rem_set()->card_set_group()->iterate_for_merge(_cl); // We should only clear the card based remembered set here as we will not // implicitly rebuild anything else during eager reclaim. Note that at the moment // (and probably never) we do not enter this path if there are other kind of From 15e1475a73444a9110537e3d171d44f71e226cb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johan=20Sj=C3=B6len?= Date: Mon, 28 Sep 2026 08:00:02 +0000 Subject: [PATCH 050/103] 8392765: Do not heap allocate every Ref Reviewed-by: iklam, fbredberg --- src/hotspot/share/memory/metaspaceClosure.cpp | 6 ++--- src/hotspot/share/memory/metaspaceClosure.hpp | 24 +++++++++++++++---- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/hotspot/share/memory/metaspaceClosure.cpp b/src/hotspot/share/memory/metaspaceClosure.cpp index 0926b55b9a30..f0b1e00506fe 100644 --- a/src/hotspot/share/memory/metaspaceClosure.cpp +++ b/src/hotspot/share/memory/metaspaceClosure.cpp @@ -45,10 +45,10 @@ void MetaspaceClosure::push_impl(MetaspaceClosure::Ref* ref) { } if (_nest_level < MAX_NEST_LEVEL) { do_push(ref); - delete ref; } else { - ref->set_next(_pending_refs); - _pending_refs = ref; + Ref* cloned_ref = ref->clone_on_cheap(); + cloned_ref->set_next(_pending_refs); + _pending_refs = cloned_ref; } } diff --git a/src/hotspot/share/memory/metaspaceClosure.hpp b/src/hotspot/share/memory/metaspaceClosure.hpp index 20e5528e9bc9..bde6d9704001 100644 --- a/src/hotspot/share/memory/metaspaceClosure.hpp +++ b/src/hotspot/share/memory/metaspaceClosure.hpp @@ -123,6 +123,7 @@ class MetaspaceClosure { virtual void metaspace_pointers_do(MetaspaceClosure *it) const = 0; virtual MetaspaceClosureType type() const = 0; virtual bool is_read_only_by_default() const = 0; + virtual Ref* clone_on_cheap() const = 0; virtual ~Ref() {} address obj() const { @@ -198,6 +199,11 @@ class MetaspaceClosure { virtual bool not_null() const override { return dereference() != nullptr; } virtual int size() const override { return get_size(dereference()); } virtual MetaspaceClosureType type() const override { return as_type(dereference()->type()); } + virtual Ref* clone_on_cheap() const override { + MSORef* cloned_obj = new MSORef(_mpp, writability()); + cloned_obj->set_enclosing_obj(this->enclosing_obj()); + return cloned_obj; + } virtual void metaspace_pointers_do(MetaspaceClosure *it) const override { dereference()->metaspace_pointers_do(it); } @@ -226,6 +232,11 @@ class MetaspaceClosure { virtual bool not_null() const override { return dereference() != nullptr; } virtual int size() const override { return (int)heap_word_size(sizeof(*dereference())); } virtual MetaspaceClosureType type() const override { return MetaspaceClosureType::GrowableArrayType; } + virtual Ref* clone_on_cheap() const override { + GrowableArrayRef* cloned_obj = new GrowableArrayRef(_mpp, writability()); + cloned_obj->set_enclosing_obj(this->enclosing_obj()); + return cloned_obj; + } virtual void metaspace_pointers_do(MetaspaceClosure *it) const override { GrowableArray* array = dereference(); log_trace(aot)("Iter(GrowableArray): %p [%d]", array, array->length()); @@ -267,6 +278,11 @@ class MetaspaceClosure { virtual bool not_null() const override { precond(dereference() != nullptr); return true; } virtual int size() const override { return (int)heap_word_size(byte_size()); } virtual MetaspaceClosureType type() const override { return MetaspaceClosureType::CArrayType; } + virtual Ref* clone_on_cheap() const override { + CArrayRef* cloned_obj = new CArrayRef(_mpp, _num_elems, writability()); + cloned_obj->set_enclosing_obj(this->enclosing_obj()); + return cloned_obj; + } virtual void metaspace_pointers_do(MetaspaceClosure *it) const override { metaspace_pointers_do_impl(it, dereference()); } @@ -325,9 +341,8 @@ class MetaspaceClosure { private: template void push_with_ref(T** mpp, Writability w) { - // We cannot make stack allocation because the Ref may need to be saved in - // _pending_refs to avoid overflowing the C call stack - push_impl(new REF_TYPE(mpp, w)); + REF_TYPE ref(mpp, w); + push_impl(&ref); } public: @@ -363,7 +378,8 @@ class MetaspaceClosure { // --- The buffer of GrowableArray template void push_c_array(T** mpp, int num_elems, Writability w = _default) { - push_impl(new CArrayRef(mpp, num_elems, w)); + CArrayRef ref(mpp, num_elems, w); + push_impl(&ref); } }; From 34d30f9b0f162996b81f4b3e8cc0e49cb5c37f00 Mon Sep 17 00:00:00 2001 From: Ivan Bereziuk Date: Mon, 28 Sep 2026 08:38:00 +0000 Subject: [PATCH 051/103] 8390055: Make container test mounts frendlier to SELinux Reviewed-by: cnorrbin, rsunderbabu --- .../jtreg/containers/docker/DockerBasicTest.java | 4 ++-- .../jtreg/containers/docker/ShareTmpDir.java | 2 +- .../jtreg/containers/docker/TestCPUSets.java | 4 ++-- .../jtreg/containers/docker/TestJFREvents.java | 4 ++-- .../containers/docker/TestJFRNetworkEvents.java | 4 ++-- .../jtreg/containers/docker/TestJFRWithJMX.java | 4 ++-- .../containers/docker/TestJcmdWithSideCar.java | 4 ++-- .../containers/docker/TestLimitsUpdating.java | 2 +- test/hotspot/jtreg/containers/docker/TestPids.java | 4 ++-- .../platform/docker/TestDockerCpuMetrics.java | 14 +++++++------- .../platform/docker/TestDockerMemoryMetrics.java | 12 ++++++------ .../docker/TestDockerMemoryMetricsSubgroup.java | 5 +++-- .../platform/docker/TestLimitsUpdating.java | 2 +- .../platform/docker/TestSystemMetrics.java | 4 ++-- .../platform/docker/TestUseContainerSupport.java | 4 ++-- .../lib/jdk/test/lib/containers/docker/Common.java | 4 ++-- 16 files changed, 39 insertions(+), 38 deletions(-) diff --git a/test/hotspot/jtreg/containers/docker/DockerBasicTest.java b/test/hotspot/jtreg/containers/docker/DockerBasicTest.java index 403f46f6ab40..05d05a174e3d 100644 --- a/test/hotspot/jtreg/containers/docker/DockerBasicTest.java +++ b/test/hotspot/jtreg/containers/docker/DockerBasicTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, 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,7 +73,7 @@ private static void testHelloDocker() throws Exception { DockerRunOptions opts = new DockerRunOptions(imageNameAndTag, "/jdk/bin/java", "HelloDocker") .addJavaOpts("-cp", "/test-classes/") - .addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/"); + .addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/:z"); DockerTestUtils.dockerRunJava(opts) .shouldHaveExitValue(0) diff --git a/test/hotspot/jtreg/containers/docker/ShareTmpDir.java b/test/hotspot/jtreg/containers/docker/ShareTmpDir.java index 065f9ed8a7be..51b3e79a1936 100644 --- a/test/hotspot/jtreg/containers/docker/ShareTmpDir.java +++ b/test/hotspot/jtreg/containers/docker/ShareTmpDir.java @@ -74,7 +74,7 @@ private static void test() throws Exception { started2.delete(); DockerRunOptions opts = new DockerRunOptions(imageName, "/jdk/bin/java", "WaitForFlagFile"); Object lock = new Object(); - opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/"); + opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/:z"); opts.addDockerOpts("--volume", sharedtmpdir.getAbsolutePath() + ":/tmp/:z"); opts.addJavaOpts("-Xlog:os+container=trace", "-Xlog:perf*=debug", "-cp", "/test-classes/"); diff --git a/test/hotspot/jtreg/containers/docker/TestCPUSets.java b/test/hotspot/jtreg/containers/docker/TestCPUSets.java index 001914a66866..6efab26d1a44 100644 --- a/test/hotspot/jtreg/containers/docker/TestCPUSets.java +++ b/test/hotspot/jtreg/containers/docker/TestCPUSets.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, 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 @@ -118,7 +118,7 @@ private static void log(String msg) { private static DockerRunOptions commonOpts() { DockerRunOptions opts = new DockerRunOptions(imageName, "/jdk/bin/java", "PrintContainerInfo"); - opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/"); + opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/:z"); opts.addJavaOpts("-Xlog:os+container=trace", "-cp", "/test-classes/"); Common.addWhiteBoxOpts(opts); return opts; diff --git a/test/hotspot/jtreg/containers/docker/TestJFREvents.java b/test/hotspot/jtreg/containers/docker/TestJFREvents.java index c31123104795..ddbf468ee00f 100644 --- a/test/hotspot/jtreg/containers/docker/TestJFREvents.java +++ b/test/hotspot/jtreg/containers/docker/TestJFREvents.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, 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 @@ -264,7 +264,7 @@ private static void testProcessInfo() throws Exception { private static DockerRunOptions commonDockerOpts() { return new DockerRunOptions(imageName, "/jdk/bin/java", "JfrReporter") - .addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/") + .addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/:z") .addJavaOpts("-cp", "/test-classes/"); } diff --git a/test/hotspot/jtreg/containers/docker/TestJFRNetworkEvents.java b/test/hotspot/jtreg/containers/docker/TestJFRNetworkEvents.java index 2c7120c577c5..f3d1ed388d6f 100644 --- a/test/hotspot/jtreg/containers/docker/TestJFRNetworkEvents.java +++ b/test/hotspot/jtreg/containers/docker/TestJFRNetworkEvents.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, 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 @@ -61,7 +61,7 @@ public static void main(String[] args) throws Exception { private static void runTest(String event) throws Exception { DockerRunOptions opts = new DockerRunOptions(imageName, "/jdk/bin/java", "JfrNetwork") - .addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/") + .addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/:z") .addJavaOpts("-cp", "/test-classes/") .addDockerOpts("--hostname", JfrNetwork.HOST_NAME) .addClassOptions(event); diff --git a/test/hotspot/jtreg/containers/docker/TestJFRWithJMX.java b/test/hotspot/jtreg/containers/docker/TestJFRWithJMX.java index 7c26af9b27a4..ed8a83ab3085 100644 --- a/test/hotspot/jtreg/containers/docker/TestJFRWithJMX.java +++ b/test/hotspot/jtreg/containers/docker/TestJFRWithJMX.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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 @@ -123,7 +123,7 @@ static void test() throws Exception { static ProcessBuilder buildDockerJavaProcess(String containerName) throws Exception { DockerRunOptions opts = new DockerRunOptions(imageName, "/jdk/bin/java", "EventProducer") .addDockerOpts("--name", containerName) - .addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/") + .addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/:z") .addDockerOpts("--hostname", "jmx-jfr-test") .addDockerOpts("-p", "" + PORT + ":" + PORT) .addJavaOpts("-cp", "/test-classes/") diff --git a/test/hotspot/jtreg/containers/docker/TestJcmdWithSideCar.java b/test/hotspot/jtreg/containers/docker/TestJcmdWithSideCar.java index 28a7a20553f8..b9e90e730a6d 100644 --- a/test/hotspot/jtreg/containers/docker/TestJcmdWithSideCar.java +++ b/test/hotspot/jtreg/containers/docker/TestJcmdWithSideCar.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, 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 @@ -220,7 +220,7 @@ private static long findProcess(OutputAnalyzer out, String name) throws Exceptio private static DockerRunOptions commonDockerOpts(String className) { return new DockerRunOptions(IMAGE_NAME, "/jdk/bin/java", className) - .addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/") + .addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/:z") .addJavaOpts("-cp", "/test-classes/"); } diff --git a/test/hotspot/jtreg/containers/docker/TestLimitsUpdating.java b/test/hotspot/jtreg/containers/docker/TestLimitsUpdating.java index b6682ec37ebc..61f70b917b20 100644 --- a/test/hotspot/jtreg/containers/docker/TestLimitsUpdating.java +++ b/test/hotspot/jtreg/containers/docker/TestLimitsUpdating.java @@ -83,7 +83,7 @@ private static void testLimitUpdates() throws Exception { flag.delete(); started.delete(); DockerRunOptions opts = new DockerRunOptions(imageName, "/jdk/bin/java", "LimitUpdateChecker"); - opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/"); + opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/:z"); opts.addDockerOpts("--volume", sharedtmpdir.getAbsolutePath() + ":/tmp:z"); opts.addDockerOpts("--cpu-period", Integer.toString(CPU_PERIOD)); opts.addDockerOpts("--cpu-quota", Integer.toString(INITIAL_CPU_COUNT * CPU_PERIOD)); diff --git a/test/hotspot/jtreg/containers/docker/TestPids.java b/test/hotspot/jtreg/containers/docker/TestPids.java index acb148c1bc02..9992855da713 100644 --- a/test/hotspot/jtreg/containers/docker/TestPids.java +++ b/test/hotspot/jtreg/containers/docker/TestPids.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2021 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -74,7 +74,7 @@ private static void testPids() throws Exception { private static DockerRunOptions commonOpts() { DockerRunOptions opts = new DockerRunOptions(imageName, "/jdk/bin/java", "PrintContainerInfo"); - opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/"); + opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/:z"); opts.addJavaOpts("-Xlog:os+container=trace", "-cp", "/test-classes/"); Common.addWhiteBoxOpts(opts); return opts; diff --git a/test/jdk/jdk/internal/platform/docker/TestDockerCpuMetrics.java b/test/jdk/jdk/internal/platform/docker/TestDockerCpuMetrics.java index 042996a353bf..72229ce247c9 100644 --- a/test/jdk/jdk/internal/platform/docker/TestDockerCpuMetrics.java +++ b/test/jdk/jdk/internal/platform/docker/TestDockerCpuMetrics.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -102,7 +102,7 @@ private static void testCpuSetMems(String value) throws Exception { Common.logNewTestCase("testCpuSetMems, mem nodes = " + value); DockerRunOptions opts = new DockerRunOptions(imageName, "/jdk/bin/java", "MetricsCpuTester"); - opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/"); + opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/:z"); opts.addDockerOpts("--cpuset-mems=" + value); opts.addJavaOpts("-cp", "/test-classes/").addJavaOpts("--add-exports", "java.base/jdk.internal.platform=ALL-UNNAMED"); opts.addClassOptions("cpumems", value); @@ -117,7 +117,7 @@ private static void testCpuSet(String value, boolean addCgroupMount) throws Exce Common.logNewTestCase("testCpuSet, value = " + value); DockerRunOptions opts = new DockerRunOptions(imageName, "/jdk/bin/java", "MetricsCpuTester"); - opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/"); + opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/:z"); if (addCgroupMount) { // Extra cgroup mount should be ignored by product code opts.addDockerOpts("--volume", "/sys/fs/cgroup:/cgroup-in:ro"); @@ -137,7 +137,7 @@ private static void testCpuQuota(long quota, long period, boolean addCgroupMount Common.logNewTestCase("testCpuQuota, quota = " + quota + ", period = " + period); DockerRunOptions opts = new DockerRunOptions(imageName, "/jdk/bin/java", "MetricsCpuTester"); - opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/"); + opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/:z"); if (addCgroupMount) { // Extra cgroup mount should be ignored by product code opts.addDockerOpts("--volume", "/sys/fs/cgroup:/cgroup-in:ro"); @@ -152,7 +152,7 @@ private static void testCpuShares(int shares) throws Exception { Common.logNewTestCase("testCpuShares, shares = " + shares); DockerRunOptions opts = new DockerRunOptions(imageName, "/jdk/bin/java", "MetricsCpuTester"); - opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/"); + opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/:z"); opts.addDockerOpts("--cpu-shares=" + shares); opts.addJavaOpts("-cp", "/test-classes/").addJavaOpts("--add-exports", "java.base/jdk.internal.platform=ALL-UNNAMED"); opts.addClassOptions("cpushares", shares + ""); @@ -163,7 +163,7 @@ private static void testCpuThrottling(double cpus) throws Exception { Common.logNewTestCase("testCpuThrottling, cpus = " + cpus); DockerRunOptions opts = new DockerRunOptions(imageName, "/jdk/bin/java", "MetricsCpuTester"); - opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/"); + opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/:z"); opts.addDockerOpts("--cpus=" + cpus); opts.addJavaOpts("-cp", "/test-classes/").addJavaOpts("--add-exports", "java.base/jdk.internal.platform=ALL-UNNAMED"); opts.addClassOptions("cpus", cpus + ""); @@ -174,7 +174,7 @@ private static void testComboOptions(String cpuset, int quota, int period, int s Common.logNewTestCase("testComboOptions, shares = " + shares); DockerRunOptions opts = new DockerRunOptions(imageName, "/jdk/bin/java", "MetricsCpuTester"); - opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/"); + opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/:z"); opts.addDockerOpts("--cpuset-cpus", "" + cpuset) .addDockerOpts("--cpu-period=" + period) .addDockerOpts("--cpu-quota=" + quota) diff --git a/test/jdk/jdk/internal/platform/docker/TestDockerMemoryMetrics.java b/test/jdk/jdk/internal/platform/docker/TestDockerMemoryMetrics.java index 78878879783a..5a903d56624b 100644 --- a/test/jdk/jdk/internal/platform/docker/TestDockerMemoryMetrics.java +++ b/test/jdk/jdk/internal/platform/docker/TestDockerMemoryMetrics.java @@ -93,7 +93,7 @@ private static void testMemoryLimit(String value, boolean addCgroupMount) throws Common.logNewTestCase("testMemoryLimit, value = " + value); DockerRunOptions opts = new DockerRunOptions(imageName, "/jdk/bin/java", "MetricsMemoryTester"); - opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/") + opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/:z") .addDockerOpts("--memory=" + value) .addJavaOpts("-cp", "/test-classes/") .addJavaOpts("--add-exports", "java.base/jdk.internal.platform=ALL-UNNAMED") @@ -119,7 +119,7 @@ private static void testMemoryFailCount(String memory, String heap, String memor // killed by the OOM killer. DockerRunOptions preOpts = new DockerRunOptions(imageName, "/jdk/bin/java", "-version"); - preOpts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/") + preOpts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/:z") .addDockerOpts("--memory=" + memory) .addDockerOpts("--memory-swap=" + memoryAndSwap) .addJavaOpts("-XX:+AlwaysPreTouch") @@ -139,7 +139,7 @@ private static void testMemoryFailCount(String memory, String heap, String memor // DockerRunOptions opts = new DockerRunOptions(imageName, "/jdk/bin/java", "MetricsMemoryTester"); - opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/") + opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/:z") .addDockerOpts("--memory=" + memory) .addDockerOpts("--memory-swap=" + memoryAndSwap) .addJavaOpts("-cp", "/test-classes/") @@ -159,7 +159,7 @@ private static void testMemoryAndSwapLimit(String memory, String memandswap) thr Common.logNewTestCase("testMemoryAndSwapLimit, memory = " + memory + ", memory and swap = " + memandswap); DockerRunOptions opts = new DockerRunOptions(imageName, "/jdk/bin/java", "MetricsMemoryTester"); - opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/") + opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/:z") .addDockerOpts("--memory=" + memory) .addDockerOpts("--memory-swap=" + memandswap) .addJavaOpts("-cp", "/test-classes/") @@ -172,7 +172,7 @@ private static void testOomKillFlag(String value, boolean oomKillFlag) throws Ex Common.logNewTestCase("testOomKillFlag, oomKillFlag = " + oomKillFlag); DockerRunOptions opts = new DockerRunOptions(imageName, "/jdk/bin/java", "MetricsMemoryTester"); - opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/") + opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/:z") .addDockerOpts("--memory=" + value); if (!oomKillFlag) { opts.addDockerOpts("--oom-kill-disable"); @@ -188,7 +188,7 @@ private static void testMemorySoftLimit(String mem, String softLimit) throws Exc Common.logNewTestCase("testMemorySoftLimit, memory = " + mem + ", soft limit = " + softLimit); DockerRunOptions opts = new DockerRunOptions(imageName, "/jdk/bin/java", "MetricsMemoryTester"); - opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/") + opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/:z") .addDockerOpts("--memory=" + mem) .addDockerOpts("--memory-reservation=" + softLimit); opts.addJavaOpts("-cp", "/test-classes/") diff --git a/test/jdk/jdk/internal/platform/docker/TestDockerMemoryMetricsSubgroup.java b/test/jdk/jdk/internal/platform/docker/TestDockerMemoryMetricsSubgroup.java index 23a1733893d0..e53b80b39e84 100644 --- a/test/jdk/jdk/internal/platform/docker/TestDockerMemoryMetricsSubgroup.java +++ b/test/jdk/jdk/internal/platform/docker/TestDockerMemoryMetricsSubgroup.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2025, BELLSOFT. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -88,7 +89,7 @@ private static void testMemoryLimitSubgroupV1(String innerSize, String outerGrou new DockerRunOptions(imageName, "sh", "-c"); opts.javaOpts = new ArrayList<>(); opts.appendTestJavaOptions = false; - opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/") + opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/:z") .addDockerOpts("--volume", Utils.TEST_JDK + ":/jdk") .addDockerOpts("--privileged") .addDockerOpts("--cgroupns=" + (privateNamespace ? "private" : "host")) @@ -110,7 +111,7 @@ private static void testMemoryLimitSubgroupV2(String innerSize, String outerGrou new DockerRunOptions(imageName, "sh", "-c"); opts.javaOpts = new ArrayList<>(); opts.appendTestJavaOptions = false; - opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/") + opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/:z") .addDockerOpts("--volume", Utils.TEST_JDK + ":/jdk") .addDockerOpts("--privileged") .addDockerOpts("--cgroupns=" + (privateNamespace ? "private" : "host")) diff --git a/test/jdk/jdk/internal/platform/docker/TestLimitsUpdating.java b/test/jdk/jdk/internal/platform/docker/TestLimitsUpdating.java index 0d3d84ecd4f0..d0b992876a2e 100644 --- a/test/jdk/jdk/internal/platform/docker/TestLimitsUpdating.java +++ b/test/jdk/jdk/internal/platform/docker/TestLimitsUpdating.java @@ -82,7 +82,7 @@ private static void testLimitUpdates() throws Exception { flag.delete(); started.delete(); DockerRunOptions opts = new DockerRunOptions(imageName, "/jdk/bin/java", "LimitUpdateChecker"); - opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/"); + opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/:z"); opts.addDockerOpts("--volume", sharedtmpdir.getAbsolutePath() + ":/tmp:z"); opts.addDockerOpts("--cpu-period", Integer.toString(CPU_PERIOD)); opts.addDockerOpts("--cpu-quota", Integer.toString(INITIAL_CPU_COUNT * CPU_PERIOD)); diff --git a/test/jdk/jdk/internal/platform/docker/TestSystemMetrics.java b/test/jdk/jdk/internal/platform/docker/TestSystemMetrics.java index 1af38843a5ca..498cef122d69 100644 --- a/test/jdk/jdk/internal/platform/docker/TestSystemMetrics.java +++ b/test/jdk/jdk/internal/platform/docker/TestSystemMetrics.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -49,7 +49,7 @@ public static void main(String[] args) throws Exception { Common.logNewTestCase("Test SystemMetrics"); DockerRunOptions opts = new DockerRunOptions(imageName, "/jdk/bin/java", "jdk.test.lib.containers.cgroup.MetricsTester"); - opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/"); + opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/:z"); opts.addDockerOpts("--memory=256m"); opts.addJavaOpts("-cp", "/test-classes/"); opts.addJavaOpts("--add-exports", "java.base/jdk.internal.platform=ALL-UNNAMED"); diff --git a/test/jdk/jdk/internal/platform/docker/TestUseContainerSupport.java b/test/jdk/jdk/internal/platform/docker/TestUseContainerSupport.java index d1a43a2ba941..ef594a335187 100644 --- a/test/jdk/jdk/internal/platform/docker/TestUseContainerSupport.java +++ b/test/jdk/jdk/internal/platform/docker/TestUseContainerSupport.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2020, Red Hat, Inc. - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, 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 @@ -59,7 +59,7 @@ private static void testUseContainerSupport(boolean useContainerSupport) throws DockerRunOptions opts = new DockerRunOptions(imageName, "/jdk/bin/java", "CheckUseContainerSupport"); opts.addClassOptions(Boolean.valueOf(useContainerSupport).toString()); - opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/"); + opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/:z"); if (useContainerSupport) { opts.addJavaOpts("-XX:+UseContainerSupport"); } else { diff --git a/test/lib/jdk/test/lib/containers/docker/Common.java b/test/lib/jdk/test/lib/containers/docker/Common.java index 2cc0510f3a66..8ed7ed819245 100644 --- a/test/lib/jdk/test/lib/containers/docker/Common.java +++ b/test/lib/jdk/test/lib/containers/docker/Common.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, 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 @@ -89,7 +89,7 @@ public static DockerRunOptions newOptsShowSettings(String imageName) { public static DockerRunOptions newOpts(String imageName, String testClass) { DockerRunOptions opts = new DockerRunOptions(imageName, "/jdk/bin/java", testClass); - opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/"); + opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/:z"); opts.addJavaOpts("-Xlog:os+container=trace", "-cp", "/test-classes/"); return opts; } From b52915c46b2f17cd4aeb556bd5157eca9a2bcab8 Mon Sep 17 00:00:00 2001 From: Varada M Date: Mon, 28 Sep 2026 09:47:54 +0000 Subject: [PATCH 052/103] 8390361: PPC64: Add Adler32 intrinsic implementation Reviewed-by: amitkumar, mdoerr --- src/hotspot/cpu/ppc/assembler_ppc.hpp | 4 +- src/hotspot/cpu/ppc/assembler_ppc.inline.hpp | 2 +- src/hotspot/cpu/ppc/stubGenerator_ppc.cpp | 249 +++++++++++++++++++ src/hotspot/cpu/ppc/vm_version_ppc.cpp | 6 +- 4 files changed, 254 insertions(+), 7 deletions(-) diff --git a/src/hotspot/cpu/ppc/assembler_ppc.hpp b/src/hotspot/cpu/ppc/assembler_ppc.hpp index 87c12f3e4ed5..366f2ce0e1f5 100644 --- a/src/hotspot/cpu/ppc/assembler_ppc.hpp +++ b/src/hotspot/cpu/ppc/assembler_ppc.hpp @@ -684,7 +684,7 @@ class Assembler : public AbstractAssembler { VMHADDSHS_OPCODE=(4u << OPCODE_SHIFT | 32u ), VMHRADDSHS_OPCODE=(4u << OPCODE_SHIFT | 33u ), VMLADDUHM_OPCODE=(4u << OPCODE_SHIFT | 34u ), - VMSUBUHM_OPCODE= (4u << OPCODE_SHIFT | 36u ), + VMSUMUBM_OPCODE= (4u << OPCODE_SHIFT | 36u ), VMSUMMBM_OPCODE= (4u << OPCODE_SHIFT | 37u ), VMSUMSHM_OPCODE= (4u << OPCODE_SHIFT | 40u ), VMSUMSHS_OPCODE= (4u << OPCODE_SHIFT | 41u ), @@ -2292,7 +2292,7 @@ class Assembler : public AbstractAssembler { inline void vmhaddshs(VectorRegister d, VectorRegister a, VectorRegister b, VectorRegister c); inline void vmhraddshs(VectorRegister d,VectorRegister a, VectorRegister b, VectorRegister c); inline void vmladduhm(VectorRegister d, VectorRegister a, VectorRegister b, VectorRegister c); - inline void vmsubuhm( VectorRegister d, VectorRegister a, VectorRegister b, VectorRegister c); + inline void vmsumubm( VectorRegister d, VectorRegister a, VectorRegister b, VectorRegister c); inline void vmsummbm( VectorRegister d, VectorRegister a, VectorRegister b, VectorRegister c); inline void vmsumshm( VectorRegister d, VectorRegister a, VectorRegister b, VectorRegister c); inline void vmsumshs( VectorRegister d, VectorRegister a, VectorRegister b, VectorRegister c); diff --git a/src/hotspot/cpu/ppc/assembler_ppc.inline.hpp b/src/hotspot/cpu/ppc/assembler_ppc.inline.hpp index 7929e895923b..ec45c96415eb 100644 --- a/src/hotspot/cpu/ppc/assembler_ppc.inline.hpp +++ b/src/hotspot/cpu/ppc/assembler_ppc.inline.hpp @@ -1011,7 +1011,7 @@ inline void Assembler::vmuluwm( VectorRegister d, VectorRegister a, VectorRegist inline void Assembler::vmhaddshs(VectorRegister d,VectorRegister a, VectorRegister b, VectorRegister c) { emit_int32( VMHADDSHS_OPCODE | vrt(d) | vra(a) | vrb(b)| vrc(c)); } inline void Assembler::vmhraddshs(VectorRegister d,VectorRegister a,VectorRegister b, VectorRegister c) { emit_int32( VMHRADDSHS_OPCODE| vrt(d) | vra(a) | vrb(b)| vrc(c)); } inline void Assembler::vmladduhm(VectorRegister d,VectorRegister a, VectorRegister b, VectorRegister c) { emit_int32( VMLADDUHM_OPCODE | vrt(d) | vra(a) | vrb(b)| vrc(c)); } -inline void Assembler::vmsubuhm(VectorRegister d, VectorRegister a, VectorRegister b, VectorRegister c) { emit_int32( VMSUBUHM_OPCODE | vrt(d) | vra(a) | vrb(b)| vrc(c)); } +inline void Assembler::vmsumubm(VectorRegister d, VectorRegister a, VectorRegister b, VectorRegister c) { emit_int32( VMSUMUBM_OPCODE | vrt(d) | vra(a) | vrb(b)| vrc(c)); } inline void Assembler::vmsummbm(VectorRegister d, VectorRegister a, VectorRegister b, VectorRegister c) { emit_int32( VMSUMMBM_OPCODE | vrt(d) | vra(a) | vrb(b)| vrc(c)); } inline void Assembler::vmsumshm(VectorRegister d, VectorRegister a, VectorRegister b, VectorRegister c) { emit_int32( VMSUMSHM_OPCODE | vrt(d) | vra(a) | vrb(b)| vrc(c)); } inline void Assembler::vmsumshs(VectorRegister d, VectorRegister a, VectorRegister b, VectorRegister c) { emit_int32( VMSUMSHS_OPCODE | vrt(d) | vra(a) | vrb(b)| vrc(c)); } diff --git a/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp b/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp index 351d177bb1f9..cbb3af06c8a5 100644 --- a/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp +++ b/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp @@ -3719,6 +3719,252 @@ class StubGenerator: public StubCodeGenerator { return stub_address; } + address generate_updateBytesAdler32() { + + __ align(CodeEntryAlignment); + StubId stub_id = StubId::stubgen_updateBytesAdler32_id; + StubCodeMark mark(this, stub_id); + address start = __ function_entry(); + + const uint32_t BASE = 65521; + const uint32_t NMAX = 5552; + + Label L_nmax; + Label L_nmax_loop; + Label L_by16; + Label L_by16_loop; + Label L_by1; + Label L_by1_loop; + Label L_do_mod; + Label L_combine; + + Register adler = R3_ARG1; + Register buf = R4_ARG2; + Register len = R5_ARG3; + + Register s1 = R6; + Register s2 = R7; + Register base = R8; + Register nmax = R9; + Register count = R10; + Register tmp0 = R11; + Register tmp1 = R12; + Register magic = R2; + + VectorRegister vdata = VR0; + VectorRegister vones = VR1; + VectorRegister vweights = VR2; + VectorRegister vacc1 = VR3; + VectorRegister vacc2 = VR4; + VectorRegister vp = VR5; + + __ load_const_optimized(base, BASE); + __ load_const_optimized(nmax, NMAX); + + // magic number to compute division by BASE in combination with right shift by 15 + __ load_const_optimized(magic, (uint32_t)0x80078071, R0); + + // load tables + __ compute_vp_for_byte_vector_unaligned(vp, vacc1); + + // load adler ones + // {1, <*16 times>} + __ vspltisb(vones, 1); + + // load adler weights + // {16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1} + __ li(R0, 0); + __ lvsl(vweights, R0); + __ vspltisb(vacc1, 15); + __ vxor(vweights, vweights, vacc1); + __ vaddubm(vweights, vweights, vones); + + // split Adler + __ clrldi(s1, adler, 48); // low 16 bits + __ srwi(s2, adler, 16); // high 16 bits + + // len == 0 ? + __ cmpwi(CR0, len, 0); + __ beq(CR0, L_combine); + + // len < 16 ? + __ cmpwi(CR0, len, 16); + __ blt(CR0, L_by1); + + // len >= NMAX ? + __ load_const_optimized(count, (int)(NMAX / 16)); + __ bind(L_nmax); + + __ cmpw(CR0, len, nmax); + __ blt(CR0, L_by16); + + __ mtctr(count); + __ align(32); + __ bind(L_nmax_loop); + + generate_updateBytesAdler32_accum(s1, s2, buf, tmp0, tmp1, vdata, + vones, vweights, vacc1, vacc2, vp); + + __ bdnz(L_nmax_loop); + + // s1 = s1 % BASE + __ mulhwu(tmp0, s1, magic); + __ srwi(tmp1, tmp0, 15); + __ mullw(tmp1, tmp1, base); + __ subf(s1, tmp1, s1); + + // s2 = s2 % BASE + __ mulhwu(tmp0, s2, magic); + __ srwi(tmp1, tmp0, 15); + __ mullw(tmp1, tmp1, base); + __ subf(s2, tmp1, s2); + + __ subf(len, nmax, len); + + __ cmpw(CR0, len, nmax); + __ bge(CR0, L_nmax); + + // remaining 16 byte chunks + __ bind(L_by16); + + __ cmpwi(CR0, len, 16); + __ blt(CR0, L_by1); + + __ align(32); + __ bind(L_by16_loop); + + generate_updateBytesAdler32_accum(s1, s2, buf, tmp0, tmp1, vdata, + vones, vweights, vacc1, vacc2, vp); + + __ addi(len, len, -16); + + __ cmpwi(CR0, len, 16); + __ bge(CR0, L_by16_loop); + + // handles remaining bytes when len < 16 + __ bind(L_by1); + + __ cmpwi(CR0, len, 0); + __ beq(CR0, L_do_mod); + __ mtctr(len); + __ align(32); + __ bind(L_by1_loop); + + __ lbz(tmp0, 0, buf); + __ addi(buf, buf, 1); + __ add(s1, s1, tmp0); + __ add(s2, s2, s1); + + __ bdnz(L_by1_loop); + + // final reduction + __ bind(L_do_mod); + + // s1 = s1 % base + __ mulhwu(tmp0, s1, magic); + __ srwi(tmp1, tmp0, 15); + __ mullw(tmp1, tmp1, base); + __ subf(s1, tmp1, s1); + + // s2 = s2 % base + __ mulhwu(tmp0, s2, magic); + __ srwi(tmp1, tmp0, 15); + __ mullw(tmp1, tmp1, base); + __ subf(s2, tmp1, s2); + + // combine + __ bind(L_combine); + + __ slwi(tmp0, s2, 16); + __ orr(adler, s1, tmp0); + __ blr(); + return start; + } + + void generate_updateBytesAdler32_accum(Register s1, Register s2, Register buf, + Register tmp0, Register tmp1, VectorRegister vdata, + VectorRegister vones, VectorRegister vweights, + VectorRegister vacc1, VectorRegister vacc2, VectorRegister vp) { + + // load 16 input bytes + __ load_byte_vector_unaligned(vdata, 0, buf, tmp0, vp); + + // compute the weighted sum + + // accumulator cleared to zero + __ vspltisb(vacc2, 0); + + // (i/p bytes) vdata = {b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15} + // (weights) vweights = {16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1} + // (accum) vacc2 = {0, (x16 times) } + // L0 : b0*16 + b1*15 + b2*14 + b3*13 + 0 + // L1 : b4*12 + b5*11 + b6*10 + b7*9 + 0 + // L2 : b8*8 + b9*7 + b10*6 + b11*5 + 0 + // L3 : b12*4 + b13*3 + b14*2 + b15*1 + 0 + // vacc2 = {L0, L1, L2, L3} + __ vmsumubm(vacc2, vdata, vweights, vacc2); + + // reduce 4 lanes into 1 scalar + // 1. vacc1 = rotate vacc2 by 8 bytes + // vacc1 = {L2, L3, L0, L1} + // 2. add lanes together; vacc2 = vacc2 + vacc1 + // vacc2 = {L0+L2, L1+L3, L2+L0, L3+L1} + __ vsldoi(vacc1, vacc2, vacc2, 8); + __ vadduwm(vacc2, vacc2, vacc1); + + // rotate by 4 bytes and + // add all 4 lanes equal total + __ vsldoi(vacc1, vacc2, vacc2, 4); + __ vadduwm(vacc2, vacc2, vacc1); + + // extract scalar from lane 0 + // tmp0 = weighted_sum + __ mfvsrwz(tmp0, vacc2.to_vsr()); + + // s2 += s1*16 + weighted_sum + __ slwi(tmp1, s1, 4); + __ add(tmp0, tmp0, tmp1); + __ add(s2, s2, tmp0); + + // compute the byte sum + + // accumulator cleared to zero + __ vspltisb(vacc1, 0); + + // (i/p bytes) vdata = {b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15} + // (ones) vones = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1} + // (accum) vacc1 = {0, <*16 times>} + // L0 = b0 + b1 + b2 + b3 + 0 + // L1 = b4 + b5 + b6 + b7 + 0 + // L2 = b8 + b9 + b10 + b11 + 0 + // L3 = b12 + b13 + b14 + b15 + 0 + // vacc1 = {L0, L1, L2, L3} + __ vmsumubm(vacc1, vdata, vones, vacc1); + + // reduce 4 lanes into 1 scalar + // 1. vacc2 = rotate vacc1 by 8 bytes + // vacc2 = {L2, L3, L0, L1} + // 2. add lanes together; vacc1 = vacc1 + vacc2 + // vacc1 = {L0+L2, L1+L3, L2+L0, L3+L1} + __ vsldoi(vacc2, vacc1, vacc1, 8); + __ vadduwm(vacc1, vacc1, vacc2); + + // rotate by 4 bytes and + // add all 4 lanes equal total + __ vsldoi(vacc2, vacc1, vacc1, 4); + __ vadduwm(vacc1, vacc1, vacc2); + + // extract scalar from lane 0 + // tmp0 = byte_sum + __ mfvsrwz(tmp0, vacc1.to_vsr()); + + // s1 = s1 + byte_sum + __ add(s1, s1, tmp0); + + // advance the buffer pointer + __ addi(buf, buf, 16); + } + #ifdef VM_LITTLE_ENDIAN // The following Base64 decode intrinsic is based on an algorithm outlined // in here: @@ -5016,6 +5262,9 @@ void generate_lookup_secondary_supers_table_stub() { StubRoutines::_sha512_implCompress = generate_sha512_implCompress(StubId::stubgen_sha512_implCompress_id); StubRoutines::_sha512_implCompressMB = generate_sha512_implCompress(StubId::stubgen_sha512_implCompressMB_id); } + if (UseAdler32Intrinsics) { + StubRoutines::_updateBytesAdler32 = generate_updateBytesAdler32(); + } #ifdef VM_LITTLE_ENDIAN // Currently supported on PPC64LE only diff --git a/src/hotspot/cpu/ppc/vm_version_ppc.cpp b/src/hotspot/cpu/ppc/vm_version_ppc.cpp index 1752040c8155..adb819082e90 100644 --- a/src/hotspot/cpu/ppc/vm_version_ppc.cpp +++ b/src/hotspot/cpu/ppc/vm_version_ppc.cpp @@ -249,10 +249,8 @@ void VM_Version::initialize() { } } - // TODO: Provide implementation. - if (UseAdler32Intrinsics) { - warning("Adler32Intrinsics not available on this CPU."); - FLAG_SET_DEFAULT(UseAdler32Intrinsics, false); + if (FLAG_IS_DEFAULT(UseAdler32Intrinsics)) { + FLAG_SET_DEFAULT(UseAdler32Intrinsics, true); } // The AES intrinsic stubs require AES instruction support. From 06b0d76e7fdf3b828fa935d228cc4f2b55755ab5 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Mon, 28 Sep 2026 09:50:14 +0000 Subject: [PATCH 053/103] 8393009: G1: Refinement errorneously subtracts yield duration from sweep duration Reviewed-by: ayang --- src/hotspot/share/gc/g1/g1ConcurrentRefineStats.cpp | 2 +- src/hotspot/share/gc/g1/g1ConcurrentRefineStats.hpp | 6 +++--- src/hotspot/share/gc/g1/g1ConcurrentRefineStats.inline.hpp | 6 +++--- src/hotspot/share/gc/g1/g1Policy.cpp | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1ConcurrentRefineStats.cpp b/src/hotspot/share/gc/g1/g1ConcurrentRefineStats.cpp index 4fa32b388bd6..687b13e723d1 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentRefineStats.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentRefineStats.cpp @@ -37,7 +37,7 @@ void G1ConcurrentRefineStats::add_atomic(const G1LocalRefineStats* other) { } void G1ConcurrentRefineStats::reset() { - _sweep_duration.store_relaxed(0); + _sweep_work_duration.store_relaxed(0); _yield_during_sweep_duration.store_relaxed(0); _cards_scanned.store_relaxed(0); _cards_clean.store_relaxed(0); diff --git a/src/hotspot/share/gc/g1/g1ConcurrentRefineStats.hpp b/src/hotspot/share/gc/g1/g1ConcurrentRefineStats.hpp index 6f4af71081b2..f7407a598462 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentRefineStats.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentRefineStats.hpp @@ -51,8 +51,8 @@ struct G1LocalRefineStats { // Global statistics for concurrent refinement processing. class G1ConcurrentRefineStats : public CHeapObj { - Atomic _sweep_duration; // Time spent sweeping the table finding non-clean cards - // and refining them. + Atomic _sweep_work_duration; // Time spent sweeping the table finding non-clean cards + // and refining them without yields. Atomic _yield_during_sweep_duration; // Time spent yielding during the sweep (not doing the sweep). Atomic _cards_scanned; // Total number of cards scanned. @@ -67,7 +67,7 @@ class G1ConcurrentRefineStats : public CHeapObj { public: // Time spent performing sweeping the refinement table (includes actual refinement, // but not yield time). - inline jlong sweep_duration() const; + inline jlong sweep_work_duration() const; inline jlong yield_during_sweep_duration() const; inline jlong refine_duration() const; diff --git a/src/hotspot/share/gc/g1/g1ConcurrentRefineStats.inline.hpp b/src/hotspot/share/gc/g1/g1ConcurrentRefineStats.inline.hpp index 2ef35caab087..56e019340125 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentRefineStats.inline.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentRefineStats.inline.hpp @@ -27,8 +27,8 @@ #include "gc/g1/g1ConcurrentRefineStats.hpp" -inline jlong G1ConcurrentRefineStats::sweep_duration() const { - return _sweep_duration.load_relaxed() - yield_during_sweep_duration(); +inline jlong G1ConcurrentRefineStats::sweep_work_duration() const { + return _sweep_work_duration.load_relaxed(); } inline jlong G1ConcurrentRefineStats::yield_during_sweep_duration() const { @@ -80,7 +80,7 @@ inline size_t G1ConcurrentRefineStats::cards_to_cset() const { } inline void G1ConcurrentRefineStats::inc_sweep_duration(jlong t) { - _sweep_duration.fetch_then_add(t, memory_order_relaxed); + _sweep_work_duration.fetch_then_add(t, memory_order_relaxed); } inline void G1ConcurrentRefineStats::inc_yield_during_sweep_duration(jlong t) { diff --git a/src/hotspot/share/gc/g1/g1Policy.cpp b/src/hotspot/share/gc/g1/g1Policy.cpp index 53f3597db121..066d057c4d65 100644 --- a/src/hotspot/share/gc/g1/g1Policy.cpp +++ b/src/hotspot/share/gc/g1/g1Policy.cpp @@ -612,7 +612,7 @@ void G1Policy::record_full_collection_end(size_t allocation_word_size) { static void log_refinement_stats(const G1ConcurrentRefineStats& stats) { log_debug(gc, refine, stats) ("Refinement: sweep: %.2fms, yield: %.2fms refined: %zu, dirtied: %zu", - TimeHelper::counter_to_millis(stats.sweep_duration()), + TimeHelper::counter_to_millis(stats.sweep_work_duration()), TimeHelper::counter_to_millis(stats.yield_during_sweep_duration()), stats.refined_cards(), stats.cards_pending()); @@ -624,7 +624,7 @@ void G1Policy::record_refinement_stats(G1ConcurrentRefineStats* refine_stats) { // Record the rate at which cards were refined. // Don't update the rate if the current sample is empty or time is zero (which is // the case during GC). - double refinement_time = TimeHelper::counter_to_millis(refine_stats->sweep_duration()); + double refinement_time = TimeHelper::counter_to_millis(refine_stats->sweep_work_duration()); size_t refined_cards = refine_stats->refined_cards(); if ((refined_cards > 0) && (refinement_time > 0)) { double rate = refined_cards / refinement_time; From cb5bb2d3ddc687bdd1121fcd0680a60018126c7c Mon Sep 17 00:00:00 2001 From: ByeongGyu Jeon Date: Mon, 28 Sep 2026 11:00:02 +0000 Subject: [PATCH 054/103] 8392804: Mark test test/hotspot/jtreg/gc/g1/TestGCLogMessages.java as vm.flagless Reviewed-by: tschatzl --- test/hotspot/jtreg/gc/g1/TestGCLogMessages.java | 1 + 1 file changed, 1 insertion(+) diff --git a/test/hotspot/jtreg/gc/g1/TestGCLogMessages.java b/test/hotspot/jtreg/gc/g1/TestGCLogMessages.java index 16b73d4c354c..10d61dd3da99 100644 --- a/test/hotspot/jtreg/gc/g1/TestGCLogMessages.java +++ b/test/hotspot/jtreg/gc/g1/TestGCLogMessages.java @@ -29,6 +29,7 @@ * @summary Ensure the output for a minor GC with G1 * includes the expected necessary messages. * @requires vm.gc.G1 + * @requires vm.flagless * @library /test/lib * @modules java.base/jdk.internal.misc * java.management From a6a071ef9dc40f5ca782d698db44ddebbf52c6a4 Mon Sep 17 00:00:00 2001 From: George Wort Date: Mon, 28 Sep 2026 12:42:29 +0000 Subject: [PATCH 055/103] 8384510: Improve lookup_secondary_supers_table_const on AArch64 Co-authored-by: Andrew Haley Reviewed-by: aph, adinn --- src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp index ca7299b04ea9..e1bbb4a6aecd 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp @@ -1693,10 +1693,18 @@ bool MacroAssembler::lookup_secondary_supers_table_const(Register r_sub_klass, // Get the first array index that can contain super_klass into r_array_index. if (bit != 0) { - shld(vtemp, vtemp, Klass::SECONDARY_SUPERS_TABLE_MASK - bit); + int shift = Klass::SECONDARY_SUPERS_TABLE_MASK - bit; + shld(vtemp, vtemp, shift); cnt(vtemp, T8B, vtemp); - addv(vtemp, T8B, vtemp); - fmovd(r_array_index, vtemp); + // If the left shift is so great that all bytes below the most + // significant are zero, don't add across all byte lanes, just use + // the top byte. + if (BitsPerLong - shift <= BitsPerByte) { + umov(r_array_index, vtemp, B, BytesPerLong - 1); + } else { + addv(vtemp, T8B, vtemp); + umov(r_array_index, vtemp, B, 0); + } } else { mov(r_array_index, (u1)1); } From 218db6ccf31e0d899e15802f7fc1eda4e8d77a35 Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Mon, 28 Sep 2026 13:44:44 +0000 Subject: [PATCH 056/103] 8393083: GHA: Windows ARM64 runners use MSVC 2026 Reviewed-by: sgehwolf, erikj --- .github/workflows/build-windows.yml | 2 +- .github/workflows/main.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index e95f36a388aa..dc1aedd362d9 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -147,7 +147,7 @@ jobs: run: | # Run Visual Studio Installer '/c/Program Files (x86)/Microsoft Visual Studio/Installer/vs_installer.exe' \ - modify --quiet --installPath 'C:\Program Files\Microsoft Visual Studio\2022\Enterprise' \ + modify --quiet --installPath 'C:\Program Files\Microsoft Visual Studio\18\Enterprise' \ --add Microsoft.VisualStudio.Component.VC.${{ inputs.msvc-toolset-version }}.ARM64 if: ${{ ( inputs.architecture == 'ARM64') && (steps.toolchain-check-arm64.outputs.toolchain-installed != 'true') }} diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6151bef13d9c..8f5e16d2e534 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -385,7 +385,7 @@ jobs: platform: windows-aarch64 runs-on: windows-11-arm architecture: 'ARM64' - msvc-toolset-version: '14.44' + msvc-toolset-version: '14.51' configure-arguments: ${{ github.event.inputs.configure-arguments }} make-arguments: ${{ github.event.inputs.make-arguments }} dry-run: ${{ needs.prepare.outputs.dry-run == 'true' }} From 3574dfe98ba205b21c835e949fb2652905cfde8e Mon Sep 17 00:00:00 2001 From: Arno Zeller Date: Mon, 28 Sep 2026 14:25:11 +0000 Subject: [PATCH 057/103] 8392780: Sporadic failure of test ServerRenegoWithTwoVersions.java and NoImpactServerRenego.java Reviewed-by: clanger, mbaesken --- .../SSLSocketImpl/NoImpactServerRenego.java | 19 +++++++++---------- .../ServerRenegoWithTwoVersions.java | 19 +++++++++---------- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/test/jdk/sun/security/ssl/SSLSocketImpl/NoImpactServerRenego.java b/test/jdk/sun/security/ssl/SSLSocketImpl/NoImpactServerRenego.java index 35a5a3cc035f..99792a4c2f6e 100644 --- a/test/jdk/sun/security/ssl/SSLSocketImpl/NoImpactServerRenego.java +++ b/test/jdk/sun/security/ssl/SSLSocketImpl/NoImpactServerRenego.java @@ -41,22 +41,24 @@ import java.io.*; import java.net.*; import java.security.Security; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import javax.net.ssl.*; public class NoImpactServerRenego implements HandshakeCompletedListener { - static byte handshakesCompleted = 0; + final CountDownLatch handshakeLatch = new CountDownLatch(2); /* * Define what happens when handshaking is completed */ public void handshakeCompleted(HandshakeCompletedEvent event) { synchronized (this) { - handshakesCompleted++; + handshakeLatch.countDown(); System.out.println("Session: " + event.getSession().toString()); System.out.println("Seen handshake completed #" + - handshakesCompleted); + (2 - handshakeLatch.getCount())); } } @@ -274,14 +276,11 @@ public static void main(String[] args) throws Exception { } /* - * Give the Handshaker Thread a chance to run + * Wait until both HandshakeCompleted events have been delivered, + * with a timeout to avoid hanging forever if something goes wrong. */ - Thread.sleep(1000); - - synchronized (this) { - if (handshakesCompleted != 2) { - throw new Exception("Didn't see 2 handshake completed events."); - } + if (!handshakeLatch.await(10, TimeUnit.SECONDS)) { + throw new Exception("Didn't see 2 handshake completed events."); } } diff --git a/test/jdk/sun/security/ssl/SSLSocketImpl/ServerRenegoWithTwoVersions.java b/test/jdk/sun/security/ssl/SSLSocketImpl/ServerRenegoWithTwoVersions.java index f62e7a9e1cf2..0181f2595335 100644 --- a/test/jdk/sun/security/ssl/SSLSocketImpl/ServerRenegoWithTwoVersions.java +++ b/test/jdk/sun/security/ssl/SSLSocketImpl/ServerRenegoWithTwoVersions.java @@ -40,22 +40,24 @@ import java.io.*; import java.net.*; import java.security.Security; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import javax.net.ssl.*; public class ServerRenegoWithTwoVersions implements HandshakeCompletedListener { - static byte handshakesCompleted = 0; + final CountDownLatch handshakeLatch = new CountDownLatch(2); /* * Define what happens when handshaking is completed */ public void handshakeCompleted(HandshakeCompletedEvent event) { synchronized (this) { - handshakesCompleted++; + handshakeLatch.countDown(); System.out.println("Session: " + event.getSession().toString()); System.out.println("Seen handshake completed #" + - handshakesCompleted); + (2 - handshakeLatch.getCount())); } } @@ -271,14 +273,11 @@ public static void main(String[] args) throws Exception { } /* - * Give the Handshaker Thread a chance to run + * Wait until both HandshakeCompleted events have been delivered, + * with a timeout to avoid hanging forever if something goes wrong. */ - Thread.sleep(1000); - - synchronized (this) { - if (handshakesCompleted != 2) { - throw new Exception("Didn't see 2 handshake completed events."); - } + if (!handshakeLatch.await(10, TimeUnit.SECONDS)) { + throw new Exception("Didn't see 2 handshake completed events."); } } From 2a86c264f270ff8538ac3902d7b174e6f3a11768 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20Gr=C3=B6nlund?= Date: Mon, 28 Sep 2026 14:30:29 +0000 Subject: [PATCH 058/103] 8393015: JFR: ResizeableHashTable only resizes if you ask kindly Reviewed-by: egahlin --- .../share/jfr/support/methodtracer/jfrClassFilterClosure.cpp | 1 + src/hotspot/share/jfr/support/methodtracer/jfrMethodTracer.cpp | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/hotspot/share/jfr/support/methodtracer/jfrClassFilterClosure.cpp b/src/hotspot/share/jfr/support/methodtracer/jfrClassFilterClosure.cpp index 803493f4a067..238763863c30 100644 --- a/src/hotspot/share/jfr/support/methodtracer/jfrClassFilterClosure.cpp +++ b/src/hotspot/share/jfr/support/methodtracer/jfrClassFilterClosure.cpp @@ -66,6 +66,7 @@ void JfrFilterClassClosure::do_klass(Klass* k) { if (!_classes_to_modify->contains(klass_id)) { jclass mirror = mirror_as_local_jni_handle(ik, _thread); _classes_to_modify->put(klass_id, mirror); + _classes_to_modify->maybe_grow(); } } } diff --git a/src/hotspot/share/jfr/support/methodtracer/jfrMethodTracer.cpp b/src/hotspot/share/jfr/support/methodtracer/jfrMethodTracer.cpp index 90b81c760380..1d704f05a154 100644 --- a/src/hotspot/share/jfr/support/methodtracer/jfrMethodTracer.cpp +++ b/src/hotspot/share/jfr/support/methodtracer/jfrMethodTracer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, 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 @@ -104,6 +104,7 @@ static void register_placeholder(const InstanceKlass* ik, const JfrMethodProcess JfrTraceTagging::tag_sticky(ik, mp); assert(!placeholder_table()->contains(JfrTraceId::load_raw(ik)), "invariant"); placeholder_table()->put(JfrTraceId::load_raw(ik), ik); + placeholder_table()->maybe_grow(); } // Quick and unlocked check to see if the Method Tracer has been activated. From 7b8c2716a31342a20d6fae1c4c3ce6ffbe74e56a Mon Sep 17 00:00:00 2001 From: Heetae Park Date: Mon, 28 Sep 2026 14:49:20 +0000 Subject: [PATCH 059/103] 8392624: Example in DRBGParameters.Capability class description has typo and is incorrect Reviewed-by: weijun --- .../share/classes/java/security/DrbgParameters.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/java.base/share/classes/java/security/DrbgParameters.java b/src/java.base/share/classes/java/security/DrbgParameters.java index 5cd8b171c72f..3362b00a19d3 100644 --- a/src/java.base/share/classes/java/security/DrbgParameters.java +++ b/src/java.base/share/classes/java/security/DrbgParameters.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, 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 @@ -260,8 +260,8 @@ private DrbgParameters() { *

      * Capability requested = ...;
      * SecureRandom s = SecureRandom.getInstance("DRBG",
-     *         DrbgParameters(-1, requested, null));
-     * Capability effective = ((DrbgParametes.Initiate) s.getParameters())
+     *         DrbgParameters.instantiation(-1, requested, null));
+     * Capability effective = ((DrbgParameters.Instantiation) s.getParameters())
      *         .getCapability();
*
* From 1369cf2620c4cece0d770211da2e814378034850 Mon Sep 17 00:00:00 2001 From: Nizar Benalla Date: Mon, 28 Sep 2026 17:06:37 +0000 Subject: [PATCH 060/103] 8390674: Markdown Code link [Class.EnumSubclass#Enum] throws NoSuchElementException Reviewed-by: hannesw --- .../markdown/MarkdownTransformer.java | 4 +- .../testMarkdown/TestMarkdownLinks.java | 38 ++++++++++++++++++- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/src/jdk.internal.md/share/classes/jdk/internal/markdown/MarkdownTransformer.java b/src/jdk.internal.md/share/classes/jdk/internal/markdown/MarkdownTransformer.java index d50af3a9bdd5..ca0bd253e30c 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/markdown/MarkdownTransformer.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/markdown/MarkdownTransformer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, 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 @@ -806,7 +806,7 @@ public void visit(Link link) { var saveTrees = trees; trees = new ArrayList<>(); try { - copyStartPos = getStartPos(link.getFirstChild()); + copyStartPos = getStartPos(link) + 1; visitChildren(link); copyTo(getEndPos(link.getLastChild())); diff --git a/test/langtools/jdk/javadoc/doclet/testMarkdown/TestMarkdownLinks.java b/test/langtools/jdk/javadoc/doclet/testMarkdown/TestMarkdownLinks.java index 24e9a798e1b6..3bd3e26db453 100644 --- a/test/langtools/jdk/javadoc/doclet/testMarkdown/TestMarkdownLinks.java +++ b/test/langtools/jdk/javadoc/doclet/testMarkdown/TestMarkdownLinks.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, 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 @@ -23,7 +23,7 @@ /* * @test - * @bug 8298405 + * @bug 8298405 8390674 * @summary Markdown support in the standard doclet * @library /tools/lib ../../lib * @modules jdk.javadoc/jdk.javadoc.internal.tool @@ -100,6 +100,40 @@ public void m2() { } } + @Test + public void testLinkWithLineBreak(Path base) throws Exception { + Path src = base.resolve("src"); + tb.writeJavaFiles(src, """ + package p; + public class OtherClass { + ///[ + /// OtherClass.InnerClass#FIRST] + public void m1() {} + + ///[\s\s + /// OtherClass.InnerClass#SECOND] + public void m2() {} + + public static enum InnerClass { + FIRST, + SECOND, + THIRD; + } + } + """); + + javadoc("-d", base.resolve("api").toString(), + "-Xdoclint:none", + "--source-path", + src.toString(), + "p"); + + checkExit(Exit.OK); + checkOutput("p/OtherClass.html", true, + "", + ""); + } + @Test public void testLinkWithDescription(Path base) throws Exception { Path src = base.resolve("src"); From 4773ea56cb4bc1532eabfede712ed8b5a918e757 Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Mon, 28 Sep 2026 17:32:28 +0000 Subject: [PATCH 061/103] 8392649: Shenandoah: Verification processes nmethods with wrong GC state Reviewed-by: wkemper, kdnilsen --- .../shenandoahBarrierSetNMethod.cpp | 3 ++ .../gc/shenandoah/shenandoahRootVerifier.cpp | 39 ++++++++++++++++--- .../gc/shenandoah/shenandoahRootVerifier.hpp | 5 +++ 3 files changed, 42 insertions(+), 5 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSetNMethod.cpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSetNMethod.cpp index 5adf5b034506..753d564a647f 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSetNMethod.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSetNMethod.cpp @@ -31,6 +31,7 @@ #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahLock.hpp" #include "gc/shenandoah/shenandoahNMethod.inline.hpp" +#include "gc/shenandoah/shenandoahRootVerifier.hpp" #include "gc/shenandoah/shenandoahStackChunkGCData.hpp" #include "gc/shenandoah/shenandoahStackWatermark.hpp" #include "gc/shenandoah/shenandoahThreadLocalData.hpp" @@ -41,6 +42,8 @@ #include "runtime/threadWXSetters.inline.hpp" bool ShenandoahBarrierSetNMethod::nmethod_entry_barrier(nmethod* nm) { + assert(!ShenandoahGCStateResetter::is_active(), "Resetter must not be active"); + if (!is_armed(nm)) { // Some other thread got here first and healed the oops. // No need to continue. We only need to sync up the changes done by others. diff --git a/src/hotspot/share/gc/shenandoah/shenandoahRootVerifier.cpp b/src/hotspot/share/gc/shenandoah/shenandoahRootVerifier.cpp index c678760faf68..eb4eb78fa6dc 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahRootVerifier.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahRootVerifier.cpp @@ -35,26 +35,55 @@ #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahPhaseTimings.hpp" #include "gc/shenandoah/shenandoahRootVerifier.hpp" +#include "gc/shenandoah/shenandoahSATBMarkQueueSet.hpp" #include "gc/shenandoah/shenandoahScanRemembered.inline.hpp" #include "gc/shenandoah/shenandoahUtils.hpp" #include "runtime/javaThread.hpp" #include "runtime/jniHandles.hpp" +#include "runtime/stackWatermarkSet.hpp" #include "runtime/threads.hpp" +#include "runtime/threadSMR.hpp" #include "utilities/debug.hpp" #include "utilities/enumIterator.hpp" +Atomic ShenandoahGCStateResetter::_active_count; + ShenandoahGCStateResetter::ShenandoahGCStateResetter() : _heap(ShenandoahHeap::heap()), _saved_gc_state(_heap->gc_state()), _saved_gc_state_changed(_heap->_gc_state_changed) { - // Clear state to deactivate barriers. Indicate that state has changed - // so that verifier threads will use this value, rather than thread local - // values (which we are _not_ changing here). - _heap->_gc_state.clear(); - _heap->_gc_state_changed = true; + + // Need to complete GC processing before deactivating the barriers. + // Once the GC state is dropped, we cannot allow GC-state dependent fixups, + // that would patch barriers or process the oops incorrectly. Verifier code + // can enter stack watermark processing as part of regular thread root work. + // This pretends Java threads have fixed up all state before we go for verification. + // Do this unconditionally in all callers to get to the same consensus point. + for (JavaThreadIteratorWithHandle jtiwh; JavaThread* jt = jtiwh.next();) { + StackWatermarkSet::finish_processing(jt, nullptr, StackWatermarkKind::gc); + } + + // Processing stack frames can also enqueue elements in current thread SATB. + // We need to flush it here, to avoid triggering the empty SATB verification code. + ShenandoahSATBMarkQueueSet& satb_qs = ShenandoahBarrierSet::satb_mark_queue_set(); + satb_qs.flush_queue(ShenandoahThreadLocalData::satb_mark_queue(Thread::current())); + + // From this moment on, level-1 resetter is active. + if (_active_count.compare_set(0, 1, memory_order_relaxed)) { + // Clear state to deactivate barriers. Indicate that state has changed + // so that verifier threads will use this value, rather than thread local + // values (which we are _not_ changing here). + _heap->_gc_state.clear(); + _heap->_gc_state_changed = true; + } } ShenandoahGCStateResetter::~ShenandoahGCStateResetter() { + if (_active_count.add_then_fetch(-1, memory_order_relaxed) > 0) { + // Nested, nothing to do. + return; + } + _heap->_gc_state.set(_saved_gc_state); _heap->_gc_state_changed = _saved_gc_state_changed; assert(_heap->gc_state() == _saved_gc_state, "Should be restored"); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahRootVerifier.hpp b/src/hotspot/share/gc/shenandoah/shenandoahRootVerifier.hpp index 1f3cb4004657..a41249abb3cc 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahRootVerifier.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahRootVerifier.hpp @@ -28,9 +28,12 @@ #include "memory/allocation.hpp" #include "memory/iterator.hpp" +#include "runtime/atomic.hpp" class ShenandoahGCStateResetter : public StackObj { private: + static Atomic _active_count; + ShenandoahHeap* const _heap; const char _saved_gc_state; const bool _saved_gc_state_changed; @@ -38,6 +41,8 @@ class ShenandoahGCStateResetter : public StackObj { public: ShenandoahGCStateResetter(); ~ShenandoahGCStateResetter(); + + static bool is_active() { return _active_count.load_relaxed() > 0; } }; class ShenandoahRootVerifier : public AllStatic { From d6c4fe7b39d5d9f1a4758ca5214aa95c7a84b92d Mon Sep 17 00:00:00 2001 From: moseoh Date: Mon, 28 Sep 2026 18:19:53 +0000 Subject: [PATCH 062/103] 8392223: CLDR provider mutates shared cached time zone name arrays Reviewed-by: naoto --- .../cldr/CLDRTimeZoneNameProviderImpl.java | 5 +- .../provider/TimeZoneNameConcurrencyTest.java | 121 ++++++++++++++++++ .../provider/TimeZoneNameOrderTest.java | 90 +++++++++++++ 3 files changed, 215 insertions(+), 1 deletion(-) create mode 100644 test/jdk/sun/util/locale/provider/TimeZoneNameConcurrencyTest.java create mode 100644 test/jdk/sun/util/locale/provider/TimeZoneNameOrderTest.java diff --git a/src/java.base/share/classes/sun/util/cldr/CLDRTimeZoneNameProviderImpl.java b/src/java.base/share/classes/sun/util/cldr/CLDRTimeZoneNameProviderImpl.java index 2ca99f6b6180..9cec97259b1e 100644 --- a/src/java.base/share/classes/sun/util/cldr/CLDRTimeZoneNameProviderImpl.java +++ b/src/java.base/share/classes/sun/util/cldr/CLDRTimeZoneNameProviderImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -87,6 +87,9 @@ protected String[] getDisplayNameArray(String id, Locale locale) { if (namesSuper != null) { // CLDR's resource bundle has a translated entry for this id. // Fix up names if needed, either missing or no-inheritance + // Work on a copy so derived names are not cached and + // inherited by child locales. + namesSuper = Arrays.copyOf(namesSuper, namesSuper.length); namesSuper[INDEX_TZID] = id; for(int i = INDEX_STD_LONG; i < namesSuper.length; i++) { // index 0 is the 'id' itself diff --git a/test/jdk/sun/util/locale/provider/TimeZoneNameConcurrencyTest.java b/test/jdk/sun/util/locale/provider/TimeZoneNameConcurrencyTest.java new file mode 100644 index 000000000000..9756386b1288 --- /dev/null +++ b/test/jdk/sun/util/locale/provider/TimeZoneNameConcurrencyTest.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2026, 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 8392223 + * @summary Verify that concurrent time zone name lookups match + * sequential lookups + * @library /test/lib + * @run main TimeZoneNameConcurrencyTest + */ +import java.util.Locale; +import java.util.TimeZone; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.CyclicBarrier; + +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +public class TimeZoneNameConcurrencyTest { + static final int FORKS = 8; + static final int THREADS = 32; + + public static void main(String[] args) throws Exception { + if (args.length > 0) { + if (args[0].equals("serial")) { + printNames(TimeZone.getTimeZone("Asia/Seoul")); + } else { + child(); + } + return; + } + // Names after concurrent first lookups must match a single-threaded + // run in a fresh JVM. + String expected = run("serial"); + for (int i = 0; i < FORKS; i++) { + String actual = run("child"); + if (!expected.equals(actual)) { + throw new RuntimeException("Fork " + i + + ": serial names=" + expected + + ", concurrent names=" + actual); + } + } + System.out.println("OK: " + FORKS + " forks match serial names"); + } + + private static String run(String mode) throws Exception { + ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder( + "-Xmx64m", "TimeZoneNameConcurrencyTest", mode); + OutputAnalyzer output = ProcessTools.executeProcess(pb); + output.shouldHaveExitValue(0); + return output.getStdout().trim(); + } + + static void child() throws Exception { + TimeZone tz = TimeZone.getTimeZone("Asia/Seoul"); + CyclicBarrier barrier = new CyclicBarrier(THREADS); + var failures = new ConcurrentLinkedQueue(); + String[] names = new String[THREADS]; + Thread[] ts = new Thread[THREADS]; + for (int i = 0; i < THREADS; i++) { + final int index = i; + final Locale l = (i % 2 == 1) ? Locale.ENGLISH : Locale.US; + ts[i] = new Thread(() -> { + try { + barrier.await(); + names[index] = tz.getDisplayName(false, TimeZone.SHORT, l); + } catch (Throwable t) { + failures.add(t); + } + }); + ts[i].start(); + } + for (Thread t : ts) { + t.join(); + } + if (!failures.isEmpty()) { + RuntimeException failure = new RuntimeException("Worker failed"); + failures.forEach(failure::addSuppressed); + throw failure; + } + String us = tz.getDisplayName(false, TimeZone.SHORT, Locale.US); + String en = tz.getDisplayName(false, TimeZone.SHORT, Locale.ENGLISH); + for (int i = 0; i < THREADS; i++) { + String name = (i % 2 == 1) ? en : us; + if (!name.equals(names[i])) { + throw new RuntimeException("Thread " + i + " got " + + names[i] + ", main thread got " + name); + } + } + System.out.println(us); + System.out.println(en); + } + + private static void printNames(TimeZone tz) { + String us = tz.getDisplayName(false, TimeZone.SHORT, Locale.US); + String en = tz.getDisplayName(false, TimeZone.SHORT, Locale.ENGLISH); + System.out.println(us); + System.out.println(en); + } +} diff --git a/test/jdk/sun/util/locale/provider/TimeZoneNameOrderTest.java b/test/jdk/sun/util/locale/provider/TimeZoneNameOrderTest.java new file mode 100644 index 000000000000..2dfab2b8818f --- /dev/null +++ b/test/jdk/sun/util/locale/provider/TimeZoneNameOrderTest.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2026, 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 8392223 + * @summary A time zone display name must not depend on which locale was + * looked up first in the process + * @library /test/lib + * @run main TimeZoneNameOrderTest + */ +import java.util.Locale; +import java.util.TimeZone; + +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +public class TimeZoneNameOrderTest { + + // zone, target locale, parent locale looked up first, daylight, style. + // Before the fix, names derived for the parent locale were written into + // its cached array and inherited by the child locale on a later lookup. + private static final String[][] CASES = { + {"Africa/Casablanca", "fr-FR", "und", "false", "SHORT"}, + {"Atlantic/St_Helena", "en-IN", "en", "true", "LONG"}, + }; + + public static void main(String[] args) throws Exception { + if (args.length > 0) { + child(args); + return; + } + boolean failed = false; + for (String[] c : CASES) { + String direct = run(c, false); + String parentFirst = run(c, true); + System.out.printf("%s %s %s %s: direct=%s parentFirst=%s%n", + c[0], c[1], c[3].equals("true") ? "dst" : "std", c[4], + direct, parentFirst); + if (!direct.equals(parentFirst)) { + failed = true; + } + } + if (failed) { + throw new RuntimeException( + "display name depends on the order of locale lookups"); + } + } + + private static String run(String[] c, boolean parentFirst) + throws Exception { + ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder( + "TimeZoneNameOrderTest", + c[0], c[1], parentFirst ? c[2] : "-", c[3], c[4]); + OutputAnalyzer output = ProcessTools.executeProcess(pb); + output.shouldHaveExitValue(0); + return output.getStdout().trim(); + } + + private static void child(String[] a) { + TimeZone tz = TimeZone.getTimeZone(a[0]); + Locale target = Locale.forLanguageTag(a[1]); + boolean daylight = Boolean.parseBoolean(a[3]); + int style = a[4].equals("LONG") ? TimeZone.LONG : TimeZone.SHORT; + if (!a[2].equals("-")) { + tz.getDisplayName(daylight, style, Locale.forLanguageTag(a[2])); + } + System.out.println(tz.getDisplayName(daylight, style, target)); + } +} From 4798413be61d7e86f0ce5370b95fe157b6d9b28e Mon Sep 17 00:00:00 2001 From: Phil Race Date: Mon, 28 Sep 2026 21:25:01 +0000 Subject: [PATCH 063/103] 8383368: Dubious code in XAtomList.java Reviewed-by: avu, azvegint, serb --- .../unix/classes/sun/awt/X11/XAtomList.java | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/java.desktop/unix/classes/sun/awt/X11/XAtomList.java b/src/java.desktop/unix/classes/sun/awt/X11/XAtomList.java index 7ec335f9f4eb..178a1293afc1 100644 --- a/src/java.desktop/unix/classes/sun/awt/X11/XAtomList.java +++ b/src/java.desktop/unix/classes/sun/awt/X11/XAtomList.java @@ -39,20 +39,6 @@ final class XAtomList { public XAtomList() { } - /** - * Creates instance of XAtomList and initializes it with - * the contents pointer by {@code data}. - * Uses default display to initialize atoms. - */ - public XAtomList(long data, int count) { - init(data, count); - } - private void init(long data, int count) { - for (int i = 0; i < count; i++) { - add(new XAtom(XToolkit.getDisplay(), XAtom.getAtom(data+count*XAtom.getAtomSize()))); - } - } - /** * Creates instance of XAtomList and initializes it with * the arrays of atoms. Array can contain null atoms. From 2f4cbd2fbeeccd63bc10832f04c5386b5dedcce0 Mon Sep 17 00:00:00 2001 From: Eric Fang Date: Tue, 29 Sep 2026 02:36:57 +0000 Subject: [PATCH 064/103] 8380424: C2: Fix missing identity optimization for vector nodes Reviewed-by: vlivanov, xgong --- src/hotspot/share/opto/phaseX.cpp | 65 ++++++-- test/hotspot/jtreg/ProblemList.txt | 2 - .../VectorStoreMaskIdentityStressTest.java | 147 ++++++++++++++++++ 3 files changed, 198 insertions(+), 16 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/vectorapi/VectorStoreMaskIdentityStressTest.java diff --git a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp index 32f71e889b52..c0d9908d46e0 100644 --- a/src/hotspot/share/opto/phaseX.cpp +++ b/src/hotspot/share/opto/phaseX.cpp @@ -2127,17 +2127,6 @@ void PhaseIterGVN::verify_Identity_for(Node* n) { return; } - if (n->is_Vector()) { - // Found with tier1-3. Not investigated yet. - // The observed issue was with AndVNode::Identity and - // VectorStoreMaskNode::Identity (see JDK-8370863). - // - // Found with: - // compiler/vectorapi/VectorStoreMaskIdentityTest.java - // -XX:CompileThreshold=100 -XX:-TieredCompilation -XX:VerifyIterativeGVN=1110 - return; - } - Node* i = apply_identity(n); // If we cannot find any other Identity, we are happy. if (i == n) { @@ -2539,7 +2528,7 @@ void PhaseIterGVN::add_users_of_use_to_worklist(Node* n, Node* use, Unique_Node_ } } - uint use_op = use->Opcode(); + int use_op = use->Opcode(); if(use->is_Cmp()) { // Enable CMP/BOOL optimization add_users_to_worklist0(use, worklist); // Put Bool on worklist for (DUIterator_Fast jmax, j = use->fast_outs(jmax); j < jmax; j++) { @@ -2656,14 +2645,16 @@ void PhaseIterGVN::add_users_of_use_to_worklist(Node* n, Node* use, Unique_Node_ auto is_boundary = [](Node* n){ return !n->is_ValueType(); }; use->visit_uses(push_the_uses_to_worklist, is_boundary, true); } - // If changed Cast input, notify down for Phi, Sub, and Xor - all do "uncast" + // If changed Cast input, notify down for nodes that do "uncast". // Patterns: // ConstraintCast+ -> Sub // ConstraintCast+ -> Phi // ConstraintCast+ -> Xor + // ConstraintCast+ -> VectorUnbox if (use->is_ConstraintCast()) { auto push_the_uses_to_worklist = [&](Node* n){ - if (n->is_Phi() || n->is_Sub() || n->Opcode() == Op_XorI || n->Opcode() == Op_XorL) { + if (n->is_Phi() || n->is_Sub() || n->Opcode() == Op_XorI || + n->Opcode() == Op_XorL || n->Opcode() == Op_VectorUnbox) { worklist.push(n); } }; @@ -2878,6 +2869,52 @@ void PhaseIterGVN::add_users_of_use_to_worklist(Node* n, Node* use, Unique_Node_ add_users_to_worklist_if(worklist, use, [](Node* u) { return u->Opcode() == Op_VectorMaskToLong; }); } + // Walk through a VectorMaskCast chain so a change of the inner mask still + // notifies: + // (VectorMaskCast+ x) => x + // (VectorStoreMask (VectorMaskCast* (VectorLoadMask x))) => (x) + if (use_op == Op_VectorMaskCast) { + auto push_the_uses_to_worklist = [&](Node* n) { + if (n->Opcode() == Op_VectorStoreMask || n->Opcode() == Op_VectorMaskCast) { + worklist.push(n); + } + }; + auto is_boundary = [](Node* n) { return n->Opcode() != Op_VectorMaskCast; }; + use->visit_uses(push_the_uses_to_worklist, is_boundary, true); + } + + // Nested-operation optimizations in AndVNode::Identity and OrVNode::Identity, + // e.g. (a & b) & a => a & b. + if (use_op == Op_AndV || use_op == Op_OrV || + use_op == Op_AndVMask || use_op == Op_OrVMask) { + add_users_to_worklist_if(worklist, use, [use_op](Node* u) { + return u->Opcode() == use_op; + }); + } + + // All-zeros / all-ones optimizations in AndVNode::Identity and + // OrVNode::Identity, e.g. src & all-ones => src. + if (use_op == Op_Replicate || use_op == Op_MaskAll) { + add_users_to_worklist_if(worklist, use, [](Node* u) { + return u->Opcode() == Op_AndV || u->Opcode() == Op_OrV || + u->Opcode() == Op_AndVMask || u->Opcode() == Op_OrVMask; + }); + } + + // f(f(x)) => x for f = Reverse / ReverseBytes + if (use_op == Op_ReverseV || use_op == Op_ReverseBytesV) { + add_users_to_worklist_if(worklist, use, [use_op](Node* u) { + return u->Opcode() == use_op; + }); + } + + // Vector shift by zero, see ShiftVNode::Identity + if (use_op == Op_LShiftCntV || use_op == Op_RShiftCntV) { + add_users_to_worklist_if(worklist, use, [](Node* u) { + return u->is_ShiftV(); + }); + } + // From CastX2PNode::Ideal // CastX2P(AddX(x, y)) // CastX2P(SubX(x, y)) diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index 67751b5e1b52..7e7fd9a93de8 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -69,8 +69,6 @@ compiler/c2/aarch64/TestStaticCallStub.java 8359963 generic-aarch64 compiler/escapeAnalysis/TestBCEscapeAnalyzerOverflow.java 8387392 windows-aarch64 -compiler/vectorapi/VectorStoreMaskIdentityTest.java 8388281 generic-all - compiler/vectorapi/AllBitsSetVectorMatchRuleTest.java 8391806 generic-aarch64 compiler/vectorapi/VectorMaskCompareNotTest.java 8392087 windows-x64 diff --git a/test/hotspot/jtreg/compiler/vectorapi/VectorStoreMaskIdentityStressTest.java b/test/hotspot/jtreg/compiler/vectorapi/VectorStoreMaskIdentityStressTest.java new file mode 100644 index 000000000000..ed8875ca1925 --- /dev/null +++ b/test/hotspot/jtreg/compiler/vectorapi/VectorStoreMaskIdentityStressTest.java @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2026, NVIDIA CORPORATION & 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 8380424 +* @library /test/lib / +* @summary VectorStoreMaskNode Identity optimization stress tests +* @modules jdk.incubator.vector +* +* @run driver ${test.main.class} +*/ + +// Tests that VectorStoreMaskNode::Identity is not missed because of +// incremental inlining order and vector boxing/unboxing. + +package compiler.vectorapi; + +import compiler.lib.ir_framework.*; +import jdk.incubator.vector.*; + +public class VectorStoreMaskIdentityStressTest { + private static final VectorSpecies B64 = ByteVector.SPECIES_64; + private static final VectorSpecies S64 = ShortVector.SPECIES_64; + private static final VectorSpecies S128 = ShortVector.SPECIES_128; + private static final VectorSpecies I64 = IntVector.SPECIES_64; + private static final VectorSpecies I128 = IntVector.SPECIES_128; + private static final VectorSpecies L128 = LongVector.SPECIES_128; + private static final VectorSpecies L256 = LongVector.SPECIES_256; + private static final VectorSpecies F128 = FloatVector.SPECIES_128; + private static final VectorSpecies D128 = DoubleVector.SPECIES_128; + + private static final int LENGTH = 256; // large enough + private static boolean[] mask_in; + private static boolean[] mask_out; + + static { + mask_in = new boolean[LENGTH]; + mask_out = new boolean[LENGTH]; + for (int i = 0; i < LENGTH; i++) { + mask_in[i] = (i & 3) == 0; + } + } + + @ForceInline + private static void testThreeCastsKernel(VectorSpecies from_species, + VectorSpecies to_species1, + VectorSpecies to_species2, + VectorSpecies to_species3) { + VectorMask.fromArray(from_species, mask_in, 0) + .cast(to_species1) + .cast(to_species2) + .cast(to_species3).intoArray(mask_out, 0); + } + + // The cast chain does not change the boolean value of any lane, so the first + // "length" lanes of mask_out must equal the corresponding lanes of mask_in. + private static void verifyResult(int length) { + for (int i = 0; i < length; i++) { + if (mask_out[i] != mask_in[i]) { + throw new RuntimeException("Mismatch at lane " + i + ": " + mask_out[i] + " != " + mask_in[i]); + } + } + } + + @Test + @IR(counts = { IRNode.LOAD_VECTOR_Z, IRNode.VECTOR_SIZE_8, ">= 1", + IRNode.LOAD_VECTOR_Z, IRNode.VECTOR_SIZE_4, ">= 1", + IRNode.VECTOR_LOAD_MASK, "= 0", + IRNode.VECTOR_STORE_MASK, "= 0", + IRNode.VECTOR_MASK_CAST, "= 0" }, + applyIfCPUFeatureOr = { "asimd", "true", "avx", "true" }, + applyIf = { "MaxVectorSize", ">= 16" }) + public static void testVectorMaskStoreIdentity1() { + testThreeCastsKernel(B64, S128, B64, S128); + testThreeCastsKernel(I128, S64, F128, S64); + } + + @Check(test = "testVectorMaskStoreIdentity1") + public static void checkVectorMaskStoreIdentity1() { + verifyResult(S128.length()); + } + + @Test + @IR(counts = { IRNode.LOAD_VECTOR_Z, IRNode.VECTOR_SIZE_2, ">= 1", + IRNode.VECTOR_LOAD_MASK, "= 0", + IRNode.VECTOR_STORE_MASK, "= 0", + IRNode.VECTOR_MASK_CAST, "= 0" }, + applyIfCPUFeatureOr = { "asimd", "true" }, + applyIf = { "MaxVectorSize", ">= 16" }) + public static void testVectorMaskStoreIdentity2() { + testThreeCastsKernel(L128, I64, D128, I64); + } + + @Check(test = "testVectorMaskStoreIdentity2") + public static void checkVectorMaskStoreIdentity2() { + verifyResult(L128.length()); + } + + @Test + @IR(counts = { IRNode.LOAD_VECTOR_Z, IRNode.VECTOR_SIZE_4, ">= 1", + IRNode.VECTOR_LOAD_MASK, "= 0", + IRNode.VECTOR_STORE_MASK, "= 0", + IRNode.VECTOR_MASK_CAST, "= 0" }, + applyIfCPUFeatureOr = { "sve", "true", "avx2", "true" }, + applyIf = { "MaxVectorSize", "> 16" }) + public static void testVectorMaskStoreIdentity3() { + testThreeCastsKernel(L256, I128, F128, S64); + } + + @Check(test = "testVectorMaskStoreIdentity3") + public static void checkVectorMaskStoreIdentity3() { + verifyResult(L256.length()); + } + + public static void main(String[] args) { + TestFramework testFramework = new TestFramework(); + testFramework.addFlags("--add-modules=jdk.incubator.vector", + "-ea", + "-esa", + "-XX:CompileThreshold=100", + "-XX:-TieredCompilation", + "-XX:+IgnoreUnrecognizedVMOptions", + "-XX:VerifyIterativeGVN=1110") + .start(); + } +} From 7ae98eea882ce27a11135725cb8291747954b14a Mon Sep 17 00:00:00 2001 From: Erik Gahlin Date: Tue, 29 Sep 2026 05:39:33 +0000 Subject: [PATCH 065/103] 8393058: JFR: 'jfr print' can't handle all characters Reviewed-by: mgronlun --- .../jfr/internal/tool/EventPrintWriter.java | 4 +- .../jdk/jfr/internal/tool/JSONWriter.java | 31 +++- .../jdk/jfr/internal/tool/XMLWriter.java | 54 +++++-- test/jdk/jdk/jfr/tool/TestPrintUnicode.java | 152 ++++++++++++++++++ 4 files changed, 224 insertions(+), 17 deletions(-) create mode 100644 test/jdk/jdk/jfr/tool/TestPrintUnicode.java diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/EventPrintWriter.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/EventPrintWriter.java index d87579b60cde..c79cff07d743 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/EventPrintWriter.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/EventPrintWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -29,6 +29,7 @@ import java.io.IOException; import java.io.PrintWriter; import java.math.BigInteger; +import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.util.ArrayList; import java.util.HashMap; @@ -47,6 +48,7 @@ import jdk.jfr.internal.consumer.JdkJfrConsumer; abstract class EventPrintWriter extends StructuredWriter { + static final String UTF8_REPLACEMENT = StandardCharsets.UTF_8.newDecoder().replacement(); enum ValueType { TIMESPAN, TIMESTAMP, UNSIGNED, OTHER diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/JSONWriter.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/JSONWriter.java index 880c1392808d..87203085b046 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/JSONWriter.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/JSONWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, 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 @@ -210,11 +210,27 @@ private void printArrayBegin() { } private void printEscaped(String text) { - for (int i = 0; i < text.length(); i++) { - printEscaped(text.charAt(i)); + int i = 0; + while (i < text.length()) { + int codePoint = text.codePointAt(i); + printCodePoint(codePoint); + i += Character.charCount(codePoint); } } + private void printCodePoint(int codePoint) { + if (isHighSurrogate(codePoint) || isLowSurrogate(codePoint)) { // Unpaired surrogates + printEscaped(EventPrintWriter.UTF8_REPLACEMENT); + return; + } + if (codePoint > 0xFFFF) { + printEscaped(Character.highSurrogate(codePoint)); + printEscaped(Character.lowSurrogate(codePoint)); + return; + } + printEscaped((char)codePoint); + } + private void printEscaped(char c) { if (c == '\b') { print("\\b"); @@ -256,4 +272,13 @@ private void printEscaped(char c) { } print(c); } + + private static boolean isHighSurrogate(int cp) { + return Character.MIN_HIGH_SURROGATE <= cp && cp <= Character.MAX_HIGH_SURROGATE; + } + + private static boolean isLowSurrogate(int cp) { + return Character.MIN_LOW_SURROGATE <= cp && cp <= Character.MAX_LOW_SURROGATE; + } + } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/XMLWriter.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/XMLWriter.java index 056642eb4aa4..968e75fc2a2f 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/XMLWriter.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/XMLWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, 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 @@ -35,6 +35,7 @@ import jdk.jfr.consumer.RecordedObject; final class XMLWriter extends EventPrintWriter { + public XMLWriter(PrintWriter destination) { super(destination); } @@ -167,38 +168,65 @@ private void printEndElement(String elementName) { } private void printEscaped(String text) { - for (int i = 0; i < text.length(); i++) { - printEscaped(text.charAt(i)); + int i = 0; + while (i < text.length()) { + int codePoint = text.codePointAt(i); + printEscaped(codePoint); + i += Character.charCount(codePoint); } } - private void printEscaped(char c) { - if (c == 34) { + // Valid XML 1.0 characters are taken from https://www.w3.org/TR/xml/#charsets + // Section 2.2 + // Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF] + private static boolean isValid(int cp) { + if (cp == 0x09 || cp == 0x0A || cp == 0x0D) { + return true; + } + if (0x20 <= cp && cp <= 0xD7FF) { + return true; + } + if (0xE000 <= cp && cp <= 0xFFFD) { + return true; + } + if (0x10000 <= cp && cp <= 0x10FFFF) { + return true; + } + return false; + } + + private void printEscaped(int cp) { + if (!isValid(cp)) { + // Not a valid XML 1.0 character. Use UTF-8 replacement character + print(EventPrintWriter.UTF8_REPLACEMENT); + return; + } + if (cp == 34) { print("""); return; } - if (c == 38) { + if (cp == 38) { print("&"); return; } - if (c == 39) { + if (cp == 39) { print("'"); return; } - if (c == 60) { + if (cp == 60) { print("<"); return; } - if (c == 62) { + if (cp == 62) { print(">"); return; } - if (c > 0x7F) { - print("&#"); - print((int) c); + if (cp > 0x7F) { + print("&#x"); + print(Integer.toHexString(cp)); print(';'); return; } - print(c); + print((char) cp); } } diff --git a/test/jdk/jdk/jfr/tool/TestPrintUnicode.java b/test/jdk/jdk/jfr/tool/TestPrintUnicode.java new file mode 100644 index 000000000000..13d68af1c55f --- /dev/null +++ b/test/jdk/jdk/jfr/tool/TestPrintUnicode.java @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2026, 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. + */ + +package jdk.jfr.tool; + +import java.io.IOException; +import java.io.StringReader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; + +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + +import org.xml.sax.InputSource; +import org.xml.sax.XMLReader; + +import jdk.jfr.Description; +import jdk.jfr.Event; +import jdk.jfr.Recording; +import jdk.jfr.StackTrace; +import jdk.test.lib.json.JSONValue; +import jdk.test.lib.process.OutputAnalyzer; + +/** + * @test + * @requires vm.flagless + * @summary Tests printing of tricky characters + * @requires vm.hasJFR + * + * @library /test/lib /test/jdk + * @modules java.scripting java.xml jdk.jfr + * + * @run main/othervm jdk.jfr.tool.TestPrintUnicode + */ +public class TestPrintUnicode { + + private static final String UTF8_REPLACEMENT = StandardCharsets.UTF_8.newDecoder().replacement(); + // Surfer emoji. See https://www.unicode.org/Public/18.0.0/charts/PDF/U1F300.pdf + private static final String SURFER_EMOJI = new String(Character.toChars(0x1F3C4)); + + @StackTrace(false) + static class MessageEvent extends Event { + @Description("High and low surrogates that don't come in a pair") + String unpairedSurrogates; + + @Description("Characters that are not valid according XML 1.0 specification") + String illegalXML; + + @Description("Unicode character that requires a high and low surrogate") + String emoji; + } + + public static void main(String... args) throws Throwable { + Path file = makeRecording(); + testXML(file); + testJSON(file); + } + + private static Path makeRecording() throws IOException { + Path file = Path.of("recording.jfr"); + try (Recording r = new Recording()) { + r.start(); + MessageEvent event = new MessageEvent(); + // Unicode code points larger than 0xFFFF (16 bits) are + // encoded as two values called high and low surrogates (where 10 bits vary). + // High surrogates start at 0xD800 and are followed + // by a low surrogate starting at 0xDC00 + // By picking a second value that is not a low surrogate, + // in this case 'G', the high surrogate becomes unpaired. After the 'G' a + // lone low surrogate is added. + event.unpairedSurrogates = "\uD800G\uDC00"; + // Illegal XML character. + // See https://www.w3.org/TR/xml/#charsets Section 2.2 + event.illegalXML = "\u0008"; + event.emoji = SURFER_EMOJI; + event.commit(); + r.stop(); + r.dump(file); + } + return file.toAbsolutePath(); + } + + private static void testJSON(Path file) throws Throwable { + OutputAnalyzer output = ExecuteHelper.jfr("print", "--json", file.toString()); + String jsonText = output.getStdout(); + JSONValue json = JSONValue.parse(jsonText); + JSONValue values = json.get("recording").get("events").elements().getFirst().get("values"); + + String expectedSurrogates = UTF8_REPLACEMENT + "G" + UTF8_REPLACEMENT; + String expectedIllegalXML = "\b"; + assertJSONValue(values, "unpairedSurrogates", expectedSurrogates); + assertJSONValue(values, "illegalXML", expectedIllegalXML); + assertJSONValue(values, "emoji", SURFER_EMOJI); + } + + private static void assertJSONValue(JSONValue jsonValue, String field, String expected) throws Exception { + String value = jsonValue.get(field).asString(); + if (!value.equals(expected)) { + throw new Exception("Expected " + expected + " for field " + field + ", got " + value); + } + } + + private static void testXML(Path file) throws Throwable { + OutputAnalyzer output = ExecuteHelper.jfr("print", "--xml", file.toString()); + String xml = output.getStdout(); + SAXParserFactory factory = SAXParserFactory.newInstance(); + factory.setNamespaceAware(true); + SAXParser sp = factory.newSAXParser(); + XMLReader xr = sp.getXMLReader(); + xr.parse(new InputSource(new StringReader(xml))); + + String expectedSurrogates = """ + $RG$R + """; + String expectedIllegalXML = """ + $R + """; + String expectedEmoji = """ + 🏄 + """; + assertXMLValue(xml, expectedSurrogates, "Expected replacement character for unpaired surrogate"); + assertXMLValue(xml, expectedIllegalXML, "Expected replacement character for illegal XML character"); + assertXMLValue(xml, expectedEmoji, "Expected unicode emoji to be 🏄"); + } + + private static void assertXMLValue(String xml, String expected, String message) throws Exception { + expected = expected.replace("$R", UTF8_REPLACEMENT).stripTrailing(); + if (!xml.contains(expected)) { + throw new Exception(message); + } + } +} From 7cdbdfa1cba2582e2ace8d04b070aef381a897ea Mon Sep 17 00:00:00 2001 From: Erik Gahlin Date: Tue, 29 Sep 2026 06:16:39 +0000 Subject: [PATCH 066/103] 8393059: JFR: EventStream::onClose should follow specification Reviewed-by: mgronlun --- .../internal/consumer/AbstractEventStream.java | 16 +++++++++++++++- .../consumer/recordingstream/TestOnClose.java | 14 +++++++++++++- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/AbstractEventStream.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/AbstractEventStream.java index e5f599ef7e46..3607860a9c30 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/AbstractEventStream.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/AbstractEventStream.java @@ -165,7 +165,21 @@ public final void onFlush(Runnable action) { @Override public final void onClose(Runnable action) { Objects.requireNonNull(action, "action"); - streamConfiguration.addCloseAction(action); + if (!addClose(action)) { + action.run(); + } + } + + private boolean addClose(Runnable action) { + // Lock ensures action registration is serialized with + // the closed state change in EventStream::close implementations. + synchronized (parserState) { + if (!parserState.isClosed()) { + streamConfiguration.addCloseAction(action); + return true; + } + } + return false; } @Override diff --git a/test/jdk/jdk/jfr/api/consumer/recordingstream/TestOnClose.java b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestOnClose.java index e4c29aa1c91c..1bb20a6cbd3f 100644 --- a/test/jdk/jdk/jfr/api/consumer/recordingstream/TestOnClose.java +++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestOnClose.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, 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 @@ -48,6 +48,18 @@ public static void main(String... args) throws Exception { testOnClosedUnstarted(); testOnClosedStarted(); testOnCloseOnce(); + testOnCloseImmediately(); + } + + private static void testOnCloseImmediately() { + try (RecordingStream rs = new RecordingStream()) { + rs.close(); + AtomicBoolean state = new AtomicBoolean(); + rs.onClose(() -> state.set(true)); + if (!state.get()) { + throw new AssertionError("Expected close action to run immediately on an already closed stream"); + } + } } private static void testOnCloseOnce() { From 1e6235e4f4cda3b2141e7bc403c4e98594dd96ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Jeli=C5=84ski?= Date: Tue, 29 Sep 2026 06:56:30 +0000 Subject: [PATCH 067/103] 8392957: (tz) Clean up the Windows TimeZone_md.c Reviewed-by: naoto, jlu --- .../windows/native/libjava/TimeZone_md.c | 369 +++--------------- 1 file changed, 54 insertions(+), 315 deletions(-) diff --git a/src/java.base/windows/native/libjava/TimeZone_md.c b/src/java.base/windows/native/libjava/TimeZone_md.c index 65b84f080755..95d4980bca5c 100644 --- a/src/java.base/windows/native/libjava/TimeZone_md.c +++ b/src/java.base/windows/native/libjava/TimeZone_md.c @@ -29,88 +29,11 @@ #include "jvm.h" #include "TimeZone_md.h" -#define VALUE_UNKNOWN 0 -#define VALUE_KEY 1 -#define VALUE_MAPID 2 -#define VALUE_GMTOFFSET 3 - #define MAX_ZONE_CHAR 256 -#define MAX_MAPID_LENGTH 32 #define MAX_REGION_LENGTH 4 -#define NT_TZ_KEY "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones" -#define WIN_TZ_KEY "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Time Zones" #define WIN_CURRENT_TZ_KEY "System\\CurrentControlSet\\Control\\TimeZoneInformation" -typedef struct _TziValue { - LONG bias; - LONG stdBias; - LONG dstBias; - SYSTEMTIME stdDate; - SYSTEMTIME dstDate; -} TziValue; - -/* - * Registry key names - */ -static void *keyNames[] = { - (void *) L"StandardName", - (void *) "StandardName", - (void *) L"Std", - (void *) "Std" -}; - -/* - * Indices to keyNames[] - */ -#define STANDARD_NAME 0 -#define STD_NAME 2 - -/* - * Calls RegGetValue to get the value for the specified key. - * First tries the Unicode version, if that fails, - * falls back to the ANSI version and converts to Unicode. - * - * `keyIndex' is an index value to the keyNames in Unicode - * (WCHAR). `keyIndex' + 1 points to its ANSI value. - * - * Returns the status value. ERROR_SUCCESS if succeeded, a - * non-ERROR_SUCCESS value otherwise. - */ -static LONG -getValueInRegistry(HKEY hKey, - int keyIndex, - LPBYTE buf, - LPDWORD bufLengthPtr) -{ - LONG ret; - DWORD bufLength = *bufLengthPtr; - char val[MAX_ZONE_CHAR]; - DWORD valSize; - int len; - - ret = RegGetValueW(hKey, NULL, (WCHAR*) keyNames[keyIndex], - RRF_RT_REG_SZ, NULL, buf, bufLengthPtr); - if (ret == ERROR_SUCCESS) { - return ret; - } - - valSize = sizeof(val); - ret = RegGetValueA(hKey, NULL, (char*) keyNames[keyIndex+1], - RRF_RT_REG_SZ, NULL, val, &valSize); - if (ret != ERROR_SUCCESS) { - return ret; - } - - len = MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, - (LPCSTR) val, -1, - (LPWSTR) buf, bufLength/sizeof(WCHAR)); - if (len <= 0) { - return ERROR_BADKEY; - } - return ERROR_SUCCESS; -} - /* * Produces custom name "GMT+hh:mm" from the given bias in buffer. */ @@ -135,233 +58,6 @@ static void customZoneName(LONG bias, char *buffer, size_t bufSize) { } } -/* - * Gets the current time zone entry in the "Time Zones" registry. - */ -static int getWinTimeZone(char *winZoneName, size_t winZoneNameBufSize) -{ - DYNAMIC_TIME_ZONE_INFORMATION dtzi; - DWORD timeType; - DWORD bufSize; - DWORD val; - HANDLE hKey = NULL; - LONG ret; - - /* - * Get the dynamic time zone information so that time zone redirection - * can be supported. (see JDK-7044727) - */ - timeType = GetDynamicTimeZoneInformation(&dtzi); - if (timeType == TIME_ZONE_ID_INVALID) { - goto err; - } - - /* - * Make sure TimeZoneKeyName is available from the API call. If - * DynamicDaylightTime is disabled, return a custom time zone name - * based on the GMT offset. Otherwise, return the TimeZoneKeyName - * value. - */ - if (dtzi.TimeZoneKeyName[0] != 0) { - if (dtzi.DynamicDaylightTimeDisabled) { - customZoneName(dtzi.Bias, winZoneName, winZoneNameBufSize); - return VALUE_GMTOFFSET; - } - wcstombs(winZoneName, dtzi.TimeZoneKeyName, MAX_ZONE_CHAR); - return VALUE_KEY; - } - - /* - * If TimeZoneKeyName is not available, check whether StandardName - * is available to fall back to the older API GetTimeZoneInformation. - * If not, directly read the value from registry keys. - */ - if (dtzi.StandardName[0] == 0) { - ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_CURRENT_TZ_KEY, 0, - KEY_READ, (PHKEY)&hKey); - if (ret != ERROR_SUCCESS) { - goto err; - } - - /* - * Determine if auto-daylight time adjustment is turned off. - */ - bufSize = sizeof(val); - ret = RegGetValueA(hKey, NULL, "DynamicDaylightTimeDisabled", - RRF_RT_REG_DWORD, NULL, (LPBYTE) &val, &bufSize); - if (ret != ERROR_SUCCESS) { - goto err; - } - /* - * Return a custom time zone name if auto-daylight time adjustment - * is disabled. - */ - if (val == 1) { - customZoneName(dtzi.Bias, winZoneName, winZoneNameBufSize); - (void) RegCloseKey(hKey); - return VALUE_GMTOFFSET; - } - - bufSize = MAX_ZONE_CHAR; - ret = RegGetValueA(hKey, NULL, "TimeZoneKeyName", - RRF_RT_REG_SZ, NULL, (LPBYTE) winZoneName, &bufSize); - if (ret != ERROR_SUCCESS) { - goto err; - } - (void) RegCloseKey(hKey); - return VALUE_KEY; - } else { - /* - * Fall back to GetTimeZoneInformation - */ - TIME_ZONE_INFORMATION tzi; - HANDLE hSubKey = NULL; - DWORD nSubKeys, i; - TCHAR subKeyName[MAX_ZONE_CHAR]; - TCHAR szValue[MAX_ZONE_CHAR]; - WCHAR stdNameInReg[MAX_ZONE_CHAR]; - TziValue tempTzi; - WCHAR *stdNamePtr = tzi.StandardName; - - timeType = GetTimeZoneInformation(&tzi); - if (timeType == TIME_ZONE_ID_INVALID) { - goto err; - } - - ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_CURRENT_TZ_KEY, 0, - KEY_READ, (PHKEY)&hKey); - if (ret == ERROR_SUCCESS) { - /* - * Determine if auto-daylight time adjustment is turned off. - */ - bufSize = sizeof(val); - ret = RegGetValueA(hKey, NULL, "DynamicDaylightTimeDisabled", - RRF_RT_REG_DWORD, NULL, (LPBYTE) &val, &bufSize); - if (ret == ERROR_SUCCESS) { - if (val == 1 && tzi.DaylightDate.wMonth != 0) { - (void) RegCloseKey(hKey); - customZoneName(tzi.Bias, winZoneName, winZoneNameBufSize); - return VALUE_GMTOFFSET; - } - } - - /* - * Win32 problem: If the length of the standard time name is equal - * to (or probably longer than) 32 in the registry, - * GetTimeZoneInformation() on NT returns a null string as its - * standard time name. We need to work around this problem by - * getting the same information from the TimeZoneInformation - * registry. - */ - if (tzi.StandardName[0] == 0) { - bufSize = sizeof(stdNameInReg); - ret = getValueInRegistry(hKey, STANDARD_NAME, (LPBYTE) stdNameInReg, &bufSize); - if (ret != ERROR_SUCCESS) { - goto err; - } - stdNamePtr = stdNameInReg; - } - (void) RegCloseKey(hKey); - } - - /* - * Open the "Time Zones" registry. - */ - ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, NT_TZ_KEY, 0, KEY_READ, (PHKEY)&hKey); - if (ret != ERROR_SUCCESS) { - ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_TZ_KEY, 0, KEY_READ, (PHKEY)&hKey); - /* - * If both failed, then give up. - */ - if (ret != ERROR_SUCCESS) { - return VALUE_UNKNOWN; - } - } - - /* - * Get the number of subkeys of the "Time Zones" registry for - * enumeration. - */ - ret = RegQueryInfoKey(hKey, NULL, NULL, NULL, &nSubKeys, - NULL, NULL, NULL, NULL, NULL, NULL, NULL); - if (ret != ERROR_SUCCESS) { - goto err; - } - - /* - * Compare to the "Std" value of each subkey and find the entry that - * matches the current control panel setting. - */ - for (i = 0; i < nSubKeys; ++i) { - DWORD size = sizeof(subKeyName); - ret = RegEnumKeyEx(hKey, i, subKeyName, &size, NULL, NULL, NULL, NULL); - if (ret != ERROR_SUCCESS) { - goto err; - } - ret = RegOpenKeyEx(hKey, subKeyName, 0, KEY_READ, (PHKEY)&hSubKey); - if (ret != ERROR_SUCCESS) { - goto err; - } - - size = sizeof(szValue); - ret = getValueInRegistry(hSubKey, STD_NAME, szValue, &size); - if (ret != ERROR_SUCCESS) { - RegCloseKey(hSubKey); - break; - } - - if (wcscmp((WCHAR *)szValue, stdNamePtr) == 0) { - /* - * Some localized Win32 platforms use a same name to - * different time zones. So, we can't rely only on the name - * here. We need to check GMT offsets and transition dates - * to make sure it's the registry of the current time - * zone. - */ - DWORD tziValueSize = sizeof(tempTzi); - ret = RegGetValueA(hSubKey, NULL, "TZI", RRF_RT_REG_BINARY, NULL, - (unsigned char*) &tempTzi, &tziValueSize); - if (ret == ERROR_SUCCESS) { - if ((tzi.Bias != tempTzi.bias) || - (memcmp((const void *) &tzi.StandardDate, - (const void *) &tempTzi.stdDate, - sizeof(SYSTEMTIME)) != 0)) { - goto out; - } - - if (tzi.DaylightBias != 0) { - if ((tzi.DaylightBias != tempTzi.dstBias) || - (memcmp((const void *) &tzi.DaylightDate, - (const void *) &tempTzi.dstDate, - sizeof(SYSTEMTIME)) != 0)) { - goto out; - } - } - } - - /* - * found matched record, terminate search - */ - strcpy(winZoneName, subKeyName); - RegCloseKey(hSubKey); - break; - } - out: - (void) RegCloseKey(hSubKey); - } - - (void) RegCloseKey(hKey); - } - - return VALUE_KEY; - - err: - if (hKey != NULL) { - (void) RegCloseKey(hKey); - } - return VALUE_UNKNOWN; -} - /* * The mapping table file name. */ @@ -494,21 +190,64 @@ char *findJavaTZ_md(const char *java_home_dir) { char winZoneName[MAX_ZONE_CHAR]; char *std_timezone = NULL; - int result; - result = getWinTimeZone(winZoneName, sizeof(winZoneName)); + DYNAMIC_TIME_ZONE_INFORMATION dtzi; + DWORD timeType; + LONG activeBias; - if (result != VALUE_UNKNOWN) { - if (result == VALUE_GMTOFFSET) { - std_timezone = _strdup(winZoneName); - } else { - std_timezone = matchJavaTZ(java_home_dir, winZoneName); - if (std_timezone == NULL) { - std_timezone = getGMTOffsetID(); - } + /* + * Get the dynamic time zone information so that time zone redirection + * can be supported. (see JDK-7044727) + */ + timeType = GetDynamicTimeZoneInformation(&dtzi); + if (timeType == TIME_ZONE_ID_INVALID) { + /* + * If GetDynamicTimeZoneInformation fails, return NULL. + * The time zone detection will then fail over to getGMTOffsetID + */ + return NULL; + } + + /* + * If DynamicDaylightTime is disabled, + * return a custom time zone name based on the GMT offset. + */ + if (dtzi.DynamicDaylightTimeDisabled != 0) { + customZoneName(dtzi.Bias, winZoneName, MAX_ZONE_CHAR); + return _strdup(winZoneName); + } + /* + * Map TimeZoneKeyName to a Java TZ. + * If that succeeds, return the Java TZ name. + */ + if (dtzi.TimeZoneKeyName[0] != 0) { + wcstombs(winZoneName, dtzi.TimeZoneKeyName, MAX_ZONE_CHAR); + std_timezone = matchJavaTZ(java_home_dir, winZoneName); + if (std_timezone != NULL) { + return std_timezone; } } - return std_timezone; + + /* + * If TimeZoneKeyName is not set or no mapping exists in tzmappings, + * return a custom time zone name based on the current GMT offset. + */ + switch (timeType) { + case TIME_ZONE_ID_UNKNOWN: + activeBias = dtzi.Bias; + break; + case TIME_ZONE_ID_STANDARD: + activeBias = dtzi.Bias + dtzi.StandardBias; + break; + case TIME_ZONE_ID_DAYLIGHT: + activeBias = dtzi.Bias + dtzi.DaylightBias; + break; + default: + /* should not happen */ + return NULL; + } + customZoneName(activeBias, winZoneName, MAX_ZONE_CHAR); + return _strdup(winZoneName); } /** From 2042269dff58a05962ef6a5f9fecd4fda4a2ae6b Mon Sep 17 00:00:00 2001 From: Erik Gahlin Date: Tue, 29 Sep 2026 07:12:47 +0000 Subject: [PATCH 068/103] 8393072: JFR: Views cpu-information and native-libraries not listed by 'jfr view' Reviewed-by: mgronlun --- .../jdk/jfr/internal/util/Columnizer.java | 20 ++++++++++--------- test/jdk/jdk/jfr/tool/TestView.java | 15 ++++++++++++-- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/util/Columnizer.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/util/Columnizer.java index 91da118eb5cc..c15382250d8d 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/util/Columnizer.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/util/Columnizer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, 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 @@ -43,6 +43,7 @@ public final class Columnizer { private static final class Column { int maxWidth; + int height; List entries = new ArrayList<>(); public void add(String text) { entries.add(text); @@ -54,16 +55,17 @@ public void add(String text) { public Columnizer(List texts, int columnCount) { List list = new ArrayList<>(texts); Collections.sort(list); - int columnHeight = (list.size() + columnCount - 1) / columnCount; + for (int i = 0; i < columnCount; i++) { + columns.add(new Column()); + } + for (int i = 0; i < list.size(); i++) { + columns.get(i % columnCount).height++; + } int index = 0; - Column column = null; - for (String text : list) { - if (index % columnHeight == 0) { - column = new Column(); - columns.add(column); + for (Column column : columns) { + for (int i = 0; i < column.height; i++) { + column.add(list.get(index++)); } - column.add(text); - index++; } } diff --git a/test/jdk/jdk/jfr/tool/TestView.java b/test/jdk/jdk/jfr/tool/TestView.java index 56a1e3edc3be..0cda778ff690 100644 --- a/test/jdk/jdk/jfr/tool/TestView.java +++ b/test/jdk/jdk/jfr/tool/TestView.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, 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 @@ -25,7 +25,7 @@ import java.io.FileWriter; import java.nio.file.Files; import java.nio.file.Path; - +import jdk.jfr.internal.query.Report; import jdk.test.lib.Utils; import jdk.test.lib.process.OutputAnalyzer; /** @@ -35,6 +35,7 @@ * @requires vm.hasJFR * @requires (vm.gc == "G1" | vm.gc == null) * & vm.opt.ExplicitGCInvokesConcurrent != false + * @modules jdk.jfr/jdk.jfr.internal.query * @library /test/lib /test/jdk * @run main/othervm -XX:-ExplicitGCInvokesConcurrent -XX:-DisableExplicitGC * -XX:+UseG1GC jdk.jfr.tool.TestView @@ -43,12 +44,22 @@ public class TestView { public static void main(String... args) throws Throwable { testIncorrectUsage(); + testViewHelp(); String recordingFile = ExecuteHelper.createProfilingRecording().toAbsolutePath().toString(); testEventType(recordingFile); testFormView(recordingFile); testTableView(recordingFile); } + private static void testViewHelp() throws Throwable { + OutputAnalyzer output = ExecuteHelper.jfr("view"); + for (Report r: Report.getReports()) { + if (!output.contains(r.name())) { + throw new Exception("Missing view '" + r.name() + "' in help text"); + } + } + } + private static void testIncorrectUsage() throws Throwable { OutputAnalyzer output = ExecuteHelper.jfr("view"); output.shouldContain("missing file"); From bacddc4b1ef085636be6711d8ecdee665646f7fd Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Tue, 29 Sep 2026 09:01:52 +0000 Subject: [PATCH 069/103] 8392849: G1: Very large values for G1PeriodicGCInterval causes very frequent scheduling Reviewed-by: ayang, lmesnik --- src/hotspot/share/gc/g1/g1ServiceThread.cpp | 13 +++- src/hotspot/share/gc/g1/g1_globals.hpp | 1 + .../gc/g1/TestPeriodicGCLongInterval.java | 76 +++++++++++++++++++ 3 files changed, 87 insertions(+), 3 deletions(-) create mode 100644 test/hotspot/jtreg/gc/g1/TestPeriodicGCLongInterval.java diff --git a/src/hotspot/share/gc/g1/g1ServiceThread.cpp b/src/hotspot/share/gc/g1/g1ServiceThread.cpp index 3c96123d14fb..561b5fd51a56 100644 --- a/src/hotspot/share/gc/g1/g1ServiceThread.cpp +++ b/src/hotspot/share/gc/g1/g1ServiceThread.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 @@ -69,12 +69,19 @@ void G1ServiceThread::register_task(G1ServiceTask* task, jlong delay_ms) { } void G1ServiceThread::schedule(G1ServiceTask* task, jlong delay_ms, bool notify) { + guarantee(delay_ms >= 0, "Delay must be non-negative"); guarantee(task->is_registered(), "Must be registered before scheduled"); guarantee(task->next() == nullptr, "Task already in queue"); + // Make sure that the deadline time stamp does not overflow. + jlong freq = os::elapsed_frequency() / MILLIUNITS; + jlong max_delay_ms = MIN2(delay_ms, max_jlong / freq); + jlong max_delay_counter = TimeHelper::millis_to_counter(max_delay_ms); + // Schedule task by setting the task time and adding it to queue. - jlong delay = TimeHelper::millis_to_counter(delay_ms); - task->set_time(os::elapsed_counter() + delay); + julong deadline = (julong)os::elapsed_counter() + (julong)max_delay_counter; + jlong bounded_deadline = checked_cast(MIN2(deadline, (julong)(max_jlong - 1))); + task->set_time(bounded_deadline); MonitorLocker ml(&_monitor, Mutex::_no_safepoint_check_flag); _task_queue.add_ordered(task); diff --git a/src/hotspot/share/gc/g1/g1_globals.hpp b/src/hotspot/share/gc/g1/g1_globals.hpp index 6b17e95c1e1e..9252455b7fcb 100644 --- a/src/hotspot/share/gc/g1/g1_globals.hpp +++ b/src/hotspot/share/gc/g1/g1_globals.hpp @@ -339,6 +339,7 @@ "Number of milliseconds after a previous GC to wait before " \ "triggering a periodic gc. A value of zero disables periodically "\ "enforced gc cycles.") \ + range(0, NOT_LP64(max_uintx) LP64_ONLY(max_jlong)) \ \ product(bool, G1PeriodicGCInvokesConcurrent, true, \ "Determines the kind of periodic GC. Set to true to have G1 " \ diff --git a/test/hotspot/jtreg/gc/g1/TestPeriodicGCLongInterval.java b/test/hotspot/jtreg/gc/g1/TestPeriodicGCLongInterval.java new file mode 100644 index 000000000000..3c1a0331945e --- /dev/null +++ b/test/hotspot/jtreg/gc/g1/TestPeriodicGCLongInterval.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2026, 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. + */ + +package gc.g1; + +/** + * @test TestPeriodicGCLongInterval.java + * @bug 8392849 + * @requires vm.gc.G1 + * @requires vm.flagless + * @summary Verify that periodic gc interval setting does not overflow the scheduling calculation. + * @library /test/lib / + * @modules java.base/jdk.internal.misc + * @modules java.management/sun.management + * @run driver gc.g1.TestPeriodicGCLongInterval + */ + +import jdk.test.lib.Platform; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +public class TestPeriodicGCLongInterval { + + private static final String PERIODIC_GC_SCHEDULED = "Checking for periodic GC"; + + private static boolean containsOnce(String haystack, String needle) { + return haystack.indexOf(needle) != -1 && haystack.indexOf(needle) == haystack.lastIndexOf(needle); + } + + public static void main(String[] args) throws Exception { + long interval = Platform.is64bit() ? Long.MAX_VALUE : 0xFFFF_FFFFL; + OutputAnalyzer output = ProcessTools.executeLimitedTestJava("-XX:+UseG1GC", + "-XX:G1PeriodicGCInterval=" + interval, + "-Xlog:gc+init,gc+periodic=debug", + "-Xmx10M", + GCTest.class.getName()); + + System.out.println(output.getStdout()); + + if (!containsOnce(output.getStdout(), PERIODIC_GC_SCHEDULED)) { + throw new Exception("Periodic task executed more than once."); + } + + output.shouldContain("Periodic GC: Enabled"); + output.shouldContain("Checking for periodic GC"); + output.shouldHaveExitValue(0); + } + + static class GCTest { + public static void main(String [] args) throws Exception { + System.out.println("Waiting for messages..."); + Thread.sleep(1000); + System.out.println("Done"); + } + } +} From 8ab8af1c77b1bf6e74f7223620d39e60e57f4169 Mon Sep 17 00:00:00 2001 From: Marc Chevalier Date: Tue, 29 Sep 2026 09:18:48 +0000 Subject: [PATCH 070/103] 8393002: [Valhalla] C2: assert(is_ValueType()) failed: invalid node class: Con Reviewed-by: qamai, chagedorn, thartmann --- src/hotspot/share/opto/valuetypenode.cpp | 43 ++++++++++++++++++++---- 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/src/hotspot/share/opto/valuetypenode.cpp b/src/hotspot/share/opto/valuetypenode.cpp index 048eecbb165a..5d1eec59189e 100644 --- a/src/hotspot/share/opto/valuetypenode.cpp +++ b/src/hotspot/share/opto/valuetypenode.cpp @@ -270,6 +270,26 @@ ciField* ValueTypeNode::field(uint index) const { return value_klass()->declared_nonstatic_field_at(index); } +static uint add_top_to_safepoint(ciValueKlass* vk, SafePointNode* sfpt) { + uint cnt = 0; + for (int i = 0; i < vk->nof_declared_nonstatic_fields(); ++i) { + ciField* field = vk->declared_nonstatic_field_at(i); + assert(!field->is_flat() || field->type()->is_value_klass(), "must be a value type"); + if (field->is_flat()) { + cnt += add_top_to_safepoint(field->type()->as_value_klass(), sfpt); + if (field->is_null_free()) { + continue; + } + // The null marker of a flat field is added right after we scalarize that field, so the next + // add_req either stands for the null marker of `field`, or for the whole `field` itself if + // it isn't flat. It's top anyway. + } + sfpt->add_req(Compile::current()->top()); + cnt++; + } + return cnt; +} + uint ValueTypeNode::add_fields_to_safepoint(Unique_Node_List& worklist, SafePointNode* sfpt) const { uint cnt = 0; for (uint i = 0; i < field_count(); ++i) { @@ -277,12 +297,23 @@ uint ValueTypeNode::add_fields_to_safepoint(Unique_Node_List& worklist, SafePoin ciField* field = this->field(i); assert(!field->is_flat() || field->type()->is_value_klass(), "must be a value type"); if (field->is_flat()) { - ValueTypeNode* vt = value->as_ValueType(); - cnt += vt->add_fields_to_safepoint(worklist, sfpt); - if (!field->is_null_free()) { - // The null marker of a flat field is added right after we scalarize that field - sfpt->add_req(vt->get_null_marker()); - cnt++; + if (value->is_top()) { + // An input is top. We act like it was a value type whose fields are all top. + Node* top = value; + cnt += add_top_to_safepoint(field->type()->as_value_klass(), sfpt); + if (!field->is_null_free()) { + // The null marker of a flat field is added right after we scalarize that field + sfpt->add_req(top); + cnt++; + } + } else { + ValueTypeNode* vt = value->as_ValueType(); + cnt += vt->add_fields_to_safepoint(worklist, sfpt); + if (!field->is_null_free()) { + // The null marker of a flat field is added right after we scalarize that field + sfpt->add_req(vt->get_null_marker()); + cnt++; + } } continue; } From c5e5091f10ea3266633d8e82142baac33de23c1b Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Tue, 29 Sep 2026 10:52:26 +0000 Subject: [PATCH 071/103] 8375645: C2: NPE in compiled method does not occur with interpreter Reviewed-by: chagedorn, thartmann, kvn --- src/hotspot/share/opto/callnode.cpp | 8 +- src/hotspot/share/opto/cfgnode.cpp | 361 +++++++----------- src/hotspot/share/opto/cfgnode.hpp | 153 +++++++- src/hotspot/share/opto/intrinsicnode.cpp | 58 ++- src/hotspot/share/opto/intrinsicnode.hpp | 2 - src/hotspot/share/opto/memnode.cpp | 18 +- src/hotspot/share/opto/multnode.cpp | 38 +- src/hotspot/share/opto/multnode.hpp | 22 +- test/hotspot/jtreg/ProblemList.txt | 4 - .../escapeAnalysis/TestSplitIncorrectPhi.java | 121 ++++++ .../igvn/TestDeadNonsensicalMemoryPhi.java | 65 ++++ 11 files changed, 548 insertions(+), 302 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/escapeAnalysis/TestSplitIncorrectPhi.java create mode 100644 test/hotspot/jtreg/compiler/igvn/TestDeadNonsensicalMemoryPhi.java diff --git a/src/hotspot/share/opto/callnode.cpp b/src/hotspot/share/opto/callnode.cpp index 5dc459c0ca48..be9bf9a20b8a 100644 --- a/src/hotspot/share/opto/callnode.cpp +++ b/src/hotspot/share/opto/callnode.cpp @@ -1456,7 +1456,7 @@ Node* CallStaticJavaNode::replace_identity_hash_code(PhaseIterGVN* igvn) { } Node* new_mem = kit.reset_memory(); assert(in(TypeFunc::Memory) == new_mem, "must not modify memory"); - return TupleNode::make(tf()->range_cc(), igvn->C->top(), kit.i_o(), new_mem, kit.frameptr(), kit.returnadr(), replace); + return TupleNode::make(tf()->range_cc(), adr_type(), igvn->C->top(), kit.i_o(), new_mem, kit.frameptr(), kit.returnadr(), replace); } // Try to replace a runtime call to the substitutability test by either a simple pointer comparison @@ -1503,7 +1503,7 @@ Node* CallStaticJavaNode::replace_is_substitutable(PhaseIterGVN* igvn) { } Node* new_mem = kit.reset_memory(); assert(in(TypeFunc::Memory) == new_mem, "must not modify memory"); - return TupleNode::make(tf()->range_cc(), igvn->C->top(), kit.i_o(), new_mem, kit.frameptr(), kit.returnadr(), replace); + return TupleNode::make(tf()->range_cc(), adr_type(), igvn->C->top(), kit.i_o(), new_mem, kit.frameptr(), kit.returnadr(), replace); } #ifndef PRODUCT @@ -1672,7 +1672,7 @@ bool CallLeafPureNode::is_dead() const { TupleNode* CallLeafPureNode::make_tuple_of_input_state_and_top_return_values(const Compile* C) const { // Transparently propagate input state but parameters TupleNode* tuple = TupleNode::make( - tf()->range_cc(), + tf()->range_cc(), nullptr, in(TypeFunc::Control), in(TypeFunc::I_O), in(TypeFunc::Memory), @@ -2957,7 +2957,7 @@ TupleNode* PowDNode::make_tuple_of_input_state_and_result(PhaseIterGVN* phase, N Compile* C = phase->C; C->remove_macro_node(this); TupleNode* tuple = TupleNode::make( - tf()->range_cc(), + tf()->range_cc(), nullptr, control, in(TypeFunc::I_O), in(TypeFunc::Memory), diff --git a/src/hotspot/share/opto/cfgnode.cpp b/src/hotspot/share/opto/cfgnode.cpp index 6799e83dd572..18f6e21671a6 100644 --- a/src/hotspot/share/opto/cfgnode.cpp +++ b/src/hotspot/share/opto/cfgnode.cpp @@ -34,6 +34,7 @@ #include "opto/convertnode.hpp" #include "opto/loopnode.hpp" #include "opto/machnode.hpp" +#include "opto/memnode.hpp" #include "opto/movenode.hpp" #include "opto/mulnode.hpp" #include "opto/narrowptrnode.hpp" @@ -45,6 +46,7 @@ #include "opto/subnode.hpp" #include "opto/valuetypenode.hpp" #include "opto/vectornode.hpp" +#include "utilities/bitMap.hpp" #include "utilities/vmError.hpp" // Portions of code courtesy of Clifford Click @@ -1603,7 +1605,7 @@ Node* PhiNode::Identity(PhaseGVN* phase) { for (DUIterator_Fast imax, i = phi_reg->fast_outs(imax); i < imax; i++) { Node* u = phi_reg->fast_out(i); assert(!u->is_Phi() || u->in(0) == phi_reg, "broken Phi/Region subgraph"); - if (u->is_Phi() && u->req() == phi_len && can_be_replaced_by(u->as_Phi())) { + if (u->is_Phi() && u->req() == phi_len && can_be_replaced_by(phase, u->as_Phi())) { return u; } } @@ -2231,44 +2233,122 @@ bool PhiNode::must_wait_for_region_in_irreducible_loop(PhaseGVN* phase) const { return false; } -// Check if splitting a bot memory Phi through a parent MergeMem may lead to -// non-termination. For more details, see comments at the call site in -// PhiNode::Ideal. -bool PhiNode::is_split_through_mergemem_terminating() const { +// Fix all cases when this bottom memory Phi has MergeMem inputs. +// If none of the inputs of this Phi is a MergeMem, no processing is necessary and no progress is +// made, return nullptr. +// If for all inputs that are MergeMems, the non-top inputs of them correspond to alias classes +// that have been excluded from this Phi, skip the MergeMems by rewiring the inputs of this Phi to +// the base memory of those MergeMem, and return this to denote progress. +// Otherwise, split the Phi, which allows the MergeMem inputs to be pushed down. +// In all cases, the intention is to avoid having a bottom memory Phi with a MergeMem input, which +// helps simplify the memory graph. +Node* PhiNode::split_through_mergemem(PhaseIterGVN& igvn) { + assert(type() == Type::MEMORY, "must be a memory Phi"); + assert(adr_type() == TypePtr::BOTTOM, "must be a bottom memory Phi"); + + bool need_splitting = false; + bool need_process = false; + for (uint input_idx = 1; input_idx < req(); input_idx++) { + MergeMemNode* input = in(input_idx)->isa_MergeMem(); + if (input == nullptr) { + continue; + } + + need_process = true; + for (MergeMemStream mms(input); mms.next_non_empty();) { + if (mms.at_base_memory()) { + continue; + } + + if (_excluded_idx == nullptr || !_excluded_idx->contains(mms.alias_idx())) { + need_splitting = true; + break; + } + } + + if (need_splitting) { + break; + } + } + + if (!need_process) { + return nullptr; + } + + // If a MergeMem input of this bottom memory Phi has non-empty inputs only at alias classes which + // has been excluded from this Phi, then we can ignore all those non-empty inputs, rewire this + // Phi directly to the base memory of that MergeMem input + if (!need_splitting) { + for (uint input_idx = 1; input_idx < req(); input_idx++) { + MergeMemNode* input = in(input_idx)->isa_MergeMem(); + if (input == nullptr) { + continue; + } + + Node* new_input = input->base_memory(); + assert(new_input != input, "MergeMemNode cannot reference itself"); + set_req_X(input_idx, new_input, &igvn); + } + return this; + } + + // Split this Phi through its MergeMem inputs + // Phi(...MergeMem(m0, m1:AT1, m2:AT2)...) into + // MergeMem(Phi(...m0...), Phi:AT1(...m1...), Phi:AT2(...m2...)) + // The new bottom memory Phi Phi(...m0...) records alias classes that have been split (AT1, AT2) + PhiNode* new_base = (PhiNode*) clone(); + // Must eagerly register phis, since they participate in loops. + igvn.register_new_node_with_optimizer(new_base); + + // Record the alias indices that are newly split ResourceMark rm; - VectorSet visited; - GrowableArray worklist; - worklist.push(this); - visited.set(this->_idx); - auto maybe_add_to_worklist = [&](Node* input) { - if (input != nullptr && - (input->is_MergeMem() || input->is_memory_phi()) && - !visited.test_set(input->_idx)) { - worklist.push(input); - assert(input->adr_type() == TypePtr::BOTTOM, - "should only visit bottom memory"); - } - }; - while (worklist.length() > 0) { - const Node* n = worklist.pop(); - if (n->is_MergeMem()) { - Node* input = n->as_MergeMem()->base_memory(); - if (input == this) { - return false; + ResourceBitMap new_excluded_idx(igvn.C->num_alias_types()); + if (_excluded_idx != nullptr) { + _excluded_idx->copy_into(new_excluded_idx); + } + + MergeMemNode* result = MergeMemNode::make(new_base); + for (uint input_idx = 1; input_idx < req(); input_idx++) { + MergeMemNode* input = in(input_idx)->isa_MergeMem(); + if (input == nullptr) { + continue; + } + + // TODO revisit this with JDK-8247216 + // Put 'input' on the worklist because it might be modified by MergeMemStream::iteration_setup + igvn.record_for_igvn(input); + + for (MergeMemStream mms(result, input); mms.next_non_empty2();) { + // If we have not seen this slice yet, make a phi for it. + bool made_new_phi = false; + if (mms.is_empty()) { + Node* new_phi = new_base->slice_memory(mms.adr_type(igvn.C)); + made_new_phi = true; + igvn.register_new_node_with_optimizer(new_phi); + mms.set_memory(new_phi); } - maybe_add_to_worklist(input); - } else { - assert(n->is_memory_phi(), "invariant"); - for (uint i = PhiNode::Input; i < n->req(); i++) { - Node* input = n->in(i); - if (input == this) { - return false; - } - maybe_add_to_worklist(input); + Node* phi = mms.memory(); + assert(made_new_phi || phi->in(input_idx) == input, "replace the i-th merge by a slice"); + phi->set_req_X(input_idx, mms.memory2(), &igvn); + new_excluded_idx.set_bit(mms.alias_idx()); + } + } + new_base->_excluded_idx = ExcludedAliasIdx::make(igvn.C, new_excluded_idx); + + // Distribute all self-loops + for (MergeMemStream mms(result); mms.next_non_empty();) { + Node* phi = mms.memory(); + for (uint i = 1; i < req(); i++) { + if (phi->in(i) == this) { + phi->set_req_X(i, phi, &igvn); } } } - return true; + + // We could immediately transform the new Phi nodes here, but that can result in creating an + // excessive number of new nodes within a single IGVN iteration. We have put the Phi nodes on the + // IGVN worklist, so they are transformed later on in any case. + return result; } // Is one of the inputs a Cast that has not been processed by igvn yet? @@ -2612,191 +2692,32 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { // It would make the parser's memory-merge logic sick.) // (MergeMemNode is not dead_loop_safe - need to check for dead loop.) if (progress == nullptr && can_reshape && type() == Type::MEMORY) { + PhaseIterGVN* igvn = phase->is_IterGVN(); + assert(igvn != nullptr, "must be during IGVN"); - // See if this Phi should be sliced. Determine the merge width of input - // MergeMems and check if there is a direct loop to self, as illustrated - // below. - // - // +-------------+ - // | | - // (base_memory) v | - // MergeMem | - // | | - // v | - // Phi (this) | - // | | - // +-----------+ - // - // Generally, there are issues with non-termination with such circularity - // (see comment further below). However, if there is a direct loop to self, - // splitting the Phi through the MergeMem will result in the below. - // - // +---+ - // | | - // v | - // Phi | - // |\ | - // | +-+ - // (base_memory) v - // MergeMem - // - // This split breaks the circularity and consequently does not lead to - // non-termination. - uint merge_width = 0; - // TODO revisit this with JDK-8247216 - bool mergemem_only = true; - bool split_always_terminates = false; // Is splitting guaranteed to terminate? - for( uint i=1; iis_MergeMem()) { - MergeMemNode* n = ii->as_MergeMem(); - merge_width = MAX2(merge_width, n->req()); - if (n->base_memory() == this) { - split_always_terminates = true; - } - } else { - mergemem_only = false; + if (const TypePtr* at = adr_type(); at == TypePtr::BOTTOM) { + progress = split_through_mergemem(*igvn); + if (progress != nullptr && progress != this) { + return progress; } - } - - // There are cases with circular dependencies between bottom Phis - // and MergeMems. Below is a minimal example. - // - // +------------+ - // | | - // (base_memory) v | - // MergeMem | - // | | - // v | - // Phi (this) | - // | | - // v | - // Phi | - // | | - // +----------+ - // - // Here, we cannot break the circularity through a self-loop as there - // are two Phis involved. Repeatedly splitting the Phis through the - // MergeMem leads to non-termination. We check for non-termination below. - // Only check for non-termination if necessary. - if (!mergemem_only && !split_always_terminates && adr_type() == TypePtr::BOTTOM && - merge_width > Compile::AliasIdxRaw) { - split_always_terminates = is_split_through_mergemem_terminating(); - } - - if (merge_width > Compile::AliasIdxRaw) { - // found at least one non-empty MergeMem - const TypePtr* at = adr_type(); - if (at != TypePtr::BOTTOM) { - // Patch the existing phi to select an input from the merge: - // Phi:AT1(...MergeMem(m0, m1, m2)...) into - // Phi:AT1(...m1...) - int alias_idx = phase->C->get_alias_index(at); - for (uint i=1; iis_MergeMem()) { - MergeMemNode* n = ii->as_MergeMem(); - // compress paths and change unreachable cycles to TOP - // If not, we can update the input infinitely along a MergeMem cycle - // Equivalent code is in MemNode::Ideal_common - Node *m = phase->transform(n); - if (outcnt() == 0) { // Above transform() may kill us! - return top; - } - // If transformed to a MergeMem, get the desired slice - // Otherwise the returned node represents memory for every slice - Node *new_mem = (m->is_MergeMem()) ? - m->as_MergeMem()->memory_at(alias_idx) : m; - // Update input if it is progress over what we have now - if (new_mem != ii) { - set_req_X(i, new_mem, phase->is_IterGVN()); - progress = this; - } - } - } - } else if (mergemem_only || split_always_terminates) { - // If all inputs reference this phi (directly or through data nodes) - - // it is a dead loop. - bool saw_safe_input = false; - for (uint j = 1; j < req(); ++j) { - Node* n = in(j); - if (n->is_MergeMem()) { - MergeMemNode* mm = n->as_MergeMem(); - if (mm->base_memory() == this || mm->base_memory() == mm->empty_memory()) { - // Skip this input if it references back to this phi or if the memory path is dead - continue; - } - } - if (!is_unsafe_data_reference(n)) { - saw_safe_input = true; // found safe input - break; - } - } - if (!saw_safe_input) { - // There is a dead loop: All inputs are either dead or reference back to this phi - return top; - } - - // Phi(...MergeMem(m0, m1:AT1, m2:AT2)...) into - // MergeMem(Phi(...m0...), Phi:AT1(...m1...), Phi:AT2(...m2...)) - PhaseIterGVN* igvn = phase->is_IterGVN(); - assert(igvn != nullptr, "sanity check"); - PhiNode* new_base = (PhiNode*) clone(); - // Must eagerly register phis, since they participate in loops. - igvn->register_new_node_with_optimizer(new_base); - - MergeMemNode* result = MergeMemNode::make(new_base); - for (uint i = 1; i < req(); ++i) { - Node *ii = in(i); - if (ii->is_MergeMem()) { - MergeMemNode* n = ii->as_MergeMem(); - if (igvn) { - // TODO revisit this with JDK-8247216 - // Put 'n' on the worklist because it might be modified by MergeMemStream::iteration_setup - igvn->_worklist.push(n); - } - for (MergeMemStream mms(result, n); mms.next_non_empty2(); ) { - // If we have not seen this slice yet, make a phi for it. - bool made_new_phi = false; - if (mms.is_empty()) { - Node* new_phi = new_base->slice_memory(mms.adr_type(phase->C)); - made_new_phi = true; - igvn->register_new_node_with_optimizer(new_phi); - mms.set_memory(new_phi); - } - Node* phi = mms.memory(); - assert(made_new_phi || phi->in(i) == n, "replace the i-th merge by a slice"); - phi->set_req_X(i, mms.memory2(), phase); - } - } - } - // Distribute all self-loops. - { // (Extra braces to hide mms.) - for (MergeMemStream mms(result); mms.next_non_empty(); ) { - Node* phi = mms.memory(); - for (uint i = 1; i < req(); ++i) { - if (phi->in(i) == this) { - phi->set_req_X(i, phi, phase); - } - } - } + } else { + // Patch the existing phi to select an input from the merge: + // Phi:AT1(...MergeMem(m0, m1, m2)...) into + // Phi:AT1(...m1...) + int alias_idx = igvn->C->get_alias_index(at); + for (uint input_idx = 1; input_idx < req(); input_idx++) { + MergeMemNode* input = in(input_idx)->isa_MergeMem(); + if (input == nullptr) { + continue; } - // We could immediately transform the new Phi nodes here, but that can - // result in creating an excessive number of new nodes within a single - // IGVN iteration. We have put the Phi nodes on the IGVN worklist, so - // they are transformed later on in any case. - - // Replace self with the result. - return result; + Node* new_input = input->memory_at(alias_idx); + assert(new_input != input, "MergeMemNode cannot reference itself"); + set_req_X(input_idx, new_input, igvn); + progress = this; } } + // // Other optimizations on the memory chain // @@ -2935,7 +2856,7 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { for (DUIterator_Fast imax, i = phi_reg->fast_outs(imax); i < imax; i++) { Node* u = phi_reg->fast_out(i); assert(!u->is_Phi() || (u->in(0) == phi_reg && u->req() == phi_len), "broken Phi/Region subgraph"); - if (u->is_Phi() && u->as_Phi()->can_be_replaced_by(this)) { + if (u->is_Phi() && u->as_Phi()->can_be_replaced_by(phase, this)) { igvn->_worklist.push(u); } } @@ -3407,9 +3328,15 @@ const TypeTuple* PhiNode::collect_types(PhaseGVN* phase) const { return TypeTuple::make(types.length(), flds); } -bool PhiNode::can_be_replaced_by(const PhiNode* other) const { - return type() == Type::MEMORY && other->type() == Type::MEMORY && adr_type() != TypePtr::BOTTOM && - other->adr_type() == TypePtr::BOTTOM && has_same_inputs_as(other); +bool PhiNode::can_be_replaced_by(PhaseGVN* phase, const PhiNode* other) const { + const TypePtr* at = adr_type(); + return type() == Type::MEMORY && other->type() == Type::MEMORY && at != TypePtr::BOTTOM && + other->adr_type() == TypePtr::BOTTOM && has_same_inputs_as(other) && + // Normally, the alias class corresponding to 'at' not being in '_excluded_idx' does not + // imply that it is contained in the bottom memory Phi 'other'. However, since 'this' and + // 'other' have the same inputs, either both represent the memory state of 'at', or + // neither does, both cases allow replacing 'this' with 'other'. + (other->_excluded_idx == nullptr || !other->_excluded_idx->contains(phase->C->get_alias_index(at))); } Node* PhiNode::clone_through_phi(Node* root_phi, const Type* t, uint c, PhaseIterGVN* igvn) { diff --git a/src/hotspot/share/opto/cfgnode.hpp b/src/hotspot/share/opto/cfgnode.hpp index 79245185d777..5dfdbcf38d94 100644 --- a/src/hotspot/share/opto/cfgnode.hpp +++ b/src/hotspot/share/opto/cfgnode.hpp @@ -31,6 +31,7 @@ #include "opto/predicates_enums.hpp" #include "opto/type.hpp" #include "runtime/arguments.hpp" +#include "utilities/bitMap.hpp" // Portions of code courtesy of Clifford Click @@ -165,6 +166,151 @@ class PhiNode : public TypeNode { const int _inst_index; // Alias index of the instance memory slice. // Array elements references have the same alias_idx but different offset. const int _inst_offset; // Offset of the instance memory slice. + + // Bottom memory Phis are peculiar. Their address type, TypePtr::BOTTOM, suggests that they + // represent the complete memory state, but that is not always true. Bottom memory Phis can + // represent a partial memory state that does not need to include all alias classes. + // + // Consider a bottom memory Phi bot_phi and an arbitrary alias class mem: + // + // 1. Sometimes, bot_phi does not contain the memory state corresponding to mem (i.e. not every + // input to bot_phi captures the latest memory state for mem), and there is another memory Phi + // mem_phi at the same region representing the memory state corresponding to mem: + // + // if (b) { + // Call1(); + // MemProj1(Call1); + // } else { + // Call2(); + // MemProj2(Call2); + // Store1(_, MemProj2, p, v): mem; + // } + // bot_phi = Phi(MemProj1, MemProj2); + // mem_phi = Phi(MemProj1, Store1); + // + // In this example, bot_phi cannot contain the memory state corresponding to mem, because + // MemProj2 does not capture Store1 that writes into that memory. + // + // 2. Sometimes, bot_phi contains the memory state corresponding to mem, even if there is another + // memory Phi at the same region representing the memory state corresponding to mem: + // + // Call1(); + // MemProj1(Call1); + // Store1(_, MemProj1, p1, v): mem; + // MergeMem1(MemProj1, Top, Store1); + // Load1(_, Store1, p2): mem; + // Load2(_, MergeMem1, p3): mem; + // + // We somehow want to multiversion some statements: + // + // Call1(); + // MemProj1(Call1); + // if (b) { + // Store11(_, MemProj1, p1, v): mem; + // MergeMem11(MemProj1, Top, Store11); + // } else { + // Store12(_, MemProj1, p1, v): mem; + // MergeMem12(MemProj1, Top, Store12); + // } + // bot_phi = Phi(MergeMem11, MergeMem12); + // mem_phi = Phi(Store11, Store12); + // Load1(_, mem_phi, p2): mem; + // Load2(_, bot_phi, p3): mem; + // + // In this example, bot_phi must contain the memory state corresponding to mem, because there is + // a load corresponding to mem from it. This pattern can eventually be simplified by IGVN, but + // until then, the existence of mem_phi does not mean that bot_phi does not contain the memory + // state corresponding to mem. + // + // 3. Sometimes, bot_phi does not contain the memory state corresponding to mem, even if there is + // not any memory Phi at the same region representing the memory state corresponding to mem: + // + // Call1(); + // MemProj1(Call1); + // Store1(_, MemProj1, p1, v): mem; + // if (b) { + // MergeMem1(MemProj1, Top, Store1); + // } + // bot_phi = Phi(MergeMem1, MemProj1); + // Load1(_, Store1, p2): mem; + // + // In this case, bot_phi does not contain the memory state corresponding to mem, because in one + // branch, its input is MemProj1, which does not capture the latest store Store1 of the alias + // class mem. A load from the alias class mem Load1 will have its memory input being Store1, not + // bot_phi. + // + // Those situations can be solved by pushing the MergeMems down through their bottom memory Phi + // outputs. However, naively doing so can lead to infinite loop, because a Phi can be a + // transitive input of itself, which means keeping pushing the MergeMem down may eventually + // results in it being an input of the original Phi again. To tackle that issue, we observe that + // pushing a MergeMem down through a bottom memory Phi effectively splits that Phi into multiple + // Phis each of which represents an alias class, and the new bottom memory Phi must not contain + // the memory states of those alias classes that are split. For example, by transforming: + // + // Proj1 Store + // \ / + // MergeMem Proj2 + // \ / + // Phi + // | + // ... + // + // Into: + // + // Proj1 Proj2 Store Proj2(this is the same node as the other Proj2) + // \ / \ / + // Phi1 Phi2 + // \ / + // MergeMem + // | + // ... + // + // While it is uncertain which memory states the original Phi contains, it is certain that Phi1 + // (the new bottom memory Phi) does not contain the memory state for the alias class represented + // by Phi2 after the split, because it misses the latest Store into that alias class. + // + // As a result, if we record which alias classes have been split from each bottom memory Phi, it + // is certain that the transformation will terminate, because if every MergeMem input of a bottom + // memory Phi has all their non-top inputs being known to be excluded from that Phi, then the Phi + // does not have to be split anymore (i.e. it can simply skip the MergeMems and is rewired to the + // MergeMems' base inputs instead of having the MergeMems pushed through it). Otherwise, the + // split is performed, more alias classes are split from the Phi, which means some progress is + // made. + // + // This does not need to be exact, it is fine if it misses alias classes that a bottom memory Phi + // does not include, but it must not contain alias classes that the Phi includes. + class ExcludedAliasIdx { + private: + BitMapView _view; + + ExcludedAliasIdx(BitMap::bm_word_t* payload, BitMap::idx_t bit_size) : _view(payload, bit_size) {} + + public: + static const ExcludedAliasIdx* make(Compile* C, const BitMap& excluded_idx) { + Arena* arena = C->node_arena(); + char* ptr = static_cast(arena->Amalloc(sizeof(ExcludedAliasIdx) + excluded_idx.size_in_bytes())); + BitMap::bm_word_t* payload = reinterpret_cast(ptr + sizeof(ExcludedAliasIdx)); + memset(payload, 0, excluded_idx.size_in_bytes()); + ExcludedAliasIdx* res = ::new(ptr) ExcludedAliasIdx(payload, excluded_idx.size()); + res->_view.set_from(excluded_idx); + return res; + } + + bool contains(int alias_idx) const { + assert(alias_idx >= 0, "invalid idx %d", alias_idx); + BitMap::idx_t idx = alias_idx; + return idx < _view.size() && _view.at(idx); + } + + void copy_into(BitMap& dst) const { + for (BitMap::Iterator iter(_view); !iter.is_empty(); iter.step()) { + dst.set_bit(iter.index()); + } + } + }; + + const ExcludedAliasIdx* _excluded_idx; + // Size is bigger to hold the _adr_type field. virtual uint hash() const; // Check the type virtual bool cmp( const Node &n ) const; @@ -180,7 +326,7 @@ class PhiNode : public TypeNode { bool must_wait_for_region_in_irreducible_loop(PhaseGVN* phase) const; - bool is_split_through_mergemem_terminating() const; + Node* split_through_mergemem(PhaseIterGVN& igvn); void verify_type_stability(const PhaseGVN* phase, const Type* union_of_input_types, const Type* new_type) const NOT_DEBUG_RETURN; bool wait_for_cast_input_igvn(const PhaseIterGVN* igvn) const; @@ -201,7 +347,8 @@ class PhiNode : public TypeNode { _inst_mem_id(imid), _inst_id(iid), _inst_index(iidx), - _inst_offset(ioffs) + _inst_offset(ioffs), + _excluded_idx(nullptr) { init_class_id(Class_Phi); init_req(0, r); @@ -284,7 +431,7 @@ class PhiNode : public TypeNode { #endif //ASSERT const TypeTuple* collect_types(PhaseGVN* phase) const; - bool can_be_replaced_by(const PhiNode* other) const; + bool can_be_replaced_by(PhaseGVN* phase, const PhiNode* other) const; }; //------------------------------GotoNode--------------------------------------- diff --git a/src/hotspot/share/opto/intrinsicnode.cpp b/src/hotspot/share/opto/intrinsicnode.cpp index 887681233f16..0b7f5ed21eae 100644 --- a/src/hotspot/share/opto/intrinsicnode.cpp +++ b/src/hotspot/share/opto/intrinsicnode.cpp @@ -36,47 +36,41 @@ uint StrIntrinsicNode::match_edge(uint idx) const { return idx == 2 || idx == 3; } -//------------------------------Ideal------------------------------------------ -// Return a node which is more "ideal" than the current node. Strip out -// control copies -Node* StrIntrinsicNode::Ideal(PhaseGVN* phase, bool can_reshape) { - if (remove_dead_region(phase, can_reshape)) return this; +static Node* ideal_mem_intrinsic_node(PhaseGVN* phase, bool can_reshape, Node* n) { + if (n->remove_dead_region(phase, can_reshape)) { + return n; + } + // Don't bother trying to transform a dead node - if (in(0) && in(0)->is_top()) return nullptr; - - if (can_reshape) { - Node* mem = in(MemNode::Memory); - // If mem input is a MergeMem, get the desired slice - uint alias_idx = phase->C->get_alias_index(adr_type()); - mem = mem->is_MergeMem() ? mem->as_MergeMem()->memory_at(alias_idx) : mem; - if (mem != in(MemNode::Memory)) { - set_req_X(MemNode::Memory, mem, phase); - return this; + if (n->in(0) && n->in(0)->is_top()) { + return nullptr; + } + + // If mem input is a MergeMem, get the desired slice + if (const TypePtr* adr_type = n->adr_type(); can_reshape && adr_type != TypePtr::BOTTOM) { + Node* mem = n->in(MemNode::Memory); + uint alias_idx = phase->C->get_alias_index(adr_type); + Node* new_mem = mem->is_MergeMem() ? mem->as_MergeMem()->memory_at(alias_idx) : mem; + if (new_mem != mem) { + n->set_req_X(MemNode::Memory, new_mem, phase); + return n; } } - return nullptr; -} -//------------------------------Value------------------------------------------ -const Type* StrIntrinsicNode::Value(PhaseGVN* phase) const { - if (in(0) && phase->type(in(0)) == Type::TOP) return Type::TOP; - return bottom_type(); + return nullptr; } -//============================================================================= //------------------------------Ideal------------------------------------------ // Return a node which is more "ideal" than the current node. Strip out // control copies -Node* StrCompressedCopyNode::Ideal(PhaseGVN* phase, bool can_reshape) { - return remove_dead_region(phase, can_reshape) ? this : nullptr; +Node* StrIntrinsicNode::Ideal(PhaseGVN* phase, bool can_reshape) { + return ideal_mem_intrinsic_node(phase, can_reshape, this); } -//============================================================================= -//------------------------------Ideal------------------------------------------ -// Return a node which is more "ideal" than the current node. Strip out -// control copies -Node* StrInflatedCopyNode::Ideal(PhaseGVN* phase, bool can_reshape) { - return remove_dead_region(phase, can_reshape) ? this : nullptr; +//------------------------------Value------------------------------------------ +const Type* StrIntrinsicNode::Value(PhaseGVN* phase) const { + if (in(0) && phase->type(in(0)) == Type::TOP) return Type::TOP; + return bottom_type(); } uint VectorizedHashCodeNode::match_edge(uint idx) const { @@ -85,7 +79,7 @@ uint VectorizedHashCodeNode::match_edge(uint idx) const { } Node* VectorizedHashCodeNode::Ideal(PhaseGVN* phase, bool can_reshape) { - return remove_dead_region(phase, can_reshape) ? this : nullptr; + return ideal_mem_intrinsic_node(phase, can_reshape, this); } const Type* VectorizedHashCodeNode::Value(PhaseGVN* phase) const { @@ -105,7 +99,7 @@ uint EncodeISOArrayNode::match_edge(uint idx) const { // Return a node which is more "ideal" than the current node. Strip out // control copies Node* EncodeISOArrayNode::Ideal(PhaseGVN* phase, bool can_reshape) { - return remove_dead_region(phase, can_reshape) ? this : nullptr; + return ideal_mem_intrinsic_node(phase, can_reshape, this); } //------------------------------Value------------------------------------------ diff --git a/src/hotspot/share/opto/intrinsicnode.hpp b/src/hotspot/share/opto/intrinsicnode.hpp index 1fe61cfb1785..230ad8d81fe4 100644 --- a/src/hotspot/share/opto/intrinsicnode.hpp +++ b/src/hotspot/share/opto/intrinsicnode.hpp @@ -147,7 +147,6 @@ class StrCompressedCopyNode final : public StrIntrinsicNode { StrIntrinsicNode(control, arymem, s1, s2, c, none), _adr_type(adr_type) {}; virtual int Opcode() const override; virtual const Type* bottom_type() const override { return TypeInt::INT; } - virtual Node* Ideal(PhaseGVN* phase, bool can_reshape) override; private: virtual uint size_of() const override { return sizeof(StrCompressedCopyNode); } @@ -169,7 +168,6 @@ class StrInflatedCopyNode final : public StrIntrinsicNode { StrIntrinsicNode(control, arymem, s1, s2, c, none), _adr_type(adr_type) {}; virtual int Opcode() const override; virtual const Type* bottom_type() const override { return Type::MEMORY; } - virtual Node* Ideal(PhaseGVN* phase, bool can_reshape) override; private: virtual uint size_of() const override { return sizeof(StrInflatedCopyNode); } diff --git a/src/hotspot/share/opto/memnode.cpp b/src/hotspot/share/opto/memnode.cpp index e2581a356978..48725ad405dd 100644 --- a/src/hotspot/share/opto/memnode.cpp +++ b/src/hotspot/share/opto/memnode.cpp @@ -525,6 +525,13 @@ Node *MemNode::Ideal_common(PhaseGVN *phase, bool can_reshape) { return NodeSentinel; // caller will return null } + // Weird access to null+offset, either is a dead unsafe access that cannot be proved to be so, or + // should be folded later + if (t_adr->base() == Type::AnyPtr) { + assert(t_adr->is_ptr()->ptr() == TypePtr::Null, "must be null"); + return NodeSentinel; // caller will return null + } + // Do NOT remove or optimize the next lines: ensure a new alias index // is allocated for an oop pointer type before Escape Analysis. // Note: C++ will not remove it since the call has side effect. @@ -532,17 +539,6 @@ Node *MemNode::Ideal_common(PhaseGVN *phase, bool can_reshape) { int alias_idx = phase->C->get_alias_index(t_adr->is_ptr()); } - Node* base = nullptr; - if (address->is_AddP()) { - base = address->in(AddPNode::Base); - } - if (base != nullptr && phase->type(base)->higher_equal(TypePtr::NULL_PTR) && - !t_adr->isa_rawptr()) { - // Note: raw address has TOP base and top->higher_equal(TypePtr::NULL_PTR) is true. - // Skip this node optimization if its address has TOP base. - return NodeSentinel; // caller will return null - } - // Avoid independent memory operations Node* old_mem = mem; diff --git a/src/hotspot/share/opto/multnode.cpp b/src/hotspot/share/opto/multnode.cpp index 27a3a3400cdc..6f763563f690 100644 --- a/src/hotspot/share/opto/multnode.cpp +++ b/src/hotspot/share/opto/multnode.cpp @@ -151,27 +151,25 @@ const Type *ProjNode::bottom_type() const { return proj_type(in(0)->bottom_type()); } -const TypePtr *ProjNode::adr_type() const { - if (bottom_type() == Type::MEMORY) { - // in(0) might be a narrow MemBar; otherwise we will report TypePtr::BOTTOM - Node* ctrl = in(0); - if (ctrl->Opcode() == Op_Tuple) { - // Jumping over Tuples: the i-th projection of a Tuple is the i-th input of the Tuple. - ctrl = ctrl->in(_con); - } - // node is dead or we are in the process of removing a dead subgraph - if (ctrl == nullptr || ctrl->is_top()) { - return nullptr; - } - const TypePtr* adr_type = ctrl->adr_type(); - #ifdef ASSERT - if (!VMError::is_error_reported() && !Node::in_dump()) - assert(adr_type != nullptr, "source must have adr_type"); - #endif - return adr_type; +const TypePtr* ProjNode::adr_type() const { + if (bottom_type() != Type::MEMORY) { + assert(bottom_type()->base() != Type::Memory, "no other memories?"); + return nullptr; } - assert(bottom_type()->base() != Type::Memory, "no other memories?"); - return nullptr; + + Node* ctrl = in(0); + // node is dead or we are in the process of removing a dead subgraph + if (ctrl == nullptr || ctrl->is_top()) { + return nullptr; + } + + const TypePtr* adr_type = ctrl->adr_type(); +#ifdef ASSERT + if (!VMError::is_error_reported() && !Node::in_dump()) { + assert(adr_type != nullptr, "source must have adr_type"); + } +#endif // ASSERT + return adr_type; } bool ProjNode::pinned() const { return in(0)->pinned(); } diff --git a/src/hotspot/share/opto/multnode.hpp b/src/hotspot/share/opto/multnode.hpp index 6e07421a4869..8cc6cc1e0dfa 100644 --- a/src/hotspot/share/opto/multnode.hpp +++ b/src/hotspot/share/opto/multnode.hpp @@ -280,7 +280,11 @@ template ProjNode* MultiNode::apply_to_projs(DUIterator_Fast& i * after the current IGVN. */ class TupleNode : public MultiNode { +private: const TypeTuple* _tf; + const TypePtr* _adr_type; + + TupleNode(const TypeTuple* tf, const TypePtr* adr_type) : MultiNode(tf->cnt()), _tf(tf), _adr_type(adr_type) {} template static void make_helper(TupleNode* tn, uint i, Node* node, NN... nn) { @@ -291,21 +295,21 @@ class TupleNode : public MultiNode { static void make_helper(TupleNode*, uint) {} public: - TupleNode(const TypeTuple* tf) : MultiNode(tf->cnt()), _tf(tf) {} - - int Opcode() const override; - const Type* bottom_type() const override { return _tf; } - /* Give as many `Node*` as you want in the `nn` pack: - * TupleNode::make(tf, input1) - * TupleNode::make(tf, input1, input2, input3, input4) + * TupleNode::make(tf, adr_type, input1) + * TupleNode::make(tf, adr_type, input1, input2, input3, input4) */ template - static TupleNode* make(const TypeTuple* tf, NN... nn) { - TupleNode* tn = new TupleNode(tf); + static TupleNode* make(const TypeTuple* tf, const TypePtr* adr_type, NN... nn) { + TupleNode* tn = new TupleNode(tf, adr_type); make_helper(tn, 0, nn...); return tn; } + + int Opcode() const override; + const Type* bottom_type() const override { return _tf; } + const TypePtr* adr_type() const override { return _adr_type; } + uint size_of() const override { return sizeof(TupleNode); } }; #endif // SHARE_OPTO_MULTNODE_HPP diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index 7e7fd9a93de8..e708bd8cbcb6 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -200,10 +200,6 @@ vmTestbase/nsk/monitoring/ThreadMXBean/findMonitorDeadlockedThreads/find006/Test ############################################################################# -# Value Objects failures start here: -applications/ctw/modules/java_base.java 8375645 generic-all -applications/ctw/modules/java_base_2.java 8375645 generic-all - compiler/codegen/TestRedundantLea.java#Spill 8361089 generic-all compiler/vectorapi/TestVectorReassociations.java 8388927 generic-all diff --git a/test/hotspot/jtreg/compiler/escapeAnalysis/TestSplitIncorrectPhi.java b/test/hotspot/jtreg/compiler/escapeAnalysis/TestSplitIncorrectPhi.java new file mode 100644 index 000000000000..062295adfcfa --- /dev/null +++ b/test/hotspot/jtreg/compiler/escapeAnalysis/TestSplitIncorrectPhi.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2026, 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. + */ + +package compiler.escapeAnalysis; + +/* + * @test + * @bug 8375645 + * @summary Ambiguous memory Phis may lead to EA choosing the wrong one in split_unique_types. + * @run main ${test.main.class} + * @run main/othervm -Xbatch -XX:-TieredCompilation -XX:CompileOnly=${test.main.class}::test + * ${test.main.class} + */ +public class TestSplitIncorrectPhi { + public static void main(String[] args) { + for (int i = 0; i < 10_000; i++) { + test(i % 2 == 0); + } + } + + private static void test(boolean flag) { + A a = null; + if (flag) { + C c = new C(); + B b = new B(); + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 8; j++) { + switch (i) { + case -1, -2, -3 : + break; + case 0 : + b.c = c; + } + } + } + b.c = c; + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 8; j++) { + switch (i) { + case -1, -2, -3 : + break; + case 0 : + a = new A(); + } + } + } + for (int i = 0; i < 32; i++) { + if (i < 10) { + a = new A(); + } + } + a = new A(); + for (int i = 1; i < 32; i++) { + a = new A(); + } + for (int i = 1; i < 32; i++) { + a = new A(); + } + for (int i = 1; i < 32; i++) { + a = new A(); + } + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 8; j++) { + switch (i) { + case -1, -2, -3 : + break; + case 0 : + a.b = b; + } + } + } + a.b = b; + } + if (a != null) { + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 8; j++) { + switch (i) { + case -1, -2, -3 : + break; + case 0 : + a.b.c.f = java.lang.Integer.valueOf(0x42); + } + } + } + a.b.c.f = java.lang.Integer.valueOf(0x42); + } + } + + private static class A { + public B b; + } + + private static class B { + public C c; + } + + private static class C { + public int f; + } +} diff --git a/test/hotspot/jtreg/compiler/igvn/TestDeadNonsensicalMemoryPhi.java b/test/hotspot/jtreg/compiler/igvn/TestDeadNonsensicalMemoryPhi.java new file mode 100644 index 000000000000..e46592cf22c9 --- /dev/null +++ b/test/hotspot/jtreg/compiler/igvn/TestDeadNonsensicalMemoryPhi.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2026, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ +package compiler.igvn; + +/* + * @test + * @bug 8375645 + * @summary A dead memory Phi has nonsensical inputs, it is a memory Phi in a loop but both of its + * inputs are not in that loop. + * @run main ${test.main.class} + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+IgnoreUnrecognizedVMOptions + * -XX:CompileOnly=${test.main.class}::test -XX:-TieredCompilation -Xcomp + * -XX:+StressIGVN -XX:+StressCCP -XX:+StressLoopPeeling + * ${test.main.class} + */ +public class TestDeadNonsensicalMemoryPhi { + long x; + int[] iArrFld; + + public static void main(String[] args) { + TestDeadNonsensicalMemoryPhi obj = new TestDeadNonsensicalMemoryPhi(); + for (int i = 0; i < 100; i++) { + try { + obj.test(); + } catch (NullPointerException e) {} + } + } + + void test() { + for (int i = 7; i < 152; i++) { + x = 0; + } + for (int i = 3; i < 273; i++) { + for (long j = 3; j < 97; j++) { + if (i == 7) { + for (int k = 2; k > 1; k--) { + iArrFld[1] = i; + } + } + } + } + } +} From c1565def6c3d6c6b4700b0d0b863a90f1ca7e61a Mon Sep 17 00:00:00 2001 From: Prasanta Sadhukhan Date: Tue, 29 Sep 2026 11:23:19 +0000 Subject: [PATCH 072/103] 8392783: Win32PrintService.getUIClassNamesForRole throws ArrayIndexOutOfBoundsException Reviewed-by: prr, serb --- .../classes/sun/print/Win32PrintService.java | 6 +-- .../javax/print/GetUIClassNameForRole.java | 50 +++++++++++++++++++ 2 files changed, 52 insertions(+), 4 deletions(-) create mode 100644 test/jdk/javax/print/GetUIClassNameForRole.java diff --git a/src/java.desktop/windows/classes/sun/print/Win32PrintService.java b/src/java.desktop/windows/classes/sun/print/Win32PrintService.java index 1f5373c1f4bf..5e2d6b9b6617 100644 --- a/src/java.desktop/windows/classes/sun/print/Win32PrintService.java +++ b/src/java.desktop/windows/classes/sun/print/Win32PrintService.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -1693,9 +1693,7 @@ public String[] getUIClassNamesForRole(int role) { return null; } if (role == DocumentPropertiesUI.DOCUMENTPROPERTIES_ROLE) { - String[] names = new String[0]; - names[0] = DocumentPropertiesUI.DOCPROPERTIESCLASSNAME; - return names; + return new String[] { DocumentPropertiesUI.DOCPROPERTIESCLASSNAME }; } throw new IllegalArgumentException("Unsupported role"); } diff --git a/test/jdk/javax/print/GetUIClassNameForRole.java b/test/jdk/javax/print/GetUIClassNameForRole.java new file mode 100644 index 000000000000..0dfacfb9b5ec --- /dev/null +++ b/test/jdk/javax/print/GetUIClassNameForRole.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2026, 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 8392783 + * @summary Win32PrintService.getUIClassNamesForRole + * throws ArrayIndexOutOfBoundsException + * @run main GetUIClassNameForRole + */ + +import javax.print.PrintService; +import javax.print.PrintServiceLookup; +import javax.print.ServiceUIFactory; + +public class GetUIClassNameForRole { + private static final int DOCUMENT_PROPERTIES_ROLE = + ServiceUIFactory.RESERVED_UIROLE + 100; + + public static void main(String[] args) { + + for (PrintService service : + PrintServiceLookup.lookupPrintServices(null, null)) { + ServiceUIFactory factory = service.getServiceUIFactory(); + if (factory != null) { + factory.getUIClassNamesForRole(DOCUMENT_PROPERTIES_ROLE); + } + } + } +} From b25517fcd6e3262669c84cf4237c1134f74af1b0 Mon Sep 17 00:00:00 2001 From: Richard Reingruber Date: Tue, 29 Sep 2026 13:51:53 +0000 Subject: [PATCH 073/103] 8392914: Several jdk/internal/vm/Continuation/BasicExt.java test cases fail with -XX:CompileThresholdScaling=0.01 Reviewed-by: mdoerr, dbriemann --- test/jdk/jdk/internal/vm/Continuation/BasicExt.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/jdk/jdk/internal/vm/Continuation/BasicExt.java b/test/jdk/jdk/internal/vm/Continuation/BasicExt.java index 254583bf18b5..e9e1e803c27b 100644 --- a/test/jdk/jdk/internal/vm/Continuation/BasicExt.java +++ b/test/jdk/jdk/internal/vm/Continuation/BasicExt.java @@ -47,6 +47,7 @@ * @summary Collection of basic continuation tests. CompilationPolicy controls which frames in a sequence should be compiled when calling Continuation.yield(). * @requires vm.continuations * @requires vm.flavor == "server" & vm.opt.TieredCompilation != true + * @requires vm.opt.CompileThresholdScaling == null * @modules java.base/jdk.internal.vm * @library /test/lib /test/hotspot/jtreg * @build java.base/java.lang.StackWalkerHelper @@ -67,6 +68,7 @@ * @summary Collection of basic continuation tests. CompilationPolicy controls which frames in a sequence should be compiled when calling Continuation.yield(). * @requires vm.continuations * @requires vm.flavor == "server" & vm.opt.TieredCompilation != true + * @requires vm.opt.CompileThresholdScaling == null * @modules java.base/jdk.internal.vm * @library /test/lib /test/hotspot/jtreg * @build java.base/java.lang.StackWalkerHelper @@ -87,6 +89,7 @@ * @summary Collection of basic continuation tests. CompilationPolicy controls which frames in a sequence should be compiled when calling Continuation.yield(). * @requires vm.continuations * @requires vm.flavor == "server" & vm.opt.TieredCompilation != true + * @requires vm.opt.CompileThresholdScaling == null * @modules java.base/jdk.internal.vm * @library /test/lib /test/hotspot/jtreg * @build java.base/java.lang.StackWalkerHelper @@ -107,6 +110,7 @@ * @summary Collection of basic continuation tests. CompilationPolicy controls which frames in a sequence should be compiled when calling Continuation.yield(). * @requires vm.continuations * @requires vm.flavor == "server" & vm.opt.TieredCompilation != true + * @requires vm.opt.CompileThresholdScaling == null * @modules java.base/jdk.internal.vm * @library /test/lib /test/hotspot/jtreg * @build java.base/java.lang.StackWalkerHelper @@ -147,6 +151,7 @@ * @summary Collection of basic continuation tests. CompilationPolicy controls which frames in a sequence should be compiled when calling Continuation.yield(). * @requires vm.continuations * @requires vm.flavor == "server" & vm.opt.TieredCompilation != true + * @requires vm.opt.CompileThresholdScaling == null * @modules java.base/jdk.internal.vm * @library /test/lib /test/hotspot/jtreg * @build java.base/java.lang.StackWalkerHelper @@ -167,6 +172,7 @@ * @summary Collection of basic continuation tests. CompilationPolicy controls which frames in a sequence should be compiled when calling Continuation.yield(). * @requires vm.continuations * @requires vm.flavor == "server" & vm.opt.TieredCompilation != true + * @requires vm.opt.CompileThresholdScaling == null * @modules java.base/jdk.internal.vm * @library /test/lib /test/hotspot/jtreg * @build java.base/java.lang.StackWalkerHelper @@ -187,6 +193,7 @@ * @summary Collection of basic continuation tests. CompilationPolicy controls which frames in a sequence should be compiled when calling Continuation.yield(). * @requires vm.continuations * @requires vm.flavor == "server" & vm.opt.TieredCompilation != true + * @requires vm.opt.CompileThresholdScaling == null * @modules java.base/jdk.internal.vm * @library /test/lib /test/hotspot/jtreg * @build java.base/java.lang.StackWalkerHelper @@ -207,6 +214,7 @@ * @summary Collection of basic continuation tests. CompilationPolicy controls which frames in a sequence should be compiled when calling Continuation.yield(). * @requires vm.continuations * @requires vm.flavor == "server" & vm.opt.TieredCompilation != true + * @requires vm.opt.CompileThresholdScaling == null * @modules java.base/jdk.internal.vm * @library /test/lib /test/hotspot/jtreg * @build java.base/java.lang.StackWalkerHelper From 1741f98e6a9ca530bb2ea0b3a2b72a6b2e7a949a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20=C3=96sterlund?= Date: Tue, 29 Sep 2026 14:04:24 +0000 Subject: [PATCH 074/103] 8393020: ZGC: Dynamic GC worker adjustment logic flaw Reviewed-by: aboldtch, stefank, tschatzl --- src/hotspot/share/gc/z/zDirector.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/hotspot/share/gc/z/zDirector.cpp b/src/hotspot/share/gc/z/zDirector.cpp index 9ff56ce74bb7..e5a060415884 100644 --- a/src/hotspot/share/gc/z/zDirector.cpp +++ b/src/hotspot/share/gc/z/zDirector.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 @@ -219,8 +219,8 @@ static ZDriverRequest rule_soft_minor_allocation_rate_dynamic(const ZDirectorSta double serial_gc_time_passed, double parallel_gc_time_passed) { return rule_minor_allocation_rate_dynamic(stats, - 0.0 /* serial_gc_time_passed */, - 0.0 /* parallel_gc_time_passed */, + serial_gc_time_passed, + parallel_gc_time_passed, false /* conservative_alloc_rate */, stats._heap._soft_max_heap_size /* capacity */); } @@ -229,8 +229,8 @@ static ZDriverRequest rule_semi_hard_minor_allocation_rate_dynamic(const ZDirect double serial_gc_time_passed, double parallel_gc_time_passed) { return rule_minor_allocation_rate_dynamic(stats, - 0.0 /* serial_gc_time_passed */, - 0.0 /* parallel_gc_time_passed */, + serial_gc_time_passed, + parallel_gc_time_passed, false /* conservative_alloc_rate */, ZHeap::heap()->max_capacity() /* capacity */); } @@ -239,8 +239,8 @@ static ZDriverRequest rule_hard_minor_allocation_rate_dynamic(const ZDirectorSta double serial_gc_time_passed, double parallel_gc_time_passed) { return rule_minor_allocation_rate_dynamic(stats, - 0.0 /* serial_gc_time_passed */, - 0.0 /* parallel_gc_time_passed */, + serial_gc_time_passed, + parallel_gc_time_passed, true /* conservative_alloc_rate */, ZHeap::heap()->max_capacity() /* capacity */); } From fec788eccb23b2b48d4aa5be050e322f9a2c0c1b Mon Sep 17 00:00:00 2001 From: Naoto Sato Date: Tue, 29 Sep 2026 15:49:06 +0000 Subject: [PATCH 075/103] 8392994: DateTimeFormatter prints specific zone names for offset-only values Reviewed-by: jlu --- .../java/time/format/DateTimeFormatterBuilder.java | 14 ++++++++------ .../time/format/TestZoneTextPrinterParser.java | 13 ++++++++++++- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java b/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java index 9e3621a6b54d..9bbffe48a5e3 100644 --- a/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java +++ b/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java @@ -4512,12 +4512,14 @@ public boolean format(DateTimePrintContext context, StringBuilder buf, boolean o TemporalAccessor dt = context.getTemporal(); int type = GENERIC; if (!isGeneric) { - // Check if an explicit metazone DST offset exists - String dstOffset = TimeZoneNameUtility.explicitDstOffset(zname); - if (dt.isSupported(OFFSET_SECONDS) && dstOffset != null) { - type = ZoneOffset.from(dt).equals(ZoneOffset.of(dstOffset)) ? DST : STD; - } else if (dt.isSupported(ChronoField.INSTANT_SECONDS)) { - type = zone.getRules().isDaylightSavings(Instant.from(dt)) ? DST : STD; + if (dt.isSupported(INSTANT_SECONDS)) { + // Check if an explicit metazone DST offset exists + String dstOffset = TimeZoneNameUtility.explicitDstOffset(zname); + if (dt.isSupported(OFFSET_SECONDS) && dstOffset != null) { + type = ZoneOffset.from(dt).equals(ZoneOffset.of(dstOffset)) ? DST : STD; + } else { + type = zone.getRules().isDaylightSavings(Instant.from(dt)) ? DST : STD; + } } else if (dt.isSupported(ChronoField.EPOCH_DAY) && dt.isSupported(ChronoField.NANO_OF_DAY)) { LocalDate date = LocalDate.ofEpochDay(dt.getLong(ChronoField.EPOCH_DAY)); diff --git a/test/jdk/java/time/test/java/time/format/TestZoneTextPrinterParser.java b/test/jdk/java/time/test/java/time/format/TestZoneTextPrinterParser.java index bdcfea0467e1..31076248f04c 100644 --- a/test/jdk/java/time/test/java/time/format/TestZoneTextPrinterParser.java +++ b/test/jdk/java/time/test/java/time/format/TestZoneTextPrinterParser.java @@ -27,6 +27,7 @@ import static org.junit.jupiter.api.Assertions.fail; import java.text.DateFormatSymbols; +import java.time.OffsetTime; import java.time.ZoneId; import java.time.ZoneOffset; import java.time.ZonedDateTime; @@ -52,11 +53,12 @@ import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; /* * @test * @bug 8081022 8151876 8166875 8177819 8189784 8206980 8277049 8278434 8346948 - * 8174269 8371842 8388214 + * 8174269 8371842 8388214 8392994 * @key randomness */ @@ -300,4 +302,13 @@ public void test_roundTripAtOverlap(String pattern, String input) { assertEquals(input, dtf.format(ZonedDateTime.parse(input, dtf))); assertEquals(input, dtf.format(ZonedDateTime.parse(lc, dtf))); } + + @ParameterizedTest + @ValueSource(strings = {"-07:00", "-08:00", "+09:00"}) + public void test_offsetTimeUsesGenericZoneName(String offset) { + DateTimeFormatter format = + DateTimeFormatter.ofPattern("zzzz", Locale.US) + .withZone(ZoneId.of("America/Vancouver")); + assertEquals("Pacific Time", format.format(OffsetTime.parse("12:00" + offset))); + } } From f6ea1d15ca006a81cae3bc200a5844992756380e Mon Sep 17 00:00:00 2001 From: Mikael Vidstedt Date: Tue, 29 Sep 2026 16:05:41 +0000 Subject: [PATCH 076/103] 8384866: Bump minimum boot jdk to JDK 27 Reviewed-by: erikj, iris --- .github/workflows/main.yml | 2 +- make/conf/github-actions.conf | 28 ++++++++++++++-------------- make/conf/jib-profiles.js | 2 +- make/conf/version-numbers.conf | 2 +- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8f5e16d2e534..c778ca628571 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -345,7 +345,7 @@ jobs: platform: macos-x64 runs-on: 'macos-15-intel' xcode-toolset-version: '16.4' - configure-arguments: ${{ github.event.inputs.configure-arguments }} + configure-arguments: ${{ github.event.inputs.configure-arguments }} --enable-deprecated-ports make-arguments: ${{ github.event.inputs.make-arguments }} dry-run: ${{ needs.prepare.outputs.dry-run == 'true' }} if: needs.prepare.outputs.macos-x64 == 'true' diff --git a/make/conf/github-actions.conf b/make/conf/github-actions.conf index 6c5805f0764e..db5161b70019 100644 --- a/make/conf/github-actions.conf +++ b/make/conf/github-actions.conf @@ -29,29 +29,29 @@ GTEST_VERSION=1.14.0 JTREG_VERSION=8.3+1 LINUX_X64_BOOT_JDK_EXT=tar.gz -LINUX_X64_BOOT_JDK_URL=https://download.java.net/java/GA/jdk26/c3cc523845074aa0af4f5e1e1ed4151d/35/GPL/openjdk-26_linux-x64_bin.tar.gz -LINUX_X64_BOOT_JDK_SHA256=83c78367f8c81257beef72aca4bbbf8e6dac8ca2b3a4546a85879a09e6e4e128 +LINUX_X64_BOOT_JDK_URL=https://download.java.net/java/GA/jdk27/55ce5470a6294008af0057ff4626d0e5/35/GPL/openjdk-27_linux-x64_bin.tar.gz +LINUX_X64_BOOT_JDK_SHA256=95fc37eb3a18a27a26d5904c2d89d52bace8dafa9a078ca27f4747fbc4bf070b LINUX_AARCH64_BOOT_JDK_EXT=tar.gz -LINUX_AARCH64_BOOT_JDK_URL=https://download.java.net/java/GA/jdk26/c3cc523845074aa0af4f5e1e1ed4151d/35/GPL/openjdk-26_linux-aarch64_bin.tar.gz -LINUX_AARCH64_BOOT_JDK_SHA256=403ccf451e88d0be9e1dec129fcb9318de9752121e0eb92dfa9a8cf06f249007 +LINUX_AARCH64_BOOT_JDK_URL=https://download.java.net/java/GA/jdk27/55ce5470a6294008af0057ff4626d0e5/35/GPL/openjdk-27_linux-aarch64_bin.tar.gz +LINUX_AARCH64_BOOT_JDK_SHA256=da4e9dde1fff90204739e969187bab4751bd59a2a1c479672e1a1810f7dd23ea ALPINE_LINUX_X64_BOOT_JDK_EXT=tar.gz -ALPINE_LINUX_X64_BOOT_JDK_URL=https://github.com/adoptium/temurin26-binaries/releases/download/jdk-26%2B35/OpenJDK26U-jdk_x64_alpine-linux_hotspot_26_35.tar.gz -ALPINE_LINUX_X64_BOOT_JDK_SHA256=c105e581fdccb4e7120d889235d1ad8d5b2bed0af4972bc881e0a8ba687c94a4 +ALPINE_LINUX_X64_BOOT_JDK_URL=https://github.com/adoptium/temurin27-binaries/releases/download/jdk-27%2B35/OpenJDK27U-jdk_x64_alpine-linux_hotspot_27_35.tar.gz +ALPINE_LINUX_X64_BOOT_JDK_SHA256=45aef7cd5ae667c6f5dd100c96a99e5a00e61c512664bb13326cf149b7d37e0f MACOS_AARCH64_BOOT_JDK_EXT=tar.gz -MACOS_AARCH64_BOOT_JDK_URL=https://download.java.net/java/GA/jdk26/c3cc523845074aa0af4f5e1e1ed4151d/35/GPL/openjdk-26_macos-aarch64_bin.tar.gz -MACOS_AARCH64_BOOT_JDK_SHA256=254586bcd1bf6dcd125ad667ac32562cb1e2ab1abf3a61fb117b6fabb571e765 +MACOS_AARCH64_BOOT_JDK_URL=https://download.java.net/java/GA/jdk27/55ce5470a6294008af0057ff4626d0e5/35/GPL/openjdk-27_macos-aarch64_bin.tar.gz +MACOS_AARCH64_BOOT_JDK_SHA256=ff42134c1bc5ac81c5a91a95fd4df87479db5d0499ba875d6adb5bda5626c3e8 MACOS_X64_BOOT_JDK_EXT=tar.gz -MACOS_X64_BOOT_JDK_URL=https://download.java.net/java/GA/jdk26/c3cc523845074aa0af4f5e1e1ed4151d/35/GPL/openjdk-26_macos-x64_bin.tar.gz -MACOS_X64_BOOT_JDK_SHA256=8642b89d889c14ede2c446fd5bbe3621c8a3082e3df02013fd1658e39f52929a +MACOS_X64_BOOT_JDK_URL=https://github.com/adoptium/temurin27-binaries/releases/download/jdk-27%2B35-ea-beta/OpenJDK27U-jdk_x64_mac_hotspot_27_35-ea.tar.gz +MACOS_X64_BOOT_JDK_SHA256=dba58de67ddff845fef0654d439fba39cf0185d3f6c1c6e323de33ced9e72018 WINDOWS_X64_BOOT_JDK_EXT=zip -WINDOWS_X64_BOOT_JDK_URL=https://download.java.net/java/GA/jdk26/c3cc523845074aa0af4f5e1e1ed4151d/35/GPL/openjdk-26_windows-x64_bin.zip -WINDOWS_X64_BOOT_JDK_SHA256=2dd2d92c9374cd49a120fe9d916732840bf6bb9f0e0cc29794917a3c08b99c5f +WINDOWS_X64_BOOT_JDK_URL=https://download.java.net/java/GA/jdk27/55ce5470a6294008af0057ff4626d0e5/35/GPL/openjdk-27_windows-x64_bin.zip +WINDOWS_X64_BOOT_JDK_SHA256=41172837168dd25a8d9fe5eb253ac1efc568c5f9ff608144bcacadfdf50f876c WINDOWS_AARCH64_BOOT_JDK_EXT=zip -WINDOWS_AARCH64_BOOT_JDK_URL=https://github.com/adoptium/temurin26-binaries/releases/download/jdk-26%2B35-ea-beta/OpenJDK26U-jdk_aarch64_windows_hotspot_26_35-ea.zip -WINDOWS_AARCH64_BOOT_JDK_SHA256=4892cd9714e222947de1a7415a696f43adf691560522dc5858c0a8af070912c0 +WINDOWS_AARCH64_BOOT_JDK_URL=https://github.com/adoptium/temurin27-binaries/releases/download/jdk-27%2B35-ea-beta/OpenJDK27U-jdk_aarch64_windows_hotspot_27_35-ea.zip +WINDOWS_AARCH64_BOOT_JDK_SHA256=0bcfb220eb885776da39cc6de50898f62fe34865e48c49d9e2a98070fe4049b5 diff --git a/make/conf/jib-profiles.js b/make/conf/jib-profiles.js index b83ca709a888..f64e2ec22706 100644 --- a/make/conf/jib-profiles.js +++ b/make/conf/jib-profiles.js @@ -387,7 +387,7 @@ var getJibProfilesCommon = function (input, data) { }; }; - common.boot_jdk_version = "26"; + common.boot_jdk_version = "27"; common.boot_jdk_build_number = "35"; common.boot_jdk_home = input.get("boot_jdk", "install_path") + "/jdk-" + common.boot_jdk_version diff --git a/make/conf/version-numbers.conf b/make/conf/version-numbers.conf index 83cfa86cf70a..2220f4db09b5 100644 --- a/make/conf/version-numbers.conf +++ b/make/conf/version-numbers.conf @@ -37,6 +37,6 @@ DEFAULT_VERSION_DATE=2027-03-23 DEFAULT_VERSION_CLASSFILE_MAJOR=72 # "`$EXPR $DEFAULT_VERSION_FEATURE + 44`" DEFAULT_VERSION_CLASSFILE_MINOR=0 DEFAULT_VERSION_DOCS_API_SINCE=11 -DEFAULT_ACCEPTABLE_BOOT_VERSIONS="26 27 28" +DEFAULT_ACCEPTABLE_BOOT_VERSIONS="27 28" DEFAULT_JDK_SOURCE_TARGET_VERSION=28 DEFAULT_PROMOTED_VERSION_PRE=ea From 39b70c571e251ed7647b1739c71794f5eae36a33 Mon Sep 17 00:00:00 2001 From: Marc Chevalier Date: Tue, 29 Sep 2026 16:07:06 +0000 Subject: [PATCH 077/103] 8391990: Avoid zero hash for value objects Reviewed-by: fparain, thartmann, jpai --- src/hotspot/share/classfile/javaClasses.cpp | 8 ++ src/hotspot/share/classfile/vmSymbols.hpp | 2 + src/hotspot/share/opto/library_call.cpp | 30 +++++- src/hotspot/share/opto/valuetypenode.cpp | 19 +++- src/hotspot/share/prims/jvm.cpp | 7 +- .../java/lang/runtime/ValueObjectMethods.java | 19 +++- .../classes/jdk/internal/misc/Unsafe.java | 10 ++ .../jdk/internal/misc/UnsafeConstants.java | 25 ++++- .../valuetypes/TestHashcodeFastPath.java | 72 ++++++++++++++ .../jtreg/runtime/Unsafe/HashCodeMask.java | 48 +++++++++ .../NonZeroIdentityHashCodeTest.java | 97 +++++++++++++++++++ 11 files changed, 321 insertions(+), 16 deletions(-) create mode 100644 test/hotspot/jtreg/runtime/Unsafe/HashCodeMask.java create mode 100644 test/hotspot/jtreg/runtime/valhalla/valuetypes/NonZeroIdentityHashCodeTest.java diff --git a/src/hotspot/share/classfile/javaClasses.cpp b/src/hotspot/share/classfile/javaClasses.cpp index 1a4200529d5a..8d5b7ab78a4f 100644 --- a/src/hotspot/share/classfile/javaClasses.cpp +++ b/src/hotspot/share/classfile/javaClasses.cpp @@ -3779,6 +3779,8 @@ class UnsafeConstantsFixup : public FieldClosure { bool _big_endian; bool _use_unaligned_access; int _data_cache_line_flush_size; + int _hash_code_mask; + int _hash_code_no_hash; public: UnsafeConstantsFixup() { // round up values for all static final fields @@ -3787,6 +3789,8 @@ class UnsafeConstantsFixup : public FieldClosure { _big_endian = LITTLE_ENDIAN_ONLY(false) BIG_ENDIAN_ONLY(true); _use_unaligned_access = UseUnalignedAccesses; _data_cache_line_flush_size = (int)VM_Version::data_cache_line_flush_size(); + _hash_code_mask = checked_cast(markWord::hash_mask); + _hash_code_no_hash = checked_cast(markWord::no_hash); } void do_field(fieldDescriptor* fd) { @@ -3805,6 +3809,10 @@ class UnsafeConstantsFixup : public FieldClosure { mirror->bool_field_put(fd->offset(), _use_unaligned_access); } else if (fd->name() == vmSymbols::data_cache_line_flush_size_name()) { mirror->int_field_put(fd->offset(), _data_cache_line_flush_size); + } else if (fd->name() == vmSymbols::hash_code_mask_name()) { + mirror->int_field_put(fd->offset(), _hash_code_mask); + } else if (fd->name() == vmSymbols::hash_code_no_hash_name()) { + mirror->int_field_put(fd->offset(), _hash_code_no_hash); } else { assert(false, "unexpected UnsafeConstants field"); } diff --git a/src/hotspot/share/classfile/vmSymbols.hpp b/src/hotspot/share/classfile/vmSymbols.hpp index f2fc6704d07f..cf83a71aaf05 100644 --- a/src/hotspot/share/classfile/vmSymbols.hpp +++ b/src/hotspot/share/classfile/vmSymbols.hpp @@ -531,6 +531,8 @@ class SerializeClosure; template(big_endian_name, "BIG_ENDIAN") \ template(use_unaligned_access_name, "UNALIGNED_ACCESS") \ template(data_cache_line_flush_size_name, "DATA_CACHE_LINE_FLUSH_SIZE") \ + template(hash_code_mask_name, "HASH_CODE_MASK") \ + template(hash_code_no_hash_name, "HASH_CODE_NO_HASH") \ template(during_unsafe_access_name, "during_unsafe_access") \ template(checkIndex_name, "checkIndex") \ template(jfr_epoch_name, "jfr_epoch") \ diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp index d74eeb3b2fcc..5bcd29695e95 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -5550,8 +5550,8 @@ Node* LibraryCallKit::get_hashcode_from_header(Node* header, RegionNode* unset_r Node* hash_val = _gvn.transform(new AndINode(hshifted_header, hash_mask)); Node* no_hash_val = _gvn.intcon(markWord::no_hash); - Node* chk_assigned = _gvn.transform(new CmpINode( hash_val, no_hash_val)); - Node* test_assigned = _gvn.transform(new BoolNode( chk_assigned, BoolTest::eq)); + Node* chk_assigned = _gvn.transform(new CmpINode(hash_val, no_hash_val)); + Node* test_assigned = _gvn.transform(new BoolNode(chk_assigned, BoolTest::eq)); generate_slow_guard(test_assigned, unset_region); @@ -5701,6 +5701,7 @@ bool LibraryCallKit::inline_native_hashcode(bool is_virtual, bool is_static) { unmasked_region->init_req(1, IfTrue(iff_is_empty_object)); unmasked_result->init_req(1, result_empty); + // There is a segment (case 2. or 3.) set_control(IfFalse(iff_is_empty_object)); Node* obj_payload_addr = basic_plus_adr(obj, ConvI2L(offset)); @@ -5729,9 +5730,28 @@ bool LibraryCallKit::inline_native_hashcode(bool is_virtual, bool is_static) { unmasked_region->init_req(3, IfTrue(iff_is_long_payload)); unmasked_result->init_req(3, result_long); - Node* fast_path_result = AndI(_gvn.transform(unmasked_result), intcon(markWord::hash_mask)); - result_reg->init_req(_value_fast_path, _gvn.transform(unmasked_region)); - result_val->init_req(_value_fast_path, fast_path_result); + set_control(_gvn.transform(unmasked_region)); + Node* hash_mask_con = intcon(markWord::hash_mask); + Node* masked_result = AndI(_gvn.transform(unmasked_result), hash_mask_con); + + // Now, we have computed the hash. But we don't want it to be markWord::no_hash. + // If it is, let's just take the hash of the Class, and since this Class is an identity object, + // it must be different from markWord::no_hash. Let's do a little diamond since it's easy enough + // and cmove experimentally failed to be as efficient. + RegionNode* avoid_no_hash_region = new RegionNode(3); + Node* no_hash_avoided_result = new PhiNode(avoid_no_hash_region, TypeInt::INT); + + Node* no_hash_con = intcon(checked_cast(markWord::no_hash)); + Node* bol_hash_would_be_no_hash = BoolCmpI(masked_result, BoolTest::eq, no_hash_con); + IfNode* iff_hash_would_be_no_hash = create_and_map_if(control(), bol_hash_would_be_no_hash, PROB_FAIR, COUNT_UNKNOWN); + avoid_no_hash_region->init_req(1, IfTrue(iff_hash_would_be_no_hash)); + no_hash_avoided_result->init_req(1, result_empty); + + avoid_no_hash_region->init_req(2, IfFalse(iff_hash_would_be_no_hash)); + no_hash_avoided_result->init_req(2, masked_result); + + result_reg->init_req(_value_fast_path, _gvn.transform(avoid_no_hash_region)); + result_val->init_req(_value_fast_path, _gvn.transform(no_hash_avoided_result)); } } } diff --git a/src/hotspot/share/opto/valuetypenode.cpp b/src/hotspot/share/opto/valuetypenode.cpp index 5d1eec59189e..f905367ff2f1 100644 --- a/src/hotspot/share/opto/valuetypenode.cpp +++ b/src/hotspot/share/opto/valuetypenode.cpp @@ -1119,7 +1119,8 @@ Node* ValueTypeNode::emit_identity_hash_code(GraphKit* kit, Node* arg, intptr_t }; Node* const thirty_one = kit->intcon(31); - Node* result = kit->intcon(checked_cast(klass_hash)); + Node* klass_hash_con = kit->intcon(checked_cast(klass_hash)); + Node* result = klass_hash_con; for (int i = 0; i < number_of_nonoop_entries; i++) { AcmpMapSegment segment = vk->get_nonoop_segment_of_acmp_map(i); int offset = segment._offset; @@ -1152,9 +1153,21 @@ Node* ValueTypeNode::emit_identity_hash_code(GraphKit* kit, Node* arg, intptr_t offset++; } } - result = kit->AndI(result, kit->intcon(markWord::hash_mask)); + Node* hash_mask_con = kit->intcon(markWord::hash_mask); + result = kit->AndI(result, hash_mask_con); - region->add_req(kit->control()); + // Now, we have computed the hash. But we don't want it to be markWord::no_hash. + // If it is, let's just take the hash of the Class, and since this Class is an identity object, + // it must be different from markWord::no_hash. Let's do a little diamond since it's easy enough + // and cmove experimentally failed to be as efficient. + Node* no_hash_con = kit->intcon(checked_cast(markWord::no_hash)); + Node* bol_hash_would_be_no_hash = kit->BoolCmpI(result, BoolTest::eq, no_hash_con); + IfNode* iff_hash_would_be_no_hash = kit->create_and_map_if(kit->control(), bol_hash_would_be_no_hash, PROB_FAIR, COUNT_UNKNOWN); + + region->add_req(kit->IfTrue(iff_hash_would_be_no_hash)); + phi_result->add_req(klass_hash_con); + + region->add_req(kit->IfFalse(iff_hash_would_be_no_hash)); phi_result->add_req(result); kit->set_control(region); diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp index 794de6c2f30a..d8a6fffaa403 100644 --- a/src/hotspot/share/prims/jvm.cpp +++ b/src/hotspot/share/prims/jvm.cpp @@ -792,11 +792,8 @@ JVM_ENTRY(jint, JVM_IHashCode(JNIEnv* env, jobject handle)) oop obj = JNIHandles::resolve_non_null(handle); if (Arguments::is_valhalla_enabled() && obj->klass()->is_value_klass()) { const intptr_t obj_identity_hash = obj->mark().hash(); - // Check if mark word contains hash code already. - // It is possible that the generated identity hash is 0, which is not - // distinct from the no_hash value. In such a case, the hash will be - // computed and set every time JVM_IHashCode is called. If that happens, - // the only consequence is losing out on the optimization. + // Check if mark word contains hash code already. The hash code is guaranteed + // not to be markWord::no_hash, which is 0. if (obj_identity_hash != markWord::no_hash) { return checked_cast(obj_identity_hash); } diff --git a/src/java.base/share/classes/java/lang/runtime/ValueObjectMethods.java b/src/java.base/share/classes/java/lang/runtime/ValueObjectMethods.java index 80ce42a8814c..bef030e5fc8d 100644 --- a/src/java.base/share/classes/java/lang/runtime/ValueObjectMethods.java +++ b/src/java.base/share/classes/java/lang/runtime/ValueObjectMethods.java @@ -131,7 +131,8 @@ private static int valueObjectHashCode(Object obj) { Class type = obj.getClass(); final Unsafe U = UNSAFE; int[] map = U.getFieldMap(type); - int result = System.identityHashCode(type); + int typeHash = System.identityHashCode(type); + int result = typeHash; int nbNonRef = map[0]; for (int i = 0; i < nbNonRef; i++) { int offset = map[i * 2 + 1]; @@ -169,6 +170,20 @@ private static int valueObjectHashCode(Object obj) { Object oa = U.getReference(obj, offset); result = 31 * result + System.identityHashCode(oa); } - return result; + // To avoid multiple computations, the hash is cached in the mark word, + // also for value objects. This field in the mark word contains the + // special value Unsafe.hashCodeNoHash() when no value was cached yet. + // + // To avoid unbounded recomputation of the hash, when the stored value would + // be Unsafe.hashCodeNoHash(), we return instead the identity hash of the + // value class. That allows to distinguish different value classes and is + // easy for the compiler to fetch. Since `type` is an identity object, its + // hash is already guaranteed not to be Unsafe.hashCodeNoHash() and to fit + // in the mark word cache. + // + // Note that the computed hash, that would be stored in the mark word, is + // not directly `result` but `result` masked to fit in the cache of the + // mark word. + return (result & U.hashCodeMask()) == U.hashCodeNoHash() ? typeHash : result; } } diff --git a/src/java.base/share/classes/jdk/internal/misc/Unsafe.java b/src/java.base/share/classes/jdk/internal/misc/Unsafe.java index 8f88dc91f5cb..f7a82e9d72db 100644 --- a/src/java.base/share/classes/jdk/internal/misc/Unsafe.java +++ b/src/java.base/share/classes/jdk/internal/misc/Unsafe.java @@ -1619,6 +1619,16 @@ public long dataCacheLineAlignDown(long address) { */ public static boolean isWritebackEnabled() { return DATA_CACHE_LINE_FLUSH_SIZE != 0; } + /** + * Reports the mask used to fit hashcodes into the cache field of the mark word. + */ + public int hashCodeMask() { return HASH_CODE_MASK; } + + /** + * Reports the value used to mark that no hash code has been cached yet. + */ + public int hashCodeNoHash() { return HASH_CODE_NO_HASH; } + //--- random trusted operations from JNI: /** diff --git a/src/java.base/share/classes/jdk/internal/misc/UnsafeConstants.java b/src/java.base/share/classes/jdk/internal/misc/UnsafeConstants.java index 65ee7b3a6167..0e3847805883 100644 --- a/src/java.base/share/classes/jdk/internal/misc/UnsafeConstants.java +++ b/src/java.base/share/classes/jdk/internal/misc/UnsafeConstants.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2019, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -112,11 +112,34 @@ private UnsafeConstants() {} static final int DATA_CACHE_LINE_FLUSH_SIZE; + /** + * Mask used to remove the highest bits of a computed raw hash code + * that would not fit in the space dedicated to hash code storage in + * objects headers. + * + * @implNote + * The actual value for this field is injected by the JVM. + */ + + static final int HASH_CODE_MASK; + + /** + * The value stored in the hash field of the mark word when no hash + * was cached yet. + * + * @implNote + * The actual value for this field is injected by the JVM. + */ + + static final int HASH_CODE_NO_HASH; + static { ADDRESS_SIZE0 = 0; PAGE_SIZE = 0; BIG_ENDIAN = false; UNALIGNED_ACCESS = false; DATA_CACHE_LINE_FLUSH_SIZE = 0; + HASH_CODE_MASK = 0; + HASH_CODE_NO_HASH = 0; } } diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestHashcodeFastPath.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestHashcodeFastPath.java index cbaec7c65b6a..133bfd06c16b 100644 --- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestHashcodeFastPath.java +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestHashcodeFastPath.java @@ -698,4 +698,76 @@ int h_short_with_int_hidden_type2(ShortWithInt a) { int h_with_oop2(WithOop a) { return System.identityHashCode(a); } + + static value class Predictable { + int i; + Predictable(int i) { this.i = i; } + @DontCompile + public static Predictable makeWouldHaveZeroHash() { + return new Predictable(Integer.MIN_VALUE - System.identityHashCode(Predictable.class) * 31); + } + @DontCompile + public static Predictable makeWouldHaveZeroHash2() { + return new Predictable(- System.identityHashCode(Predictable.class) * 31); + } + } + + // Statically expanded + @Test + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIf = {"DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {URSHIFT_L, CACHE_PATH_U}, failOn = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE}, applyIf = {"DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIf = {"DisableIntrinsic", "_identityHashCode"}) + @IR(phase = {CompilePhase.PRINT_IDEAL}, failOn = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, URSHIFT_L}, applyIf = {"DisableIntrinsic", "_identityHashCode"}) + int h_predictable(Predictable a) { + return System.identityHashCode(a); + } + + // Get hashcode fast path, repeated for another @Run + @Test + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIfAnd = {"UseHashcodeFastPath", "false", "DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIfAnd = {"UseHashcodeFastPath", "false", "DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_AND_FAST_PATH_U, RSHIFT_L, FAST_PATH_S, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIfAnd = {"UseHashcodeFastPath", "true", "DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {URSHIFT_L, CACHE_AND_FAST_PATH_U, RSHIFT_L, FAST_PATH_S, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIfAnd = {"UseHashcodeFastPath", "true", "DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIfAnd = {"UseHashcodeFastPath", "false", "DisableIntrinsic", "_identityHashCode"}) + @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIfAnd = {"UseHashcodeFastPath", "false", "DisableIntrinsic", "_identityHashCode"}) + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L, RSHIFT_L}, applyIfAnd = {"UseHashcodeFastPath", "true", "DisableIntrinsic", "_identityHashCode"}) + @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L, RSHIFT_L}, applyIfAnd = {"UseHashcodeFastPath", "true", "DisableIntrinsic", "_identityHashCode"}) + int h_object2(Object a) { + return System.identityHashCode(a); + } + + @Run(test = { + "h_predictable", + "h_object2", + }) + public void run_non_zero_hashcode() { + { + // Unmasked hash == 0x80_00_00_00 => real (masked) hash = 0 + Predictable would_have_zero_hashcode1 = Predictable.makeWouldHaveZeroHash(); + Predictable would_have_zero_hashcode2 = Predictable.makeWouldHaveZeroHash(); + Predictable would_have_zero_hashcode3 = Predictable.makeWouldHaveZeroHash(); + // The hash should not be 0 + Asserts.assertNE(h(would_have_zero_hashcode1), 0); + // The hash should be the one of the class + Asserts.assertEQ(h(would_have_zero_hashcode1), h(Predictable.class)); + // Checks that fast path handles zero hash correctly + Asserts.assertEQ(h(would_have_zero_hashcode1), h_object2(would_have_zero_hashcode2)); + // Checks that static expansion handles zero hash correctly + Asserts.assertEQ(h(would_have_zero_hashcode1), h_predictable(would_have_zero_hashcode3)); + } + { + // Unmasked hash == 0x00_00_00_00 => real (masked) hash = 0 + Predictable would_have_zero_hashcode1 = Predictable.makeWouldHaveZeroHash2(); + Predictable would_have_zero_hashcode2 = Predictable.makeWouldHaveZeroHash2(); + Predictable would_have_zero_hashcode3 = Predictable.makeWouldHaveZeroHash2(); + // The hash should not be 0 + Asserts.assertNE(h(would_have_zero_hashcode1), 0); + // The hash should be the one of the class + Asserts.assertEQ(h(would_have_zero_hashcode1), h(Predictable.class)); + // Checks that fast path handles zero hash correctly + Asserts.assertEQ(h(would_have_zero_hashcode1), h_object2(would_have_zero_hashcode2)); + // Checks that static expansion handles zero hash correctly + Asserts.assertEQ(h(would_have_zero_hashcode1), h_predictable(would_have_zero_hashcode3)); + } + } } diff --git a/test/hotspot/jtreg/runtime/Unsafe/HashCodeMask.java b/test/hotspot/jtreg/runtime/Unsafe/HashCodeMask.java new file mode 100644 index 000000000000..e337a70f4fec --- /dev/null +++ b/test/hotspot/jtreg/runtime/Unsafe/HashCodeMask.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2026, 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 + * @summary Make sure hashCodeMask() returns a value that is a power of two minus 1 + * @library /test/lib + * @modules java.base/jdk.internal.misc + * @run main ${test.main.class} + */ + +import jdk.internal.misc.Unsafe; + +public class HashCodeMask { + public static void main(String args[]) throws Exception { + Unsafe unsafe = Unsafe.getUnsafe(); + int hashCodeMask = unsafe.hashCodeMask(); + int n = hashCodeMask; + + while ((n & 1) == 1) { + n >>>= 1; + } + if (n == 0 && hashCodeMask > 0) { + return; + } + throw new RuntimeException("Expected hashCodeMask to be a power of two minus 1, and non-zero, actual hashCodeMask:" + hashCodeMask); + } +} diff --git a/test/hotspot/jtreg/runtime/valhalla/valuetypes/NonZeroIdentityHashCodeTest.java b/test/hotspot/jtreg/runtime/valhalla/valuetypes/NonZeroIdentityHashCodeTest.java new file mode 100644 index 000000000000..9b600878e27c --- /dev/null +++ b/test/hotspot/jtreg/runtime/valhalla/valuetypes/NonZeroIdentityHashCodeTest.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2026, 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 8391990 + * @summary Tests that identityHashCode does not return zero for value classes, + * this ensures the value is cached and then retrieved from the cache. + * @enablePreview + * @run main/othervm -Xint + * ${test.main.class} + */ + +package runtime.valhalla.valuetypes; + +public class NonZeroIdentityHashCodeTest { + static value class Node { + final Node left; + final Node right; + final int payload; + + public Node(boolean alternative) { + this.left = null; + this.right = null; + this.payload = (alternative ? 0 : Integer.MIN_VALUE) -31 * System.identityHashCode(Node.class); + } + + public Node(Node child) { + this.left = child; + this.right = child; + this.payload = -31 * System.identityHashCode(Node.class); + } + } + + static Node makeDeepFakeTree(int depth) { + Node node = new Node(false); + for (int i = 1; i < depth; ++i) { + node = new Node(node); + } + return node; + } + + static void test_deep() { + Node n = makeDeepFakeTree(50); + + // That performs one computation by object (50), but an approach without cache + // would take 2^50 computations (and would time out). + int actual = System.identityHashCode(n); + if (actual == 0) { + throw new RuntimeException("identityHashCode of a value object should never be zero"); + } + } + + static void test_value(boolean alternative) { + Node n = new Node(alternative); + int expected = System.identityHashCode(Node.class); + if (expected == 0) { + // That would be really broken + throw new RuntimeException("identityHashCode of an identity object should never be zero"); + } + + int actual = System.identityHashCode(n); + if (actual == 0) { + throw new RuntimeException("identityHashCode of a value object should never be zero"); + } + + if (actual != expected) { + throw new RuntimeException("identityHashCode should be the hash of the class"); + } + } + + public static void main(String[] args) { + test_value(false); + test_value(true); + test_deep(); + } +} From 4ecd5b98a1bd8d1412bcdfa6e599d4b7b411fe79 Mon Sep 17 00:00:00 2001 From: Justin Lu Date: Tue, 29 Sep 2026 16:27:25 +0000 Subject: [PATCH 078/103] 8392995: CLDR metazone offset overrides custom DateFormatSymbols and TimeZone behavior Reviewed-by: naoto --- .../classes/java/text/SimpleDateFormat.java | 18 ++- .../locale/provider/TimeZoneNameUtility.java | 4 +- .../Format/DateFormat/CustomZoneTest.java | 140 ++++++++++++++++++ 3 files changed, 158 insertions(+), 4 deletions(-) create mode 100644 test/jdk/java/text/Format/DateFormat/CustomZoneTest.java diff --git a/src/java.base/share/classes/java/text/SimpleDateFormat.java b/src/java.base/share/classes/java/text/SimpleDateFormat.java index ba1ae827776d..6957ee60bd4f 100644 --- a/src/java.base/share/classes/java/text/SimpleDateFormat.java +++ b/src/java.base/share/classes/java/text/SimpleDateFormat.java @@ -53,6 +53,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import sun.util.calendar.CalendarUtils; +import sun.util.calendar.ZoneInfo; import sun.util.calendar.ZoneInfoFile; import sun.util.locale.provider.LocaleProviderAdapter; import sun.util.locale.provider.TimeZoneNameUtility; @@ -1302,8 +1303,21 @@ private void subFormat(int patternCharIndex, int count, int zoneOffset = calendar.get(Calendar.ZONE_OFFSET); int dstOffset = calendar.get(Calendar.DST_OFFSET) + zoneOffset; - // Check if an explicit metazone DST offset exists - String explicitDstOffset = TimeZoneNameUtility.explicitDstOffset(tzid); + String explicitDstOffset = null; + // Check if an explicit metazone DST offset exists. + // Only check against instances of ZoneInfo, since the standard JDK timezones + // are guaranteed to extend this internal type. + if (tz instanceof ZoneInfo zi) { + explicitDstOffset = TimeZoneNameUtility.explicitDstOffset(tzid); + if (explicitDstOffset != null) { + // The time zone ID has an explicit dst offset. Ensure that + // our current TimeZone is canonical. + var canonicalZone = ZoneInfo.getTimeZone(tzid); + if (canonicalZone == null || !canonicalZone.equals(zi)) { + explicitDstOffset = null; + } + } + } boolean daylight = explicitDstOffset != null ? dstOffset == ZoneOffset.of(explicitDstOffset).getTotalSeconds() * 1_000 : dstOffset != zoneOffset; diff --git a/src/java.base/share/classes/sun/util/locale/provider/TimeZoneNameUtility.java b/src/java.base/share/classes/sun/util/locale/provider/TimeZoneNameUtility.java index 6c684e176c8e..014b3af680f2 100644 --- a/src/java.base/share/classes/sun/util/locale/provider/TimeZoneNameUtility.java +++ b/src/java.base/share/classes/sun/util/locale/provider/TimeZoneNameUtility.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, 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 @@ -174,7 +174,7 @@ public static Optional canonicalTZID(String id) { } /** - * {@return the explicit metazone DST offset for the specified time zone ID, if exists} + * {@return the explicit metazone DST offset for the specified time zone ID if it exists, or else null} * @param tzid the time zone ID */ public static String explicitDstOffset(String tzid) { diff --git a/test/jdk/java/text/Format/DateFormat/CustomZoneTest.java b/test/jdk/java/text/Format/DateFormat/CustomZoneTest.java new file mode 100644 index 000000000000..87ec0577abb3 --- /dev/null +++ b/test/jdk/java/text/Format/DateFormat/CustomZoneTest.java @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2026, 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 8392995 + * @summary Ensure that a custom time zone does not look up CLDR metazone data via + * an explicit offset for daylight names during SimpleDateFormat formatting. Only + * a canonical JDK TimeZone should defer to the metazone data in such cases. + * @run junit CustomZoneTest + */ + +import org.junit.jupiter.api.Test; + +import java.text.DateFormatSymbols; +import java.text.SimpleDateFormat; +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.Date; +import java.util.Locale; +import java.util.SimpleTimeZone; +import java.util.TimeZone; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +// Any fixed standard strings correspond to CLDR data as of v48.2 +public class CustomZoneTest { + + private static final String AMERICA_VANCOUVER = "America/Vancouver"; + private static final TimeZone JDK_AMERICA_VANCOUVER = + TimeZone.getTimeZone(AMERICA_VANCOUVER); + + // Fixed raw offset (that mimics daylight savings for America/Vancouver) + // but in actuality has no daylight savings rules and is named "America/Vancouver". + private static final TimeZone CUSTOM_AMERICA_VANCOUVER = + new SimpleTimeZone(-7 * 60 * 60 * 1000, AMERICA_VANCOUVER); + + private static final String[][] ZONE_STRINGS = new String[][] {{ + AMERICA_VANCOUVER, + "CUSTOM_STANDARD", + "CS", + "CUSTOM_DAYLIGHT", + "CD" + }}; + + // British Columbia (including Vancouver) permanently switched to dst on March 8, 2026. + // Date in normal standard time range before the permanent transition. + private static final Date STANDARD_VANCOUVER_DATE = Date.from( + ZonedDateTime.of(2025, 12, 1, 0, 0, 0, 0, + ZoneId.of(AMERICA_VANCOUVER)).toInstant()); + // Date after the permanent transition. We expect all zones that take DST into account to format + // this date using a daylight name. + private static final Date DAYLIGHT_VANCOUVER_DATE = Date.from( + ZonedDateTime.of(2026, 12, 1, 0, 0, 0, 0, + ZoneId.of(AMERICA_VANCOUVER)).toInstant()); + + // Custom zone with custom strings. Custom zone has no DST, and all formatted + // names should reflect the custom name -> "CUSTOM_STANDARD". + @Test + void customZoneWithStringsTest() { + DateFormatSymbols symbols = new DateFormatSymbols(Locale.US); + symbols.setZoneStrings(ZONE_STRINGS); + SimpleDateFormat format = new SimpleDateFormat("zzzz", symbols); + format.setTimeZone(CUSTOM_AMERICA_VANCOUVER); + assertEquals("CUSTOM_STANDARD", format.format(STANDARD_VANCOUVER_DATE)); + assertEquals("CUSTOM_STANDARD", format.format(DAYLIGHT_VANCOUVER_DATE)); + } + + // Custom zone without custom strings. Custom zone has no DST, and all formatted + // names should reflect the standard name -> "Pacific Standard Time". + @Test + void customZoneWithoutStringsTest() { + DateFormatSymbols symbols = new DateFormatSymbols(Locale.US); + SimpleDateFormat format = new SimpleDateFormat("zzzz", symbols); + format.setTimeZone(CUSTOM_AMERICA_VANCOUVER); + assertEquals("Pacific Standard Time", format.format(STANDARD_VANCOUVER_DATE)); + assertEquals("Pacific Standard Time", format.format(DAYLIGHT_VANCOUVER_DATE)); + } + + // Standard JDK zone without custom strings. Expect the default naming for pre/post transition. + @Test + void standardZoneWithoutStringsTest() { + DateFormatSymbols symbols = new DateFormatSymbols(Locale.US); + SimpleDateFormat format = new SimpleDateFormat("zzzz", symbols); + format.setTimeZone(JDK_AMERICA_VANCOUVER); + assertEquals("Pacific Standard Time", format.format(STANDARD_VANCOUVER_DATE)); + assertEquals("Pacific Daylight Time", format.format(DAYLIGHT_VANCOUVER_DATE)); + } + + // Standard JDK zone with custom strings. Expect the correct custom naming for pre/post transition. + @Test + void standardZoneWithStringsTest() { + DateFormatSymbols symbols = new DateFormatSymbols(Locale.US); + symbols.setZoneStrings(ZONE_STRINGS); + SimpleDateFormat format = new SimpleDateFormat("zzzz", symbols); + format.setTimeZone(JDK_AMERICA_VANCOUVER); + assertEquals("CUSTOM_STANDARD", format.format(STANDARD_VANCOUVER_DATE)); + assertEquals("CUSTOM_DAYLIGHT", format.format(DAYLIGHT_VANCOUVER_DATE)); + } + + // Standard JDK zone that has been modified. Since it is modified, it should not + // determine daylight time by deferring to the CLDR metazone data. + @Test + void modifiedStandardZoneTest() { + DateFormatSymbols symbols = new DateFormatSymbols(Locale.US); + SimpleDateFormat format = new SimpleDateFormat("zzzz", symbols); + // Grab a standard tz for America/New_York, set the ID to America/Vancouver. + // TimeZone used by sdf has New_York rules with Vancouver ID. + var modifiedJDKZone = TimeZone.getTimeZone("America/New_York"); + modifiedJDKZone.setID(AMERICA_VANCOUVER); + format.setTimeZone(modifiedJDKZone); + // If we did not check that the zone was canonical, SDF will grab + // the explicit dst offset for Vancouver, and see if it equals the + // dst offset for the zone. Since the dst offset for the zone is for New_York, + // the match would fail and daylight would be incorrectly determined as false. + assertEquals("Pacific Daylight Time", format.format( + Date.from(Instant.parse("2020-07-01T12:00:00Z")))); + } +} From 00b94cce20eb37a540a950e3bd175f999d730105 Mon Sep 17 00:00:00 2001 From: Patrick Fontanilla Date: Tue, 29 Sep 2026 19:14:17 +0000 Subject: [PATCH 079/103] 8392096: Shenandoah: Trim back logs ring buffer size Reviewed-by: shade, wkemper, kdnilsen, ruili --- src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp b/src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp index 351f412ce752..f980c1f88c88 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp @@ -152,7 +152,7 @@ void ShenandoahArguments::initialize() { // Record more information about previous cycles for improved debugging pleasure if (FLAG_IS_DEFAULT(LogEventsBufferEntries)) { - FLAG_SET_DEFAULT(LogEventsBufferEntries, 250); + FLAG_SET_DEFAULT(LogEventsBufferEntries, NOT_DEBUG(70) DEBUG_ONLY(250)); } if ((InitialHeapSize == MaxHeapSize) && ShenandoahUncommit) { From f23cb3b2f3a76e76a7084fa02f1acd5251c41d3c Mon Sep 17 00:00:00 2001 From: Shawn Emery Date: Tue, 29 Sep 2026 19:58:13 +0000 Subject: [PATCH 080/103] 8384311: sun/security/krb5/KrbKdcRep.java throws NPE Reviewed-by: jpai, weijun --- .../share/classes/sun/security/krb5/KrbKdcRep.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/java.security.jgss/share/classes/sun/security/krb5/KrbKdcRep.java b/src/java.security.jgss/share/classes/sun/security/krb5/KrbKdcRep.java index d685d5897595..dfe6cd836b52 100644 --- a/src/java.security.jgss/share/classes/sun/security/krb5/KrbKdcRep.java +++ b/src/java.security.jgss/share/classes/sun/security/krb5/KrbKdcRep.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2026, 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 @@ -88,7 +89,7 @@ static void check( for (int i = 2; i < 6; i++) { if (req.reqBody.kdcOptions.get(i) != rep.encKDCRepPart.flags.get(i)) { - if (DEBUG == null) { + if (DEBUG != null) { DEBUG.println("> KrbKdcRep.check: at #" + i + ". request for " + req.reqBody.kdcOptions.get(i) + ", received " + rep.encKDCRepPart.flags.get(i)); From 5fa62821cb89bf13b7101e20e56cd3b386dd8fa6 Mon Sep 17 00:00:00 2001 From: Phil Race Date: Tue, 29 Sep 2026 21:49:21 +0000 Subject: [PATCH 081/103] 8169188: [macos] java/awt/font/TextLayout/TextLayoutBounds.java always fail Reviewed-by: jdv, serb --- test/jdk/ProblemList.txt | 1 - .../awt/font/TextLayout/TextLayoutBounds.java | 78 ++++++++++++++----- 2 files changed, 57 insertions(+), 22 deletions(-) diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt index 460c8c1f8e9c..6d2b9a80d45f 100644 --- a/test/jdk/ProblemList.txt +++ b/test/jdk/ProblemList.txt @@ -201,7 +201,6 @@ java/awt/Window/ShapedAndTranslucentWindows/Translucent.java 8222328 windows-all java/awt/Window/AlwaysOnTop/AutoTestOnTop.java 6847593 linux-all java/awt/Window/GrabSequence/GrabSequence.java 6848409 macosx-all,linux-all java/awt/Window/LocationAtScreenCorner/LocationAtScreenCorner.java 8203371 linux-all -java/awt/font/TextLayout/TextLayoutBounds.java 8169188 generic-all java/awt/image/DrawImage/IncorrectAlphaSurface2SW.java 8056077 linux-all java/awt/print/Headless/HeadlessPrinterJob.java 8196088 windows-all sun/awt/datatransfer/SuplementaryCharactersTransferTest.java 8011371 generic-all diff --git a/test/jdk/java/awt/font/TextLayout/TextLayoutBounds.java b/test/jdk/java/awt/font/TextLayout/TextLayoutBounds.java index c7e196fb9603..a17d83959e85 100644 --- a/test/jdk/java/awt/font/TextLayout/TextLayoutBounds.java +++ b/test/jdk/java/awt/font/TextLayout/TextLayoutBounds.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, 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 @@ -20,34 +20,70 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + /* @test * @summary verify TextLayout.getBounds() return visual bounds * @bug 6323611 6761856 */ -import java.awt.*; -import java.awt.font.*; -import java.awt.geom.*; +import java.awt.Font; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.awt.font.TextLayout; +import java.awt.geom.Rectangle2D; public class TextLayoutBounds { public static void main(String args[]) { - FontRenderContext frc = new FontRenderContext(null, false, false); - Font f = new Font("SansSerif",Font.BOLD,32); - String s = new String("JAVA"); - TextLayout tl = new TextLayout(s, f, frc); - Rectangle2D tlBounds = tl.getBounds(); - GlyphVector gv = f.createGlyphVector(frc, s); - Rectangle2D gvvBounds = gv.getVisualBounds(); - Rectangle2D oBounds = tl.getOutline(null).getBounds2D(); - System.out.println("tlbounds="+tlBounds); - System.out.println("gvbounds="+gvvBounds); - System.out.println("outlineBounds="+oBounds); - if (!gvvBounds.equals(tlBounds)) { - throw new RuntimeException("Bounds differ [gvv != tl]"); - } - if (!tlBounds.equals(oBounds)) { - throw new RuntimeException("Bounds differ [tl != outline]"); - } + FontRenderContext frc = new FontRenderContext(null, false, false); + Font f = new Font("SansSerif", Font.BOLD, 32); + String s = new String("JAVA"); + + GlyphVector gv = f.createGlyphVector(frc, s); + Rectangle2D gvvBounds = gv.getVisualBounds(); + Rectangle2D gvoBounds = gv.getOutline().getBounds2D(); + + TextLayout tl = new TextLayout(s, f, frc); + Rectangle2D tlBounds = tl.getBounds(); + Rectangle2D oBounds = tl.getOutline(null).getBounds2D(); + + if (!gvvBounds.equals(gvoBounds) || + !tlBounds.equals(oBounds) || + !gvvBounds.equals(tlBounds)) + { + // Log any that are not exact + System.out.println(); + System.out.println("Font = " + f); + System.out.println("GV Visual Bounds="+gvvBounds); + System.out.println("GV Outline Bounds="+gvoBounds); + System.out.println("TL Bounds="+tlBounds); + System.out.println("TL Outline bounds="+oBounds); + + // Fail the test only if the difference is significant. + + if (!compare(gvvBounds, gvoBounds)) { + throw new RuntimeException("Bounds differ [gv visual != gv outline]"); + } + if (!compare(tlBounds, oBounds)) { + throw new RuntimeException("Bounds differ [tl visual != tl outline]"); + } + if (!compare(gvvBounds, tlBounds)) { + throw new RuntimeException("Bounds differ [tl visual != gv visual]"); + } + } } + + private static final double MAX_DIFF = 1/32.0; + private static boolean compare(double d1, double d2) { + return Math.abs(d1 - d2) < MAX_DIFF; + } + + private static boolean compare(Rectangle2D r1, Rectangle2D r2) { + return + compare(r1.getMinX(), r2. getMinX()) && + compare(r1.getMinY(), r2. getMinY()) && + compare(r1.getMaxX(), r2. getMaxX()) && + compare(r1.getMaxY(), r2. getMaxY()); + } + } From 82afce8b35b4609b377c50984b78a93af8198a58 Mon Sep 17 00:00:00 2001 From: Prasanta Sadhukhan Date: Wed, 30 Sep 2026 02:55:21 +0000 Subject: [PATCH 082/103] 8388824: JOptionPane.showInternalMessageDialog does not close after clicking on its close button Reviewed-by: prr, kizune --- .../motif/MotifInternalFrameTitlePane.java | 10 +- .../classes/javax/swing/JOptionPane.java | 7 +- .../basic/BasicInternalFrameTitlePane.java | 17 ++- .../synth/SynthInternalFrameTitlePane.java | 12 ++ .../WindowsInternalFrameTitlePane.java | 8 ++ ...ptionPaneInternalFrameNotClosableTest.java | 112 ++++++++++++++++++ 6 files changed, 158 insertions(+), 8 deletions(-) create mode 100644 test/jdk/javax/swing/JOptionPane/JOptionPaneInternalFrameNotClosableTest.java diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifInternalFrameTitlePane.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifInternalFrameTitlePane.java index 474bd707ce45..89971885f94e 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifInternalFrameTitlePane.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifInternalFrameTitlePane.java @@ -113,7 +113,7 @@ protected void assembleSystemMenu() { systemButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - systemMenu.show(systemButton, 0, BUTTON_SIZE); + showSystemMenu(); } }); @@ -144,6 +144,10 @@ private static int getButtonMnemonic(String button) { } } + private boolean isOptionDialog() { + return "optionDialog".equals(frame.getClientProperty("JInternalFrame.frameType")); + } + @Override protected void createButtons() { minimizeButton = new MinimizeButton(); @@ -245,7 +249,9 @@ public void layoutContainer(Container c) { @Override protected void showSystemMenu(){ - systemMenu.show(systemButton, 0, BUTTON_SIZE); + if (!isOptionDialog()) { + systemMenu.show(systemButton, 0, BUTTON_SIZE); + } } protected void hideSystemMenu(){ diff --git a/src/java.desktop/share/classes/javax/swing/JOptionPane.java b/src/java.desktop/share/classes/javax/swing/JOptionPane.java index c41400b9b375..c02255f5428e 100644 --- a/src/java.desktop/share/classes/javax/swing/JOptionPane.java +++ b/src/java.desktop/share/classes/javax/swing/JOptionPane.java @@ -1519,8 +1519,11 @@ public JInternalFrame createInternalFrame(Component parentComponent, "not have a valid parent"); } - // Option dialogs should be closable only - final JInternalFrame iFrame = new JInternalFrame(title, false, true, + // Option dialogs should not be resizable, closable, + // maximizable and iconifiable + // and internal option dialogs are closed via a UI action, + // not by a close button on the dialog title bar. + final JInternalFrame iFrame = new JInternalFrame(title, false, false, false, false); iFrame.putClientProperty("JInternalFrame.frameType", "optionDialog"); diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicInternalFrameTitlePane.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicInternalFrameTitlePane.java index 1cd4f7e81a2a..1642c669fc93 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicInternalFrameTitlePane.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicInternalFrameTitlePane.java @@ -186,6 +186,9 @@ public class BasicInternalFrameTitlePane extends JComponent private String maxButtonToolTip; private Handler handler; + private static final String FRAME_TYPE = "JInternalFrame.frameType"; + private static final String OPTION_DIALOG = "optionDialog"; + /** * Constructs a new instance of {@code BasicInternalFrameTitlePane}. * @@ -440,13 +443,17 @@ protected JMenuBar createSystemMenuBar() { return menuBar; } + private boolean isOptionDialog() { + return OPTION_DIALOG.equals(frame.getClientProperty(FRAME_TYPE)); + } + /** * Shows system menu. */ protected void showSystemMenu(){ - // windowMenu.setPopupMenuVisible(true); - // windowMenu.setVisible(true); - windowMenu.doClick(); + if (!isOptionDialog()) { + windowMenu.doClick(); + } } public void paintComponent(Graphics g) { @@ -903,7 +910,9 @@ public ShowSystemMenuAction(boolean show) { public void actionPerformed(ActionEvent e) { if (show) { - windowMenu.doClick(); + if (!isOptionDialog()) { + windowMenu.doClick(); + } } else { windowMenu.setVisible(false); } diff --git a/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthInternalFrameTitlePane.java b/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthInternalFrameTitlePane.java index f6eab4a5a5b8..f21befd397cc 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthInternalFrameTitlePane.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthInternalFrameTitlePane.java @@ -188,6 +188,7 @@ protected void assembleSystemMenu() { addSystemMenuItems(systemPopupMenu); enableActions(); menuButton = createNoFocusButton(); + menuButton.setVisible(!isOptionDialog()); updateMenuIcon(); menuButton.addMouseListener(new MouseAdapter() { public void mousePressed(MouseEvent e) { @@ -211,6 +212,10 @@ public void mousePressed(MouseEvent e) { setInheritsPopupMenu(true); } + private boolean isOptionDialog() { + return "optionDialog".equals(frame.getClientProperty("JInternalFrame.frameType")); + } + protected void addSystemMenuItems(JPopupMenu menu) { JMenuItem mi = menu.add(restoreAction); mi.setMnemonic(getButtonMnemonic("restore")); @@ -237,6 +242,9 @@ private static int getButtonMnemonic(String button) { } protected void showSystemMenu() { + if (isOptionDialog()) { + return; + } Insets insets = frame.getInsets(); if (!frame.isIcon()) { systemPopupMenu.show(frame, menuButton.getX(), getY() + getHeight()); @@ -354,6 +362,10 @@ public void propertyChange(PropertyChangeEvent evt) { if (evt.getPropertyName() == JInternalFrame.FRAME_ICON_PROPERTY) { updateMenuIcon(); } + if ("JInternalFrame.frameType".equals(evt.getPropertyName()) + && isOptionDialog()) { + menuButton.setVisible(false); + } } } diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsInternalFrameTitlePane.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsInternalFrameTitlePane.java index 029e139fe8f3..95d527a3cc1d 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsInternalFrameTitlePane.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsInternalFrameTitlePane.java @@ -387,7 +387,15 @@ protected void showSystemMenu(){ showSystemPopupMenu(systemLabel); } + private boolean isOptionDialog() { + return "optionDialog".equals(frame.getClientProperty("JInternalFrame.frameType")); + } + private void showSystemPopupMenu(Component invoker){ + if (isOptionDialog()) { + return; + } + Dimension dim = new Dimension(); Border border = frame.getBorder(); if (border != null) { diff --git a/test/jdk/javax/swing/JOptionPane/JOptionPaneInternalFrameNotClosableTest.java b/test/jdk/javax/swing/JOptionPane/JOptionPaneInternalFrameNotClosableTest.java new file mode 100644 index 000000000000..3e6a20c040bb --- /dev/null +++ b/test/jdk/javax/swing/JOptionPane/JOptionPaneInternalFrameNotClosableTest.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2026, 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 8388824 + * @summary Verifies that JOptionPane internal frames are not closable + * for all installed look and feels + * @run main JOptionPaneInternalFrameNotClosableTest + */ + +import java.util.ArrayList; +import java.util.List; + +import javax.swing.JDesktopPane; +import javax.swing.JInternalFrame; +import javax.swing.JOptionPane; +import javax.swing.LookAndFeel; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; + +public class JOptionPaneInternalFrameNotClosableTest { + + private static final int[] MESSAGE_TYPES = { + JOptionPane.ERROR_MESSAGE, + JOptionPane.INFORMATION_MESSAGE, + JOptionPane.WARNING_MESSAGE, + JOptionPane.QUESTION_MESSAGE, + JOptionPane.PLAIN_MESSAGE + }; + + public static void main(String[] args) throws Exception { + List closableLafs = new ArrayList<>(); + + for (UIManager.LookAndFeelInfo laf + : UIManager.getInstalledLookAndFeels()) { + System.out.println("Testing L&F: " + laf.getClassName()); + SwingUtilities.invokeAndWait( + () -> testLookAndFeel(laf, closableLafs)); + } + + if (!closableLafs.isEmpty()) { + throw new RuntimeException( + "Option-pane internal frames are closable for:\n " + + String.join(" ", closableLafs)); + } + } + + private static void testLookAndFeel(UIManager.LookAndFeelInfo laf, + List closableLafs) { + try { + UIManager.setLookAndFeel(laf.getClassName()); + } catch (Exception e) { + throw new RuntimeException( + "Cannot set look and feel: " + laf.getName(), e); + } + + JDesktopPane desktop = new JDesktopPane(); + + for (int messageType : MESSAGE_TYPES) { + JOptionPane optionPane = + new JOptionPane("Message", messageType); + JInternalFrame internalFrame = + optionPane.createInternalFrame(desktop, "Title"); + + try { + if (internalFrame.isClosable()) { + closableLafs.add(laf.getName() + " (" + + messageTypeName(messageType) + ")\n"); + } + } finally { + internalFrame.dispose(); + } + } + } + + private static String messageTypeName(int messageType) { + return switch (messageType) { + case JOptionPane.ERROR_MESSAGE -> + "JOptionPane.ERROR_MESSAGE"; + case JOptionPane.INFORMATION_MESSAGE -> + "JOptionPane.INFORMATION_MESSAGE"; + case JOptionPane.WARNING_MESSAGE -> + "JOptionPane.WARNING_MESSAGE"; + case JOptionPane.QUESTION_MESSAGE -> + "JOptionPane.QUESTION_MESSAGE"; + case JOptionPane.PLAIN_MESSAGE -> + "JOptionPane.PLAIN_MESSAGE"; + default -> "message type " + messageType; + }; + } +} From 4da0e27daa980a74c5843b0de46af9884c3fc07d Mon Sep 17 00:00:00 2001 From: Kuai Wei Date: Wed, 30 Sep 2026 02:59:51 +0000 Subject: [PATCH 083/103] 8393079: AArch64: rthread is used as a scratch register in generate_buffered_value_type_adapter() Reviewed-by: adinn, thartmann --- src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp b/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp index fff80f01bb7d..a43210c69447 100644 --- a/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp @@ -2927,7 +2927,7 @@ BufferedValueTypeBlob* SharedRuntime::generate_buffered_value_type_adapter(const Register Rresult = r14; // See StubGenerator::generate_call_stub(). __ ldr(r0, Address(Rresult)); __ resolve_jobject(r0 /* value */, - rthread /* thread */, + r11 /* tmp */, r12 /* tmp */); __ str(r0, Address(Rresult)); From 3f150a4ae4253d52fb2503e1760d86a5718169b7 Mon Sep 17 00:00:00 2001 From: Mohamed Issa Date: Wed, 30 Sep 2026 04:39:54 +0000 Subject: [PATCH 084/103] 8392723: Restore Float16 to integral conversion behavior on AVX10.2 targets Reviewed-by: sviswanathan, mhaessig, sparasa, jbhateja --- src/hotspot/cpu/x86/x86.ad | 9 +++---- .../compiler/lib/ir_framework/IRNode.java | 5 ++++ .../vectorapi/TestFloat16ToIntegralConv.java | 24 +++++++++++++++---- .../Float16ToIntegralConvBenchmark.java | 20 +++++++++++++--- 4 files changed, 47 insertions(+), 11 deletions(-) diff --git a/src/hotspot/cpu/x86/x86.ad b/src/hotspot/cpu/x86/x86.ad index 05a368dced00..45d91a9949c9 100644 --- a/src/hotspot/cpu/x86/x86.ad +++ b/src/hotspot/cpu/x86/x86.ad @@ -17979,9 +17979,9 @@ instruct convF2HF_mem_reg(memory mem, regF src, kReg ktmp, rRegI rtmp) %{ ins_pipe( pipe_slow ); %} -instruct vconvF2HF(vec dst, vec src) %{ +instruct vconvF2HF_reg_reg(vec dst, vec src) %{ match(Set dst (VectorCastF2HF src)); - format %{ "vector_conv_F2HF $dst $src" %} + format %{ "vcvtps2ph $dst $src" %} ins_encode %{ int vlen_enc = vector_length_encoding(this, $src); __ vcvtps2ph($dst$$XMMRegister, $src$$XMMRegister, 0x04, vlen_enc); @@ -18019,10 +18019,10 @@ instruct vconvHF2F_reg_mem(vec dst, memory mem) %{ ins_pipe( pipe_slow ); %} -instruct vconvHF2F(vec dst, vec src) %{ +instruct vconvHF2F_reg_reg(vec dst, vec src) %{ match(Set dst (VectorCastHF2F src)); ins_cost(125); - format %{ "vector_conv_HF2F $dst,$src" %} + format %{ "vcvtph2ps $dst,$src" %} ins_encode %{ int vlen_enc = vector_length_encoding(this); __ vcvtph2ps($dst$$XMMRegister, $src$$XMMRegister, vlen_enc); @@ -22054,6 +22054,7 @@ instruct vcastLtoX_evex(vec dst, vec src) %{ instruct castHFtoX_reg_evex(vec dst, vec src, vec xtmp1, vec xtmp2, kReg ktmp1, kReg ktmp2, rFlagsReg cr) %{ predicate(VM_Version::supports_avx512_fp16() && + !VM_Version::supports_avx10_2() && is_integral_type(Matcher::vector_element_basic_type(n))); match(Set dst (VectorCastF2X (VectorCastHF2F src))); effect(TEMP dst, TEMP xtmp1, TEMP xtmp2, TEMP ktmp1, TEMP ktmp2, KILL cr); diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java index c0bb586f16ef..9c11f56003b7 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java @@ -3123,6 +3123,11 @@ public static void anyStoreOfNodes(String irNodePlaceholder, String fieldHolder) machOnlyNameRegex(X86_VCAST_HF2X, "castHFtoX_reg_evex"); } + public static final String X86_VCONV_HF2F = PREFIX + "X86_VCONV_HF2F" + POSTFIX; + static { + machOnlyNameRegex(X86_VCONV_HF2F, "vconvHF2F_reg_(reg|mem)"); + } + public static final String XOR = PREFIX + "XOR" + POSTFIX; static { beforeMatchingNameRegex(XOR, "Xor(I|L)"); diff --git a/test/hotspot/jtreg/compiler/vectorapi/TestFloat16ToIntegralConv.java b/test/hotspot/jtreg/compiler/vectorapi/TestFloat16ToIntegralConv.java index 2ff72137e245..902b5d695477 100644 --- a/test/hotspot/jtreg/compiler/vectorapi/TestFloat16ToIntegralConv.java +++ b/test/hotspot/jtreg/compiler/vectorapi/TestFloat16ToIntegralConv.java @@ -202,7 +202,11 @@ public void runConvHF2B_VAPI() { @Test @IR(counts = {IRNode.X86_VCAST_HF2X, "> 0"}, phase = CompilePhase.FINAL_CODE, - applyIfCPUFeature = {"avx512_fp16", "true"}) + applyIfCPUFeatureAnd = {"avx512_fp16", "true", "avx10_2", "false"}) + @IR(counts = {IRNode.X86_VCONV_HF2F, "> 0"}, phase = CompilePhase.FINAL_CODE, + applyIfCPUFeature = {"avx10_2", "true"}) + @IR(counts = {IRNode.X86_VCAST_F2X_AVX10_2, "> 0"}, phase = CompilePhase.FINAL_CODE, + applyIfCPUFeature = {"avx10_2", "true"}) public void testVecConvHF2I() { for (int i = 0; i < SIZE; i++) { vecIout[i] = (int) Float.float16ToFloat(fp16inp[i]); @@ -217,7 +221,11 @@ public void runVecConvHF2I() { @Test @IR(counts = {IRNode.X86_VCAST_HF2X, "> 0"}, phase = CompilePhase.FINAL_CODE, - applyIfCPUFeature = {"avx512_fp16", "true"}) + applyIfCPUFeatureAnd = {"avx512_fp16", "true", "avx10_2", "false"}) + @IR(counts = {IRNode.X86_VCONV_HF2F, "> 0"}, phase = CompilePhase.FINAL_CODE, + applyIfCPUFeature = {"avx10_2", "true"}) + @IR(counts = {IRNode.X86_VCAST_F2X_AVX10_2, "> 0"}, phase = CompilePhase.FINAL_CODE, + applyIfCPUFeature = {"avx10_2", "true"}) public void testVecConvHF2L() { for (int i = 0; i < SIZE; i++) { vecLout[i] = (long) Float.float16ToFloat(fp16inp[i]); @@ -232,7 +240,11 @@ public void runVecConvHF2L() { @Test @IR(counts = {IRNode.X86_VCAST_HF2X, "> 0"}, phase = CompilePhase.FINAL_CODE, - applyIfCPUFeature = {"avx512_fp16", "true"}) + applyIfCPUFeatureAnd = {"avx512_fp16", "true", "avx10_2", "false"}) + @IR(counts = {IRNode.X86_VCONV_HF2F, "> 0"}, phase = CompilePhase.FINAL_CODE, + applyIfCPUFeature = {"avx10_2", "true"}) + @IR(counts = {IRNode.X86_VCAST_F2X_AVX10_2, "> 0"}, phase = CompilePhase.FINAL_CODE, + applyIfCPUFeature = {"avx10_2", "true"}) public void testVecConvHF2S() { for (int i = 0; i < SIZE; i++) { vecSout[i] = (short) Float.float16ToFloat(fp16inp[i]); @@ -247,7 +259,11 @@ public void runVecConvHF2S() { @Test @IR(counts = {IRNode.X86_VCAST_HF2X, "> 0"}, phase = CompilePhase.FINAL_CODE, - applyIfCPUFeature = {"avx512_fp16", "true"}) + applyIfCPUFeatureAnd = {"avx512_fp16", "true", "avx10_2", "false"}) + @IR(counts = {IRNode.X86_VCONV_HF2F, "> 0"}, phase = CompilePhase.FINAL_CODE, + applyIfCPUFeature = {"avx10_2", "true"}) + @IR(counts = {IRNode.X86_VCAST_F2X_AVX10_2, "> 0"}, phase = CompilePhase.FINAL_CODE, + applyIfCPUFeature = {"avx10_2", "true"}) public void testVecConvHF2B() { for (int i = 0; i < SIZE; i++) { vecBout[i] = (byte) Float.float16ToFloat(fp16inp[i]); diff --git a/test/micro/org/openjdk/bench/jdk/incubator/vector/Float16ToIntegralConvBenchmark.java b/test/micro/org/openjdk/bench/jdk/incubator/vector/Float16ToIntegralConvBenchmark.java index 694663e04992..f914d0904d4a 100644 --- a/test/micro/org/openjdk/bench/jdk/incubator/vector/Float16ToIntegralConvBenchmark.java +++ b/test/micro/org/openjdk/bench/jdk/incubator/vector/Float16ToIntegralConvBenchmark.java @@ -31,8 +31,10 @@ @OutputTimeUnit(TimeUnit.MILLISECONDS) @State(Scope.Thread) -@Fork(jvmArgs = {"--add-modules=jdk.incubator.vector"}) -public class Float16ToIntegralConvBenchmark { +@Fork(value = 3, jvmArgs = {"--add-modules=jdk.incubator.vector"}) +@Warmup(iterations = 3, time = 3) +@Measurement(iterations = 5, time = 5) +public abstract class Float16ToIntegralConvBenchmark { @Param({"1024", "2048"}) int size; @@ -57,7 +59,7 @@ public void BmSetup() { IntStream.range(0, size).forEach( i -> { if ((i % 100) == 0) { - fp16inp[i] = float16ToRawShortBits(specialValues[i % specialValues.length]); + fp16inp[i] = float16ToRawShortBits(specialValues[(i / 100) % specialValues.length]); } } ); @@ -118,4 +120,16 @@ public void convHF2B_VAPI() { bout[i] = shortBitsToFloat16(fp16inp[i]).byteValue(); } } + + @Fork(value = 2, jvmArgs = { + "-XX:+UseSuperWord", "--add-modules=jdk.incubator.vector" + }) + public static class Float16ToIntegralConvBenchmarkSuperWord extends Float16ToIntegralConvBenchmark { + } + + @Fork(value = 2, jvmArgs = { + "-XX:-UseSuperWord", "--add-modules=jdk.incubator.vector" + }) + public static class Float16ToIntegralConvBenchmarkNonSuperWord extends Float16ToIntegralConvBenchmark { + } } From 4edf7c0ea909dde1da2edc706d8bbd5c0674b7af Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Wed, 30 Sep 2026 05:07:10 +0000 Subject: [PATCH 085/103] 8393082: Shenandoah: SATB flush on thread detach should be the last action Reviewed-by: xpeng, wkemper, kdnilsen --- src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp index 7811940cc6f6..a383aae9aefd 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp @@ -159,8 +159,6 @@ void ShenandoahBarrierSet::on_thread_attach(Thread *thread) { } void ShenandoahBarrierSet::on_thread_detach(Thread *thread) { - SATBMarkQueue& queue = ShenandoahThreadLocalData::satb_mark_queue(thread); - _satb_mark_queue_set.flush_queue(queue); if (thread->is_Java_thread()) { PLAB* gclab = ShenandoahThreadLocalData::gclab(thread); if (gclab != nullptr) { @@ -186,6 +184,10 @@ void ShenandoahBarrierSet::on_thread_detach(Thread *thread) { _heap->flush_region_pin_cache(JavaThread::cast(thread)); } + + // Flush after processing everything, to catch stray SATB additions. + SATBMarkQueue& queue = ShenandoahThreadLocalData::satb_mark_queue(thread); + _satb_mark_queue_set.flush_queue(queue); } void ShenandoahBarrierSet::keepalive_barrier_slow(oop obj, Filter filter) { From bb10529f7558de770d06c326adc98bc5a761751b Mon Sep 17 00:00:00 2001 From: Marc Chevalier Date: Wed, 30 Sep 2026 07:18:28 +0000 Subject: [PATCH 086/103] 8392742: Test compiler/exceptions/TestStackWalkPerf.java timed out Reviewed-by: qamai, thartmann --- test/hotspot/jtreg/compiler/exceptions/TestStackWalkPerf.java | 1 + 1 file changed, 1 insertion(+) diff --git a/test/hotspot/jtreg/compiler/exceptions/TestStackWalkPerf.java b/test/hotspot/jtreg/compiler/exceptions/TestStackWalkPerf.java index 24332faf0b9c..fd565c94d19e 100644 --- a/test/hotspot/jtreg/compiler/exceptions/TestStackWalkPerf.java +++ b/test/hotspot/jtreg/compiler/exceptions/TestStackWalkPerf.java @@ -26,6 +26,7 @@ * @bug 8389130 * @summary With Valhalla, frame::sender became a bit too big and is not as spontaneously inlined as before. * This causes some measurable performance regressions in cases where walking the stack is frequent. + * @requires vm.debug == false * @run main/othervm -Xbatch * -XX:-TieredCompilation * -XX:CompileCommand=dontinline,${test.main.class}::fillInStackTrace From aab0e5942c27dbdeb923b2659fd1c111e208ac1c Mon Sep 17 00:00:00 2001 From: Yunbo Zhang Date: Wed, 30 Sep 2026 07:23:49 +0000 Subject: [PATCH 087/103] 8392140: Async UL: Add an explicit "off" mode to async UL Reviewed-by: dholmes, jsjolen --- .../share/logging/logConfiguration.cpp | 21 +++-- src/java.base/share/man/java.md | 3 +- .../runtime/logging/AsyncLogModeTest.java | 87 +++++++++++++++++++ 3 files changed, 102 insertions(+), 9 deletions(-) create mode 100644 test/hotspot/jtreg/runtime/logging/AsyncLogModeTest.java diff --git a/src/hotspot/share/logging/logConfiguration.cpp b/src/hotspot/share/logging/logConfiguration.cpp index 1ba45cc050fa..cfd9c7f834b9 100644 --- a/src/hotspot/share/logging/logConfiguration.cpp +++ b/src/hotspot/share/logging/logConfiguration.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 @@ -654,14 +654,17 @@ void LogConfiguration::print_command_line_help(outputStream* out) { out->cr(); out->print_cr("Asynchronous logging (off by default):"); - out->print_cr(" -Xlog:async[:[mode]]"); + out->print_cr(" -Xlog:async[:[off|stall|drop]]"); out->print_cr(" All log messages are written to an intermediate buffer first and will then be flushed" - " to the corresponding log outputs by a standalone thread. Write operations at logsites are" - " guaranteed non-blocking."); - out->print_cr(" A mode, either 'drop' or 'stall', may be provided. If 'drop' is provided then" - " messages will be dropped if there is no room in the intermediate buffer." - " If 'stall' is provided then the log operation will wait for room to be made by the output thread, without dropping any messages." - " The default mode is 'drop'."); + " to the corresponding log outputs by a standalone thread."); + out->print_cr(" A mode, either 'off', 'stall' or 'drop', may be provided."); + out->print_cr(" If 'off' is provided then asynchronous logging is disabled."); + out->print_cr(" If 'stall' is provided then the log operation will wait for room to be made" + " by the output thread, without dropping any messages."); + out->print_cr(" If 'drop' is provided then messages will be dropped if there is no room" + " in the intermediate buffer."); + out->print_cr(" Log entry write operations are guaranteed to be non-blocking in 'drop' mode."); + out->print_cr(" The default mode is 'drop'."); out->cr(); @@ -744,6 +747,8 @@ bool LogConfiguration::parse_async_argument(const char* async_tail) { if (*async_tail == '\0') { // Default is to drop. LogConfiguration::set_async_mode(LogConfiguration::AsyncMode::Drop); + } else if (strcmp(async_tail, ":off") == 0) { + LogConfiguration::set_async_mode(LogConfiguration::AsyncMode::Off); } else if (strcmp(async_tail, ":stall") == 0) { LogConfiguration::set_async_mode(LogConfiguration::AsyncMode::Stall); } else if (strcmp(async_tail, ":drop") == 0) { diff --git a/src/java.base/share/man/java.md b/src/java.base/share/man/java.md index a9b219362172..dbb8566b373d 100644 --- a/src/java.base/share/man/java.md +++ b/src/java.base/share/man/java.md @@ -3476,7 +3476,7 @@ By default logging messages are output synchronously - each log message is writt the designated output when the logging call is made. You can instead use asynchronous logging mode by specifying: -`-Xlog:async[:[stall|drop]]` +`-Xlog:async[:[off|stall|drop]]` : Write all logging asynchronously. In asynchronous logging mode, log sites enqueue all logging messages to an intermediate buffer @@ -3485,6 +3485,7 @@ intermediate buffer is bounded. On buffer exhaustion the enqueuing message is ei or logging threads are stalled until the flushing thread catches up (`async:stall`). If no specific mode is chosen, then `async:drop` is chosen by default. Log entry write operations are guaranteed to be non-blocking in the `async:drop` case. +Use `async:off` to explicitly disable asynchronous logging. The option `-XX:AsyncLogBufferSize=N` specifies the memory budget in bytes for the intermediate buffer. The default value should be big enough to cater for most cases. Users can provide a custom value to diff --git a/test/hotspot/jtreg/runtime/logging/AsyncLogModeTest.java b/test/hotspot/jtreg/runtime/logging/AsyncLogModeTest.java new file mode 100644 index 000000000000..67a1e9e0be9f --- /dev/null +++ b/test/hotspot/jtreg/runtime/logging/AsyncLogModeTest.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2026, 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 8392140 + * @summary Test command-line modes for asynchronous logging + * @requires vm.flagless + * @library /test/lib + * @run driver AsyncLogModeTest + */ + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +public class AsyncLogModeTest { + // Emitted after the asynchronous logging buffers have been allocated. + private static final String BUFFER_INITIALIZED = "AsyncLogBuffer estimates memory use:"; + + private static OutputAnalyzer execute(List arguments) throws Exception { + ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder( + arguments.toArray(new String[0])); + return new OutputAnalyzer(pb.start()); + } + + private static void checkAsyncMode(boolean enabled, String... modes) throws Exception { + List arguments = new ArrayList<>(); + Collections.addAll(arguments, modes); + arguments.add("-Xlog:logging=info"); + arguments.add("-version"); + + OutputAnalyzer output = execute(arguments); + output.shouldHaveExitValue(0); + if (enabled) { + output.shouldContain(BUFFER_INITIALIZED); + } else { + output.shouldNotContain(BUFFER_INITIALIZED); + } + } + + private static void checkInvalidMode() throws Exception { + OutputAnalyzer output = execute(List.of("-Xlog:async:offfoo", "-version")); + output.shouldNotHaveExitValue(0); + output.shouldContain("Invalid -Xlog option '-Xlog:async:offfoo'"); + } + + private static void checkHelp() throws Exception { + OutputAnalyzer output = execute(List.of("-Xlog:help")); + output.shouldHaveExitValue(0); + output.shouldContain("-Xlog:async[:[off|stall|drop]]"); + } + + public static void main(String[] args) throws Exception { + checkAsyncMode(false, "-Xlog:async:off"); + checkAsyncMode(false, "-Xlog:async", "-Xlog:async:off"); + checkAsyncMode(false, "-Xlog:async:stall", "-Xlog:async:off"); + checkAsyncMode(true, "-Xlog:async:off", "-Xlog:async"); + checkAsyncMode(true, "-Xlog:async:stall"); + checkAsyncMode(true, "-Xlog:async:drop"); + checkInvalidMode(); + checkHelp(); + } +} From 43beedb6d73b97d482eaeb4cc3ba18c8ae9c3506 Mon Sep 17 00:00:00 2001 From: Roland Westrelin Date: Wed, 30 Sep 2026 07:25:40 +0000 Subject: [PATCH 088/103] 8391917: C2: dead loop detected crash with long range checks in short running loop Reviewed-by: mchevalier, qamai, chagedorn --- src/hotspot/share/opto/loopnode.cpp | 10 +++- src/hotspot/share/opto/predicates.cpp | 18 ++++--- src/hotspot/share/opto/predicates.hpp | 9 ++-- .../TestShortLoopTransformationRunsTwice.java | 53 +++++++++++++++++++ 4 files changed, 80 insertions(+), 10 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/longcountedloops/TestShortLoopTransformationRunsTwice.java diff --git a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp index 65d47dac0bf6..6fc07d83a923 100644 --- a/src/hotspot/share/opto/loopnode.cpp +++ b/src/hotspot/share/opto/loopnode.cpp @@ -1289,9 +1289,17 @@ bool PhaseIdealLoop::try_make_short_running_loop(IdealLoopTree* loop, jint strid register_new_node(new_limit, predicates.entry()); } else { assert(bt == T_INT && known_short_running_loop, "only CountedLoop statically known to be short running"); + // If an OpaqueLoopInitNode is shared by several template assertion predicates, the code that updates them (below) + // will encounter it several times. Updating it more than once introduces a dead loop: + // 1- current OpaqueLoopInitNode is replaced by (AddI (OpaqueLoopInitNode ..)) with a new OpaqueLoopInitNode + // 2- The new OpaqueLoopInitNode in (AddI (OpaqueLoopInitNode ..)) is encountered again and replaced by the same + // (AddI (OpaqueLoopInitNode ..)) resulting in a dead loop + // new_nodes is set so the new OpaqueLoopInitNode is ignored by UpdateInitForTemplateAssertionPredicates so step 2 + // doesn't happen + uint last_node_index = C->unique(); PredicateIterator predicate_iterator(entry_control); Node* new_init = new_assertion_predicate_opaque_init(entry_control, init, int_zero); - UpdateInitForTemplateAssertionPredicates update_init_for_template_assertion_predicates(new_init, this); + UpdateInitForTemplateAssertionPredicates update_init_for_template_assertion_predicates(new_init, this, last_node_index); predicate_iterator.for_each(update_init_for_template_assertion_predicates); } IfNode* exit_test = head->loopexit(); diff --git a/src/hotspot/share/opto/predicates.cpp b/src/hotspot/share/opto/predicates.cpp index 89bc4374ca62..2e9c19f72ba6 100644 --- a/src/hotspot/share/opto/predicates.cpp +++ b/src/hotspot/share/opto/predicates.cpp @@ -258,11 +258,14 @@ class ReplaceOpaqueStrideInput : public BFSActions { class ReplaceOpaqueInitNode : public BFSActions { Node* _new_opaque_init_node; PhaseIterGVN& _igvn; + const uint _last_node_index; public: - ReplaceOpaqueInitNode(Node* new_opaque_init_node, PhaseIterGVN& igvn) + ReplaceOpaqueInitNode(Node* new_opaque_init_node, PhaseIterGVN& igvn, uint last_node_index) : _new_opaque_init_node(new_opaque_init_node), - _igvn(igvn) {} + _igvn(igvn), + _last_node_index(last_node_index) { + } NONCOPYABLE(ReplaceOpaqueInitNode); void replace_for(OpaqueTemplateAssertionPredicateNode* opaque_node) { @@ -275,7 +278,10 @@ class ReplaceOpaqueInitNode : public BFSActions { } bool is_target_node(Node* node) const override { - return node->is_OpaqueLoopInit(); + // An OpaqueLoopInit node could be shared between several template assertion predicates: in that case, the + // OpaqueLoopInit encountered here could be one that was replaced already. The _last_node_index guarantees it's not + // replaced again. + return node->is_OpaqueLoopInit() && node->_idx < _last_node_index; } void target_node_action(Node* child, uint i) override { @@ -292,9 +298,9 @@ void TemplateAssertionPredicate::replace_opaque_stride_input(Node* new_stride, P } // Replace the OpaqueLoopInitNode with 'new_init' and leave the other nodes unchanged. -void TemplateAssertionPredicate::replace_opaque_init_node(Node* new_init, PhaseIterGVN& igvn) const { +void TemplateAssertionPredicate::replace_opaque_init_node(Node* new_init, PhaseIterGVN& igvn, uint last_node_index) const { DEBUG_ONLY(verify();) - ReplaceOpaqueInitNode replace_opaque_init_node(new_init, igvn); + ReplaceOpaqueInitNode replace_opaque_init_node(new_init, igvn, last_node_index); replace_opaque_init_node.replace_for(opaque_node()); } @@ -1244,7 +1250,7 @@ void UpdateStrideForAssertionPredicates::connect_initialized_assertion_predicate } void UpdateInitForTemplateAssertionPredicates::visit(const TemplateAssertionPredicate& template_assertion_predicate) { - template_assertion_predicate.replace_opaque_init_node(_new_init, _phase->igvn()); + template_assertion_predicate.replace_opaque_init_node(_new_init, _phase->igvn(), _last_node_index); } // Do the following to find and eliminate useless Parse and Template Assertion Predicates: diff --git a/src/hotspot/share/opto/predicates.hpp b/src/hotspot/share/opto/predicates.hpp index 32c59edda315..cf295432104c 100644 --- a/src/hotspot/share/opto/predicates.hpp +++ b/src/hotspot/share/opto/predicates.hpp @@ -441,7 +441,7 @@ class TemplateAssertionPredicate : public Predicate { TemplateAssertionPredicate clone_and_replace_init(Node* new_control, Node* new_input, CountedLoopNode* new_loop_node, PhaseIdealLoop* phase) const; void replace_opaque_stride_input(Node* new_stride, PhaseIterGVN& igvn) const; - void replace_opaque_init_node(Node* new_init, PhaseIterGVN& igvn) const; + void replace_opaque_init_node(Node* new_init, PhaseIterGVN& igvn, uint last_node_index) const; InitializedAssertionPredicate initialize(PhaseIdealLoop* phase) const; void rewire_loop_data_dependencies(IfTrueNode* target_predicate, const NodeInLoopBody& data_in_loop_body, const PhaseIdealLoop* phase) const; @@ -1326,11 +1326,14 @@ class UpdateStrideForAssertionPredicates : public PredicateVisitor { class UpdateInitForTemplateAssertionPredicates : public PredicateVisitor { Node* const _new_init; PhaseIdealLoop* const _phase; + uint _last_node_index; // only applied to nodes created before Compile::unique() was captured in _last_node_index public: - UpdateInitForTemplateAssertionPredicates(Node* const new_init, PhaseIdealLoop* phase) + UpdateInitForTemplateAssertionPredicates(Node* const new_init, PhaseIdealLoop* phase, uint last_node_index) : _new_init(new_init), - _phase(phase) {} + _phase(phase), + _last_node_index(last_node_index) { + } NONCOPYABLE(UpdateInitForTemplateAssertionPredicates); using PredicateVisitor::visit; diff --git a/test/hotspot/jtreg/compiler/longcountedloops/TestShortLoopTransformationRunsTwice.java b/test/hotspot/jtreg/compiler/longcountedloops/TestShortLoopTransformationRunsTwice.java new file mode 100644 index 000000000000..24cf2e9f922c --- /dev/null +++ b/test/hotspot/jtreg/compiler/longcountedloops/TestShortLoopTransformationRunsTwice.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2026 IBM Corporation. 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 8391917 + * @run main/othervm -XX:-BackgroundCompilation ${test.main.class} + */ + +package compiler.longcountedloops; +import java.util.Objects; + +public class TestShortLoopTransformationRunsTwice { + public static void main(String[] args) { + float[] array = new float[1000]; + for (int i = 0; i < 20_000; i++) { + test1(1000, array, array, 4000); + } + } + + private static float test1(int stop, float[] array1, float[] array2, long longLength) { + int j = 2; + for (; j < 4; j *= 2) { + } + float res = 0; + for (int i = 1; i < stop; i++) { + res += array1[i] + array2[i]; + Objects.checkIndex(i, longLength); + Objects.checkIndex(i * (j - 3), longLength); + } + return res; + } +} From fb268a6f43fb7e7a0b962d10504a686acfd883ff Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Wed, 30 Sep 2026 07:40:12 +0000 Subject: [PATCH 089/103] 8392967: NTLMAuthSequence potential memory leak in getCredentialsHandle Reviewed-by: djelinski, dfuchs --- src/java.base/windows/native/libnet/NTLMAuthSequence.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/java.base/windows/native/libnet/NTLMAuthSequence.c b/src/java.base/windows/native/libnet/NTLMAuthSequence.c index 214df84ccb8e..655866db58c0 100644 --- a/src/java.base/windows/native/libnet/NTLMAuthSequence.c +++ b/src/java.base/windows/native/libnet/NTLMAuthSequence.c @@ -71,7 +71,7 @@ JNIEXPORT jlong JNICALL Java_sun_net_www_protocol_http_ntlm_NTLMAuthSequence_get const CHAR *pUser = 0; const CHAR *pDomain = 0; const CHAR *pPassword = 0; - CredHandle *pCred; + CredHandle *pCred = NULL; TimeStamp ltime; jboolean isCopy; SECURITY_STATUS ss = SEC_E_INTERNAL_ERROR; @@ -142,6 +142,7 @@ JNIEXPORT jlong JNICALL Java_sun_net_www_protocol_http_ntlm_NTLMAuthSequence_get if (ss == SEC_E_OK) { return (jlong) pCred; } else { + free(pCred); return 0; } } From 38fc82a90e21ae34047eec6fff8bf043d2ef7dd5 Mon Sep 17 00:00:00 2001 From: Daisuke Yamazaki Date: Wed, 30 Sep 2026 10:48:10 +0000 Subject: [PATCH 090/103] 8393041: [Valhalla] Significant regression in C1 code with --enable-preview Reviewed-by: thartmann, dlong --- src/hotspot/share/c1/c1_Runtime1.cpp | 29 +++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/src/hotspot/share/c1/c1_Runtime1.cpp b/src/hotspot/share/c1/c1_Runtime1.cpp index 0f8cb60cd955..bfed6fea6dcd 100644 --- a/src/hotspot/share/c1/c1_Runtime1.cpp +++ b/src/hotspot/share/c1/c1_Runtime1.cpp @@ -485,15 +485,34 @@ JRT_END static void profile_flat_array(JavaThread* current, bool load, bool null_free) { + // Skip profiling if array access profiling is disabled. + if (!C1UpdateMethodData || !MethodData::profile_array_accesses()) { + return; + } + ResourceMark rm(current); - vframeStream vfst(current, true); - assert(!vfst.at_end(), "Java frame must exist"); + RegisterMap reg_map( + current, + RegisterMap::UpdateMap::skip, + RegisterMap::ProcessFrames::include, + RegisterMap::WalkContinuation::skip + ); + + frame f_runtime = current->last_frame(); + assert(f_runtime.is_runtime_frame(), "must be a runtime frame"); + + frame f_caller = f_runtime.sender(®_map); + assert(f_caller.is_compiled_frame(), "must be compiled"); + + nmethod* nm = f_caller.cb()->as_nmethod(); // Check if array access profiling is enabled - if (vfst.nm()->comp_level() != CompLevel_full_profile || !C1UpdateMethodData) { + if (nm->comp_level() != CompLevel_full_profile) { return; } - int bci = vfst.bci(); - Method* method = vfst.method(); + + SimpleScopeDesc scope(nm, f_caller.pc()); + int bci = scope.bci(); + Method* method = scope.method(); MethodData* md = method->method_data(); if (md != nullptr) { // Lock to access ProfileData, and ensure lock is not broken by a safepoint From c391dbd7e0ec5c736b204b9eaa2c677e6d9d6271 Mon Sep 17 00:00:00 2001 From: Alexander Zvegintsev Date: Wed, 30 Sep 2026 10:56:32 +0000 Subject: [PATCH 091/103] 8393249: JOptionPaneInternalFrameNotClosableTest fails in headless mode with unsupported GTK LookAndFeel Reviewed-by: jdv --- .../JOptionPane/JOptionPaneInternalFrameNotClosableTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/jdk/javax/swing/JOptionPane/JOptionPaneInternalFrameNotClosableTest.java b/test/jdk/javax/swing/JOptionPane/JOptionPaneInternalFrameNotClosableTest.java index 3e6a20c040bb..d874f997ac9e 100644 --- a/test/jdk/javax/swing/JOptionPane/JOptionPaneInternalFrameNotClosableTest.java +++ b/test/jdk/javax/swing/JOptionPane/JOptionPaneInternalFrameNotClosableTest.java @@ -24,6 +24,7 @@ /* * @test * @bug 8388824 + * @key headful * @summary Verifies that JOptionPane internal frames are not closable * for all installed look and feels * @run main JOptionPaneInternalFrameNotClosableTest @@ -35,7 +36,6 @@ import javax.swing.JDesktopPane; import javax.swing.JInternalFrame; import javax.swing.JOptionPane; -import javax.swing.LookAndFeel; import javax.swing.SwingUtilities; import javax.swing.UIManager; From 08865d4743783f762dfe42e39323cc86f3c92e13 Mon Sep 17 00:00:00 2001 From: Casper Norrbin Date: Wed, 30 Sep 2026 12:33:29 +0000 Subject: [PATCH 092/103] 8392794: gc/arguments/TestMaxRAMPercentage.java fails in containers Reviewed-by: sgehwolf, ayang, tschatzl --- src/hotspot/share/prims/whitebox.cpp | 6 ++++++ .../gc/arguments/TestMaxRAMPercentage.java | 19 ++++++++++--------- test/lib/jdk/test/whitebox/WhiteBox.java | 5 +++++ 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/hotspot/share/prims/whitebox.cpp b/src/hotspot/share/prims/whitebox.cpp index 2d0213eceeed..abf868554101 100644 --- a/src/hotspot/share/prims/whitebox.cpp +++ b/src/hotspot/share/prims/whitebox.cpp @@ -2672,6 +2672,11 @@ WB_ENTRY(jboolean, WB_IsContainerized(JNIEnv* env, jobject o)) return os::is_containerized(); WB_END +// Physical memory of the machine (respecting container limits) +WB_ENTRY(jlong, WB_PhysicalMemory(JNIEnv* env, jobject o)) + return static_cast(os::physical_memory()); +WB_END + // Physical memory of the host machine (including containers) WB_ENTRY(jlong, WB_HostPhysicalMemory(JNIEnv* env, jobject o)) return static_cast(os::Machine::physical_memory()); @@ -3197,6 +3202,7 @@ static JNINativeMethod methods[] = { {CC"hostPhysicalSwap", CC"()J", (void*)&WB_HostPhysicalSwap }, {CC"hostAvailableMemory", CC"()J", (void*)&WB_HostAvailableMemory }, {CC"hostCPUs", CC"()I", (void*)&WB_HostCPUs }, + {CC"physicalMemory", CC"()J", (void*)&WB_PhysicalMemory }, {CC"printOsInfo", CC"()V", (void*)&WB_PrintOsInfo }, {CC"disableElfSectionCache", CC"()V", (void*)&WB_DisableElfSectionCache }, {CC"resolvedMethodItemsCount", CC"()J", (void*)&WB_ResolvedMethodItemsCount }, diff --git a/test/hotspot/jtreg/gc/arguments/TestMaxRAMPercentage.java b/test/hotspot/jtreg/gc/arguments/TestMaxRAMPercentage.java index 0cf34a0d239e..d0a437b0bde2 100644 --- a/test/hotspot/jtreg/gc/arguments/TestMaxRAMPercentage.java +++ b/test/hotspot/jtreg/gc/arguments/TestMaxRAMPercentage.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, 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,6 +30,7 @@ * @library /test/lib * @library / * @requires vm.bits == "64" + * @requires vm.flagless * @modules java.base/jdk.internal.misc * java.management * @build jdk.test.whitebox.WhiteBox @@ -121,19 +122,19 @@ public static void main(String args[]) throws Exception { long M = 1L * 1024L * 1024L; long headroom = 64 * M; - long requiredHostMemory = maxcoopheap + headroom; + long requiredTotalMemory = maxcoopheap + headroom; - // Get host memory - long hostMemory = wb.hostPhysicalMemory(); + // Get total memory. This needs to be the container-aware value (if any) for the test to work + long totalMemory = wb.physicalMemory(); - System.out.println("hostMemory: " + hostMemory + ", requiredHostMemory: " + requiredHostMemory); + System.out.println("totalMemory: " + totalMemory + ", requiredTotalMemory: " + requiredTotalMemory); - if (hostMemory < requiredHostMemory) { - throw new SkippedException("Not enough RAM on machine to run. Test skipped!"); + if (totalMemory < requiredTotalMemory) { + throw new SkippedException("Not enough RAM to run. Test skipped!"); } - double MaxRAMPercentage = ((double)maxcoopheap / hostMemory) * 100.0; - double headroomPercentage = ((double)headroom / hostMemory) * 100.0; + double MaxRAMPercentage = ((double)maxcoopheap / totalMemory) * 100.0; + double headroomPercentage = ((double)headroom / totalMemory) * 100.0; // Args: MaxRAMPercentage, forcecoop, expectheap, expectcoop checkMaxRAMSize(MaxRAMPercentage - headroomPercentage, false, maxcoopheap - (long)headroom, true); diff --git a/test/lib/jdk/test/whitebox/WhiteBox.java b/test/lib/jdk/test/whitebox/WhiteBox.java index 06cca9a7b73d..1c40fef15284 100644 --- a/test/lib/jdk/test/whitebox/WhiteBox.java +++ b/test/lib/jdk/test/whitebox/WhiteBox.java @@ -849,6 +849,11 @@ public native int validateCgroup(boolean cgroupsV2Enabled, String procSelfCgroup, String procSelfMountinfo); public native void printOsInfo(); + + // Total physical memory as seen by the VM, respecting container limits. + public native long physicalMemory(); + + // Total physical memory as seen by the VM, deliberately ignoring container limits. public native long hostPhysicalMemory(); public native long hostAvailableMemory(); public native long hostPhysicalSwap(); From 1fd41c5458a9444c2e376545560c6943f83b98b8 Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Wed, 30 Sep 2026 13:17:11 +0000 Subject: [PATCH 093/103] 8392609: Remove default argument from os::strdup mem_tag parameter Reviewed-by: stefank, tschatzl, jsjolen --- src/hotspot/cpu/arm/vm_version_arm_32.cpp | 2 +- src/hotspot/cpu/ppc/vm_version_ppc.cpp | 2 +- src/hotspot/cpu/s390/vm_version_s390.cpp | 6 ++-- src/hotspot/os/aix/os_aix.cpp | 2 +- src/hotspot/os/aix/porting_aix.cpp | 6 ++-- .../os/linux/cgroupSubsystem_linux.cpp | 32 +++++++++---------- src/hotspot/os/linux/cgroupUtil_linux.cpp | 16 +++++----- .../os/linux/cgroupV1Subsystem_linux.cpp | 8 ++--- .../os/linux/cgroupV1Subsystem_linux.hpp | 4 +-- .../os/linux/cgroupV2Subsystem_linux.cpp | 12 +++---- src/hotspot/os/posix/os_posix.cpp | 4 +-- src/hotspot/os/windows/perfMemory_windows.cpp | 2 +- .../linux_riscv/vm_version_linux_riscv.cpp | 4 +-- src/hotspot/share/asm/codeBuffer.hpp | 2 +- src/hotspot/share/cds/cdsConfig.cpp | 2 +- src/hotspot/share/ci/ciEnv.cpp | 2 +- .../share/classfile/resolutionErrors.cpp | 6 ++-- src/hotspot/share/code/aotCodeCache.cpp | 2 +- src/hotspot/share/code/codeHeapState.cpp | 10 +++--- .../share/compiler/compilationFailureInfo.cpp | 6 ++-- src/hotspot/share/compiler/compilerEvent.cpp | 2 +- src/hotspot/share/compiler/compilerOracle.cpp | 2 +- src/hotspot/share/compiler/disassembler.cpp | 2 +- src/hotspot/share/oops/symbol.cpp | 4 +-- src/hotspot/share/opto/runtime.hpp | 2 +- src/hotspot/share/prims/whitebox.cpp | 2 +- src/hotspot/share/runtime/arguments.cpp | 4 +-- src/hotspot/share/runtime/java.cpp | 10 +++--- src/hotspot/share/runtime/javaThread.cpp | 2 +- src/hotspot/share/runtime/os.hpp | 2 +- src/hotspot/share/utilities/elfFile.cpp | 2 +- test/hotspot/gtest/code/test_codestrings.cpp | 6 ++-- .../gtest/logging/test_logDecorators.cpp | 4 +-- 33 files changed, 87 insertions(+), 87 deletions(-) diff --git a/src/hotspot/cpu/arm/vm_version_arm_32.cpp b/src/hotspot/cpu/arm/vm_version_arm_32.cpp index 6d653ac3fd89..0be3cac4c386 100644 --- a/src/hotspot/cpu/arm/vm_version_arm_32.cpp +++ b/src/hotspot/cpu/arm/vm_version_arm_32.cpp @@ -295,7 +295,7 @@ void VM_Version::initialize() { (has_multiprocessing_extensions() ? ", mp_ext" : "")); // buf is started with ", " or is empty - _cpu_info_string = os::strdup(buf); + _cpu_info_string = os::strdup(buf, mtInternal); if (has_simd()) { if (FLAG_IS_DEFAULT(UsePopCountInstruction)) { diff --git a/src/hotspot/cpu/ppc/vm_version_ppc.cpp b/src/hotspot/cpu/ppc/vm_version_ppc.cpp index adb819082e90..a42e9492c135 100644 --- a/src/hotspot/cpu/ppc/vm_version_ppc.cpp +++ b/src/hotspot/cpu/ppc/vm_version_ppc.cpp @@ -192,7 +192,7 @@ void VM_Version::initialize() { (has_brw() ? " brw" : "") // Make sure number of %s matches num_features! ); - _cpu_info_string = os::strdup(buf); + _cpu_info_string = os::strdup(buf, mtInternal); if (Verbose) { print_features(); } diff --git a/src/hotspot/cpu/s390/vm_version_s390.cpp b/src/hotspot/cpu/s390/vm_version_s390.cpp index e0e0b2a1d54f..81cd8625cc31 100644 --- a/src/hotspot/cpu/s390/vm_version_s390.cpp +++ b/src/hotspot/cpu/s390/vm_version_s390.cpp @@ -426,7 +426,7 @@ void VM_Version::set_cpu_info_string() { _model_string = "unknown model"; strcpy(buf, "z/Architecture (ambiguous detection)"); } - _cpu_info_string = os::strdup(buf); + _cpu_info_string = os::strdup(buf, mtInternal); if (has_Crypto_AES()) { assert(strlen(_cpu_info_string) + 3*8 < sizeof(buf), "increase buffer size"); @@ -436,7 +436,7 @@ void VM_Version::set_cpu_info_string() { has_Crypto_AES192() ? ", aes192" : "", has_Crypto_AES256() ? ", aes256" : ""); os::free((void *)_cpu_info_string); - _cpu_info_string = os::strdup(buf); + _cpu_info_string = os::strdup(buf, mtInternal); } if (has_Crypto_SHA()) { @@ -448,7 +448,7 @@ void VM_Version::set_cpu_info_string() { has_Crypto_SHA512() ? ", sha512" : "", has_Crypto_GHASH() ? ", ghash" : ""); os::free((void *)_cpu_info_string); - _cpu_info_string = os::strdup(buf); + _cpu_info_string = os::strdup(buf, mtInternal); } } diff --git a/src/hotspot/os/aix/os_aix.cpp b/src/hotspot/os/aix/os_aix.cpp index 784562b33a95..f49deec92bab 100644 --- a/src/hotspot/os/aix/os_aix.cpp +++ b/src/hotspot/os/aix/os_aix.cpp @@ -1090,7 +1090,7 @@ void *os::dll_load(const char *filename, char *ebuf, int ebuflen) { const char* pointer_to_dot = strrchr(filename, '.'); if (pointer_to_dot != nullptr && strcmp(pointer_to_dot, old_extension) == 0) { static_assert(sizeof(old_extension) >= sizeof(new_extension)); - char* tmp_path = os::strdup(filename); + char* tmp_path = os::strdup(filename, mtInternal); size_t prefix_size = pointer_delta(pointer_to_dot, filename, 1); os::snprintf_checked(tmp_path + prefix_size, sizeof(old_extension), "%s", new_extension); result = dll_load_library(tmp_path, &eno, ebuf, ebuflen); diff --git a/src/hotspot/os/aix/porting_aix.cpp b/src/hotspot/os/aix/porting_aix.cpp index 1e289bbab3a7..34336f0020de 100644 --- a/src/hotspot/os/aix/porting_aix.cpp +++ b/src/hotspot/os/aix/porting_aix.cpp @@ -979,7 +979,7 @@ static bool search_file_in_LIBPATH(const char* path, struct stat64x* stat) { if (path == nullptr) return false; - char* path2 = os::strdup(path); + char* path2 = os::strdup(path, mtInternal); // if exist, strip off trailing (shr_64.o) or similar char* substr; if (path2[strlen(path2) - 1] == ')' && (substr = strrchr(path2, '('))) { @@ -1023,7 +1023,7 @@ static bool search_file_in_LIBPATH(const char* path, struct stat64x* stat) { Libpath.print("%s:%s", env, rtv_linkedin_libpath()); } - char* libpath = os::strdup(Libpath.base()); + char* libpath = os::strdup(Libpath.base(), mtInternal); char *saveptr, *token; for (token = strtok_r(libpath, ":", &saveptr); token != nullptr; token = strtok_r(nullptr, ":", &saveptr)) { @@ -1062,7 +1062,7 @@ void* Aix_dlopen(const char* filename, int Flags, int *eno, const char** error_r char* member = nullptr; const char* substr; if (filename[strlen(filename) - 1] == ')' && (substr = strrchr(filename, '('))) { - member = os::strdup(substr); + member = os::strdup(substr, mtInternal); } unsigned i = 0; diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp index b9f95228c810..d08846cdfb70 100644 --- a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp @@ -182,16 +182,16 @@ void CgroupSubsystemFactory::set_controller_paths(CgroupInfo* cg_infos, name, mount_path, cg_infos[controller]._mount_path); os::free(cg_infos[controller]._mount_path); os::free(cg_infos[controller]._root_mount_path); - cg_infos[controller]._mount_path = os::strdup(mount_path); - cg_infos[controller]._root_mount_path = os::strdup(root_path); + cg_infos[controller]._mount_path = os::strdup(mount_path, mtInternal); + cg_infos[controller]._root_mount_path = os::strdup(root_path, mtInternal); cg_infos[controller]._read_only = read_only; } else { log_debug(os, container)("Duplicate %s controllers detected. Picking %s, skipping %s.", name, cg_infos[controller]._mount_path, mount_path); } } else { - cg_infos[controller]._mount_path = os::strdup(mount_path); - cg_infos[controller]._root_mount_path = os::strdup(root_path); + cg_infos[controller]._mount_path = os::strdup(mount_path, mtInternal); + cg_infos[controller]._root_mount_path = os::strdup(root_path, mtInternal); cg_infos[controller]._read_only = read_only; } } @@ -288,7 +288,7 @@ bool CgroupSubsystemFactory::determine_type(CgroupInfo* cg_infos, while ((controller = strsep(&p, ISSPACE_CHARS)) != nullptr) { int i; if ((i = cg_v2_controller_index(controller)) != -1) { - cg_infos[i]._name = os::strdup(controller); + cg_infos[i]._name = os::strdup(controller, mtInternal); cg_infos[i]._enabled = true; if (i == PIDS_IDX || i == CPUSET_IDX) { log_debug(os, container)("Detected optional %s controller entry in %s", @@ -333,24 +333,24 @@ bool CgroupSubsystemFactory::determine_type(CgroupInfo* cg_infos, continue; } if (strcmp(name, "memory") == 0) { - cg_infos[MEMORY_IDX]._name = os::strdup(name); + cg_infos[MEMORY_IDX]._name = os::strdup(name, mtInternal); cg_infos[MEMORY_IDX]._hierarchy_id = hierarchy_id; cg_infos[MEMORY_IDX]._enabled = (enabled == 1); } else if (strcmp(name, "cpuset") == 0) { - cg_infos[CPUSET_IDX]._name = os::strdup(name); + cg_infos[CPUSET_IDX]._name = os::strdup(name, mtInternal); cg_infos[CPUSET_IDX]._hierarchy_id = hierarchy_id; cg_infos[CPUSET_IDX]._enabled = (enabled == 1); } else if (strcmp(name, "cpu") == 0) { - cg_infos[CPU_IDX]._name = os::strdup(name); + cg_infos[CPU_IDX]._name = os::strdup(name, mtInternal); cg_infos[CPU_IDX]._hierarchy_id = hierarchy_id; cg_infos[CPU_IDX]._enabled = (enabled == 1); } else if (strcmp(name, "cpuacct") == 0) { - cg_infos[CPUACCT_IDX]._name = os::strdup(name); + cg_infos[CPUACCT_IDX]._name = os::strdup(name, mtInternal); cg_infos[CPUACCT_IDX]._hierarchy_id = hierarchy_id; cg_infos[CPUACCT_IDX]._enabled = (enabled == 1); } else if (strcmp(name, "pids") == 0) { log_debug(os, container)("Detected optional pids controller entry in %s", controllers_file); - cg_infos[PIDS_IDX]._name = os::strdup(name); + cg_infos[PIDS_IDX]._name = os::strdup(name, mtInternal); cg_infos[PIDS_IDX]._hierarchy_id = hierarchy_id; cg_infos[PIDS_IDX]._enabled = (enabled == 1); } @@ -410,20 +410,20 @@ bool CgroupSubsystemFactory::determine_type(CgroupInfo* cg_infos, while (!cgroups_v2_enabled && (token = strsep(&controllers, ",")) != nullptr) { if (strcmp(token, "memory") == 0) { assert(hierarchy_id == cg_infos[MEMORY_IDX]._hierarchy_id, "/proc/cgroups and /proc/self/cgroup hierarchy mismatch for memory"); - cg_infos[MEMORY_IDX]._cgroup_path = os::strdup(cgroup_path); + cg_infos[MEMORY_IDX]._cgroup_path = os::strdup(cgroup_path, mtInternal); } else if (strcmp(token, "cpuset") == 0) { assert(hierarchy_id == cg_infos[CPUSET_IDX]._hierarchy_id, "/proc/cgroups and /proc/self/cgroup hierarchy mismatch for cpuset"); - cg_infos[CPUSET_IDX]._cgroup_path = os::strdup(cgroup_path); + cg_infos[CPUSET_IDX]._cgroup_path = os::strdup(cgroup_path, mtInternal); } else if (strcmp(token, "cpu") == 0) { assert(hierarchy_id == cg_infos[CPU_IDX]._hierarchy_id, "/proc/cgroups and /proc/self/cgroup hierarchy mismatch for cpu"); - cg_infos[CPU_IDX]._cgroup_path = os::strdup(cgroup_path); + cg_infos[CPU_IDX]._cgroup_path = os::strdup(cgroup_path, mtInternal); } else if (strcmp(token, "cpuacct") == 0) { assert(hierarchy_id == cg_infos[CPUACCT_IDX]._hierarchy_id, "/proc/cgroups and /proc/self/cgroup hierarchy mismatch for cpuacct"); - cg_infos[CPUACCT_IDX]._cgroup_path = os::strdup(cgroup_path); + cg_infos[CPUACCT_IDX]._cgroup_path = os::strdup(cgroup_path, mtInternal); } else if (strcmp(token, "pids") == 0) { assert(hierarchy_id == cg_infos[PIDS_IDX]._hierarchy_id, "/proc/cgroups (%d) and /proc/self/cgroup (%d) hierarchy mismatch for pids", cg_infos[PIDS_IDX]._hierarchy_id, hierarchy_id); - cg_infos[PIDS_IDX]._cgroup_path = os::strdup(cgroup_path); + cg_infos[PIDS_IDX]._cgroup_path = os::strdup(cgroup_path, mtInternal); } } if (cgroups_v2_enabled) { @@ -434,7 +434,7 @@ bool CgroupSubsystemFactory::determine_type(CgroupInfo* cg_infos, } for (int i = 0; i < CG_INFO_LENGTH; i++) { assert(cg_infos[i]._cgroup_path == nullptr, "cgroup path must only be set once"); - cg_infos[i]._cgroup_path = os::strdup(cgroup_path); + cg_infos[i]._cgroup_path = os::strdup(cgroup_path, mtInternal); } } } diff --git a/src/hotspot/os/linux/cgroupUtil_linux.cpp b/src/hotspot/os/linux/cgroupUtil_linux.cpp index 78eb51f1eeab..04f847d2cb44 100644 --- a/src/hotspot/os/linux/cgroupUtil_linux.cpp +++ b/src/hotspot/os/linux/cgroupUtil_linux.cpp @@ -95,8 +95,8 @@ void CgroupUtil::adjust_controller(CgroupMemoryController* mem, physical_memory_ return; } log_trace(os, container)("Adjusting controller path for memory: %s", mem->subsystem_path()); - char* orig = os::strdup(mem->cgroup_path()); - char* cg_path = os::strdup(orig); + char* orig = os::strdup(mem->cgroup_path(), mtInternal); + char* cg_path = os::strdup(orig, mtInternal); char* last_slash; assert(cg_path[0] == '/', "cgroup path must start with '/'"); char* limit_cg_path = nullptr; @@ -111,7 +111,7 @@ void CgroupUtil::adjust_controller(CgroupMemoryController* mem, physical_memory_ if (limit < lowest_limit) { lowest_limit = limit; os::free(limit_cg_path); // handles nullptr - limit_cg_path = os::strdup(cg_path); + limit_cg_path = os::strdup(cg_path, mtInternal); } } // need to check limit at mount point @@ -120,7 +120,7 @@ void CgroupUtil::adjust_controller(CgroupMemoryController* mem, physical_memory_ if (limit < lowest_limit) { lowest_limit = limit; os::free(limit_cg_path); // handles nullptr - limit_cg_path = os::strdup("/"); + limit_cg_path = os::strdup("/", mtInternal); } assert(lowest_limit <= upper_bound, "limit must not exceed upper bound"); if (lowest_limit != orig_limit) { @@ -158,8 +158,8 @@ void CgroupUtil::adjust_controller(CgroupCpuController* cpu, double upper_bound) return; } log_trace(os, container)("Adjusting controller path for cpu: %s", cpu->subsystem_path()); - char* orig = os::strdup(cpu->cgroup_path()); - char* cg_path = os::strdup(orig); + char* orig = os::strdup(cpu->cgroup_path(), mtInternal); + char* cg_path = os::strdup(orig, mtInternal); char* last_slash; assert(cg_path[0] == '/', "cgroup path must start with '/'"); double lowest_limit = get_updated_cpu_limit(cpu, upper_bound, upper_bound); @@ -174,7 +174,7 @@ void CgroupUtil::adjust_controller(CgroupCpuController* cpu, double upper_bound) if (cpus != upper_bound && cpus < lowest_limit) { lowest_limit = cpus; os::free(limit_cg_path); // handles nullptr - limit_cg_path = os::strdup(cg_path); + limit_cg_path = os::strdup(cg_path, mtInternal); } } // need to check limit at mount point @@ -183,7 +183,7 @@ void CgroupUtil::adjust_controller(CgroupCpuController* cpu, double upper_bound) if (cpus != upper_bound && cpus < lowest_limit) { lowest_limit = cpus; os::free(limit_cg_path); // handles nullptr - limit_cg_path = os::strdup("/"); + limit_cg_path = os::strdup("/", mtInternal); } assert(lowest_limit >= 0, "limit must be positive"); if (lowest_limit != orig_limit) { diff --git a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp index e42b7a133919..1e213a63ef4b 100644 --- a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp @@ -88,7 +88,7 @@ void CgroupV1Controller::set_subsystem_path(const char* cgroup_path) { os::free(_path); _path = nullptr; } - _cgroup_path = os::strdup(cgroup_path); + _cgroup_path = os::strdup(cgroup_path, mtInternal); stringStream ss; if (_root != nullptr && cgroup_path != nullptr) { ss.print_raw(_mount_point); @@ -120,7 +120,7 @@ void CgroupV1Controller::set_subsystem_path(const char* cgroup_path) { } } } - _path = os::strdup(ss.base()); + _path = os::strdup(ss.base(), mtInternal); } } @@ -473,13 +473,13 @@ void CgroupV1MemoryController::print_version_specific_info(outputStream* st, phy char* CgroupV1Subsystem::cpu_cpuset_cpus() { char cpus[1024]; CONTAINER_READ_STRING_CHECKED(_cpuset, "/cpuset.cpus", "cpuset.cpus", cpus, 1024); - return os::strdup(cpus); + return os::strdup(cpus, mtInternal); } char* CgroupV1Subsystem::cpu_cpuset_memory_nodes() { char mems[1024]; CONTAINER_READ_STRING_CHECKED(_cpuset, "/cpuset.mems", "cpuset.mems", mems, 1024); - return os::strdup(mems); + return os::strdup(mems, mtInternal); } /* cpu_quota diff --git a/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp b/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp index af8d0efd3788..6fe0c7babf7d 100644 --- a/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp @@ -44,11 +44,11 @@ class CgroupV1Controller: public CgroupController { public: CgroupV1Controller(char *root, char *mountpoint, - bool ro) : _root(os::strdup(root)), + bool ro) : _root(os::strdup(root, mtInternal)), _read_only(ro), _path(nullptr) { _cgroup_path = nullptr; - _mount_point = os::strdup(mountpoint); + _mount_point = os::strdup(mountpoint, mtInternal); } // Shallow copy constructor CgroupV1Controller(const CgroupV1Controller& o) : _root(o._root), diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp index b69feab2cb4d..222454173468 100644 --- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp @@ -33,8 +33,8 @@ CgroupV2Controller::CgroupV2Controller(char* mount_path, char *cgroup_path, bool ro) : _read_only(ro), _path(construct_path(mount_path, cgroup_path)) { - _cgroup_path = os::strdup(cgroup_path); - _mount_point = os::strdup(mount_path); + _cgroup_path = os::strdup(cgroup_path, mtInternal); + _mount_point = os::strdup(mount_path, mtInternal); } // Shallow copy constructor CgroupV2Controller::CgroupV2Controller(const CgroupV2Controller& o) : @@ -168,13 +168,13 @@ bool CgroupV2Subsystem::is_containerized() { char* CgroupV2Subsystem::cpu_cpuset_cpus() { char cpus[1024]; CONTAINER_READ_STRING_CHECKED(unified(), "/cpuset.cpus", "cpuset.cpus", cpus, 1024); - return os::strdup(cpus); + return os::strdup(cpus, mtInternal); } char* CgroupV2Subsystem::cpu_cpuset_memory_nodes() { char mems[1024]; CONTAINER_READ_STRING_CHECKED(unified(), "/cpuset.mems", "cpuset.mems", mems, 1024); - return os::strdup(mems); + return os::strdup(mems, mtInternal); } bool CgroupV2CpuController::cpu_period(int& result) { @@ -354,7 +354,7 @@ void CgroupV2Controller::set_subsystem_path(const char* cgroup_path) { if (_cgroup_path != nullptr) { os::free(_cgroup_path); } - _cgroup_path = os::strdup(cgroup_path); + _cgroup_path = os::strdup(cgroup_path, mtInternal); if (_path != nullptr) { os::free(_path); } @@ -387,7 +387,7 @@ char* CgroupV2Controller::construct_path(char* mount_path, const char* cgroup_pa if (strcmp(cgroup_path, "/") != 0) { ss.print_raw(cgroup_path); } - return os::strdup(ss.base()); + return os::strdup(ss.base(), mtInternal); } /* pids_max diff --git a/src/hotspot/os/posix/os_posix.cpp b/src/hotspot/os/posix/os_posix.cpp index e181986e4052..bb1c6605f5ba 100644 --- a/src/hotspot/os/posix/os_posix.cpp +++ b/src/hotspot/os/posix/os_posix.cpp @@ -322,7 +322,7 @@ int os::create_file_for_heap(const char* dir) { int fd; #if defined(LINUX) && defined(O_TMPFILE) - char* native_dir = os::strdup(dir); + char* native_dir = os::strdup(dir, mtInternal); if (native_dir == nullptr) { vm_exit_during_initialization(err_msg("strdup failed during creation of backing file for heap (%s)", os::strerror(errno))); return -1; @@ -854,7 +854,7 @@ void os::dll_unload(void *lib) { char* l_pathdup = nullptr; l_path = os::Linux::dll_path(lib); if (l_path != nullptr) { - l_path = l_pathdup = os::strdup(l_path); + l_path = l_pathdup = os::strdup(l_path, mtInternal); } #endif // LINUX diff --git a/src/hotspot/os/windows/perfMemory_windows.cpp b/src/hotspot/os/windows/perfMemory_windows.cpp index 8e698c53d283..319eb4e781ab 100644 --- a/src/hotspot/os/windows/perfMemory_windows.cpp +++ b/src/hotspot/os/windows/perfMemory_windows.cpp @@ -1281,7 +1281,7 @@ static HANDLE create_sharedmem_resources(const char* dirname, const char* filena // the file has been successfully created and the file mapping // object has been created. sharedmem_fileHandle = fh; - sharedmem_fileName = os::strdup(filename); + sharedmem_fileName = os::strdup(filename, mtInternal); return fmh; } diff --git a/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp b/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp index bace57b9d32e..934f272036cb 100644 --- a/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp +++ b/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp @@ -206,7 +206,7 @@ void VM_Version::setup_cpu_available_features() { } } - _cpu_info_string = os::strdup(buf); + _cpu_info_string = os::strdup(buf, mtInternal); _features_string = _cpu_info_string + features_offset; } @@ -265,7 +265,7 @@ char* VM_Version::os_uarch_additional_features() { } if (ret == nullptr) { if (strncmp(buf, "uarch", sizeof "uarch" - 1) == 0) { - ret = os::strdup(p + 2); + ret = os::strdup(p + 2, mtInternal); ret[strcspn(ret, "\n")] = '\0'; } } diff --git a/src/hotspot/share/asm/codeBuffer.hpp b/src/hotspot/share/asm/codeBuffer.hpp index 05d80c75b3ef..ff79b391fd1e 100644 --- a/src/hotspot/share/asm/codeBuffer.hpp +++ b/src/hotspot/share/asm/codeBuffer.hpp @@ -302,7 +302,7 @@ class CodeSection { class CHeapString : public CHeapObj { public: - CHeapString(const char* str) : _string(os::strdup(str)) {} + CHeapString(const char* str) : _string(os::strdup(str, mtCode)) {} ~CHeapString(); const char* string() const { return _string; } diff --git a/src/hotspot/share/cds/cdsConfig.cpp b/src/hotspot/share/cds/cdsConfig.cpp index ae4430d1e85c..35d28f5068b2 100644 --- a/src/hotspot/share/cds/cdsConfig.cpp +++ b/src/hotspot/share/cds/cdsConfig.cpp @@ -169,7 +169,7 @@ const char* CDSConfig::default_archive_path() { tmp.print_raw("_preview"); } tmp.print_raw(".jsa"); - _default_archive_path = os::strdup(tmp.base()); + _default_archive_path = os::strdup(tmp.base(), mtInternal); } return _default_archive_path; } diff --git a/src/hotspot/share/ci/ciEnv.cpp b/src/hotspot/share/ci/ciEnv.cpp index b698149dc2a2..783037535f4c 100644 --- a/src/hotspot/share/ci/ciEnv.cpp +++ b/src/hotspot/share/ci/ciEnv.cpp @@ -1257,7 +1257,7 @@ bool ciEnv::dyno_loc(const InstanceKlass* ik, const char *&loc) const { // Associate the current location descriptor with the given class and record for later lookup. void ciEnv::set_dyno_loc(const InstanceKlass* ik) { - const char *loc = os::strdup(_dyno_name); + const char *loc = os::strdup(_dyno_name, mtCompiler); bool found = false; int pos = _dyno_klasses->find_sorted(ik, found); if (found) { diff --git a/src/hotspot/share/classfile/resolutionErrors.cpp b/src/hotspot/share/classfile/resolutionErrors.cpp index 958d4812cbbc..4e7b78e78196 100644 --- a/src/hotspot/share/classfile/resolutionErrors.cpp +++ b/src/hotspot/share/classfile/resolutionErrors.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, 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 @@ -99,9 +99,9 @@ ResolutionErrorEntry* ResolutionErrorTable::find_entry(const constantPoolHandle& ResolutionErrorEntry::ResolutionErrorEntry(Symbol* error, const char* message, Symbol* cause, const char* cause_msg): _error(error), - _message(message != nullptr ? os::strdup(message) : nullptr), + _message(message != nullptr ? os::strdup(message, mtClass) : nullptr), _cause(cause), - _cause_msg(cause_msg != nullptr ? os::strdup(cause_msg) : nullptr), + _cause_msg(cause_msg != nullptr ? os::strdup(cause_msg, mtClass) : nullptr), _nest_host_error(nullptr) { Symbol::maybe_increment_refcount(_error); diff --git a/src/hotspot/share/code/aotCodeCache.cpp b/src/hotspot/share/code/aotCodeCache.cpp index 0ea298521d20..e79405dc4b45 100644 --- a/src/hotspot/share/code/aotCodeCache.cpp +++ b/src/hotspot/share/code/aotCodeCache.cpp @@ -2337,7 +2337,7 @@ const char* AOTCodeAddressTable::add_C_string(const char* str) { // Add one new string. // Passed in string can be freed and used space become inaccessible. // Duplicate string for future compare. - const char* dup = os::strdup(str); + const char* dup = os::strdup(str, mtCode); _C_strings->at_put_grow(_C_strings_count, dup); _C_strings_id->at_put_grow(_C_strings_count, -1); log_trace(aot, codecache, stringtable)("add_C_string: [%d] " INTPTR_FORMAT " '%s'", _C_strings_count, p2i(dup), dup); diff --git a/src/hotspot/share/code/codeHeapState.cpp b/src/hotspot/share/code/codeHeapState.cpp index a545285d1da0..9f50ced5c35d 100644 --- a/src/hotspot/share/code/codeHeapState.cpp +++ b/src/hotspot/share/code/codeHeapState.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2018, 2019 SAP SE. All rights reserved. + * Copyright (c) 2018, 2026 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 @@ -730,9 +730,9 @@ void CodeHeapState::aggregate(outputStream* out, CodeHeap* heap, size_t granular ResourceMark rm; Method* method = nm->method(); if (nm->is_in_use() || nm->is_not_entrant()) { - blob_name = os::strdup(method->name_and_sig_as_C_string()); + blob_name = os::strdup(method->name_and_sig_as_C_string(), mtCode); } else { - blob_name = os::strdup(cb->name()); + blob_name = os::strdup(cb->name(), mtCode); } nm_size = nm->total_size(); compile_id = nm->compile_id(); @@ -765,7 +765,7 @@ void CodeHeapState::aggregate(outputStream* out, CodeHeap* heap, size_t granular break; } } else { - blob_name = os::strdup(cb->name()); + blob_name = os::strdup(cb->name(), mtCode); } //------------------------------------------ @@ -1261,7 +1261,7 @@ void CodeHeapState::print_usedSpace(outputStream* out, CodeHeap* heap) { for (unsigned int i = 0; i != tsbStopper; i = TopSizeArray[i].index) { printed_topSizeBlocks++; if (TopSizeArray[i].blob_name == nullptr) { - TopSizeArray[i].blob_name = os::strdup("unnamed blob or blob name unavailable"); + TopSizeArray[i].blob_name = os::strdup("unnamed blob or blob name unavailable", mtCode); } // heap->find_start() is safe. Only works on _segmap. // Returns nullptr or void*. Returned CodeBlob may be uninitialized. diff --git a/src/hotspot/share/compiler/compilationFailureInfo.cpp b/src/hotspot/share/compiler/compilationFailureInfo.cpp index 3940b4849f64..d0c60d9084cc 100644 --- a/src/hotspot/share/compiler/compilationFailureInfo.cpp +++ b/src/hotspot/share/compiler/compilationFailureInfo.cpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2023, 2024, Red Hat, Inc. and/or its affiliates. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Red Hat, Inc. and/or its affiliates. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -47,7 +47,7 @@ int CompilationFailureInfo::current_compile_id_or_0() { CompilationFailureInfo::CompilationFailureInfo(const char* failure_reason) : _stack(2), - _failure_reason(os::strdup(failure_reason)), + _failure_reason(os::strdup(failure_reason, mtCompiler)), _elapsed_seconds(os::elapsedTime()), _compile_id(current_compile_id_or_0()) {} diff --git a/src/hotspot/share/compiler/compilerEvent.cpp b/src/hotspot/share/compiler/compilerEvent.cpp index 4dd1be73bece..abb13c331be0 100644 --- a/src/hotspot/share/compiler/compilerEvent.cpp +++ b/src/hotspot/share/compiler/compilerEvent.cpp @@ -101,7 +101,7 @@ int CompilerEvent::PhaseEvent::get_phase_id(const char* phase_name, bool may_exi } index = _phase_names->length(); - _phase_names->append(use_strdup ? os::strdup(phase_name) : phase_name); + _phase_names->append(use_strdup ? os::strdup(phase_name, mtCompiler) : phase_name); } if (register_jfr_serializer) { JfrSerializer::register_serializer(TYPE_COMPILERPHASETYPE, false, new CompilerPhaseTypeConstant()); diff --git a/src/hotspot/share/compiler/compilerOracle.cpp b/src/hotspot/share/compiler/compilerOracle.cpp index a36f2bbf89c7..f90990a65a26 100644 --- a/src/hotspot/share/compiler/compilerOracle.cpp +++ b/src/hotspot/share/compiler/compilerOracle.cpp @@ -1255,7 +1255,7 @@ bool compilerOracle_init() { // Register default compile commands first - any commands specified via CompileCommand will // supersede these default commands. for (int i = 0; default_compile_commands[i] != nullptr; i ++) { - char* s = os::strdup(default_compile_commands[i]); + char* s = os::strdup(default_compile_commands[i], mtCompiler); success = CompilerOracle::parse_from_line_quietly(s); os::free(s); assert(success, "default compile command \"%s\" failed to parse", default_compile_commands[i]); diff --git a/src/hotspot/share/compiler/disassembler.cpp b/src/hotspot/share/compiler/disassembler.cpp index 9dc8956d98d1..b5cfb31c782c 100644 --- a/src/hotspot/share/compiler/disassembler.cpp +++ b/src/hotspot/share/compiler/disassembler.cpp @@ -281,7 +281,7 @@ void decode_env::print_hook_comments(address pc, bool newline) { if (len > 0 && line[len-1] == '\n') { line[len-1] = '\0'; } - _cached_src_lines->append(os::strdup(line)); + _cached_src_lines->append(os::strdup(line, mtCompiler)); } fclose(fp); _print_file_name = true; diff --git a/src/hotspot/share/oops/symbol.cpp b/src/hotspot/share/oops/symbol.cpp index 99912dbc939f..dba3fad23248 100644 --- a/src/hotspot/share/oops/symbol.cpp +++ b/src/hotspot/share/oops/symbol.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -152,7 +152,7 @@ void Symbol::print_symbol_on(outputStream* st) const { // stream it could resize, using the same resource arena. ResourceMark rm; s = as_quoted_ascii(); - s = os::strdup(s); + s = os::strdup(s, mtInternal); } if (s == nullptr) { st->print("(null)"); diff --git a/src/hotspot/share/opto/runtime.hpp b/src/hotspot/share/opto/runtime.hpp index 023c1ddfaa91..a7c87fb43ab1 100644 --- a/src/hotspot/share/opto/runtime.hpp +++ b/src/hotspot/share/opto/runtime.hpp @@ -77,7 +77,7 @@ class NamedCounter : public CHeapObj { public: NamedCounter(const char *n, CounterTag tag = NoTag): - _name(n == nullptr ? nullptr : os::strdup(n)), + _name(n == nullptr ? nullptr : os::strdup(n, mtCompiler)), _count(0), _tag(tag), _next(nullptr) {} diff --git a/src/hotspot/share/prims/whitebox.cpp b/src/hotspot/share/prims/whitebox.cpp index abf868554101..c3d419c62d2d 100644 --- a/src/hotspot/share/prims/whitebox.cpp +++ b/src/hotspot/share/prims/whitebox.cpp @@ -1563,7 +1563,7 @@ CodeHeap* WhiteBox::get_code_heap(CodeBlobType blob_type) { struct CodeBlobStub { CodeBlobStub(const CodeBlob* blob) : - name(os::strdup(blob->name())), + name(os::strdup(blob->name(), mtInternal)), size(blob->size()), blob_type(static_cast(WhiteBox::get_blob_type(blob))), address((jlong) blob), diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index 74750495db1b..1a044215b6f5 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -2693,7 +2693,7 @@ class ScopedVMInitArgs : public StackObj { for (int i = 0; i < options->length(); i++) { options_arr[i] = options->at(i); - options_arr[i].optionString = os::strdup(options_arr[i].optionString); + options_arr[i].optionString = os::strdup(options_arr[i].optionString, mtArguments); if (options_arr[i].optionString == nullptr) { // Rely on the destructor to do cleanup. _args.nOptions = i; @@ -2820,7 +2820,7 @@ jint Arguments::parse_options_environment_variable(const char* name, return JNI_OK; } - if ((buffer = os::strdup(buffer)) == nullptr) { + if ((buffer = os::strdup(buffer, mtArguments)) == nullptr) { return JNI_ENOMEM; } diff --git a/src/hotspot/share/runtime/java.cpp b/src/hotspot/share/runtime/java.cpp index 084269a530c5..d854db4f131b 100644 --- a/src/hotspot/share/runtime/java.cpp +++ b/src/hotspot/share/runtime/java.cpp @@ -759,21 +759,21 @@ void JDK_Version::to_string(char* buffer, size_t buflen) const { } void JDK_Version::set_java_version(const char* version) { - _java_version = os::strdup(version); + _java_version = os::strdup(version, mtInternal); } void JDK_Version::set_runtime_name(const char* name) { - _runtime_name = os::strdup(name); + _runtime_name = os::strdup(name, mtInternal); } void JDK_Version::set_runtime_version(const char* version) { - _runtime_version = os::strdup(version); + _runtime_version = os::strdup(version, mtInternal); } void JDK_Version::set_runtime_vendor_version(const char* vendor_version) { - _runtime_vendor_version = os::strdup(vendor_version); + _runtime_vendor_version = os::strdup(vendor_version, mtInternal); } void JDK_Version::set_runtime_vendor_vm_bug_url(const char* vendor_vm_bug_url) { - _runtime_vendor_vm_bug_url = os::strdup(vendor_vm_bug_url); + _runtime_vendor_vm_bug_url = os::strdup(vendor_vm_bug_url, mtInternal); } diff --git a/src/hotspot/share/runtime/javaThread.cpp b/src/hotspot/share/runtime/javaThread.cpp index c2b9abd75544..66057c3fc771 100644 --- a/src/hotspot/share/runtime/javaThread.cpp +++ b/src/hotspot/share/runtime/javaThread.cpp @@ -848,7 +848,7 @@ void JavaThread::exit(bool destroy_vm, ExitType exit_type) { char* thread_name = nullptr; if (log_is_enabled(Debug, os, thread, timer)) { ResourceMark rm(this); - thread_name = os::strdup(name()); + thread_name = os::strdup(name(), mtInternal); } if (log_is_enabled(Info, os, thread)) { diff --git a/src/hotspot/share/runtime/os.hpp b/src/hotspot/share/runtime/os.hpp index 88b891eaf768..9b02b59d740b 100644 --- a/src/hotspot/share/runtime/os.hpp +++ b/src/hotspot/share/runtime/os.hpp @@ -1001,7 +1001,7 @@ class os: AllStatic { // handles null pointers static void free (void *memblock); - static char* strdup(const char *, MemTag mem_tag = mtInternal); // Like strdup + static char* strdup(const char *, MemTag mem_tag); // Like strdup // Like strdup, but exit VM when strdup() returns null static char* strdup_check_oom(const char*, MemTag mem_tag); diff --git a/src/hotspot/share/utilities/elfFile.cpp b/src/hotspot/share/utilities/elfFile.cpp index e81f91282929..4fb8682d7722 100644 --- a/src/hotspot/share/utilities/elfFile.cpp +++ b/src/hotspot/share/utilities/elfFile.cpp @@ -107,7 +107,7 @@ MarkedFileReader::~MarkedFileReader() { } ElfFile::ElfFile(const char* filepath) : - _next(nullptr), _filepath(os::strdup(filepath)), _file(nullptr), + _next(nullptr), _filepath(os::strdup(filepath, mtInternal)), _file(nullptr), _symbol_tables(nullptr), _string_tables(nullptr), _shdr_string_table(nullptr), _funcDesc_table(nullptr), _status(NullDecoder::no_error), _dwarf_file(nullptr) { memset(&_elfHdr, 0, sizeof(_elfHdr)); diff --git a/test/hotspot/gtest/code/test_codestrings.cpp b/test/hotspot/gtest/code/test_codestrings.cpp index 46a8ff12f089..4afb6fb7cf03 100644 --- a/test/hotspot/gtest/code/test_codestrings.cpp +++ b/test/hotspot/gtest/code/test_codestrings.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ -54,7 +54,7 @@ static const char* replace_addr_expr(const char* str) std::basic_string tmp5 = std::regex_replace(tmp4, std::regex("\\s+:\\s+hlt[ \\t]+(?!\\n\\s+;;)"), ""); std::basic_string red = std::regex_replace(tmp5, std::regex("(\\s+:\\s+nop)[ \\t]*"), "$1"); - return os::strdup(red.c_str()); + return os::strdup(red.c_str(), mtTest); } static const char* delete_header_line(const char* str) @@ -64,7 +64,7 @@ static const char* delete_header_line(const char* str) std::basic_string red = std::regex_replace(str, std::regex("Decoding.+bytes\\n"), ""); - return os::strdup(red.c_str()); + return os::strdup(red.c_str(), mtTest); } static void asm_remarks_check(const AsmRemarks &rem1, diff --git a/test/hotspot/gtest/logging/test_logDecorators.cpp b/test/hotspot/gtest/logging/test_logDecorators.cpp index 61b9bb620203..1cd3c99bc09c 100644 --- a/test/hotspot/gtest/logging/test_logDecorators.cpp +++ b/test/hotspot/gtest/logging/test_logDecorators.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, 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 @@ -99,7 +99,7 @@ TEST(LogDecorators, from_and_to_abbr) { ASSERT_EQ(decorator, decorator2); // Test case insensitivity - char* abbr_cpy = os::strdup(abbr); + char* abbr_cpy = os::strdup(abbr, mtTest); abbr_cpy[0] = toupper(abbr_cpy[0]); decorator2 = LogDecorators::from_string(abbr_cpy); os::free(abbr_cpy); From 4da20c030c4281a87f3dd5dbc34ee485389dfe2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20H=C3=A4ssig?= Date: Wed, 30 Sep 2026 13:45:25 +0000 Subject: [PATCH 094/103] 8257761: [mlvm] make JDI tests tolerate exceptions caused by code cache exhaustion Reviewed-by: thartmann, mchevalier --- test/hotspot/jtreg/ProblemList.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index e708bd8cbcb6..922567005a82 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -169,7 +169,6 @@ vmTestbase/jit/escape/LockCoarsening/LockCoarsening001.java 8148743 generic-all vmTestbase/jit/escape/LockCoarsening/LockCoarsening002.java 8208259 generic-all vmTestbase/vm/mlvm/indy/func/jvmti/redefineClassInBootstrap/TestDescription.java 8013267 generic-all -vmTestbase/vm/mlvm/meth/stress/jdi/breakpointInCompiledCode/Test.java 8257761 generic-all vmTestbase/vm/mlvm/indy/func/jvmti/mergeCP_indy2none_a/TestDescription.java 8013267 generic-all vmTestbase/vm/mlvm/indy/func/jvmti/mergeCP_indy2manyDiff_b/TestDescription.java 8013267 generic-all vmTestbase/vm/mlvm/indy/func/jvmti/mergeCP_indy2manySame_b/TestDescription.java 8013267 generic-all From b1979f73ad2b7358807490eb7a155a77c3df8e57 Mon Sep 17 00:00:00 2001 From: Matias Saavedra Silva Date: Wed, 30 Sep 2026 14:12:29 +0000 Subject: [PATCH 095/103] 8390025: AOTMode=record fails to resolve file: URLs in JAR manifest Class-Path on Windows Reviewed-by: iklam, kvn --- src/hotspot/share/cds/aotClassLocation.cpp | 17 +++- src/hotspot/share/classfile/classLoader.cpp | 2 +- .../aotCache/ClassPathAttributeTest.java | 86 +++++++++++++++++++ 3 files changed, 100 insertions(+), 5 deletions(-) create mode 100644 test/hotspot/jtreg/runtime/cds/appcds/aotCache/ClassPathAttributeTest.java diff --git a/src/hotspot/share/cds/aotClassLocation.cpp b/src/hotspot/share/cds/aotClassLocation.cpp index 9963a3e89990..d7337dead952 100644 --- a/src/hotspot/share/cds/aotClassLocation.cpp +++ b/src/hotspot/share/cds/aotClassLocation.cpp @@ -661,10 +661,19 @@ void AOTClassLocationConfig::add_class_location(JavaThread* current, GrowableCla size_t name_len = strlen(file_start); if (name_len > 0) { ResourceMark rm(current); - size_t libname_len = dir_len + name_len; - char* libname = NEW_RESOURCE_ARRAY(char, libname_len + 1); - int n = os::snprintf(libname, libname_len + 1, "%.*s%s", dir_len, dir_name, file_start); - assert((size_t)n == libname_len, "Unexpected number of characters in string"); + char* libname; + + if (strncasecmp(file_start, "file:", 5) == 0) { + // If the "file:" prefix is present in the attribute, the subsequent + // path is absolute. Remove this prefix from the path name and use + // the absolute path rather than appending to the parent directory. + libname = ClassLoader::uri_to_path(file_start); + } else { + size_t libname_len = dir_len + name_len; + libname = NEW_RESOURCE_ARRAY(char, libname_len + 1); + int n = os::snprintf(libname, libname_len + 1, "%.*s%s", dir_len, dir_name, file_start); + assert((size_t)n == libname_len, "Unexpected number of characters in string"); + } // Avoid infinite recursion when two JAR files refer to each // other via cpattr. diff --git a/src/hotspot/share/classfile/classLoader.cpp b/src/hotspot/share/classfile/classLoader.cpp index 62411a09556b..15a8a0c576a0 100644 --- a/src/hotspot/share/classfile/classLoader.cpp +++ b/src/hotspot/share/classfile/classLoader.cpp @@ -1247,7 +1247,7 @@ InstanceKlass* ClassLoader::load_class(Symbol* name, PackageEntry* pkg_entry, bo #if INCLUDE_CDS static const char* skip_uri_protocol(const char* source) { - if (strncmp(source, "file:", 5) == 0) { + if (strncasecmp(source, "file:", 5) == 0) { // file: protocol path could start with file:/ or file:/// // locate the char after all the forward slashes int offset = 5; diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/ClassPathAttributeTest.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/ClassPathAttributeTest.java new file mode 100644 index 000000000000..d653ff4e4ae1 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/ClassPathAttributeTest.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2026, 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 + * @summary Test the Class-Path attribute in a JAR manifest + * @bug 8390025 + * @requires vm.cds.supports.aot.class.linking + * @library /test/lib + * @build ClassPathAttributeTest + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar ClassPathAttributeApp + * @run driver ClassPathAttributeTest + */ + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import jdk.test.lib.cds.CDSJarUtils; +import jdk.test.lib.cds.CDSJarUtils.JarOptions; +import jdk.test.lib.cds.SimpleCDSAppTester; +import jdk.test.lib.helpers.ClassFileInstaller; +import jdk.test.lib.Platform; +import jdk.test.lib.process.OutputAnalyzer; + +public class ClassPathAttributeTest { + public static void main(String[] args) throws Exception { + final String mainClass = "ClassPathAttributeApp"; + final String appJar = ClassFileInstaller.getJarPath("app.jar"); + final String emptyDir = "empty"; + final String emptyJar = "cp.jar"; + + String appAbsolutePath = Path.of(appJar).toAbsolutePath().toString().replace("\\", "/"); + final String classPathAttribute = Paths.get(appJar).toUri().toString(); + + // Create manifest using absolute path + Path manifest = Path.of("cp.mf"); + Files.writeString(manifest, + "Manifest-Version: 1.0\n" + + "Class-Path: " + classPathAttribute + "\n\n"); + Files.createDirectory(Path.of(emptyDir)); + CDSJarUtils.buildFromDirectory(emptyJar, emptyDir, JarOptions.of().setManifest(manifest.toString())); + + // Use JAR with no class files which calls the app through the class path in the + // manifest file. + SimpleCDSAppTester.of("ClassPathAttribute") + .addVmArgs("-Xlog:cds,aot,class+path=debug") + .classpath(emptyJar) + .appCommandLine(mainClass) + .setTrainingChecker((OutputAnalyzer out) -> { + out.shouldContain("ClassPathAttributeApp loaded successfully"); + // Prefix "file:" should not be in the classpath + out.shouldNotMatch("path \\[\\d+\\] = file:.*"); + out.shouldMatch("path \\[\\d+\\] = " + appAbsolutePath); + }) + .runAOTWorkflow(); + } +} + +final class ClassPathAttributeApp { + public static void main(String[] args) { + System.out.println("ClassPathAttributeApp loaded successfully"); + System.out.println("ClassPathAttributeApp.class = " + ClassPathAttributeApp.class.getResource("/ClassPathAttributeApp.class")); + System.out.println("java.class.path = " + System.getProperty("java.class.path")); + } +} From 4eb79a8998600406f3bde0811fe5c31eabc3eee4 Mon Sep 17 00:00:00 2001 From: Mohamed Issa Date: Wed, 30 Sep 2026 14:53:56 +0000 Subject: [PATCH 096/103] 8373834: Make AVX10 instructions available to floating point bytecode operations Reviewed-by: sviswanathan, sparasa --- src/hotspot/cpu/x86/macroAssembler_x86.cpp | 92 +++++++++++++--------- src/hotspot/cpu/x86/templateTable_x86.cpp | 68 ++++++++++------ 2 files changed, 96 insertions(+), 64 deletions(-) diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.cpp b/src/hotspot/cpu/x86/macroAssembler_x86.cpp index ae0e02783b22..d09813760e77 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp @@ -10347,41 +10347,53 @@ void MacroAssembler::generate_fill_avx3(BasicType type, Register to, Register va void MacroAssembler::convert_f2i(Register dst, XMMRegister src) { - Label done; - cvttss2sil(dst, src); - // Conversion instructions do not match JLS for overflow, underflow and NaN -> fixup in stub - cmpl(dst, 0x80000000); // float_sign_flip - jccb(Assembler::notEqual, done); - subptr(rsp, 8); - movflt(Address(rsp, 0), src); - call(RuntimeAddress(CAST_FROM_FN_PTR(address, StubRoutines::x86::f2i_fixup()))); - pop(dst); - bind(done); + if (VM_Version::supports_avx10_2()) { + evcvttss2sisl(dst, src); + } else { + Label done; + cvttss2sil(dst, src); + // Conversion instructions do not match JLS for overflow, underflow and NaN -> fixup in stub + cmpl(dst, 0x80000000); // float_sign_flip + jccb(Assembler::notEqual, done); + subptr(rsp, 8); + movflt(Address(rsp, 0), src); + call(RuntimeAddress(CAST_FROM_FN_PTR(address, StubRoutines::x86::f2i_fixup()))); + pop(dst); + bind(done); + } } void MacroAssembler::convert_d2i(Register dst, XMMRegister src) { - Label done; - cvttsd2sil(dst, src); - // Conversion instructions do not match JLS for overflow, underflow and NaN -> fixup in stub - cmpl(dst, 0x80000000); // float_sign_flip - jccb(Assembler::notEqual, done); - subptr(rsp, 8); - movdbl(Address(rsp, 0), src); - call(RuntimeAddress(CAST_FROM_FN_PTR(address, StubRoutines::x86::d2i_fixup()))); - pop(dst); - bind(done); + if (VM_Version::supports_avx10_2()) { + evcvttsd2sisl(dst, src); + } else { + Label done; + cvttsd2sil(dst, src); + // Conversion instructions do not match JLS for overflow, underflow and NaN -> fixup in stub + cmpl(dst, 0x80000000); // float_sign_flip + jccb(Assembler::notEqual, done); + subptr(rsp, 8); + movdbl(Address(rsp, 0), src); + call(RuntimeAddress(CAST_FROM_FN_PTR(address, StubRoutines::x86::d2i_fixup()))); + pop(dst); + bind(done); + } } void MacroAssembler::convert_f2l(Register dst, XMMRegister src) { - Label done; - cvttss2siq(dst, src); - cmp64(dst, ExternalAddress((address) StubRoutines::x86::double_sign_flip())); - jccb(Assembler::notEqual, done); - subptr(rsp, 8); - movflt(Address(rsp, 0), src); - call(RuntimeAddress(CAST_FROM_FN_PTR(address, StubRoutines::x86::f2l_fixup()))); - pop(dst); - bind(done); + if (VM_Version::supports_avx10_2()) { + evcvttss2sisq(dst, src); + } else { + Label done; + cvttss2siq(dst, src); + cmp64(dst, ExternalAddress((address) StubRoutines::x86::double_sign_flip())); + jccb(Assembler::notEqual, done); + subptr(rsp, 8); + movflt(Address(rsp, 0), src); + call(RuntimeAddress(CAST_FROM_FN_PTR(address, StubRoutines::x86::f2l_fixup()))); + pop(dst); + bind(done); + } } void MacroAssembler::round_float(Register dst, XMMRegister src, Register rtmp, Register rcx) { @@ -10459,15 +10471,19 @@ void MacroAssembler::round_double(Register dst, XMMRegister src, Register rtmp, } void MacroAssembler::convert_d2l(Register dst, XMMRegister src) { - Label done; - cvttsd2siq(dst, src); - cmp64(dst, ExternalAddress((address) StubRoutines::x86::double_sign_flip())); - jccb(Assembler::notEqual, done); - subptr(rsp, 8); - movdbl(Address(rsp, 0), src); - call(RuntimeAddress(CAST_FROM_FN_PTR(address, StubRoutines::x86::d2l_fixup()))); - pop(dst); - bind(done); + if (VM_Version::supports_avx10_2()) { + evcvttsd2sisq(dst, src); + } else { + Label done; + cvttsd2siq(dst, src); + cmp64(dst, ExternalAddress((address) StubRoutines::x86::double_sign_flip())); + jccb(Assembler::notEqual, done); + subptr(rsp, 8); + movdbl(Address(rsp, 0), src); + call(RuntimeAddress(CAST_FROM_FN_PTR(address, StubRoutines::x86::d2l_fixup()))); + pop(dst); + bind(done); + } } void MacroAssembler::cache_wb(Address line) diff --git a/src/hotspot/cpu/x86/templateTable_x86.cpp b/src/hotspot/cpu/x86/templateTable_x86.cpp index f5c4a1c1b20f..b79bd534405e 100644 --- a/src/hotspot/cpu/x86/templateTable_x86.cpp +++ b/src/hotspot/cpu/x86/templateTable_x86.cpp @@ -1655,23 +1655,31 @@ void TemplateTable::convert() { break; case Bytecodes::_f2i: { - Label L; - __ cvttss2sil(rax, xmm0); - __ cmpl(rax, 0x80000000); // NaN or overflow/underflow? - __ jcc(Assembler::notEqual, L); - __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::f2i), 1); - __ bind(L); + if (VM_Version::supports_avx10_2()) { + __ evcvttss2sisl(rax, xmm0); + } else { + Label L; + __ cvttss2sil(rax, xmm0); + __ cmpl(rax, 0x80000000); // NaN or overflow/underflow? + __ jcc(Assembler::notEqual, L); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::f2i), 1); + __ bind(L); + } } break; case Bytecodes::_f2l: { - Label L; - __ cvttss2siq(rax, xmm0); - // NaN or overflow/underflow? - __ cmp64(rax, ExternalAddress((address) &is_nan), rscratch1); - __ jcc(Assembler::notEqual, L); - __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::f2l), 1); - __ bind(L); + if (VM_Version::supports_avx10_2()) { + __ evcvttss2sisq(rax, xmm0); + } else { + Label L; + __ cvttss2siq(rax, xmm0); + // NaN or overflow/underflow? + __ cmp64(rax, ExternalAddress((address) &is_nan), rscratch1); + __ jcc(Assembler::notEqual, L); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::f2l), 1); + __ bind(L); + } } break; case Bytecodes::_f2d: @@ -1679,23 +1687,31 @@ void TemplateTable::convert() { break; case Bytecodes::_d2i: { - Label L; - __ cvttsd2sil(rax, xmm0); - __ cmpl(rax, 0x80000000); // NaN or overflow/underflow? - __ jcc(Assembler::notEqual, L); - __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::d2i), 1); - __ bind(L); + if (VM_Version::supports_avx10_2()) { + __ evcvttsd2sisl(rax, xmm0); + } else { + Label L; + __ cvttsd2sil(rax, xmm0); + __ cmpl(rax, 0x80000000); // NaN or overflow/underflow? + __ jcc(Assembler::notEqual, L); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::d2i), 1); + __ bind(L); + } } break; case Bytecodes::_d2l: { - Label L; - __ cvttsd2siq(rax, xmm0); - // NaN or overflow/underflow? - __ cmp64(rax, ExternalAddress((address) &is_nan), rscratch1); - __ jcc(Assembler::notEqual, L); - __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::d2l), 1); - __ bind(L); + if (VM_Version::supports_avx10_2()) { + __ evcvttsd2sisq(rax, xmm0); + } else { + Label L; + __ cvttsd2siq(rax, xmm0); + // NaN or overflow/underflow? + __ cmp64(rax, ExternalAddress((address) &is_nan), rscratch1); + __ jcc(Assembler::notEqual, L); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::d2l), 1); + __ bind(L); + } } break; case Bytecodes::_d2f: From e7b7beee0ba842769b9e6c30317ad4405a38a5f2 Mon Sep 17 00:00:00 2001 From: William Kemper Date: Wed, 30 Sep 2026 16:59:17 +0000 Subject: [PATCH 097/103] 8393229: GenShen: Remembered set scan should use compact-object-header aware accessors for update refs too Reviewed-by: shade, pfontanilla --- src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp index e58d68627e47..f704908e0cb5 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp @@ -859,7 +859,7 @@ class ShenandoahGenerationalUpdateHeapRefsTask : public WorkerTask { oop obj = cast_to_oop(p); objs.do_object(obj); HeapWord* prev_p = p; - p += obj->size(); + p += ShenandoahForwarding::size(obj); if (p < tams) { p = ctx->get_next_marked_addr(p, tams); // If there are no more marked objects before tams, this returns tams. Note that tams is From a34a1fed2ef0999c90e6c6b9b4f2c28a627612bb Mon Sep 17 00:00:00 2001 From: Rajan Halade Date: Wed, 30 Sep 2026 17:27:33 +0000 Subject: [PATCH 098/103] 8386371: Remove Entrust G4 root CA Reviewed-by: mullan --- .../security/validator/EntrustTLSPolicy.java | 7 +- .../share/data/cacerts/entrustrootcag4 | 43 --------- .../certification/CAInterop.java | 14 --- .../security/lib/cacerts/VerifyCACerts.java | 7 +- .../distrust/Entrust.java | 7 +- .../chains/entrust/entrustrootcag4-chain.pem | 92 ------------------- 6 files changed, 7 insertions(+), 163 deletions(-) delete mode 100644 src/java.base/share/data/cacerts/entrustrootcag4 delete mode 100644 test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/entrust/entrustrootcag4-chain.pem diff --git a/src/java.base/share/classes/sun/security/validator/EntrustTLSPolicy.java b/src/java.base/share/classes/sun/security/validator/EntrustTLSPolicy.java index f93e534f46ab..831d1088ed35 100644 --- a/src/java.base/share/classes/sun/security/validator/EntrustTLSPolicy.java +++ b/src/java.base/share/classes/sun/security/validator/EntrustTLSPolicy.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, 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 @@ -61,11 +61,6 @@ final class EntrustTLSPolicy { // OU=(c) 2009 Entrust, Inc. - for authorized use only, // OU=See www.entrust.net/legal-terms, O=Entrust, Inc., C=US "43DF5774B03E7FEF5FE40D931A7BEDF1BB2E6B42738C4E6D3841103D3AA7F339", - // cacerts alias: entrustrootcag4 - // DN: CN=Entrust Root Certification Authority - G4 - // OU=(c) 2015 Entrust, Inc. - for authorized use only, - // OU=See www.entrust.net/legal-terms, O=Entrust, Inc., C=US, - "DB3517D1F6732A2D5AB97C533EC70779EE3270A62FB4AC4238372460E6F01E88", // cacerts alias: entrust2048ca // DN: CN=Entrust.net Certification Authority (2048), // OU=(c) 1999 Entrust.net Limited, diff --git a/src/java.base/share/data/cacerts/entrustrootcag4 b/src/java.base/share/data/cacerts/entrustrootcag4 deleted file mode 100644 index 67a1475837bc..000000000000 --- a/src/java.base/share/data/cacerts/entrustrootcag4 +++ /dev/null @@ -1,43 +0,0 @@ -Owner: CN=Entrust Root Certification Authority - G4, OU="(c) 2015 Entrust, Inc. - for authorized use only", OU=See www.entrust.net/legal-terms, O="Entrust, Inc.", C=US -Issuer: CN=Entrust Root Certification Authority - G4, OU="(c) 2015 Entrust, Inc. - for authorized use only", OU=See www.entrust.net/legal-terms, O="Entrust, Inc.", C=US -Serial number: d9b5437fafa9390f000000005565ad58 -Valid from: Wed May 27 11:11:16 GMT 2015 until: Sun Dec 27 11:41:16 GMT 2037 -Signature algorithm name: SHA256withRSA -Subject Public Key Algorithm: 4096-bit RSA key -Version: 3 ------BEGIN CERTIFICATE----- -MIIGSzCCBDOgAwIBAgIRANm1Q3+vqTkPAAAAAFVlrVgwDQYJKoZIhvcNAQELBQAw -gb4xCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQL -Ex9TZWUgd3d3LmVudHJ1c3QubmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykg -MjAxNSBFbnRydXN0LCBJbmMuIC0gZm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMjAw -BgNVBAMTKUVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEc0 -MB4XDTE1MDUyNzExMTExNloXDTM3MTIyNzExNDExNlowgb4xCzAJBgNVBAYTAlVT -MRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1 -c3QubmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxNSBFbnRydXN0LCBJ -bmMuIC0gZm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMjAwBgNVBAMTKUVudHJ1c3Qg -Um9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEc0MIICIjANBgkqhkiG9w0B -AQEFAAOCAg8AMIICCgKCAgEAsewsQu7i0TD/pZJH4i3DumSXbcr3DbVZwbPLqGgZ -2K+EbTBwXX7zLtJTmeH+H17ZSK9dE43b/2MzTdMAArzE+NEGCJR5WIoV3imz/f3E -T+iq4qA7ec2/a0My3dl0ELn39GjUu9CH1apLiipvKgS1sqbHoHrmSKvS0VnM1n4j -5pds8ELl3FFLFUHtSUrJ3hCX1nbB76W1NhSXNdh4IjVS70O92yfbYVaCNNzLiGAM -C1rlLAHGVK/XqsEQe9IFWrhAnoanw5CGAlZSCXqc0ieCU0plUmr1POeo8pyvi73T -DtTUXm6Hnmo9RR3RXRv06QqsYJn7ibT/mCzPfB3pAqoEmh643IhuJbNsZvc8kPNX -wbMv9W3y+8qh+CmdRouzavbmZwe+LGcKKh9asj5XxNMhIWNlUpEbsZmOeX7m640A -2Vqq6nPopIICR5b+W45UYaPrL0swsIsjdXJ8ITzI9vF01Bx7owVV7rtNOzK+mndm -nqxpkCIHH2E6lr7lmk/MBTwoWdPBDFSoWWG9yHJM6Nyfh3+9nEg2XpWjDrk4JFX8 -dWbrAuMINClKxuMrLzOg2qOGpRKX/YAr2hRC45K9PvJdXmd0LhyIRyk0X+IyqJwl -N4y6mACXi0mWHv0liqzc2thddG5msP9E36EYxr5ILzeUePiVSj9/E15dWf10hkNj -c0kCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD -VR0OBBYEFJ84xFYjwznooHFs6FRM5Og6sb9nMA0GCSqGSIb3DQEBCwUAA4ICAQAS -5UKme4sPDORGpbZgQIeMJX6tuGguW8ZAdjwD+MlZ9POrYs4QjbRaZIxowLByQzTS -Gwv2LFPSypBLhmb8qoMi9IsabyZIrHZ3CL/FmFz0Jomee8O5ZDIBf9PD3Vht7LGr -hFV0d4QEJ1JrhkzO3bll/9bGXp+aEJlLdWr+aumXIOTkdnrG0CSqkM0gkLpHZPt/ -B7NTeLUKYvJzQ85BK4FqLoUWlFPUa19yIqtRLULVAJyZv967lDtX/Zr1hstWO1uI -AeV8KEsD+UmDfLJ/fOPtjqF/YFOOVZ1QNBIPt5d7bIdKROf1beyAN/BYGW5KaHbw -H5Lk6rWS02FREAutp9lfx1/cH6NcjKF+m7ee01ZvZl4HliDtC3T7Zk6LERXpgUl+ -b7DUUH8i119lAg2m9IUe2K4GS0qn0jFmwvjO5QimpAKWRGhXxNUzzxkvFMSUHHuk -2fCfDrGA4tGeEWSpiBE6doLlYsKA2KSD7ZPvfC+QsDJMlhVoSFLUmQjAJOgc47Ol -IQ6SwJAfzyBfyjs4x7dtOvPmRLgOMWuIjnDrnBdSqEGULoe256YSxXXfW8AKbnuk -5F6G+TaU33fD6Q3AOfF5u0aOq0NZJ7cguyPpVkAh7DE9ZapD8j3fcEThuk0mEDuY -n/PIjhs4ViFqUZPTkcpG2om3PVODLAgfi49T3f+sHw== ------END CERTIFICATE----- diff --git a/test/jdk/security/infra/java/security/cert/CertPathValidator/certification/CAInterop.java b/test/jdk/security/infra/java/security/cert/CertPathValidator/certification/CAInterop.java index 15fcb2fb04fd..32b8661379f5 100644 --- a/test/jdk/security/infra/java/security/cert/CertPathValidator/certification/CAInterop.java +++ b/test/jdk/security/infra/java/security/cert/CertPathValidator/certification/CAInterop.java @@ -190,17 +190,6 @@ * @run main/othervm/manual -Djava.security.debug=certpath CAInterop entrustrootcaec1 CRL */ -/* - * @test id=entrustrootcag4 - * @bug 8243321 - * @summary Interoperability tests with Entrust CAs - * @library /test/lib - * @build jtreg.SkippedException ValidatePathWithURL CAInterop - * @run main/othervm/manual -Djava.security.debug=certpath,ocsp CAInterop entrustrootcag4 OCSP - * @run main/othervm/manual -Djava.security.debug=certpath,ocsp -Dcom.sun.security.ocsp.useget=false CAInterop entrustrootcag4 OCSP - * @run main/othervm/manual -Djava.security.debug=certpath CAInterop entrustrootcag4 CRL - */ - /* * @test id=godaddyrootg2ca * @bug 8196141 @@ -636,9 +625,6 @@ private CATestURLs getTestURLs(String alias) { case "entrustrootcaec1" -> new CATestURLs("https://validec.entrust.net", "https://revokedec.entrust.net"); - case "entrustrootcag4" -> - new CATestURLs("https://validg4.entrust.net", - "https://revokedg4.entrust.net"); case "godaddyrootg2ca" -> new CATestURLs("https://valid.gdig2.catest.godaddy.com", diff --git a/test/jdk/sun/security/lib/cacerts/VerifyCACerts.java b/test/jdk/sun/security/lib/cacerts/VerifyCACerts.java index 82b6a6c257e5..db6d9a66f883 100644 --- a/test/jdk/sun/security/lib/cacerts/VerifyCACerts.java +++ b/test/jdk/sun/security/lib/cacerts/VerifyCACerts.java @@ -29,6 +29,7 @@ * 8243559 8225072 8258630 8259312 8256421 8225081 8225082 8225083 8245654 * 8305975 8304760 8307134 8295894 8314960 8317373 8317374 8318759 8319187 * 8321408 8316138 8341057 8303770 8350498 8359170 8361212 8372351 8387123 + * 8386371 * @summary Check root CA entries in cacerts file */ import java.io.ByteArrayInputStream; @@ -47,12 +48,12 @@ public class VerifyCACerts { + File.separator + "security" + File.separator + "cacerts"; // The numbers of certs now. - private static final int COUNT = 110; + private static final int COUNT = 109; // SHA-256 of cacerts, can be generated with // shasum -a 256 cacerts | sed -e 's/../&:/g' | tr '[:lower:]' '[:upper:]' | cut -c1-95 private static final String CHECKSUM - = "AA:C2:64:41:28:06:1F:83:92:54:7C:DD:95:82:61:4C:8F:FA:09:7B:17:64:A7:A8:7C:A9:F6:27:25:95:2D:BB"; + = "BB:0F:A9:B7:21:8E:8E:66:95:2B:1A:85:2F:6A:EC:F0:81:69:09:A9:56:70:F3:E4:A8:B9:AD:0D:BE:43:69:11"; // Hex formatter to upper case with ":" delimiter private static final HexFormat HEX = HexFormat.ofDelimiter(":").withUpperCase(); @@ -219,8 +220,6 @@ public class VerifyCACerts { "18:CE:6C:FE:7B:F1:4E:60:B2:E3:47:B8:DF:E8:68:CB:31:D0:2E:BB:3A:DA:27:15:69:F5:03:43:B4:6D:B3:A4"); put("amazonrootca4 [jdk]", "E3:5D:28:41:9E:D0:20:25:CF:A6:90:38:CD:62:39:62:45:8D:A5:C6:95:FB:DE:A3:C2:2B:0B:FB:25:89:70:92"); - put("entrustrootcag4 [jdk]", - "DB:35:17:D1:F6:73:2A:2D:5A:B9:7C:53:3E:C7:07:79:EE:32:70:A6:2F:B4:AC:42:38:37:24:60:E6:F0:1E:88"); put("sslrootrsaca [jdk]", "85:66:6A:56:2E:E0:BE:5C:E9:25:C1:D8:89:0A:6F:76:A8:7E:C1:6D:4D:7D:5F:29:EA:74:19:CF:20:12:3B:69"); put("sslrootevrsaca [jdk]", diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/Entrust.java b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/Entrust.java index 0e7617d1b9cc..9fd4b0038d1e 100644 --- a/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/Entrust.java +++ b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/Entrust.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, 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 @@ -28,7 +28,7 @@ /** * @test - * @bug 8337664 8341059 8361212 + * @bug 8337664 8341059 8361212 8386371 * @summary Check that TLS Server certificates chaining back to distrusted * Entrust roots are invalid * @library /test/lib @@ -46,8 +46,7 @@ public class Entrust { // Each of the roots have a test certificate chain stored in a file // named "-chain.pem". private static final String[] ROOTS_TO_TEST = new String[]{ - "entrustevca", "entrustrootcaec1", "entrustrootcag2", - "entrustrootcag4", "entrust2048ca"}; + "entrustevca", "entrustrootcaec1", "entrustrootcag2", "entrust2048ca"}; // Date when the restrictions take effect private static final ZonedDateTime DISTRUST_DATE = diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/entrust/entrustrootcag4-chain.pem b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/entrust/entrustrootcag4-chain.pem deleted file mode 100644 index e649abf28b37..000000000000 --- a/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/entrust/entrustrootcag4-chain.pem +++ /dev/null @@ -1,92 +0,0 @@ -Root Certificate: - Version: 3 (0x2) - Serial Number: - d9:b5:43:7f:af:a9:39:0f:00:00:00:00:55:65:ad:58 - Signature Algorithm: sha256WithRSAEncryption - Issuer: C=US, O=Entrust, Inc., OU=See www.entrust.net/legal-terms, OU=(c) 2015 Entrust, Inc. - for authorized use only, CN=Entrust Root Certification Authority - G4 - Validity - Not Before: May 27 11:11:16 2015 GMT - Not After : Dec 27 11:41:16 2037 GMT - ------BEGIN CERTIFICATE----- -MIIIIzCCBwugAwIBAgIQDD4I8FgD7+DVcBLMBwa39jANBgkqhkiG9w0BAQsFADCB -ujELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsT -H1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAy -MDE0IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEuMCwG -A1UEAxMlRW50cnVzdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEwxTjAeFw0y -NDA2MjgyMTQzNTRaFw0yNTA3MjgyMTQzNTNaMIHLMQswCQYDVQQGEwJDQTEQMA4G -A1UECBMHT250YXJpbzEPMA0GA1UEBxMGT3R0YXdhMRMwEQYLKwYBBAGCNzwCAQMT -AkNBMRgwFgYLKwYBBAGCNzwCAQITB09udGFyaW8xGDAWBgNVBAoTD0VudHJ1c3Qg -TGltaXRlZDEdMBsGA1UEDxMUUHJpdmF0ZSBPcmdhbml6YXRpb24xEzARBgNVBAUT -CjEwMDA0OTI4NzkxHDAaBgNVBAMTE3ZhbGlkZzQuZW50cnVzdC5uZXQwggIiMA0G -CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCfUHGdeme0jraIiUzPYtuX1G9rlCU1 -eKDqDbsgp7VIS7rI/VgbsS7oKnE6KHP+qGrXRhYdvFLFDa+REY6fVOWkLuTXhVLb -5C7ym2pi0OUMKvrGtDLUxlHiEAmkmjPDl6TLMTDrLgWOLFMRzyeTcxnZtMrxUnAf -yzSPlqm1bkN/oRp2EOiXvuSbci8UA0QswV6g8EUbRB0qyv6OophoaQYo/+KRwTJT -k6S8YDsEJnlDb8tjEhfIUjp2Md5ThBxf5Ib29aXebZ5HFh2x5VPrzOwDUPk0fVNM -pWFfiX79RW6w5Vei5qtretLohbw6b5aJmaJ1LweAEkIlhy5eUuuG6v8Efm8JSAle -eKMtflTigmayaWMVCd2GeB6LajcflAw7BUU2brRMJwMpaeXXhL/mVpjbev/5TtVD -+H9IlW3PMyQnUJc0YuUVmdi1eOM5qoQaQE4BDPHz2G41eDgT8J9Gb1FX5mT+9l2I -iJD47pwcBIw5tHCn2nuz1+8CDuYpfH2+t2LPFHVI15h1scGotZvzUJ5TzPdQqZI7 -K2LTL49Zs2HsObrGr07Vj28WyzkjIfTrVSV/29hgz1zVjUa0uyTeOzrc3VIg7NTv -RoMTTYuUeUoMSmFQ8z9CSGh7cxFlrhGjFO+66++JFNwakAEp7kS5c2qTLaapY9dM -8UMIr5951z994QIDAQABo4IDEDCCAwwwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQU -/EjjpmMa/SepMqPlglXS5AbGcScwHwYDVR0jBBgwFoAU7kfRhXHx/S23P7s+Y1h3 -F0lADpUwaAYIKwYBBQUHAQEEXDBaMCMGCCsGAQUFBzABhhdodHRwOi8vb2NzcC5l -bnRydXN0Lm5ldDAzBggrBgEFBQcwAoYnaHR0cDovL2FpYS5lbnRydXN0Lm5ldC9s -MW4tY2hhaW4yNTYuY2VyMDMGA1UdHwQsMCowKKAmoCSGImh0dHA6Ly9jcmwuZW50 -cnVzdC5uZXQvbGV2ZWwxbi5jcmwwHgYDVR0RBBcwFYITdmFsaWRnNC5lbnRydXN0 -Lm5ldDAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUF -BwMBMEsGA1UdIAREMEIwBwYFZ4EMAQEwNwYKYIZIAYb6bAoBAjApMCcGCCsGAQUF -BwIBFhtodHRwczovL3d3dy5lbnRydXN0Lm5ldC9ycGEwggF/BgorBgEEAdZ5AgQC -BIIBbwSCAWsBaQB2ABLxTjS9U3JMhAYZw48/ehP457Vih4icbTAFhOvlhiY6AAAB -kGDOC9YAAAQDAEcwRQIgWhFWhf2sBQ3ufMH0yubwLDt+3f/b5rScs09o1YEjg6MC -IQDpkgEMWBAM+NV2aCnC8QH+RH6xBqhPPt6JZTm3W+vHkwB3ABoE/0nQVB1Ar/ag -w7/x2MRnL07s7iNAaJhrF0Au3Il9AAABkGDODBQAAAQDAEgwRgIhAOgp+oas+jBr -9wOBo0QDdVQGmP8KJupfRf/MDKO+kSRjAiEA9JnEHTbFHre2TS9habVJA/3jM/t5 -CKtixwQqdpLXQUAAdgAN4fIwK9MNwUBiEgnqVS78R3R8sdfpMO8OQh60fk6qNAAA -AZBgzgwVAAAEAwBHMEUCIBOYI8rl87VepcPQlaGh6AbKhKw1UlbxIf7etR/d2M47 -AiEAkFXOVvzkP6kX/z1yRneYn0mlPbDvAFLsSDghl/gkdtYwDQYJKoZIhvcNAQEL -BQADggEBAJovgoheNHFBUpnodfOiKtpRo8AE6dLuOX1H2uRHiDg0Gza0/w95KkEE -BqjKmJIbJrs2TQJnkM0LjaubHn1TP4XC40qieMXB4ylJzC5FWDZBqMHZmLTvVY01 -irBMyub0On8d1BlEquD2r3KHQFnyUvi/uxzbNJOVbNJYglKhTI+UfcXk7zpHmNG+ -+SbBkpJkuqQ9ujG1K25FRa/01j1p4ZlDrJ3KCT7fDEf10TN0u5VX6moVT9cRVR2U -gX16BV8m/hoJVTD0fBCKIKjtklS//b+Jr49uxWFulrDwlRKyDWmBXLnqsZvpCobi -deDsWiUkcvd+DjNgpDTEHCTrXXjd8tU= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIGMjCCBBqgAwIBAgIRAKvsd/8bQQwHAAAAAFVl2AUwDQYJKoZIhvcNAQELBQAw -gb4xCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQL -Ex9TZWUgd3d3LmVudHJ1c3QubmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykg -MjAxNSBFbnRydXN0LCBJbmMuIC0gZm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMjAw -BgNVBAMTKUVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEc0 -MB4XDTE3MTEyMjIwMDQyMFoXDTMwMTIyMjIwMzQyMFowgboxCzAJBgNVBAYTAlVT -MRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1 -c3QubmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxNCBFbnRydXN0LCBJ -bmMuIC0gZm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxLjAsBgNVBAMTJUVudHJ1c3Qg -Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBMMU4wggEiMA0GCSqGSIb3DQEBAQUA -A4IBDwAwggEKAoIBAQDcSG+caYQ4xcvf+dt8bgCEHorO0g5j0H1NOtQzRXgUoG8y -QuRbJX9swyKqQZbsc18YvTV8OKA/uSNE46Jvq47TFPojWWTVLbNDqpM07e4EFYKs -A9NFzAUngijnf3ivnXA6iNPAMXaEhXmhY/YFjk8NoM7Y1PFsA0oj5hamKQ06iO/j -gvBScLmnQ1ju9Qj9IGIg18UL5AJNw0frspLUQBYVrLGaqAy5Nl2BUJKaZ4vnSLvP -nk6YrB15mo1phHae10Ba4fx7R3z8IZ/hby4OXTy/KZpu107VEQPAwTuDK8ZXxB5y -0DSzi4vaw27aLrUsq4aFqUo03gEfC31vWW76TNkFAgMBAAGjggErMIIBJzAOBgNV -HQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHSUEFjAUBggrBgEF -BQcDAQYIKwYBBQUHAwIwOwYDVR0gBDQwMjAwBgRVHSAAMCgwJgYIKwYBBQUHAgEW -Gmh0dHA6Ly93d3cuZW50cnVzdC5uZXQvcnBhMDMGCCsGAQUFBwEBBCcwJTAjBggr -BgEFBQcwAYYXaHR0cDovL29jc3AuZW50cnVzdC5uZXQwMAYDVR0fBCkwJzAloCOg -IYYfaHR0cDovL2NybC5lbnRydXN0Lm5ldC9nNGNhLmNybDAdBgNVHQ4EFgQU7kfR -hXHx/S23P7s+Y1h3F0lADpUwHwYDVR0jBBgwFoAUnzjEViPDOeigcWzoVEzk6Dqx -v2cwDQYJKoZIhvcNAQELBQADggIBACMeFFgsWmC7h6D1v8DJUkOpm/m5UhVhO0hb -pQMQKMhKkl744Y9SWG4WNmpQy743TTciEJPZFhc7ke2R6VmK8ZJUqro2awOw1RWZ -OtHla59Btf1NQd41vOVdU+qFhs8lFfXg9sK7YHTrfxHtMXLoGnkkamK3xJgn7sXa -/zUvUDBTpDCXcpO9SyHoKIQswmkIPpRyIdPF4biRdR3N+9MYmlfqN/Nk3OEZ73xZ -AUZP6Gu+f9cEiHTA8NdYHCPLJWyFnIHWK+QuTFEnKYnOYxCeroLBNOO64e8JWZ39 -kZ22BBXhHzqOCCczS7JOJTRF+JgvWuxbFwRstj8qf3fE+JndWmq2FC4hTHtpuK5K -ENuiRm5gdkXfsXmB+qB6y5gaajiTIMscGIcZIKTe2YdKrLoicvEz8k+loM7favik -vzFioTNTDHYGx3mkfElBE7ycY8n+jZE3QBBv33k28MeQi7XNgEaMc4tYwoZIdE9A -xVccXTzEQzka82dOkRB1dU0XZId9XAWv+CtNc2TjF6Wgx2seA/c6H8S0IfgQBIV2 -8iN2wZns2QFdawkdy3hMUqPnA++kuGhLW3GemsIY5dP/WxY8rd+OfLb/Ks9T1pCd -28t7PQRcQsgkYmouzrOW9ASBvYqLLdhl4y+fFXff8RkPIKMNoYP06WJvRKmky9R/ -41/nXRas ------END CERTIFICATE----- From 39c97bde1e530eae33e13582a920b35f81f8d671 Mon Sep 17 00:00:00 2001 From: Mark Powers Date: Wed, 30 Sep 2026 18:47:51 +0000 Subject: [PATCH 099/103] 8392782: Integer overflow in sun/security/util/BitArray.java constructor causes NegativeArraySizeException Reviewed-by: ascarpino --- .../classes/sun/security/util/BitArray.java | 19 ++++++--- .../BitArray/BitArrayIntegerOverflow.java | 42 +++++++++++++++++++ 2 files changed, 55 insertions(+), 6 deletions(-) create mode 100644 test/jdk/sun/security/util/BitArray/BitArrayIntegerOverflow.java diff --git a/src/java.base/share/classes/sun/security/util/BitArray.java b/src/java.base/share/classes/sun/security/util/BitArray.java index 5f18f17d10f4..aa64ee36fd26 100644 --- a/src/java.base/share/classes/sun/security/util/BitArray.java +++ b/src/java.base/share/classes/sun/security/util/BitArray.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -52,6 +52,11 @@ private static int position(int idx) { // bits big-endian in each unit return 1 << (BITS_PER_UNIT - 1 - (idx % BITS_PER_UNIT)); } + private static int byteLen(int bitLen) { + return bitLen / BITS_PER_UNIT + + (bitLen % BITS_PER_UNIT == 0 ? 0 : 1); + } + /** * Creates a BitArray of the specified size, initialized to zeros. */ @@ -62,7 +67,7 @@ public BitArray(int length) throws IllegalArgumentException { this.length = length; - repn = new byte[(length + BITS_PER_UNIT - 1)/BITS_PER_UNIT]; + repn = new byte[byteLen(length)]; } /** @@ -88,15 +93,17 @@ public BitArray(int length, byte[] a, int ofs) if (length < 0) { throw new IllegalArgumentException("Negative length for BitArray"); } - if ((a.length - ofs) * BITS_PER_UNIT < length) { + int repLength = byteLen(length); + + if (a.length - ofs < repLength) { throw new IllegalArgumentException ("Byte array too short to represent " + length + "-bit array"); } this.length = length; - int repLength = ((length + BITS_PER_UNIT - 1)/BITS_PER_UNIT); - int unusedBits = repLength*BITS_PER_UNIT - length; + int unusedBits = (BITS_PER_UNIT - length % BITS_PER_UNIT) + % BITS_PER_UNIT; byte bitMask = (byte) (0xFF << unusedBits); /* @@ -118,7 +125,7 @@ public BitArray(int length, byte[] a, int ofs) @SuppressWarnings("this-escape") public BitArray(boolean[] bits) { length = bits.length; - repn = new byte[(length + 7)/8]; + repn = new byte[byteLen(length)]; for (int i=0; i < length; i++) { set(i, bits[i]); diff --git a/test/jdk/sun/security/util/BitArray/BitArrayIntegerOverflow.java b/test/jdk/sun/security/util/BitArray/BitArrayIntegerOverflow.java new file mode 100644 index 000000000000..cce47eff4b37 --- /dev/null +++ b/test/jdk/sun/security/util/BitArray/BitArrayIntegerOverflow.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2026, 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 8392782 + * @summary Integer overflow in constructor causes NegativeArraySizeException + * @library /test/lib + * @modules java.base/sun.security.util + * @run main/othervm -Xmx64m BitArrayIntegerOverflow + */ + +import static jdk.test.lib.Asserts.assertThrows; + +import sun.security.util.BitArray; + +public class BitArrayIntegerOverflow { + public static void main(String[] args) { + assertThrows(OutOfMemoryError.class, + () -> new BitArray(Integer.MAX_VALUE - 6)); + } +} From a0500e456880a45b2b395c3842a23943e0c58ac3 Mon Sep 17 00:00:00 2001 From: Arno Zeller Date: Wed, 30 Sep 2026 18:59:07 +0000 Subject: [PATCH 100/103] 8392622: UnixNativeDispatcher.c: improve errno handling for getpw*_r / getgr*_r functions Reviewed-by: alanb, clanger --- .../native/libnio/fs/UnixNativeDispatcher.c | 132 ++++++++++++------ 1 file changed, 88 insertions(+), 44 deletions(-) diff --git a/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c b/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c index 80befb68f893..bf1f0afc3f85 100644 --- a/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c +++ b/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c @@ -1206,28 +1206,44 @@ Java_sun_nio_fs_UnixNativeDispatcher_getpwuid(JNIEnv* env, jclass this, jint uid { jbyteArray result = NULL; int buflen; - char* pwbuf; + int retry; - /* allocate buffer for password record */ + /* initial size of buffer for password record */ buflen = (int)sysconf(_SC_GETPW_R_SIZE_MAX); if (buflen == -1) buflen = ENT_BUF_SIZE; - pwbuf = (char*)malloc(buflen); - if (pwbuf == NULL) { - JNU_ThrowOutOfMemoryError(env, "native heap"); - } else { + + do { struct passwd pwent; struct passwd* p = NULL; int res = 0; - errno = 0; - RESTARTABLE(getpwuid_r((uid_t)uid, &pwent, pwbuf, (size_t)buflen, &p), res); + char* pwbuf = (char*)malloc(buflen); + if (pwbuf == NULL) { + JNU_ThrowOutOfMemoryError(env, "native heap"); + return NULL; + } + errno = 0; + res = getpwuid_r((uid_t)uid, &pwent, pwbuf, (size_t)buflen, &p); +#ifdef _AIX + if (res < 0) + res = errno; +#endif + retry = 0; if (res != 0 || p == NULL || p->pw_name == NULL || *(p->pw_name) == '\0') { /* not found or error */ - if (errno == 0) - errno = ENOENT; - throwUnixException(env, errno); + if (res == EINTR) { + /* interrupted - repeat the call */ + retry = 1; + } else if (res == ERANGE) { + /* insufficient buffer size so need larger buffer */ + buflen += ENT_BUF_SIZE; + retry = 1; + } else { + /* lookup by uid: not found is treated as an error */ + throwUnixException(env, res != 0 ? res : ENOENT); + } } else { jsize len = strlen(p->pw_name); result = (*env)->NewByteArray(env, len); @@ -1235,8 +1251,10 @@ Java_sun_nio_fs_UnixNativeDispatcher_getpwuid(JNIEnv* env, jclass this, jint uid (*env)->SetByteArrayRegion(env, result, 0, len, (jbyte*)(p->pw_name)); } } + free(pwbuf); - } + + } while (retry); return result; } @@ -1266,19 +1284,24 @@ Java_sun_nio_fs_UnixNativeDispatcher_getgrgid(JNIEnv* env, jclass this, jint gid } errno = 0; - RESTARTABLE(getgrgid_r((gid_t)gid, &grent, grbuf, (size_t)buflen, &g), res); - + res = getgrgid_r((gid_t)gid, &grent, grbuf, (size_t)buflen, &g); +#ifdef _AIX + if (res < 0) + res = errno; +#endif retry = 0; if (res != 0 || g == NULL || g->gr_name == NULL || *(g->gr_name) == '\0') { /* not found or error */ - if (errno == ERANGE) { + if (res == EINTR) { + /* interrupted - repeat the call */ + retry = 1; + } else if (res == ERANGE) { /* insufficient buffer size so need larger buffer */ buflen += ENT_BUF_SIZE; retry = 1; } else { - if (errno == 0) - errno = ENOENT; - throwUnixException(env, errno); + /* lookup by gid: not found is treated as an error */ + throwUnixException(env, res != 0 ? res : ENOENT); } } else { jsize len = strlen(g->gr_name); @@ -1300,37 +1323,54 @@ Java_sun_nio_fs_UnixNativeDispatcher_getpwnam0(JNIEnv* env, jclass this, jlong nameAddress) { jint uid = -1; - int buflen; - char* pwbuf; + int buflen, retry; - /* allocate buffer for password record */ + /* initial size of buffer for password record */ buflen = (int)sysconf(_SC_GETPW_R_SIZE_MAX); if (buflen == -1) buflen = ENT_BUF_SIZE; - pwbuf = (char*)malloc(buflen); - if (pwbuf == NULL) { - JNU_ThrowOutOfMemoryError(env, "native heap"); - } else { + + do { struct passwd pwent; struct passwd* p = NULL; int res = 0; + char* pwbuf; const char* name = (const char*)jlong_to_ptr(nameAddress); - errno = 0; - RESTARTABLE(getpwnam_r(name, &pwent, pwbuf, (size_t)buflen, &p), res); + pwbuf = (char*)malloc(buflen); + if (pwbuf == NULL) { + JNU_ThrowOutOfMemoryError(env, "native heap"); + return -1; + } + errno = 0; + res = getpwnam_r(name, &pwent, pwbuf, (size_t)buflen, &p); +#ifdef _AIX + if (res < 0) + res = errno; +#endif + retry = 0; if (res != 0 || p == NULL || p->pw_name == NULL || *(p->pw_name) == '\0') { /* not found or error */ - if (errno != 0 && errno != ENOENT && errno != ESRCH && - errno != EBADF && errno != EPERM) - { - throwUnixException(env, errno); + if (res == EINTR) { + /* interrupted - repeat the call */ + retry = 1; + } else if (res == ERANGE) { + /* insufficient buffer size so need larger buffer */ + buflen += ENT_BUF_SIZE; + retry = 1; + } else if (res != 0 && res != ENOENT && res != ESRCH && + res != EBADF && res != EPERM) { + throwUnixException(env, res); } + /* res == 0 or a tolerated not-found code: leave uid == -1 */ } else { uid = p->pw_uid; } + free(pwbuf); - } + + } while (retry); return uid; } @@ -1361,22 +1401,26 @@ Java_sun_nio_fs_UnixNativeDispatcher_getgrnam0(JNIEnv* env, jclass this, } errno = 0; - RESTARTABLE(getgrnam_r(name, &grent, grbuf, (size_t)buflen, &g), res); - + res = getgrnam_r(name, &grent, grbuf, (size_t)buflen, &g); +#ifdef _AIX + if (res < 0) + res = errno; +#endif retry = 0; if (res != 0 || g == NULL || g->gr_name == NULL || *(g->gr_name) == '\0') { /* not found or error */ - if (errno != 0 && errno != ENOENT && errno != ESRCH && - errno != EBADF && errno != EPERM) - { - if (errno == ERANGE) { - /* insufficient buffer size so need larger buffer */ - buflen += ENT_BUF_SIZE; - retry = 1; - } else { - throwUnixException(env, errno); - } + if (res == EINTR) { + /* interrupted - repeat the call */ + retry = 1; + } else if (res == ERANGE) { + /* insufficient buffer size so need larger buffer */ + buflen += ENT_BUF_SIZE; + retry = 1; + } else if (res != 0 && res != ENOENT && res != ESRCH && + res != EBADF && res != EPERM) { + throwUnixException(env, res); } + /* res == 0 or a tolerated not-found code: leave gid == -1 */ } else { gid = g->gr_gid; } From f68606021f3e07889f5209931df6ff3c17210bfa Mon Sep 17 00:00:00 2001 From: Dingli Zhang Date: Thu, 1 Oct 2026 02:05:44 +0000 Subject: [PATCH 101/103] 8393234: [Valhalla] AArch64: C1-only VM asserts in gen_c2i_adapter with --enable-preview Reviewed-by: thartmann, aph --- src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp b/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp index a43210c69447..bac01aecdedc 100644 --- a/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp @@ -645,7 +645,7 @@ static void gen_c2i_adapter(MacroAssembler *masm, // compiled code so we may not have buffers to back the value // objects. Allocate the buffers here with a runtime call for // the value arguments that needs a buffer. - RegisterSaver reg_save(true /* save_vectors */); + RegisterSaver reg_save(false /* save_vectors */); OopMap* map = reg_save.save_live_registers(masm, 0, &frame_size_in_words); frame_complete = __ offset(); From 5683a37abcece458bf3f4cdbbb88e37daaf65bdc Mon Sep 17 00:00:00 2001 From: Gui Cao Date: Thu, 1 Oct 2026 04:49:53 +0000 Subject: [PATCH 102/103] 8393054: RISC-V: TestLoadN2PUnscaled fails with user-specified heap options Reviewed-by: gli, fyang --- .../hotspot/jtreg/compiler/c2/riscv64/TestLoadN2PUnscaled.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/compiler/c2/riscv64/TestLoadN2PUnscaled.java b/test/hotspot/jtreg/compiler/c2/riscv64/TestLoadN2PUnscaled.java index 07939e9e75a0..36f489fa0b49 100644 --- a/test/hotspot/jtreg/compiler/c2/riscv64/TestLoadN2PUnscaled.java +++ b/test/hotspot/jtreg/compiler/c2/riscv64/TestLoadN2PUnscaled.java @@ -23,7 +23,7 @@ /** * @test - * @bug 8389895 + * @bug 8389895 8393054 * @summary LoadN followed by DecodeN is matched into a single lwu when * compressed oops are unscaled. * @library /test/lib / @@ -45,6 +45,7 @@ static class Link { public static void main(String[] args) { TestFramework framework = new TestFramework(); + framework.addFlags("-Xms64m", "-XX:HeapBaseMinAddress=1g"); framework.addScenarios( // A small heap is placed below 4GB, so compressed oops are // unscaled and decoding is a no-op zero-extension. From 10a1f1245c361554e29d4a0440b9e411ef1bc9ff Mon Sep 17 00:00:00 2001 From: Gui Cao Date: Thu, 1 Oct 2026 05:02:42 +0000 Subject: [PATCH 103/103] 8393051: RISC-V: Check SATP mode when loading the AOT code cache Reviewed-by: ayang, gli, dzhang, fyang --- src/hotspot/share/code/aotCodeCache.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/hotspot/share/code/aotCodeCache.hpp b/src/hotspot/share/code/aotCodeCache.hpp index a0ebb8145ebf..4cfa0064ff0e 100644 --- a/src/hotspot/share/code/aotCodeCache.hpp +++ b/src/hotspot/share/code/aotCodeCache.hpp @@ -360,6 +360,7 @@ class AOTStubData : public StackObj { do_var(bool, UseCtxFencei) /* method entry barrier stub */ \ do_var(bool, UseSecondarySupersCache) /* secondary supers cache in nmethods */ \ do_var(bool, UseZabha) /* narrow cmpxchg selection in nmethods */ \ + do_fun(int, SatpMode, (int)VM_Version::satp_mode.value()) \ do_fun(int, RVZicbozBlockSize, (int)VM_Version::zicboz_block_size.value()) \ // END #else