From bea2d48696ee2c213e475ca3aa3aa9c412b91089 Mon Sep 17 00:00:00 2001 From: Michal Sobierski Date: Mon, 23 Oct 2023 14:31:12 +0000 Subject: [PATCH 01/28] 8312475: org.jline.util.PumpReader signed byte problem Reviewed-by: shade, jlahoda --- .../share/classes/jdk/internal/org/jline/utils/PumpReader.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/PumpReader.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/PumpReader.java index a6894e7672ca3..e80a18b6f3c23 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/PumpReader.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/PumpReader.java @@ -414,7 +414,7 @@ public int read() throws IOException { return EOF; } - return buffer.get(); + return buffer.get() & 0xFF; } private boolean readUsingBuffer() throws IOException { From d888b26783cbe9b2bf0a23ae728176f2eda6b90b Mon Sep 17 00:00:00 2001 From: Ramkumar Sunderbabu Date: Mon, 23 Oct 2023 15:10:22 +0000 Subject: [PATCH 02/28] 8318071: IgnoreUnrecognizedVMOptions flag still causes failure in ArchiveHeapTestClass Reviewed-by: dholmes, shade --- .../cacheObject/ArchiveHeapTestClass.java | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchiveHeapTestClass.java b/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchiveHeapTestClass.java index f82f1a02b59f1..f597556944797 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchiveHeapTestClass.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchiveHeapTestClass.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,7 +26,7 @@ * @test * @bug 8214781 8293187 * @summary Test for the -XX:ArchiveHeapTestClass flag - * @requires vm.cds.write.archived.java.heap + * @requires vm.debug == true & vm.cds.write.archived.java.heap * @modules java.base/sun.invoke.util java.logging * @library /test/jdk/lib/testlibrary /test/lib * /test/hotspot/jtreg/runtime/cds/appcds @@ -61,11 +61,7 @@ public class ArchiveHeapTestClass { static final String ARCHIVE_TEST_FIELD_NAME = "archivedObjects"; public static void main(String[] args) throws Exception { - if (Platform.isDebugBuild()) { - testDebugBuild(); - } else { - testProductBuild(); - } + testDebugBuild(); } static OutputAnalyzer dumpHelloOnly(String... extraOpts) throws Exception { @@ -169,13 +165,6 @@ static void testDebugBuild() throws Exception { mustSucceed(output); } } - - static void testProductBuild() throws Exception { - OutputAnalyzer output; - - output = dumpHelloOnly("-XX:-IgnoreUnrecognizedVMOptions", "-XX:ArchiveHeapTestClass=NoSuchClass"); - mustFail(output, "VM option 'ArchiveHeapTestClass' is develop and is available only in debug version of VM."); - } } class CDSTestClassA { From c1aeac79ba4c5b100f05ccd4f014326e46e9520a Mon Sep 17 00:00:00 2001 From: Thomas Stuefe Date: Mon, 23 Oct 2023 15:54:04 +0000 Subject: [PATCH 03/28] 8318445: More broken bailout chains in C2 Reviewed-by: kvn, epeter --- src/hotspot/share/opto/compile.cpp | 10 +++++++++- src/hotspot/share/opto/loopTransform.cpp | 10 +++++++--- src/hotspot/share/opto/loopopts.cpp | 1 + src/hotspot/share/opto/matcher.cpp | 1 + 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index 1e353a90c97ad..c66c32fe97341 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -1854,6 +1854,7 @@ void Compile::process_for_post_loop_opts_igvn(PhaseIterGVN& igvn) { igvn._worklist.push(n); } igvn.optimize(); + if (failing()) return; assert(_for_post_loop_igvn.length() == 0, "no more delayed nodes allowed"); assert(C->parse_predicate_count() == 0, "all parse predicates should have been removed now"); @@ -2069,6 +2070,7 @@ void Compile::inline_incrementally_cleanup(PhaseIterGVN& igvn) { TracePhase tp("incrementalInline_igvn", &timers[_t_incrInline_igvn]); igvn.reset_from_gvn(initial_gvn()); igvn.optimize(); + if (failing()) return; } print_method(PHASE_INCREMENTAL_INLINE_CLEANUP, 3); } @@ -2301,6 +2303,7 @@ void Compile::Optimize() { } igvn.reset_from_gvn(initial_gvn()); igvn.optimize(); + if (failing()) return; } // Now that all inlining is over and no PhaseRemoveUseless will run, cut edge from root to loop @@ -2330,7 +2333,7 @@ void Compile::Optimize() { igvn.optimize(); print_method(PHASE_ITER_GVN_AFTER_EA, 2); - if (failing()) return; + if (failing()) return; if (congraph() != nullptr && macro_count() > 0) { TracePhase tp("macroEliminate", &timers[_t_macroEliminate]); @@ -2340,6 +2343,8 @@ void Compile::Optimize() { igvn.set_delay_transform(false); igvn.optimize(); + if (failing()) return; + print_method(PHASE_ITER_GVN_AFTER_ELIMINATION, 2); } @@ -2449,6 +2454,7 @@ void Compile::Optimize() { if (C->max_vector_size() > 0) { C->optimize_logic_cones(igvn); igvn.optimize(); + if (failing()) return; } DEBUG_ONLY( _modified_nodes = nullptr; ) @@ -4366,6 +4372,7 @@ Compile::TracePhase::TracePhase(const char* name, elapsedTimer* accumulator) } Compile::TracePhase::~TracePhase() { + if (_compile->failing()) return; #ifdef ASSERT if (PrintIdealNodeCount) { tty->print_cr("phase name='%s' nodes='%d' live='%d' live_graph_walk='%d'", @@ -5083,6 +5090,7 @@ void Compile::sort_macro_nodes() { } void Compile::print_method(CompilerPhaseType cpt, int level, Node* n) { + if (failing()) { return; } EventCompilerPhase event; if (event.should_commit()) { CompilerEvent::PhaseEvent::post(event, C->_latest_stage_start_counter, cpt, C->_compile_id, level); diff --git a/src/hotspot/share/opto/loopTransform.cpp b/src/hotspot/share/opto/loopTransform.cpp index d299e1595376a..22386b144c223 100644 --- a/src/hotspot/share/opto/loopTransform.cpp +++ b/src/hotspot/share/opto/loopTransform.cpp @@ -3605,9 +3605,13 @@ bool IdealLoopTree::iteration_split_impl(PhaseIdealLoop *phase, Node_List &old_n // Non-counted loops may be peeled; exactly 1 iteration is peeled. // This removes loop-invariant tests (usually null checks). if (!_head->is_CountedLoop()) { // Non-counted loop - if (PartialPeelLoop && phase->partial_peel(this, old_new)) { - // Partial peel succeeded so terminate this round of loop opts - return false; + if (PartialPeelLoop) { + bool rc = phase->partial_peel(this, old_new); + if (Compile::current()->failing()) { return false; } + if (rc) { + // Partial peel succeeded so terminate this round of loop opts + return false; + } } if (policy_peeling(phase)) { // Should we peel? if (PrintOpto) { tty->print_cr("should_peel"); } diff --git a/src/hotspot/share/opto/loopopts.cpp b/src/hotspot/share/opto/loopopts.cpp index fa2f5d84201fc..0a197dd413bfc 100644 --- a/src/hotspot/share/opto/loopopts.cpp +++ b/src/hotspot/share/opto/loopopts.cpp @@ -3685,6 +3685,7 @@ bool PhaseIdealLoop::partial_peel( IdealLoopTree *loop, Node_List &old_new ) { // and not a CMove (Matcher expects only bool->cmove). if (n->in(0) == nullptr && !n->is_Load() && !n->is_CMove()) { int new_clones = clone_for_use_outside_loop(loop, n, worklist); + if (C->failing()) return false; if (new_clones == -1) { too_many_clones = true; break; diff --git a/src/hotspot/share/opto/matcher.cpp b/src/hotspot/share/opto/matcher.cpp index b527a7657b240..9ed0ca624105f 100644 --- a/src/hotspot/share/opto/matcher.cpp +++ b/src/hotspot/share/opto/matcher.cpp @@ -361,6 +361,7 @@ void Matcher::match( ) { C->set_cached_top_node(n); if (!C->failing()) { Node* xroot = xform( C->root(), 1 ); + if (C->failing()) return; if (xroot == nullptr) { Matcher::soft_match_failure(); // recursive matching process failed assert(false, "instruction match failed"); From 69c0ae23a323dd547f53234848f32ed9ba41792b Mon Sep 17 00:00:00 2001 From: Erik Gahlin Date: Mon, 23 Oct 2023 16:00:14 +0000 Subject: [PATCH 04/28] 8318124: JFR: Rewrite instrumentation to use Class-File API Reviewed-by: mgronlun --- src/java.base/share/classes/module-info.java | 5 + .../jdk/jfr/consumer/RecordedEvent.java | 8 +- .../classes/jdk/jfr/internal/ASMToolkit.java | 102 -- .../jdk/jfr/internal/EventClassBuilder.java | 147 ++- .../jfr/internal/EventInstrumentation.java | 1011 ++++++++--------- .../jdk/jfr/internal/EventWriterMethod.java | 58 +- .../classes/jdk/jfr/internal/JVMUpcalls.java | 6 +- .../classes/jdk/jfr/internal/TypeLibrary.java | 14 +- .../jfr/internal/consumer/EventParser.java | 2 +- .../jdk/jfr/internal/util/Bytecode.java | 166 +++ .../classes/jdk/jfr/internal/util/Utils.java | 5 + test/jdk/jdk/jfr/jvm/TestEventWriterLog.java | 4 +- 12 files changed, 779 insertions(+), 749 deletions(-) delete mode 100644 src/jdk.jfr/share/classes/jdk/jfr/internal/ASMToolkit.java create mode 100644 src/jdk.jfr/share/classes/jdk/jfr/internal/util/Bytecode.java diff --git a/src/java.base/share/classes/module-info.java b/src/java.base/share/classes/module-info.java index bcf7daf818929..161cbe380cf8f 100644 --- a/src/java.base/share/classes/module-info.java +++ b/src/java.base/share/classes/module-info.java @@ -187,15 +187,20 @@ exports jdk.internal.classfile to jdk.jartool, jdk.jdeps, + jdk.jfr, jdk.jlink, jdk.jshell; exports jdk.internal.classfile.attribute to jdk.jartool, jdk.jdeps, + jdk.jfr, jdk.jlink; + exports jdk.internal.classfile.components to + jdk.jfr; exports jdk.internal.classfile.constantpool to jdk.jartool, jdk.jdeps, + jdk.jfr, jdk.jlink; exports jdk.internal.classfile.instruction to jdk.jdeps, diff --git a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedEvent.java b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedEvent.java index b0ffe88b9c71e..9ec117ffc887a 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedEvent.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024, 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 @@ import jdk.jfr.EventType; import jdk.jfr.ValueDescriptor; -import jdk.jfr.internal.EventInstrumentation; +import jdk.jfr.internal.util.Utils; import jdk.jfr.internal.consumer.ObjectContext; /** @@ -57,7 +57,7 @@ public final class RecordedEvent extends RecordedObject { * @return stack trace, or {@code null} if doesn't exist for the event */ public RecordedStackTrace getStackTrace() { - return getTyped(EventInstrumentation.FIELD_STACK_TRACE, RecordedStackTrace.class, null); + return getTyped(Utils.FIELD_STACK_TRACE, RecordedStackTrace.class, null); } /** @@ -67,7 +67,7 @@ public RecordedStackTrace getStackTrace() { * @return thread, or {@code null} if doesn't exist for the event */ public RecordedThread getThread() { - return getTyped(EventInstrumentation.FIELD_EVENT_THREAD, RecordedThread.class, null); + return getTyped(Utils.FIELD_EVENT_THREAD, RecordedThread.class, null); } /** diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/ASMToolkit.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/ASMToolkit.java deleted file mode 100644 index f9e088c8fdf68..0000000000000 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/ASMToolkit.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. 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 jdk.jfr.internal; - -import java.io.ByteArrayOutputStream; -import java.io.PrintWriter; - -import jdk.internal.org.objectweb.asm.ClassReader; -import jdk.internal.org.objectweb.asm.Type; -import jdk.internal.org.objectweb.asm.util.TraceClassVisitor; -import jdk.jfr.ValueDescriptor; - -final class ASMToolkit { - public static final Type TYPE_STRING = Type.getType(String.class); - private static final Type TYPE_THREAD = Type.getType(Thread.class); - private static final Type TYPE_CLASS = Type.getType(Class.class); - - public static Type toType(ValueDescriptor v) { - return switch (v.getTypeName()) { - case "byte" -> Type.BYTE_TYPE; - case "short" -> Type.SHORT_TYPE; - case "int" -> Type.INT_TYPE; - case "long" ->Type.LONG_TYPE; - case "double" -> Type.DOUBLE_TYPE; - case "float" -> Type.FLOAT_TYPE; - case "char" -> Type.CHAR_TYPE; - case "boolean" -> Type.BOOLEAN_TYPE; - case "java.lang.String" -> TYPE_STRING; - case "java.lang.Thread" -> TYPE_THREAD; - case "java.lang.Class" -> TYPE_CLASS; - default -> throw new Error("Not a valid type " + v.getTypeName()); - }; - } - - /** - * Converts "int" into "I" and "java.lang.String" into "Ljava/lang/String;" - * - * @param typeName - * type - * - * @return descriptor - */ - public static String getDescriptor(String typeName) { - return switch (typeName) { - case "int" -> "I"; - case "long" -> "J"; - case "boolean" -> "Z"; - case "float" -> "F"; - case "double" -> "D"; - case "short" -> "S"; - case "char" -> "C"; - case "byte" -> "B"; - default -> Type.getObjectType(getInternalName(typeName)).getDescriptor(); - }; - } - - /** - * Converts java.lang.String into java/lang/String - * - * @param className - * - * @return internal name - */ - public static String getInternalName(String className) { - return className.replace(".", "/"); - } - - public static void logASM(String className, byte[] bytes) { - Logger.log(LogTag.JFR_SYSTEM_BYTECODE, LogLevel.INFO, "Generated bytecode for class " + className); - if (Logger.shouldLog(LogTag.JFR_SYSTEM_BYTECODE, LogLevel.TRACE)) { - ClassReader cr = new ClassReader(bytes); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - PrintWriter w = new PrintWriter(baos); - w.println("Bytecode:"); - cr.accept(new TraceClassVisitor(w), 0); - Logger.log(LogTag.JFR_SYSTEM_BYTECODE, LogLevel.TRACE, baos.toString()); - }; - } -} diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/EventClassBuilder.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/EventClassBuilder.java index 70bd3eef1f49d..3d661f725e891 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/EventClassBuilder.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/EventClassBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,117 +25,112 @@ package jdk.jfr.internal; +import static jdk.jfr.internal.util.Bytecode.invokespecial; + +import java.lang.constant.ClassDesc; +import java.lang.constant.ConstantDescs; +import java.lang.reflect.AccessFlag; +import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicLong; -import jdk.internal.org.objectweb.asm.AnnotationVisitor; -import jdk.internal.org.objectweb.asm.ClassWriter; -import jdk.internal.org.objectweb.asm.Label; -import jdk.internal.org.objectweb.asm.MethodVisitor; -import jdk.internal.org.objectweb.asm.Opcodes; -import jdk.internal.org.objectweb.asm.Type; -import jdk.internal.org.objectweb.asm.commons.GeneratorAdapter; -import jdk.internal.org.objectweb.asm.commons.Method; +import jdk.internal.classfile.AnnotationValue; +import jdk.internal.classfile.ClassBuilder; +import jdk.internal.classfile.Classfile; +import jdk.internal.classfile.Label; +import jdk.internal.classfile.attribute.RuntimeVisibleAnnotationsAttribute; import jdk.jfr.AnnotationElement; import jdk.jfr.Event; import jdk.jfr.ValueDescriptor; - +import jdk.jfr.internal.util.Bytecode; +import jdk.jfr.internal.util.Bytecode.MethodDesc; // Helper class for building dynamic events public final class EventClassBuilder { - - private static final Type TYPE_EVENT = Type.getType(Event.class); - private static final Type TYPE_IOBE = Type.getType(IndexOutOfBoundsException.class); - private static final Method DEFAULT_CONSTRUCTOR = Method.getMethod("void ()"); - private static final Method SET_METHOD = Method.getMethod("void set (int, java.lang.Object)"); + private static final ClassDesc TYPE_EVENT = Bytecode.classDesc(Event.class); + private static final ClassDesc TYPE_IOBE = Bytecode.classDesc(IndexOutOfBoundsException.class); + private static final MethodDesc DEFAULT_CONSTRUCTOR = MethodDesc.of("", "()V"); + private static final MethodDesc SET_METHOD = MethodDesc.of("set", "(ILjava/lang/Object;)V"); private static final AtomicLong idCounter = new AtomicLong(); - private final ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); + private final String fullClassName; - private final Type type; + private final ClassDesc type; private final List fields; private final List annotationElements; public EventClassBuilder(List annotationElements, List fields) { this.fullClassName = "jdk.jfr.DynamicEvent" + idCounter.incrementAndGet(); - this.type = Type.getType("L" + fullClassName.replace(".", "/") + ";"); + this.type = ClassDesc.of(fullClassName); this.fields = fields; this.annotationElements = annotationElements; } public Class build() { - buildClassInfo(); - buildConstructor(); - buildFields(); - buildSetMethod(); - endClass(); - byte[] bytes = classWriter.toByteArray(); - ASMToolkit.logASM(fullClassName, bytes); + byte[] bytes = Classfile.of().build(ClassDesc.of(fullClassName), cb -> build(cb)); + Bytecode.log(fullClassName, bytes); return SecuritySupport.defineClass(Event.class, bytes).asSubclass(Event.class); } - private void endClass() { - classWriter.visitEnd(); + void build(ClassBuilder builder) { + buildClassInfo(builder); + buildConstructor(builder); + buildFields(builder); + buildSetMethod(builder); } - private void buildSetMethod() { - GeneratorAdapter ga = new GeneratorAdapter(Opcodes.ACC_PUBLIC, SET_METHOD, null, null, classWriter); - int index = 0; - for (ValueDescriptor v : fields) { - ga.loadArg(0); - ga.visitLdcInsn(index); - Label notEqual = new Label(); - ga.ifICmp(GeneratorAdapter.NE, notEqual); - ga.loadThis(); - ga.loadArg(1); - Type fieldType = ASMToolkit.toType(v); - ga.unbox(ASMToolkit.toType(v)); - ga.putField(type, v.getName(), fieldType); - ga.visitInsn(Opcodes.RETURN); - ga.visitLabel(notEqual); - index++; - } - ga.throwException(TYPE_IOBE, "Index must between 0 and " + fields.size()); - ga.endMethod(); + private void buildSetMethod(ClassBuilder builder) { + // void Event::set(int index, Object value); + builder.withMethod(SET_METHOD.name(), SET_METHOD.descriptor(), Classfile.ACC_PUBLIC, methodBuilder -> methodBuilder.withCode(codeBuilder -> { + int index = 0; + for (ValueDescriptor v : fields) { + codeBuilder.iload(1); + codeBuilder.ldc(index); + Label notEqual = codeBuilder.newLabel(); + codeBuilder.if_icmpne(notEqual); + codeBuilder.aload(0); // this + codeBuilder.aload(2); // value + ClassDesc cd = Bytecode.classDesc(v); + Bytecode.unbox(codeBuilder, cd); + codeBuilder.putfield(type, v.getName(), cd); + codeBuilder.return_(); + codeBuilder.labelBinding(notEqual); + index++; + } + Bytecode.throwException(codeBuilder, TYPE_IOBE, "Index must between 0 and " + fields.size()); + })); } - private void buildConstructor() { - MethodVisitor mv = classWriter.visitMethod(Opcodes.ACC_PUBLIC, DEFAULT_CONSTRUCTOR.getName(), DEFAULT_CONSTRUCTOR.getDescriptor(), null, null); - mv.visitIntInsn(Opcodes.ALOAD, 0); - mv.visitMethodInsn(Opcodes.INVOKESPECIAL, TYPE_EVENT.getInternalName(), DEFAULT_CONSTRUCTOR.getName(), DEFAULT_CONSTRUCTOR.getDescriptor(), false); - mv.visitInsn(Opcodes.RETURN); - mv.visitMaxs(0, 0); + private void buildConstructor(ClassBuilder builder) { + builder.withMethod(ConstantDescs.INIT_NAME, ConstantDescs.MTD_void, Classfile.ACC_PUBLIC, methodBuilder -> methodBuilder.withCode(codeBuilder -> { + codeBuilder.aload(0); + invokespecial(codeBuilder, TYPE_EVENT, DEFAULT_CONSTRUCTOR); + codeBuilder.return_(); + })); } - private void buildClassInfo() { - String internalSuperName = ASMToolkit.getInternalName(Event.class.getName()); - String internalClassName = type.getInternalName(); - classWriter.visit(52, Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_SUPER, internalClassName, null, internalSuperName, null); - - for (AnnotationElement a : annotationElements) { - String descriptor = ASMToolkit.getDescriptor(a.getTypeName()); - AnnotationVisitor av = classWriter.visitAnnotation(descriptor, true); + private void buildClassInfo(ClassBuilder builder) { + builder.withSuperclass(Bytecode.classDesc(Event.class)); + builder.withFlags(AccessFlag.FINAL, AccessFlag.PUBLIC, AccessFlag.SUPER); + List annotations = new ArrayList<>(); + for (jdk.jfr.AnnotationElement a : annotationElements) { + List list = new ArrayList<>(); for (ValueDescriptor v : a.getValueDescriptors()) { - Object value = a.getValue(v.getName()); - String name = v.getName(); - if (v.isArray()) { - AnnotationVisitor arrayVisitor = av.visitArray(name); - Object[] array = (Object[]) value; - for (int i = 0; i < array.length; i++) { - arrayVisitor.visit(null, array[i]); - } - arrayVisitor.visitEnd(); - } else { - av.visit(name, value); - } + // ValueDescriptor can only hold primitive + // No need to care about classes/enums + var value = a.getValue(v.getName()); + var av = AnnotationValue.of(value); + var ae = jdk.internal.classfile.AnnotationElement.of(v.getName(), av); + list.add(ae); } - av.visitEnd(); + ClassDesc cd = ClassDesc.of(a.getTypeName()); + annotations.add(jdk.internal.classfile.Annotation.of(cd, list)); } + builder.with(RuntimeVisibleAnnotationsAttribute.of(annotations)); } - private void buildFields() { + private void buildFields(ClassBuilder builder) { for (ValueDescriptor v : fields) { - String internal = ASMToolkit.getDescriptor(v.getTypeName()); - classWriter.visitField(Opcodes.ACC_PRIVATE, v.getName(), internal, null, null); + builder.withField(v.getName(), Bytecode.classDesc(v), Classfile.ACC_PRIVATE); // No need to store annotations on field since they will be replaced anyway. } } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/EventInstrumentation.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/EventInstrumentation.java index 9c3770c8fad76..fcf00a7db9b3b 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/EventInstrumentation.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/EventInstrumentation.java @@ -25,117 +25,125 @@ package jdk.jfr.internal; +import java.lang.constant.ClassDesc; +import java.lang.constant.MethodTypeDesc; import java.lang.reflect.Field; import java.lang.reflect.Modifier; -import java.lang.reflect.Parameter; import java.util.ArrayList; import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.function.Consumer; -import jdk.internal.org.objectweb.asm.ClassReader; -import jdk.internal.org.objectweb.asm.ClassWriter; -import jdk.internal.org.objectweb.asm.Label; -import jdk.internal.org.objectweb.asm.MethodVisitor; -import jdk.internal.org.objectweb.asm.Opcodes; -import jdk.internal.org.objectweb.asm.Type; -import jdk.internal.org.objectweb.asm.commons.Method; -import jdk.internal.org.objectweb.asm.tree.AnnotationNode; -import jdk.internal.org.objectweb.asm.tree.ClassNode; -import jdk.internal.org.objectweb.asm.tree.FieldNode; -import jdk.internal.org.objectweb.asm.tree.MethodNode; +import jdk.internal.classfile.Annotation; +import jdk.internal.classfile.AnnotationElement; +import jdk.internal.classfile.AnnotationValue; +import jdk.internal.classfile.ClassElement; +import jdk.internal.classfile.ClassModel; +import jdk.internal.classfile.Classfile; +import jdk.internal.classfile.CodeBuilder; +import jdk.internal.classfile.CodeBuilder.BlockCodeBuilder; +import jdk.internal.classfile.FieldModel; +import jdk.internal.classfile.Label; +import jdk.internal.classfile.MethodModel; +import jdk.internal.classfile.Opcode; +import jdk.internal.classfile.TypeKind; +import jdk.internal.classfile.attribute.RuntimeVisibleAnnotationsAttribute; +import jdk.jfr.internal.event.EventConfiguration; +import jdk.jfr.internal.event.EventWriter; import jdk.jfr.Enabled; -import jdk.jfr.Event; import jdk.jfr.Name; import jdk.jfr.Registered; import jdk.jfr.SettingControl; import jdk.jfr.SettingDefinition; -import jdk.jfr.internal.event.EventConfiguration; -import jdk.jfr.internal.event.EventWriter; import jdk.jfr.internal.util.Utils; +import jdk.jfr.internal.util.Bytecode; +import jdk.jfr.internal.util.Bytecode.FieldDesc; +import jdk.jfr.internal.util.Bytecode.MethodDesc; +import static jdk.jfr.internal.util.Bytecode.invokevirtual; +import static jdk.jfr.internal.util.Bytecode.invokestatic; +import static jdk.jfr.internal.util.Bytecode.getfield; +import static jdk.jfr.internal.util.Bytecode.putfield; +import static jdk.jfr.internal.util.Bytecode.classDesc; /** * Class responsible for adding instrumentation to a subclass of {@link Event}. * */ -public final class EventInstrumentation { - - record SettingInfo(Type paramType, String methodName) { - } - - record FieldInfo(String name, String descriptor) { - } - - public static final String FIELD_EVENT_THREAD = "eventThread"; - public static final String FIELD_STACK_TRACE = "stackTrace"; - public static final String FIELD_DURATION = "duration"; - - static final String FIELD_EVENT_CONFIGURATION = "eventConfiguration"; - static final String FIELD_START_TIME = "startTime"; - - private static final String ANNOTATION_NAME_DESCRIPTOR = Type.getDescriptor(Name.class); - private static final String ANNOTATION_REGISTERED_DESCRIPTOR = Type.getDescriptor(Registered.class); - private static final String ANNOTATION_ENABLED_DESCRIPTOR = Type.getDescriptor(Enabled.class); - private static final Type TYPE_EVENT_CONFIGURATION = Type.getType(EventConfiguration.class); - private static final Type TYPE_EVENT_WRITER = Type.getType(EventWriter.class); - private static final Type TYPE_EVENT_WRITER_FACTORY = Type.getType("Ljdk/jfr/internal/event/EventWriterFactory;"); - private static final Type TYPE_SETTING_CONTROL = Type.getType(SettingControl.class); - private static final String TYPE_OBJECT_DESCRIPTOR = Type.getDescriptor(Object.class); - private static final String TYPE_EVENT_CONFIGURATION_DESCRIPTOR = TYPE_EVENT_CONFIGURATION.getDescriptor(); - private static final String TYPE_SETTING_DEFINITION_DESCRIPTOR = Type.getDescriptor(SettingDefinition.class); - private static final Method METHOD_COMMIT = new Method("commit", Type.VOID_TYPE, new Type[0]); - private static final Method METHOD_BEGIN = new Method("begin", Type.VOID_TYPE, new Type[0]); - private static final Method METHOD_END = new Method("end", Type.VOID_TYPE, new Type[0]); - private static final Method METHOD_IS_ENABLED = new Method("isEnabled", Type.BOOLEAN_TYPE, new Type[0]); - private static final Method METHOD_TIME_STAMP = new Method("timestamp", Type.LONG_TYPE, new Type[0]); - private static final Method METHOD_GET_EVENT_WRITER_KEY = new Method("getEventWriter", TYPE_EVENT_WRITER, new Type[] { Type.LONG_TYPE }); - private static final Method METHOD_EVENT_SHOULD_COMMIT = new Method("shouldCommit", Type.BOOLEAN_TYPE, new Type[0]); - private static final Method METHOD_EVENT_CONFIGURATION_SHOULD_COMMIT = new Method("shouldCommit", Type.BOOLEAN_TYPE, new Type[] { Type.LONG_TYPE }); - private static final Method METHOD_EVENT_CONFIGURATION_GET_SETTING = new Method("getSetting", TYPE_SETTING_CONTROL, new Type[] { Type.INT_TYPE }); - private static final Method METHOD_DURATION = new Method("duration", Type.LONG_TYPE, new Type[] { Type.LONG_TYPE }); - private static final Method METHOD_RESET = new Method("reset", "()V"); - private static final Method METHOD_ENABLED = new Method("enabled", Type.BOOLEAN_TYPE, new Type[0]); - private static final Method METHOD_SHOULD_COMMIT_LONG = new Method("shouldCommit", Type.BOOLEAN_TYPE, new Type[] { Type.LONG_TYPE }); - - private final ClassNode classNode; - private final List settingInfos; - private final List fieldInfos;; +final class EventInstrumentation { + + private record SettingDesc(ClassDesc paramType, String methodName) { + } + + private static final FieldDesc FIELD_DURATION = FieldDesc.of(long.class, Utils.FIELD_DURATION); + private static final FieldDesc FIELD_EVENT_CONFIGURATION = FieldDesc.of(Object.class, "eventConfiguration");; + private static final FieldDesc FIELD_START_TIME = FieldDesc.of(long.class, Utils.FIELD_START_TIME); + private static final ClassDesc ANNOTATION_ENABLED = classDesc(Enabled.class); + private static final ClassDesc ANNOTATION_NAME = classDesc(Name.class); + private static final ClassDesc ANNOTATION_REGISTERED = classDesc(Registered.class); + private static final ClassDesc TYPE_EVENT_CONFIGURATION = classDesc(EventConfiguration.class); + private static final ClassDesc TYPE_EVENT_WRITER = classDesc(EventWriter.class); + private static final ClassDesc TYPE_EVENT_WRITER_FACTORY = ClassDesc.of("jdk.jfr.internal.event.EventWriterFactory"); + private static final ClassDesc TYPE_OBJECT = Bytecode.classDesc(Object.class); + private static final ClassDesc TYPE_SETTING_DEFINITION = Bytecode.classDesc(SettingDefinition.class); + private static final MethodDesc METHOD_BEGIN = MethodDesc.of("begin", "()V"); + private static final MethodDesc METHOD_COMMIT = MethodDesc.of("commit", "()V"); + private static final MethodDesc METHOD_DURATION = MethodDesc.of("duration", "(J)J"); + private static final MethodDesc METHOD_ENABLED = MethodDesc.of("enabled", "()Z"); + private static final MethodDesc METHOD_END = MethodDesc.of("end", "()V"); + private static final MethodDesc METHOD_EVENT_CONFIGURATION_SHOULD_COMMIT = MethodDesc.of("shouldCommit", "(J)Z"); + private static final MethodDesc METHOD_EVENT_CONFIGURATION_GET_SETTING = MethodDesc.of("getSetting", SettingControl.class, int.class); + private static final MethodDesc METHOD_EVENT_SHOULD_COMMIT = MethodDesc.of("shouldCommit", "()Z"); + private static final MethodDesc METHOD_GET_EVENT_WRITER_KEY = MethodDesc.of("getEventWriter", "(J)" + TYPE_EVENT_WRITER.descriptorString()); + private static final MethodDesc METHOD_IS_ENABLED = MethodDesc.of("isEnabled", "()Z"); + private static final MethodDesc METHOD_RESET = MethodDesc.of("reset", "()V"); + private static final MethodDesc METHOD_SHOULD_COMMIT_LONG = MethodDesc.of("shouldCommit", "(J)Z"); + private static final MethodDesc METHOD_TIME_STAMP = MethodDesc.of("timestamp", "()J"); + + private final ClassModel classModel; + private final List settingDescs; + private final List fieldDescs;; private final String eventName; private final Class superClass; private final boolean untypedEventConfiguration; - private final Method staticCommitMethod; + private final MethodDesc staticCommitMethod; private final long eventTypeId; private final boolean guardEventConfiguration; private final boolean isJDK; + private final Map> methodUpdates = new LinkedHashMap<>(); EventInstrumentation(Class superClass, byte[] bytes, long id, boolean isJDK, boolean guardEventConfiguration) { this.eventTypeId = id; this.superClass = superClass; - this.classNode = createClassNode(bytes); - this.settingInfos = buildSettingInfos(superClass, classNode); - this.fieldInfos = buildFieldInfos(superClass, classNode); - String n = annotationValue(classNode, ANNOTATION_NAME_DESCRIPTOR, String.class); - this.eventName = n == null ? classNode.name.replace("/", ".") : n; - this.staticCommitMethod = isJDK ? findStaticCommitMethod(classNode, fieldInfos) : null; + this.classModel = createClassModel(bytes); + this.settingDescs = buildSettingDescs(superClass, classModel); + this.fieldDescs = buildFieldDescs(superClass, classModel); + String n = annotationValue(classModel, ANNOTATION_NAME, String.class); + this.eventName = n == null ? classModel.thisClass().asInternalName().replace("/", ".") : n; + this.staticCommitMethod = isJDK ? findStaticCommitMethod(classModel, fieldDescs) : null; this.untypedEventConfiguration = hasUntypedConfiguration(); - // Corner case when we are forced to generate bytecode (bytesForEagerInstrumentation) - // We can't reference EventConfiguration::isEnabled() before event class has been registered, + // Corner case when we are forced to generate bytecode + // (bytesForEagerInstrumentation) + // We can't reference EventConfiguration::isEnabled() before event class has + // been registered, // so we add a guard against a null reference. this.guardEventConfiguration = guardEventConfiguration; this.isJDK = isJDK; } - public static Method findStaticCommitMethod(ClassNode classNode, List fields) { + static MethodDesc findStaticCommitMethod(ClassModel classModel, List fields) { StringBuilder sb = new StringBuilder(); sb.append("("); - for (FieldInfo field : fields) { - sb.append(field.descriptor); + for (FieldDesc field : fields) { + sb.append(field.type().descriptorString()); } sb.append(")V"); - Method m = new Method("commit", sb.toString()); - for (MethodNode method : classNode.methods) { - if ("commit".equals(method.name) && m.getDescriptor().equals(method.desc)) { + MethodDesc m = MethodDesc.of("commit", sb.toString()); + for (MethodModel method : classModel.methods()) { + String d = method.methodTypeSymbol().descriptorString(); + if (method.methodName().equalsString("commit") && m.descriptor().descriptorString().equals(d)) { return m; } } @@ -143,27 +151,24 @@ public static Method findStaticCommitMethod(ClassNode classNode, List } private boolean hasUntypedConfiguration() { - for (FieldNode field : classNode.fields) { - if (FIELD_EVENT_CONFIGURATION.equals(field.name)) { - return field.desc.equals(TYPE_OBJECT_DESCRIPTOR); + for (FieldModel f : classModel.fields()) { + if (f.fieldName().equalsString(FIELD_EVENT_CONFIGURATION.name())) { + return f.fieldType().equalsString(TYPE_OBJECT.descriptorString()); } } throw new InternalError("Class missing configuration field"); } public String getClassName() { - return classNode.name.replace("/", "."); + return classModel.thisClass().asInternalName().replace("/", "."); } - private ClassNode createClassNode(byte[] bytes) { - ClassNode classNode = new ClassNode(); - ClassReader classReader = new ClassReader(bytes); - classReader.accept(classNode, 0); - return classNode; + private ClassModel createClassModel(byte[] bytes) { + return Classfile.of().parse(bytes); } boolean isRegistered() { - Boolean result = annotationValue(classNode, ANNOTATION_REGISTERED_DESCRIPTOR, Boolean.class); + Boolean result = annotationValue(classModel, ANNOTATION_REGISTERED, Boolean.class); if (result != null) { return result.booleanValue(); } @@ -177,7 +182,7 @@ boolean isRegistered() { } boolean isEnabled() { - Boolean result = annotationValue(classNode, ANNOTATION_ENABLED_DESCRIPTOR, Boolean.class); + Boolean result = annotationValue(classModel, ANNOTATION_ENABLED, Boolean.class); if (result != null) { return result.booleanValue(); } @@ -191,18 +196,23 @@ boolean isEnabled() { } @SuppressWarnings("unchecked") - private static T annotationValue(ClassNode classNode, String typeDescriptor, Class type) { - if (classNode.visibleAnnotations != null) { - for (AnnotationNode a : classNode.visibleAnnotations) { - if (typeDescriptor.equals(a.desc)) { - List values = a.values; - if (values != null && values.size() == 2) { - Object key = values.get(0); - Object value = values.get(1); - if (key instanceof String keyName && value != null) { - if (type == value.getClass()) { - if ("value".equals(keyName)) { - return (T) value; + // Only supports String and Boolean values + private static T annotationValue(ClassModel classModel, ClassDesc classDesc, Class type) { + String typeDescriptor = classDesc.descriptorString(); + for (ClassElement ce : classModel.elements()) { + if (ce instanceof RuntimeVisibleAnnotationsAttribute rvaa) { + for (Annotation a : rvaa.annotations()) { + if (a.className().equalsString(typeDescriptor)) { + if (a.elements().size() == 1) { + AnnotationElement ae = a.elements().getFirst(); + if (ae.name().equalsString("value")) { + if (ae.value() instanceof AnnotationValue.OfBoolean ofb && type.equals(Boolean.class)) { + Boolean b = ofb.booleanValue(); + return (T)b; + } + if (ae.value() instanceof AnnotationValue.OfString ofs && type.equals(String.class)) { + String s = ofs.stringValue(); + return (T)s; } } } @@ -213,99 +223,119 @@ private static T annotationValue(ClassNode classNode, String typeDescriptor, return null; } - private static List buildSettingInfos(Class superClass, ClassNode classNode) { + private static List buildSettingDescs(Class superClass, ClassModel classModel) { Set methodSet = new HashSet<>(); - List settingInfos = new ArrayList<>(); - for (MethodNode m : classNode.methods) { - if (m.visibleAnnotations != null) { - for (AnnotationNode an : m.visibleAnnotations) { - // We can't really validate the method at this - // stage. We would need to check that the parameter - // is an instance of SettingControl. - if (TYPE_SETTING_DEFINITION_DESCRIPTOR.equals(an.desc)) { - String name = m.name; - for (AnnotationNode nameCandidate : m.visibleAnnotations) { - if (ANNOTATION_NAME_DESCRIPTOR.equals(nameCandidate.desc)) { - List values = nameCandidate.values; - if (values.size() == 1 && values.getFirst() instanceof String s) { - name = Utils.validJavaIdentifier(s, name); + List settingDescs = new ArrayList<>(); + for (MethodModel m : classModel.methods()) { + for (var me : m.elements()) { + if (me instanceof RuntimeVisibleAnnotationsAttribute rvaa) { + for (Annotation a : rvaa.annotations()) { + // We can't really validate the method at this + // stage. We would need to check that the parameter + // is an instance of SettingControl. + if (a.className().equalsString(TYPE_SETTING_DEFINITION.descriptorString())) { + String name = m.methodName().stringValue(); + // Use @Name if it exists + for (Annotation nameCandidate : rvaa.annotations()) { + if (nameCandidate.className().equalsString(ANNOTATION_NAME.descriptorString())) { + if (nameCandidate.elements().size() == 1) { + AnnotationElement ae = nameCandidate.elements().getFirst(); + if (ae.name().equalsString("value")) { + if (ae.value() instanceof AnnotationValue.OfString s) { + name = Utils.validJavaIdentifier(s.stringValue(), name); + } + } + } } } - } - Type returnType = Type.getReturnType(m.desc); - if (returnType.equals(Type.getType(Boolean.TYPE))) { - Type[] args = Type.getArgumentTypes(m.desc); - if (args.length == 1) { - Type paramType = args[0]; - methodSet.add(m.name); - settingInfos.add(new SettingInfo(paramType, m.name)); + // Add setting if method returns boolean and has one parameter + MethodTypeDesc mtd = m.methodTypeSymbol(); + if ("Z".equals(mtd.returnType().descriptorString())) { + if (mtd.parameterList().size() == 1) { + ClassDesc type = mtd.parameterList().getFirst(); + if (type.isClassOrInterface()) { + String methodName = m.methodName().stringValue(); + methodSet.add(methodName); + settingDescs.add(new SettingDesc(type, methodName)); + } + } } } } } } } - for (Class c = superClass; c != jdk.internal.event.Event.class; c = c.getSuperclass()) { + for (Class c = superClass; jdk.internal.event.Event.class != c; c = c.getSuperclass()) { for (java.lang.reflect.Method method : c.getDeclaredMethods()) { if (!methodSet.contains(method.getName())) { // skip private method in base classes if (!Modifier.isPrivate(method.getModifiers())) { if (method.getReturnType().equals(Boolean.TYPE)) { if (method.getParameterCount() == 1) { - Parameter param = method.getParameters()[0]; - Type paramType = Type.getType(param.getType()); - methodSet.add(method.getName()); - settingInfos.add(new SettingInfo(paramType, method.getName())); + Class type = method.getParameters()[0].getType(); + if (SettingControl.class.isAssignableFrom(type)) { + ClassDesc paramType = Bytecode.classDesc(type); + methodSet.add(method.getName()); + settingDescs.add(new SettingDesc(paramType, method.getName())); + } } } } } } } - return settingInfos; + return settingDescs; } - private static List buildFieldInfos(Class superClass, ClassNode classNode) { + private static List buildFieldDescs(Class superClass, ClassModel classModel) { Set fieldSet = new HashSet<>(); - List fieldInfos = new ArrayList<>(classNode.fields.size()); + List fieldDescs = new ArrayList<>(classModel.fields().size()); // These two fields are added by native as 'transient' so they will be // ignored by the loop below. // The benefit of adding them manually is that we can // control in which order they occur and we can add @Name, @Description // in Java, instead of in native. It also means code for adding implicit // fields for native can be reused by Java. - fieldInfos.add(new FieldInfo("startTime", Type.LONG_TYPE.getDescriptor())); - fieldInfos.add(new FieldInfo("duration", Type.LONG_TYPE.getDescriptor())); - for (FieldNode field : classNode.fields) { - if (!fieldSet.contains(field.name) && isValidField(field.access, Type.getType(field.desc).getClassName())) { - FieldInfo fi = new FieldInfo(field.name, field.desc); - fieldInfos.add(fi); - fieldSet.add(field.name); + fieldDescs.add(FIELD_START_TIME); + fieldDescs.add(FIELD_DURATION); + for (FieldModel field : classModel.fields()) { + if (!fieldSet.contains(field.fieldName().stringValue()) && isValidField(field.flags().flagsMask(), field.fieldTypeSymbol())) { + FieldDesc fi = FieldDesc.of(field.fieldTypeSymbol(), field.fieldName().stringValue()); + fieldDescs.add(fi); + fieldSet.add(field.fieldName().stringValue()); } } - for (Class c = superClass; c != jdk.internal.event.Event.class; c = c.getSuperclass()) { + for (Class c = superClass; jdk.internal.event.Event.class != c; c = c.getSuperclass()) { for (Field field : c.getDeclaredFields()) { // skip private field in base classes if (!Modifier.isPrivate(field.getModifiers())) { if (isValidField(field.getModifiers(), field.getType().getName())) { String fieldName = field.getName(); if (!fieldSet.contains(fieldName)) { - Type fieldType = Type.getType(field.getType()); - fieldInfos.add(new FieldInfo(fieldName, fieldType.getDescriptor())); + fieldDescs.add(FieldDesc.of(field.getType(), fieldName)); fieldSet.add(fieldName); } } } } } - return fieldInfos; + return fieldDescs; + } + + public static boolean isValidField(int access, ClassDesc classDesc) { + String className = classDesc.packageName(); + if (!className.isEmpty()) { + className = className + "."; + } + className += classDesc.displayName(); + return isValidField(access, className); } public static boolean isValidField(int access, String className) { if (Modifier.isTransient(access) || Modifier.isStatic(access)) { return false; } - return jdk.jfr.internal.Type.isValidJavaFieldType(className); + return Type.isValidJavaFieldType(className); } public byte[] buildInstrumented() { @@ -313,11 +343,24 @@ public byte[] buildInstrumented() { return toByteArray(); } - private byte[] toByteArray() { - ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); - classNode.accept(cw); - cw.visitEnd(); - return cw.toByteArray(); + byte[] toByteArray() { + return Classfile.of().build(classModel.thisClass().asSymbol(), classBuilder -> { + for (ClassElement ce : classModel) { + boolean updated = false; + if (ce instanceof MethodModel method) { + Consumer methodUpdate = findMethodUpdate(method); + if (methodUpdate != null) { + classBuilder.withMethod(method.methodName().stringValue(), method.methodTypeSymbol(), method.flags().flagsMask(), methodBuilder -> { + methodBuilder.withCode(methodUpdate); + }); + updated = true; + } + } + if (!updated) { + classBuilder.with(ce); + } + } + }); } public byte[] buildUninstrumented() { @@ -330,416 +373,347 @@ private void makeInstrumented() { updateEnabledMethod(METHOD_IS_ENABLED); // MyEvent#begin() - updateMethod(METHOD_BEGIN, methodVisitor -> { - methodVisitor.visitIntInsn(Opcodes.ALOAD, 0); - invokeStatic(methodVisitor, TYPE_EVENT_CONFIGURATION.getInternalName(), METHOD_TIME_STAMP); - methodVisitor.visitFieldInsn(Opcodes.PUTFIELD, getInternalClassName(), FIELD_START_TIME, "J"); - methodVisitor.visitInsn(Opcodes.RETURN); + updateMethod(METHOD_BEGIN, codeBuilder -> { + codeBuilder.aload(0); + invokestatic(codeBuilder, TYPE_EVENT_CONFIGURATION, METHOD_TIME_STAMP); + putfield(codeBuilder, getEventClassDesc(), FIELD_START_TIME); + codeBuilder.return_(); }); // MyEvent#end() - updateMethod(METHOD_END, methodVisitor -> { - methodVisitor.visitIntInsn(Opcodes.ALOAD, 0); - methodVisitor.visitIntInsn(Opcodes.ALOAD, 0); - methodVisitor.visitFieldInsn(Opcodes.GETFIELD, getInternalClassName(), FIELD_START_TIME, "J"); - invokeStatic(methodVisitor, TYPE_EVENT_CONFIGURATION.getInternalName(), METHOD_DURATION); - methodVisitor.visitFieldInsn(Opcodes.PUTFIELD, getInternalClassName(), FIELD_DURATION, "J"); - methodVisitor.visitInsn(Opcodes.RETURN); - methodVisitor.visitMaxs(0, 0); + updateMethod(METHOD_END, codeBuilder -> { + codeBuilder.aload(0); + codeBuilder.aload(0); + getfield(codeBuilder, getEventClassDesc(), FIELD_START_TIME); + invokestatic(codeBuilder, TYPE_EVENT_CONFIGURATION, METHOD_DURATION); + putfield(codeBuilder, getEventClassDesc(), FIELD_DURATION); + codeBuilder.return_(); }); // MyEvent#commit() or static MyEvent#commit(...) - if (staticCommitMethod != null) { - updateExistingWithEmptyVoidMethod(METHOD_COMMIT); - updateMethod(staticCommitMethod, mv -> { - // indexes the argument type array, the argument type array does not include - // 'this' - int argIndex = 0; - // indexes the proper slot in the local variable table, takes type size into - // account, therefore sometimes argIndex != slotIndex - int slotIndex = 0; - int fieldIndex = 0; - Type[] argumentTypes = Type.getArgumentTypes(staticCommitMethod.getDescriptor()); - mv.visitCode(); - Label start = new Label(); - Label endTryBlock = new Label(); - Label exceptionHandler = new Label(); - mv.visitTryCatchBlock(start, endTryBlock, exceptionHandler, "java/lang/Throwable"); - mv.visitLabel(start); - getEventWriter(mv); - // stack: [EW] - mv.visitInsn(Opcodes.DUP); - // stack: [EW], [EW] - // write begin event - getEventConfiguration(mv); - // stack: [EW], [EW], [EventConfiguration] - mv.visitLdcInsn(eventTypeId); - // stack: [EW], [EW], [EventConfiguration] [long] - visitMethod(mv, Opcodes.INVOKEVIRTUAL, TYPE_EVENT_WRITER, EventWriterMethod.BEGIN_EVENT.asASM()); - // stack: [EW], [integer] - Label excluded = new Label(); - mv.visitJumpInsn(Opcodes.IFEQ, excluded); - // stack: [EW] - // write startTime - mv.visitInsn(Opcodes.DUP); - // stack: [EW], [EW] - mv.visitVarInsn(argumentTypes[argIndex].getOpcode(Opcodes.ILOAD), slotIndex); - // stack: [EW], [EW], [long] - slotIndex += argumentTypes[argIndex++].getSize(); - visitMethod(mv, Opcodes.INVOKEVIRTUAL, TYPE_EVENT_WRITER, EventWriterMethod.PUT_LONG.asASM()); - // stack: [EW] - fieldIndex++; - // write duration - mv.visitInsn(Opcodes.DUP); - // stack: [EW], [EW] - mv.visitVarInsn(argumentTypes[argIndex].getOpcode(Opcodes.ILOAD), slotIndex); - // stack: [EW], [EW], [long] - slotIndex += argumentTypes[argIndex++].getSize(); - visitMethod(mv, Opcodes.INVOKEVIRTUAL, TYPE_EVENT_WRITER, EventWriterMethod.PUT_LONG.asASM()); - // stack: [EW] - fieldIndex++; - // write eventThread - mv.visitInsn(Opcodes.DUP); - // stack: [EW], [EW] - visitMethod(mv, Opcodes.INVOKEVIRTUAL, TYPE_EVENT_WRITER, EventWriterMethod.PUT_EVENT_THREAD.asASM()); - // stack: [EW] - // write stackTrace - mv.visitInsn(Opcodes.DUP); - // stack: [EW], [EW] - visitMethod(mv, Opcodes.INVOKEVIRTUAL, TYPE_EVENT_WRITER, EventWriterMethod.PUT_STACK_TRACE.asASM()); - // stack: [EW] - // write custom fields - while (fieldIndex < fieldInfos.size()) { - mv.visitInsn(Opcodes.DUP); - // stack: [EW], [EW] - mv.visitVarInsn(argumentTypes[argIndex].getOpcode(Opcodes.ILOAD), slotIndex); - // stack:[EW], [EW], [field] - slotIndex += argumentTypes[argIndex++].getSize(); - FieldInfo field = fieldInfos.get(fieldIndex); - EventWriterMethod eventMethod = EventWriterMethod.lookupMethod(field); - visitMethod(mv, Opcodes.INVOKEVIRTUAL, TYPE_EVENT_WRITER, eventMethod.asASM()); - // stack: [EW] - fieldIndex++; - } - // stack: [EW] - // write end event (writer already on stack) - visitMethod(mv, Opcodes.INVOKEVIRTUAL, TYPE_EVENT_WRITER, EventWriterMethod.END_EVENT.asASM()); - // stack [integer] - // notified -> restart event write attempt - mv.visitJumpInsn(Opcodes.IFEQ, start); - // stack: - mv.visitLabel(endTryBlock); - Label end = new Label(); - mv.visitJumpInsn(Opcodes.GOTO, end); - mv.visitLabel(exceptionHandler); - // stack: [ex] - mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[] { "java/lang/Throwable" }); - getEventWriter(mv); - // stack: [ex] [EW] - mv.visitInsn(Opcodes.DUP); - // stack: [ex] [EW] [EW] - Label rethrow = new Label(); - mv.visitJumpInsn(Opcodes.IFNULL, rethrow); - // stack: [ex] [EW] - mv.visitInsn(Opcodes.DUP); - // stack: [ex] [EW] [EW] - visitMethod(mv, Opcodes.INVOKEVIRTUAL, TYPE_EVENT_WRITER, METHOD_RESET); - mv.visitLabel(rethrow); - // stack:[ex] [EW] - mv.visitFrame(Opcodes.F_SAME, 0, null, 2, new Object[] { "java/lang/Throwable", TYPE_EVENT_WRITER.getInternalName() }); - mv.visitInsn(Opcodes.POP); - // stack:[ex] - mv.visitInsn(Opcodes.ATHROW); - mv.visitLabel(excluded); - // stack: [EW] - mv.visitFrame(Opcodes.F_SAME, 0, null, 1, new Object[] { TYPE_EVENT_WRITER.getInternalName() }); - mv.visitInsn(Opcodes.POP); - mv.visitLabel(end); - // stack: - mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); - mv.visitInsn(Opcodes.RETURN); - mv.visitMaxs(0, 0); - mv.visitEnd(); - }); - } else { - updateMethod(METHOD_COMMIT, methodVisitor -> { - // if (!isEnable()) { - // return; - // } - methodVisitor.visitCode(); - Label start = new Label(); - Label endTryBlock = new Label(); - Label exceptionHandler = new Label(); - methodVisitor.visitTryCatchBlock(start, endTryBlock, exceptionHandler, "java/lang/Throwable"); - methodVisitor.visitLabel(start); - methodVisitor.visitVarInsn(Opcodes.ALOAD, 0); - methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, getInternalClassName(), METHOD_IS_ENABLED.getName(), METHOD_IS_ENABLED.getDescriptor(), false); - Label l0 = new Label(); - methodVisitor.visitJumpInsn(Opcodes.IFNE, l0); - methodVisitor.visitInsn(Opcodes.RETURN); - methodVisitor.visitLabel(l0); - methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null); - // long startTime = this.startTime - methodVisitor.visitVarInsn(Opcodes.ALOAD, 0); - methodVisitor.visitFieldInsn(Opcodes.GETFIELD, getInternalClassName(), FIELD_START_TIME, "J"); - methodVisitor.visitVarInsn(Opcodes.LSTORE, 1); - // if (startTime == 0) { - // startTime = EventWriter.timestamp(); - // } else { - methodVisitor.visitVarInsn(Opcodes.LLOAD, 1); - methodVisitor.visitInsn(Opcodes.LCONST_0); - methodVisitor.visitInsn(Opcodes.LCMP); - Label durationalEvent = new Label(); - methodVisitor.visitJumpInsn(Opcodes.IFNE, durationalEvent); - methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, TYPE_EVENT_CONFIGURATION.getInternalName(), METHOD_TIME_STAMP.getName(), METHOD_TIME_STAMP.getDescriptor(), false); - methodVisitor.visitVarInsn(Opcodes.LSTORE, 1); - Label commit = new Label(); - methodVisitor.visitJumpInsn(Opcodes.GOTO, commit); - // if (duration == 0) { - // duration = EventWriter.timestamp() - startTime; - // } - // } - methodVisitor.visitLabel(durationalEvent); - methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null); - methodVisitor.visitVarInsn(Opcodes.ALOAD, 0); - methodVisitor.visitFieldInsn(Opcodes.GETFIELD, getInternalClassName(), FIELD_DURATION, "J"); - methodVisitor.visitInsn(Opcodes.LCONST_0); - methodVisitor.visitInsn(Opcodes.LCMP); - methodVisitor.visitJumpInsn(Opcodes.IFNE, commit); - methodVisitor.visitVarInsn(Opcodes.ALOAD, 0); - methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, TYPE_EVENT_CONFIGURATION.getInternalName(), METHOD_TIME_STAMP.getName(), METHOD_TIME_STAMP.getDescriptor(), false); - methodVisitor.visitVarInsn(Opcodes.LLOAD, 1); - methodVisitor.visitInsn(Opcodes.LSUB); - methodVisitor.visitFieldInsn(Opcodes.PUTFIELD, getInternalClassName(), FIELD_DURATION, "J"); - methodVisitor.visitLabel(commit); - // if (shouldCommit()) { - methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null); - methodVisitor.visitVarInsn(Opcodes.ALOAD, 0); - invokeVirtual(methodVisitor, getInternalClassName(), METHOD_EVENT_SHOULD_COMMIT); - Label end = new Label(); - methodVisitor.visitJumpInsn(Opcodes.IFEQ, end); - getEventWriter(methodVisitor); - // stack: [EW] - methodVisitor.visitInsn(Opcodes.DUP); - // stack: [EW] [EW] - getEventConfiguration(methodVisitor); - // stack: [EW] [EW] [EC] - methodVisitor.visitLdcInsn(eventTypeId); - invokeVirtual(methodVisitor, TYPE_EVENT_WRITER, EventWriterMethod.BEGIN_EVENT.asmMethod); - Label excluded = new Label(); - // stack: [EW] [int] - methodVisitor.visitJumpInsn(Opcodes.IFEQ, excluded); - // stack: [EW] - int fieldIndex = 0; - methodVisitor.visitInsn(Opcodes.DUP); - // stack: [EW] [EW] - methodVisitor.visitVarInsn(Opcodes.LLOAD, 1); - // stack: [EW] [EW] [long] - invokeVirtual(methodVisitor, TYPE_EVENT_WRITER, EventWriterMethod.PUT_LONG.asmMethod); - // stack: [EW] - fieldIndex++; - methodVisitor.visitInsn(Opcodes.DUP); - // stack: [EW] [EW] - methodVisitor.visitVarInsn(Opcodes.ALOAD, 0); - // stack: [EW] [EW] [this] - methodVisitor.visitFieldInsn(Opcodes.GETFIELD, getInternalClassName(), FIELD_DURATION, "J"); - // stack: [EW] [EW] [long] - invokeVirtual(methodVisitor, TYPE_EVENT_WRITER, EventWriterMethod.PUT_LONG.asmMethod); - // stack: [EW] - fieldIndex++; - methodVisitor.visitInsn(Opcodes.DUP); - // stack: [EW] [EW] - invokeVirtual(methodVisitor, TYPE_EVENT_WRITER, EventWriterMethod.PUT_EVENT_THREAD.asASM()); - // stack: [EW] - methodVisitor.visitInsn(Opcodes.DUP); - // stack: [EW] [EW] - invokeVirtual(methodVisitor, TYPE_EVENT_WRITER, EventWriterMethod.PUT_STACK_TRACE.asASM()); - // stack: [EW] - while (fieldIndex < fieldInfos.size()) { - FieldInfo field = fieldInfos.get(fieldIndex); - methodVisitor.visitInsn(Opcodes.DUP); - // stack: [EW] [EW] - methodVisitor.visitVarInsn(Opcodes.ALOAD, 0); - // stack: [EW] [EW] [this] - methodVisitor.visitFieldInsn(Opcodes.GETFIELD, getInternalClassName(), field.name, field.descriptor); - // stack: [EW] [EW] - EventWriterMethod eventMethod = EventWriterMethod.lookupMethod(field); - invokeVirtual(methodVisitor, TYPE_EVENT_WRITER, eventMethod.asmMethod); - // stack: [EW] - fieldIndex++; + MethodDesc m = staticCommitMethod == null ? METHOD_COMMIT : staticCommitMethod; + updateMethod(m, codeBuilder -> { + Label excluded = codeBuilder.newLabel(); + Label end = codeBuilder.newLabel(); + codeBuilder.trying(blockCodeBuilder -> { + if (staticCommitMethod != null) { + updateStaticCommit(blockCodeBuilder, excluded); + } else { + updateInstanceCommit(blockCodeBuilder, end, excluded); } - // stack:[EW] - invokeVirtual(methodVisitor, TYPE_EVENT_WRITER, EventWriterMethod.END_EVENT.asASM()); - // stack [int] + // stack: [integer] // notified -> restart event write attempt - methodVisitor.visitJumpInsn(Opcodes.IFEQ, start); - methodVisitor.visitLabel(endTryBlock); - methodVisitor.visitJumpInsn(Opcodes.GOTO, end); - methodVisitor.visitLabel(exceptionHandler); - // stack: [ex] - methodVisitor.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[] { "java/lang/Throwable" }); - getEventWriter(methodVisitor); - // stack: [ex] [EW] - methodVisitor.visitInsn(Opcodes.DUP); - // stack: [ex] [EW] [EW] - Label rethrow = new Label(); - methodVisitor.visitJumpInsn(Opcodes.IFNULL, rethrow); - // stack: [ex] [EW] - methodVisitor.visitInsn(Opcodes.DUP); - // stack: [ex] [EW] [EW] - visitMethod(methodVisitor, Opcodes.INVOKEVIRTUAL, TYPE_EVENT_WRITER, METHOD_RESET); - methodVisitor.visitLabel(rethrow); - // stack:[ex] [EW] - methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 2, new Object[] { "java/lang/Throwable", TYPE_EVENT_WRITER.getInternalName() }); - methodVisitor.visitInsn(Opcodes.POP); - // stack:[ex] - methodVisitor.visitInsn(Opcodes.ATHROW); - methodVisitor.visitLabel(excluded); - // stack: [EW] - methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 1, new Object[] { TYPE_EVENT_WRITER.getInternalName() }); - methodVisitor.visitInsn(Opcodes.POP); - methodVisitor.visitLabel(end); - // stack: - methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null); - methodVisitor.visitInsn(Opcodes.RETURN); - methodVisitor.visitMaxs(0, 0); - methodVisitor.visitEnd(); + blockCodeBuilder.ifeq(blockCodeBuilder.startLabel()); + // stack: [] + blockCodeBuilder.goto_(end); + }, catchBuilder -> { + catchBuilder.catchingAll(catchAllHandler -> { + getEventWriter(catchAllHandler); + // stack: [ex] [EW] + catchAllHandler.dup(); + // stack: [ex] [EW] [EW] + Label rethrow = catchAllHandler.newLabel(); + catchAllHandler.if_null(rethrow); + // stack: [ex] [EW] + catchAllHandler.dup(); + // stack: [ex] [EW] [EW] + invokevirtual(catchAllHandler, TYPE_EVENT_WRITER, METHOD_RESET); + catchAllHandler.labelBinding(rethrow); + // stack:[ex] [EW] + catchAllHandler.pop(); + // stack:[ex] + catchAllHandler.throwInstruction(); + }); }); - } + codeBuilder.labelBinding(excluded); + // stack: [EW] + codeBuilder.pop(); + codeBuilder.labelBinding(end); + // stack: [] + codeBuilder.return_(); + }); // MyEvent#shouldCommit() - updateMethod(METHOD_EVENT_SHOULD_COMMIT, methodVisitor -> { - Label fail = new Label(); + updateMethod(METHOD_EVENT_SHOULD_COMMIT, codeBuilder -> { + Label fail = codeBuilder.newLabel(); if (guardEventConfiguration) { - getEventConfiguration(methodVisitor); - methodVisitor.visitJumpInsn(Opcodes.IFNULL, fail); + getEventConfiguration(codeBuilder); + codeBuilder.if_null(fail); } // if (!eventConfiguration.shouldCommit(duration) goto fail; - getEventConfiguration(methodVisitor); - methodVisitor.visitVarInsn(Opcodes.ALOAD, 0); - methodVisitor.visitFieldInsn(Opcodes.GETFIELD, getInternalClassName(), FIELD_DURATION, "J"); - invokeVirtual(methodVisitor, TYPE_EVENT_CONFIGURATION, METHOD_EVENT_CONFIGURATION_SHOULD_COMMIT); - methodVisitor.visitJumpInsn(Opcodes.IFEQ, fail); - for (int index = 0; index < settingInfos.size(); index++) { - SettingInfo si = settingInfos.get(index); + getEventConfiguration(codeBuilder); + codeBuilder.aload(0); + getfield(codeBuilder, getEventClassDesc(), FIELD_DURATION); + invokevirtual(codeBuilder, TYPE_EVENT_CONFIGURATION, METHOD_EVENT_CONFIGURATION_SHOULD_COMMIT); + codeBuilder.ifeq(fail); + for (int index = 0; index < settingDescs.size(); index++) { + SettingDesc sd = settingDescs.get(index); // if (!settingsMethod(eventConfiguration.settingX)) goto fail; - methodVisitor.visitIntInsn(Opcodes.ALOAD, 0); - if (untypedEventConfiguration) { - methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_CONFIGURATION, TYPE_OBJECT_DESCRIPTOR); - } else { - methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_CONFIGURATION, TYPE_EVENT_CONFIGURATION_DESCRIPTOR); - } - methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, TYPE_EVENT_CONFIGURATION.getInternalName()); - methodVisitor.visitLdcInsn(index); - invokeVirtual(methodVisitor, TYPE_EVENT_CONFIGURATION, METHOD_EVENT_CONFIGURATION_GET_SETTING); - methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, si.paramType().getInternalName()); - methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, getInternalClassName(), si.methodName, "(" + si.paramType().getDescriptor() + ")Z", false); - methodVisitor.visitJumpInsn(Opcodes.IFEQ, fail); + codeBuilder.aload(0); + getEventConfiguration(codeBuilder); + codeBuilder.checkcast(TYPE_EVENT_CONFIGURATION); + codeBuilder.ldc(index); + invokevirtual(codeBuilder, TYPE_EVENT_CONFIGURATION, METHOD_EVENT_CONFIGURATION_GET_SETTING); + MethodTypeDesc mdesc = MethodTypeDesc.ofDescriptor("(" + sd.paramType().descriptorString() + ")Z"); + codeBuilder.checkcast(sd.paramType()); + codeBuilder.invokevirtual(getEventClassDesc(), sd.methodName(), mdesc); + codeBuilder.ifeq(fail); } // return true - methodVisitor.visitInsn(Opcodes.ICONST_1); - methodVisitor.visitInsn(Opcodes.IRETURN); + codeBuilder.iconst_1(); + codeBuilder.ireturn(); // return false - methodVisitor.visitLabel(fail); - methodVisitor.visitInsn(Opcodes.ICONST_0); - methodVisitor.visitInsn(Opcodes.IRETURN); + codeBuilder.labelBinding(fail); + codeBuilder.iconst_0(); + codeBuilder.ireturn(); }); if (isJDK) { if (hasStaticMethod(METHOD_ENABLED)) { updateEnabledMethod(METHOD_ENABLED); - }; - updateIfStaticMethodExists(METHOD_SHOULD_COMMIT_LONG, methodVisitor -> { - Label fail = new Label(); + } + + updateIfStaticMethodExists(METHOD_SHOULD_COMMIT_LONG, codeBuilder -> { + Label fail = codeBuilder.newLabel(); if (guardEventConfiguration) { // if (eventConfiguration == null) goto fail; - getEventConfiguration(methodVisitor); - methodVisitor.visitJumpInsn(Opcodes.IFNULL, fail); + getEventConfiguration(codeBuilder); + codeBuilder.if_null(fail); } // return eventConfiguration.shouldCommit(duration); - getEventConfiguration(methodVisitor); - methodVisitor.visitVarInsn(Opcodes.LLOAD, 0); - invokeVirtual(methodVisitor, TYPE_EVENT_CONFIGURATION, METHOD_EVENT_CONFIGURATION_SHOULD_COMMIT); - methodVisitor.visitInsn(Opcodes.IRETURN); + getEventConfiguration(codeBuilder); + codeBuilder.lload(0); + codeBuilder.invokevirtual(TYPE_EVENT_CONFIGURATION, METHOD_EVENT_CONFIGURATION_SHOULD_COMMIT.name(), METHOD_EVENT_CONFIGURATION_SHOULD_COMMIT.descriptor()); + codeBuilder.ireturn(); // fail: - methodVisitor.visitLabel(fail); + codeBuilder.labelBinding(fail); // return false - methodVisitor.visitInsn(Opcodes.ICONST_0); - methodVisitor.visitInsn(Opcodes.IRETURN); - methodVisitor.visitMaxs(0, 0); - methodVisitor.visitEnd(); + codeBuilder.iconst_0(); + codeBuilder.ireturn(); }); - updateIfStaticMethodExists(METHOD_TIME_STAMP, methodVisitor -> { - invokeStatic(methodVisitor, TYPE_EVENT_CONFIGURATION.getInternalName(), METHOD_TIME_STAMP); - methodVisitor.visitInsn(Opcodes.LRETURN); - methodVisitor.visitMaxs(0, 0); - methodVisitor.visitEnd(); + updateIfStaticMethodExists(METHOD_TIME_STAMP, codeBuilder -> { + invokestatic(codeBuilder, TYPE_EVENT_CONFIGURATION, METHOD_TIME_STAMP); + codeBuilder.lreturn(); }); } } - private void updateEnabledMethod(Method method) { - updateMethod(method, methodVisitor -> { - Label nullLabel = new Label(); + void updateStaticCommit(BlockCodeBuilder blockCodeBuilder, Label excluded) { + // indexes the argument type array, the argument type array does not include + // 'this' + int argIndex = 0; + // indexes the proper slot in the local variable table, takes type size into + // account, therefore sometimes argIndex != slotIndex + int slotIndex = 0; + int fieldIndex = 0; + ClassDesc[] argumentTypes = staticCommitMethod.descriptor().parameterArray(); + TypeKind tk = null; + getEventWriter(blockCodeBuilder); + // stack: [EW], + blockCodeBuilder.dup(); + // stack: [EW], [EW] + // write begin event + getEventConfiguration(blockCodeBuilder); + // stack: [EW], [EW], [EventConfiguration] + blockCodeBuilder.constantInstruction(Opcode.LDC2_W, eventTypeId); + // stack: [EW], [EW], [EventConfiguration] [long] + invokevirtual(blockCodeBuilder, TYPE_EVENT_WRITER, EventWriterMethod.BEGIN_EVENT.method()); + // stack: [EW], [integer] + blockCodeBuilder.ifeq(excluded); + // stack: [EW] + // write startTime + blockCodeBuilder.dup(); + // stack: [EW], [EW] + tk = TypeKind.from(argumentTypes[argIndex++]); + blockCodeBuilder.loadInstruction(tk, slotIndex); + // stack: [EW], [EW], [long] + slotIndex += tk.slotSize(); + invokevirtual(blockCodeBuilder, TYPE_EVENT_WRITER, EventWriterMethod.PUT_LONG.method()); + // stack: [EW] + fieldIndex++; + // write duration + blockCodeBuilder.dup(); + // stack: [EW], [EW] + tk = TypeKind.from(argumentTypes[argIndex++]); + blockCodeBuilder.loadInstruction(tk, slotIndex); + // stack: [EW], [EW], [long] + slotIndex += tk.slotSize(); + invokevirtual(blockCodeBuilder, TYPE_EVENT_WRITER, EventWriterMethod.PUT_LONG.method()); + // stack: [EW] + fieldIndex++; + // write eventThread + blockCodeBuilder.dup(); + // stack: [EW], [EW] + invokevirtual(blockCodeBuilder, TYPE_EVENT_WRITER, EventWriterMethod.PUT_EVENT_THREAD.method()); + // stack: [EW] + // write stackTrace + blockCodeBuilder.dup(); + // stack: [EW], [EW] + invokevirtual(blockCodeBuilder, TYPE_EVENT_WRITER, EventWriterMethod.PUT_STACK_TRACE.method()); + // stack: [EW] + // write custom fields + while (fieldIndex < fieldDescs.size()) { + blockCodeBuilder.dup(); + // stack: [EW], [EW] + tk = TypeKind.from(argumentTypes[argIndex++]); + blockCodeBuilder.loadInstruction(tk, slotIndex); + // stack:[EW], [EW], [field] + slotIndex += tk.slotSize(); + FieldDesc field = fieldDescs.get(fieldIndex); + EventWriterMethod eventMethod = EventWriterMethod.lookupMethod(field); + invokevirtual(blockCodeBuilder, TYPE_EVENT_WRITER, eventMethod.method()); + // stack: [EW] + fieldIndex++; + } + // stack: [EW] + // write end event (writer already on stack) + invokevirtual(blockCodeBuilder, TYPE_EVENT_WRITER, EventWriterMethod.END_EVENT.method()); + // stack: [int] + } + + void updateInstanceCommit(BlockCodeBuilder blockCodeBuilder, Label end, Label excluded) { + // if (!isEnable()) { + // return; + // } + blockCodeBuilder.aload(0); + invokevirtual(blockCodeBuilder, getEventClassDesc(), METHOD_IS_ENABLED); + Label l0 = blockCodeBuilder.newLabel(); + blockCodeBuilder.ifne(l0); + blockCodeBuilder.return_(); + blockCodeBuilder.labelBinding(l0); + // long startTime = this.startTime + blockCodeBuilder.aload(0); + getfield(blockCodeBuilder, getEventClassDesc(), FIELD_START_TIME); + blockCodeBuilder.lstore(1); + // if (startTime == 0) { + // startTime = EventWriter.timestamp(); + // } else { + blockCodeBuilder.lload(1); + blockCodeBuilder.lconst_0(); + blockCodeBuilder.lcmp(); + Label durationEvent = blockCodeBuilder.newLabel(); + blockCodeBuilder.ifne(durationEvent); + invokestatic(blockCodeBuilder, TYPE_EVENT_CONFIGURATION, METHOD_TIME_STAMP); + blockCodeBuilder.lstore(1); + Label commit = blockCodeBuilder.newLabel(); + blockCodeBuilder.goto_(commit); + // if (duration == 0) { + // duration = EventWriter.timestamp() - startTime; + // } + // } + blockCodeBuilder.labelBinding(durationEvent); + blockCodeBuilder.aload(0); + getfield(blockCodeBuilder, getEventClassDesc(), FIELD_DURATION); + blockCodeBuilder.lconst_0(); + blockCodeBuilder.lcmp(); + blockCodeBuilder.ifne(commit); + blockCodeBuilder.aload(0); + invokestatic(blockCodeBuilder, TYPE_EVENT_CONFIGURATION, METHOD_TIME_STAMP); + blockCodeBuilder.lload(1); + blockCodeBuilder.lsub(); + putfield(blockCodeBuilder, getEventClassDesc(), FIELD_DURATION); + blockCodeBuilder.labelBinding(commit); + // if (shouldCommit()) { + blockCodeBuilder.aload(0); + invokevirtual(blockCodeBuilder, getEventClassDesc(), METHOD_EVENT_SHOULD_COMMIT); + blockCodeBuilder.ifeq(end); + getEventWriter(blockCodeBuilder); + // stack: [EW] + blockCodeBuilder.dup(); + // stack: [EW] [EW] + getEventConfiguration(blockCodeBuilder); + // stack: [EW] [EW] [EC] + blockCodeBuilder.constantInstruction(Opcode.LDC2_W, eventTypeId); + invokevirtual(blockCodeBuilder, TYPE_EVENT_WRITER, EventWriterMethod.BEGIN_EVENT.method()); + // stack: [EW] [int] + blockCodeBuilder.ifeq(excluded); + // stack: [EW] + int fieldIndex = 0; + blockCodeBuilder.dup(); + // stack: [EW] [EW] + blockCodeBuilder.lload(1); + // stack: [EW] [EW] [long] + invokevirtual(blockCodeBuilder, TYPE_EVENT_WRITER, EventWriterMethod.PUT_LONG.method()); + // stack: [EW] + fieldIndex++; + blockCodeBuilder.dup(); + // stack: [EW] [EW] + blockCodeBuilder.aload(0); + // stack: [EW] [EW] [this] + getfield(blockCodeBuilder, getEventClassDesc(), FIELD_DURATION); + // stack: [EW] [EW] [long] + invokevirtual(blockCodeBuilder, TYPE_EVENT_WRITER, EventWriterMethod.PUT_LONG.method()); + // stack: [EW] + fieldIndex++; + blockCodeBuilder.dup(); + // stack: [EW] [EW] + invokevirtual(blockCodeBuilder, TYPE_EVENT_WRITER, EventWriterMethod.PUT_EVENT_THREAD.method()); + // stack: [EW] + blockCodeBuilder.dup(); + // stack: [EW] [EW] + invokevirtual(blockCodeBuilder, TYPE_EVENT_WRITER, EventWriterMethod.PUT_STACK_TRACE.method()); + // stack: [EW] + while (fieldIndex < fieldDescs.size()) { + FieldDesc field = fieldDescs.get(fieldIndex); + blockCodeBuilder.dup(); + // stack: [EW] [EW] + blockCodeBuilder.aload(0); + // stack: [EW] [EW] [this] + getfield(blockCodeBuilder, getEventClassDesc(), field); + // stack: [EW] [EW] + EventWriterMethod eventMethod = EventWriterMethod.lookupMethod(field); + invokevirtual(blockCodeBuilder, TYPE_EVENT_WRITER, eventMethod.method()); + // stack: [EW] + fieldIndex++; + } + // stack:[EW] + invokevirtual(blockCodeBuilder, TYPE_EVENT_WRITER, EventWriterMethod.END_EVENT.method()); + // stack:[int] + } + + private void updateEnabledMethod(MethodDesc method) { + updateMethod(method, codeBuilder -> { + Label nullLabel = codeBuilder.newLabel(); if (guardEventConfiguration) { - getEventConfiguration(methodVisitor); - methodVisitor.visitJumpInsn(Opcodes.IFNULL, nullLabel); + getEventConfiguration(codeBuilder); + codeBuilder.branchInstruction(Opcode.IFNULL, nullLabel); } - getEventConfiguration(methodVisitor); - invokeVirtual(methodVisitor, TYPE_EVENT_CONFIGURATION, METHOD_IS_ENABLED); - methodVisitor.visitInsn(Opcodes.IRETURN); + getEventConfiguration(codeBuilder); + invokevirtual(codeBuilder, TYPE_EVENT_CONFIGURATION, METHOD_IS_ENABLED); + codeBuilder.ireturn(); if (guardEventConfiguration) { - methodVisitor.visitLabel(nullLabel); - methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null); - methodVisitor.visitInsn(Opcodes.ICONST_0); - methodVisitor.visitInsn(Opcodes.IRETURN); + codeBuilder.labelBinding(nullLabel); + codeBuilder.iconst_0(); + codeBuilder.ireturn(); } - methodVisitor.visitMaxs(0, 0); - methodVisitor.visitEnd(); }); } - private void updateIfStaticMethodExists(Method method, Consumer code) { + private void updateIfStaticMethodExists(MethodDesc method, Consumer code) { if (hasStaticMethod(method)) { updateMethod(method, code); } } - private boolean hasStaticMethod(Method method) { - for (MethodNode m : classNode.methods) { - if (m.name.equals(method.getName()) && m.desc.equals(method.getDescriptor())) { - return Modifier.isStatic(m.access); + private boolean hasStaticMethod(MethodDesc method) { + for (MethodModel m : classModel.methods()) { + if (m.methodName().equalsString(method.name()) && m.methodTypeSymbol().equals(method.descriptor())) { + return Modifier.isStatic(m.flags().flagsMask()); } } return false; } - private void getEventWriter(MethodVisitor mv) { - mv.visitLdcInsn(EventWriterKey.getKey()); - visitMethod(mv, Opcodes.INVOKESTATIC, TYPE_EVENT_WRITER_FACTORY, METHOD_GET_EVENT_WRITER_KEY); + private void getEventWriter(CodeBuilder codeBuilder) { + codeBuilder.ldc(EventWriterKey.getKey()); + invokestatic(codeBuilder, TYPE_EVENT_WRITER_FACTORY, METHOD_GET_EVENT_WRITER_KEY); } - private void visitMethod(final MethodVisitor mv, final int opcode, final Type type, final Method method) { - mv.visitMethodInsn(opcode, type.getInternalName(), method.getName(), method.getDescriptor(), false); - } - - private static void invokeStatic(MethodVisitor methodVisitor, String className, Method m) { - methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, className, m.getName(), m.getDescriptor(), false); - } - - private static void invokeVirtual(MethodVisitor methodVisitor, String className, Method m) { - methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, className, m.getName(), m.getDescriptor(), false); - } - - private void invokeVirtual(MethodVisitor methodVisitor, Type type, Method method) { - methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, type.getInternalName(), method.getName(), method.getDescriptor(), false); - } - - private void getEventConfiguration(MethodVisitor methodVisitor) { + private void getEventConfiguration(CodeBuilder codeBuilder) { if (untypedEventConfiguration) { - methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_CONFIGURATION, TYPE_OBJECT_DESCRIPTOR); + codeBuilder.getstatic(getEventClassDesc(), FIELD_EVENT_CONFIGURATION.name(), TYPE_OBJECT); } else { - methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_CONFIGURATION, TYPE_EVENT_CONFIGURATION_DESCRIPTOR); + codeBuilder.getstatic(getEventClassDesc(), FIELD_EVENT_CONFIGURATION.name(), TYPE_EVENT_CONFIGURATION); } } @@ -754,43 +728,30 @@ private void makeUninstrumented() { updateExistingWithEmptyVoidMethod(METHOD_END); } - private final void updateExistingWithEmptyVoidMethod(Method voidMethod) { - updateMethod(voidMethod, methodVisitor -> { - methodVisitor.visitInsn(Opcodes.RETURN); + private final void updateExistingWithEmptyVoidMethod(MethodDesc voidMethod) { + updateMethod(voidMethod, codeBuilder -> { + codeBuilder.return_(); }); } - private final void updateExistingWithReturnFalse(Method voidMethod) { - updateMethod(voidMethod, methodVisitor -> { - methodVisitor.visitInsn(Opcodes.ICONST_0); - methodVisitor.visitInsn(Opcodes.IRETURN); + private final void updateExistingWithReturnFalse(MethodDesc voidMethod) { + updateMethod(voidMethod, codeBuilder -> { + codeBuilder.iconst_0(); + codeBuilder.ireturn(); }); } - private MethodNode getMethodNode(Method method) { - for (MethodNode m : classNode.methods) { - if (m.name.equals(method.getName()) && m.desc.equals(method.getDescriptor())) { - return m; - } - } - return null; + private Consumer findMethodUpdate(MethodModel mm) { + MethodDesc m = MethodDesc.of(mm.methodName().stringValue(), mm.methodType().stringValue()); + return methodUpdates.get(m); } - private final void updateMethod(Method method, Consumer code) { - MethodNode old = getMethodNode(method); - int index = classNode.methods.indexOf(old); - classNode.methods.remove(old); - MethodVisitor mv = classNode.visitMethod(old.access, old.name, old.desc, null, null); - mv.visitCode(); - code.accept(mv); - mv.visitMaxs(0, 0); - MethodNode newMethod = getMethodNode(method); - classNode.methods.remove(newMethod); - classNode.methods.add(index, newMethod); + private void updateMethod(MethodDesc method, Consumer codeBuilder) { + methodUpdates.put(method, codeBuilder); } - private String getInternalClassName() { - return classNode.name; + private ClassDesc getEventClassDesc() { + return classModel.thisClass().asSymbol(); } public String getEventName() { diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/EventWriterMethod.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/EventWriterMethod.java index 5497aa19455ee..e1cbbb99d5661 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/EventWriterMethod.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/EventWriterMethod.java @@ -25,38 +25,38 @@ package jdk.jfr.internal; -import jdk.internal.org.objectweb.asm.commons.Method; -import jdk.jfr.internal.EventInstrumentation.FieldInfo; -import jdk.jfr.internal.event.EventConfiguration; +import jdk.jfr.internal.util.Bytecode.FieldDesc; +import jdk.jfr.internal.util.Bytecode.MethodDesc; +import jdk.jfr.internal.util.Utils; public enum EventWriterMethod { - BEGIN_EVENT("(" + jdk.internal.org.objectweb.asm.Type.getType(EventConfiguration.class).getDescriptor() + "J)Z", "???", "beginEvent"), - END_EVENT("()Z", "???", "endEvent"), - PUT_BYTE("(B)V", "byte", "putByte"), - PUT_SHORT("(S)V", "short", "putShort"), - PUT_INT("(I)V", "int", "putInt"), - PUT_LONG("(J)V", "long", "putLong"), - PUT_FLOAT("(F)V", "float", "putFloat"), - PUT_DOUBLE("(D)V", "double", "putDouble"), - PUT_CHAR("(C)V", "char", "putChar"), - PUT_BOOLEAN("(Z)V", "boolean", "putBoolean"), - PUT_THREAD("(Ljava/lang/Thread;)V", Type.THREAD.getName(), "putThread"), - PUT_CLASS("(Ljava/lang/Class;)V", Type.CLASS.getName(), "putClass"), - PUT_STRING("(Ljava/lang/String;)V", Type.STRING.getName(), "putString"), - PUT_EVENT_THREAD("()V", Type.THREAD.getName(), "putEventThread"), - PUT_STACK_TRACE("()V", Type.TYPES_PREFIX + "StackTrace", "putStackTrace"); + BEGIN_EVENT("beginEvent", "(Ljdk/jfr/internal/event/EventConfiguration;J)Z", "???"), + END_EVENT("endEvent", "()Z", "???"), + PUT_BYTE("putByte", "(B)V", "B"), + PUT_SHORT("putShort", "(S)V", "S"), + PUT_INT("putInt", "(I)V", "I"), + PUT_LONG("putLong", "(J)V", "J"), + PUT_FLOAT("putFloat", "(F)V", "F"), + PUT_DOUBLE("putDouble", "(D)V", "D"), + PUT_CHAR("putChar", "(C)V", "C"), + PUT_BOOLEAN("putBoolean", "(Z)V", "Z"), + PUT_THREAD("putThread", "(Ljava/lang/Thread;)V", "Ljava/lang/Thread;"), + PUT_CLASS("putClass", "(Ljava/lang/Class;)V", "Ljava/lang/Class;"), + PUT_STRING("putString", "(Ljava/lang/String;)V", "Ljava/lang/String;"), + PUT_EVENT_THREAD("putEventThread", "()V", "???"), + PUT_STACK_TRACE("putStackTrace", "()V", "???"); - final Method asmMethod; - final String typeDescriptor; + final MethodDesc method; + final String fieldType; - EventWriterMethod(String paramSignature, String typeName, String methodName) { - this.typeDescriptor = ASMToolkit.getDescriptor(typeName); - this.asmMethod = new Method(methodName, paramSignature); + EventWriterMethod(String methodName, String paramType, String fieldType) { + this.fieldType = fieldType; + this.method = MethodDesc.of(methodName, paramType); } - public Method asASM() { - return asmMethod; + public MethodDesc method() { + return method; } /** @@ -67,16 +67,16 @@ public Method asASM() { * * @return the method */ - public static EventWriterMethod lookupMethod(FieldInfo field) { + public static EventWriterMethod lookupMethod(FieldDesc field) { // event thread - if (field.name().equals(EventInstrumentation.FIELD_EVENT_THREAD)) { + if (field.name().equals(Utils.FIELD_EVENT_THREAD)) { return EventWriterMethod.PUT_EVENT_THREAD; } for (EventWriterMethod m : EventWriterMethod.values()) { - if (field.descriptor().equals(m.typeDescriptor)) { + if (field.type().descriptorString().equals(m.fieldType)) { return m; } } - throw new Error("Unknown type " + field.descriptor()); + throw new Error("Unknown field type " + field.type()); } } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/JVMUpcalls.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/JVMUpcalls.java index 91f2e3980c204..38ac6a680bf78 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/JVMUpcalls.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/JVMUpcalls.java @@ -28,7 +28,7 @@ import jdk.jfr.internal.event.EventConfiguration; import jdk.jfr.internal.instrument.JDKEvents; -import jdk.jfr.internal.util.Utils; +import jdk.jfr.internal.util.Bytecode; /** * All upcalls from the JVM should go through this class. * @@ -75,7 +75,7 @@ static byte[] onRetransform(long traceId, boolean dummy1, boolean dummy2, Class< Logger.log(LogTag.JFR_SYSTEM, LogLevel.INFO, "Adding instrumentation to event class " + clazz.getName() + " using retransform"); EventInstrumentation ei = new EventInstrumentation(clazz.getSuperclass(), oldBytes, traceId, bootClassLoader, false); byte[] bytes = ei.buildInstrumented(); - ASMToolkit.logASM(clazz.getName(), bytes); + Bytecode.log(clazz.getName(), bytes); return bytes; } return JDKEvents.retransformCallback(clazz, oldBytes); @@ -126,7 +126,7 @@ static byte[] bytesForEagerInstrumentation(long traceId, boolean forceInstrument EventWriterKey.ensureEventWriterFactory(); Logger.log(LogTag.JFR_SYSTEM, LogLevel.INFO, "Adding " + (forceInstrumentation ? "forced " : "") + "instrumentation for event type " + eventName + " during initial class load"); byte[] bytes = ei.buildInstrumented(); - ASMToolkit.logASM(ei.getClassName() + "(" + traceId + ")", bytes); + Bytecode.log(ei.getClassName() + "(" + traceId + ")", bytes); return bytes; } catch (Throwable t) { Logger.log(LogTag.JFR_SYSTEM, LogLevel.WARN, "Unexpected error when adding instrumentation for event type " + eventName); diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/TypeLibrary.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/TypeLibrary.java index 42c74ee64c9f5..d858c56354e60 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/TypeLibrary.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/TypeLibrary.java @@ -76,26 +76,26 @@ private TypeLibrary() { private static ValueDescriptor createStartTimeField() { var annos = createStandardAnnotations("Start Time", null); annos.add(new jdk.jfr.AnnotationElement(Timestamp.class, Timestamp.TICKS)); - return PrivateAccess.getInstance().newValueDescriptor(EventInstrumentation.FIELD_START_TIME, Type.LONG, annos, 0, false, - EventInstrumentation.FIELD_START_TIME); + return PrivateAccess.getInstance().newValueDescriptor(Utils.FIELD_START_TIME, Type.LONG, annos, 0, false, + Utils.FIELD_START_TIME); } private static ValueDescriptor createStackTraceField() { var annos = createStandardAnnotations("Stack Trace", "Stack Trace starting from the method the event was committed in"); - return PrivateAccess.getInstance().newValueDescriptor(EventInstrumentation.FIELD_STACK_TRACE, Type.STACK_TRACE, annos, 0, true, - EventInstrumentation.FIELD_STACK_TRACE); + return PrivateAccess.getInstance().newValueDescriptor(Utils.FIELD_STACK_TRACE, Type.STACK_TRACE, annos, 0, true, + Utils.FIELD_STACK_TRACE); } private static ValueDescriptor createThreadField() { var annos = createStandardAnnotations("Event Thread", "Thread in which event was committed in"); - return PrivateAccess.getInstance().newValueDescriptor(EventInstrumentation.FIELD_EVENT_THREAD, Type.THREAD, annos, 0, true, - EventInstrumentation.FIELD_EVENT_THREAD); + return PrivateAccess.getInstance().newValueDescriptor(Utils.FIELD_EVENT_THREAD, Type.THREAD, annos, 0, true, + Utils.FIELD_EVENT_THREAD); } private static ValueDescriptor createDurationField() { var annos = createStandardAnnotations("Duration", null); annos.add(new jdk.jfr.AnnotationElement(Timespan.class, Timespan.TICKS)); - return PrivateAccess.getInstance().newValueDescriptor(EventInstrumentation.FIELD_DURATION, Type.LONG, annos, 0, false, EventInstrumentation.FIELD_DURATION); + return PrivateAccess.getInstance().newValueDescriptor(Utils.FIELD_DURATION, Type.LONG, annos, 0, false, Utils.FIELD_DURATION); } public static synchronized void initialize() { diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/EventParser.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/EventParser.java index c77c30eb62276..451dd4263680d 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/EventParser.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/EventParser.java @@ -25,7 +25,7 @@ package jdk.jfr.internal.consumer; -import static jdk.jfr.internal.EventInstrumentation.FIELD_DURATION; +import static jdk.jfr.internal.util.Utils.FIELD_DURATION; import java.io.IOException; import java.util.List; diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/util/Bytecode.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/util/Bytecode.java new file mode 100644 index 0000000000000..76dd8850a29b2 --- /dev/null +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/util/Bytecode.java @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. 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 jdk.jfr.internal.util; + +import jdk.jfr.ValueDescriptor; +import java.lang.constant.ClassDesc; +import java.lang.constant.ConstantDescs; +import java.lang.constant.MethodTypeDesc; +import java.util.Objects; +import jdk.jfr.internal.Logger; +import jdk.jfr.internal.LogLevel; +import jdk.jfr.internal.LogTag; +import jdk.internal.classfile.CodeBuilder; +import jdk.internal.classfile.ClassModel; +import jdk.internal.classfile.Classfile; +import jdk.internal.classfile.components.ClassPrinter; + +/** + * Helper class when working with bytecode. + */ +public final class Bytecode { + + private static final ClassDesc CD_Thread = classDesc(Thread.class); + + public record ClassMethodDesc(ClassDesc type, MethodDesc method) { + public static ClassMethodDesc of(Class clazz, String method, String desrciptor) { + return new ClassMethodDesc(classDesc(clazz), MethodDesc.of(method, desrciptor)); + } + } + + public record FieldDesc(ClassDesc type, String name) { + public static FieldDesc of(ClassDesc type, String name) { + return new FieldDesc(type, name); + } + + public static FieldDesc of(Class type, String name) { + return of(classDesc(type), name); + } + } + + public record MethodDesc(String name, MethodTypeDesc descriptor) { + public static MethodDesc of(String methodName, String descriptor) { + return new MethodDesc(methodName, MethodTypeDesc.ofDescriptor(descriptor)); + } + + public static MethodDesc of(String methodName, Class returnType, Class... parameters) { + ClassDesc[] parameterDesc = new ClassDesc[parameters.length]; + for (int i = 0; i < parameterDesc.length; i++) { + parameterDesc[i] = classDesc(parameters[i]); + } + ClassDesc returnDesc = classDesc(returnType); + MethodTypeDesc mtd = MethodTypeDesc.of(returnDesc, parameterDesc); + return new MethodDesc(methodName, mtd); + } + } + + public static ClassDesc classDesc(ValueDescriptor v) { + String typeName = v.getTypeName(); + return switch (typeName) { + case "boolean" -> ConstantDescs.CD_boolean; + case "byte" -> ConstantDescs.CD_byte; + case "short" -> ConstantDescs.CD_short; + case "char" -> ConstantDescs.CD_char; + case "int" -> ConstantDescs.CD_int; + case "long" -> ConstantDescs.CD_long; + case "double" -> ConstantDescs.CD_double; + case "float" -> ConstantDescs.CD_float; + case "java.lang.String" -> ConstantDescs.CD_String; + case "java.lang.Class" -> ConstantDescs.CD_Class; + case "java.lang.Thread" -> CD_Thread; + default -> throw new InternalError("Unsupported JFR type " + v.getTypeName()); + }; + } + + public static ClassDesc classDesc(Class clazz) { + return ClassDesc.ofDescriptor(clazz.descriptorString()); + } + + public static void getfield(CodeBuilder codeBuilder, ClassDesc owner, FieldDesc field) { + codeBuilder.getfield(owner, field.name(), field.type()); + } + + public static void putfield(CodeBuilder codeBuilder, ClassDesc owner, FieldDesc field) { + codeBuilder.putfield(owner, field.name(), field.type()); + } + + public static void invokestatic(CodeBuilder codeBuilder, ClassDesc owner, MethodDesc method) { + codeBuilder.invokestatic(owner, method.name(), method.descriptor()); + } + + public static void invokespecial(CodeBuilder codeBuilder, ClassDesc owner, MethodDesc method) { + codeBuilder.invokespecial(owner, method.name(), method.descriptor()); + } + + public static void invokevirtual(CodeBuilder codeBuilder, ClassDesc owner, MethodDesc method) { + codeBuilder.invokevirtual(owner, method.name(), method.descriptor()); + } + + public static void invokevirtual(CodeBuilder codeBuilder, ClassMethodDesc cmd) { + invokevirtual(codeBuilder, cmd.type(), cmd.method()); + } + + public static void unbox(CodeBuilder codeBuilder, ClassDesc type) { + if (!type.isPrimitive()) { + codeBuilder.checkcast(type); + return; + } + ClassMethodDesc unboxer = switch (type.descriptorString()) { + case "B" -> ClassMethodDesc.of(Byte.class, "byteValue", "()B"); + case "S" -> ClassMethodDesc.of(Short.class, "shortValue", "()S"); + case "C" -> ClassMethodDesc.of(Character.class, "charValue", "()C"); + case "I" -> ClassMethodDesc.of(Integer.class, "intValue", "()I"); + case "J" -> ClassMethodDesc.of(Long.class, "longValue", "()J"); + case "F" -> ClassMethodDesc.of(Float.class, "floatValue", "()F"); + case "D" -> ClassMethodDesc.of(Double.class, "doubleValue", "()D"); + case "Z" -> ClassMethodDesc.of(Boolean.class, "booleanValue", "()Z"); + default -> throw new InternalError("Unsupported JFR type " + type.descriptorString()); + }; + codeBuilder.checkcast(unboxer.type()); + invokevirtual(codeBuilder, unboxer); + } + + public static void throwException(CodeBuilder cb, ClassDesc type, String message) { + Objects.requireNonNull(message); + cb.new_(type); + cb.dup(); + cb.ldc(message); + MethodDesc md = MethodDesc.of("", void.class, String.class); + invokespecial(cb, type, md); + cb.athrow(); + } + + public static void log(String className, byte[] bytes) { + Logger.log(LogTag.JFR_SYSTEM_BYTECODE, LogLevel.INFO, "Generated bytecode for class " + className); + if (Logger.shouldLog(LogTag.JFR_SYSTEM_BYTECODE, LogLevel.TRACE)) { + StringBuilder out = new StringBuilder(); + out.append("Bytecode:"); + out.append(System.lineSeparator()); + ClassModel classModel = Classfile.of().parse(bytes); + ClassPrinter.toYaml(classModel, ClassPrinter.Verbosity.TRACE_ALL, out::append); + Logger.log(LogTag.JFR_SYSTEM_BYTECODE, LogLevel.TRACE, out.toString()); + } + } +} diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/util/Utils.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/util/Utils.java index 1b73971298579..4be05abca08a0 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/util/Utils.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/util/Utils.java @@ -56,6 +56,11 @@ import jdk.jfr.internal.settings.ThresholdSetting; public final class Utils { + public static final String FIELD_DURATION = "duration"; + public static final String FIELD_STACK_TRACE = "stackTrace"; + public static final String FIELD_START_TIME = "startTime"; + public static final String FIELD_EVENT_THREAD = "eventThread"; + private static final Object flushObject = new Object(); private static final String LEGACY_EVENT_NAME_PREFIX = "com.oracle.jdk."; diff --git a/test/jdk/jdk/jfr/jvm/TestEventWriterLog.java b/test/jdk/jdk/jfr/jvm/TestEventWriterLog.java index a242eede55939..9f11ee0486399 100644 --- a/test/jdk/jdk/jfr/jvm/TestEventWriterLog.java +++ b/test/jdk/jdk/jfr/jvm/TestEventWriterLog.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 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 @@ -37,6 +37,6 @@ public class TestEventWriterLog { public static void main(String[] args) throws Exception { ProcessBuilder pb = ProcessTools.createJavaProcessBuilder("-Xlog:jfr+system+bytecode=trace", "-XX:StartFlightRecording", "-version"); OutputAnalyzer output = new OutputAnalyzer(pb.start()); - output.shouldContain("extends jdk/jfr/events/AbstractJDKEvent"); + output.shouldContain("superclass: jdk/jfr/events/AbstractJDKEvent"); } } From 8d9a4b43f4fff30fd217dab2c224e641cb913c18 Mon Sep 17 00:00:00 2001 From: Sean Coffey Date: Mon, 23 Oct 2023 17:12:41 +0000 Subject: [PATCH 05/28] 8317678: Fix up hashCode() for ZipFile.Source.Key Reviewed-by: lancea, alanb, jpai --- .../share/classes/java/util/zip/ZipFile.java | 11 +- .../java/util/zip/ZipFile/ZipSourceCache.java | 162 ++++++++++++++++++ .../bench/java/util/zip/ZipFileOpen.java | 17 ++ 3 files changed, 189 insertions(+), 1 deletion(-) create mode 100644 test/jdk/java/util/zip/ZipFile/ZipSourceCache.java diff --git a/src/java.base/share/classes/java/util/zip/ZipFile.java b/src/java.base/share/classes/java/util/zip/ZipFile.java index cb9070fc885d3..bbcd3cdd71289 100644 --- a/src/java.base/share/classes/java/util/zip/ZipFile.java +++ b/src/java.base/share/classes/java/util/zip/ZipFile.java @@ -1395,6 +1395,13 @@ private static boolean isZip64ExtBlockSizeValid(int blockSize) { private int[] table; // Hash chain heads: indexes into entries private int tablelen; // number of hash heads + /** + * A class representing a key to a zip file. A key is based + * on the file key if available, or the path value if the + * file key is not available. The key is also based on the + * file's last modified time to allow for cases where a zip + * file is re-opened after it has been modified. + */ private static class Key { final BasicFileAttributes attrs; File file; @@ -1409,7 +1416,9 @@ public Key(File file, BasicFileAttributes attrs, ZipCoder zc) { public int hashCode() { long t = utf8 ? 0 : Long.MAX_VALUE; t += attrs.lastModifiedTime().toMillis(); - return ((int)(t ^ (t >>> 32))) + file.hashCode(); + Object fk = attrs.fileKey(); + return Long.hashCode(t) + + (fk != null ? fk.hashCode() : file.hashCode()); } public boolean equals(Object obj) { diff --git a/test/jdk/java/util/zip/ZipFile/ZipSourceCache.java b/test/jdk/java/util/zip/ZipFile/ZipSourceCache.java new file mode 100644 index 0000000000000..8cb1051a87c77 --- /dev/null +++ b/test/jdk/java/util/zip/ZipFile/ZipSourceCache.java @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8317678 + * @modules java.base/java.util.zip:open + * @summary Fix up hashCode() for ZipFile.Source.Key + * @run junit/othervm ZipSourceCache + */ + +import java.io.*; +import java.lang.reflect.*; +import java.nio.file.*; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.*; +import java.util.zip.*; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.*; + +public class ZipSourceCache { + + private static final String ZIPFILE_NAME = + System.currentTimeMillis() + "-bug8317678.zip"; + private static final String ZIPENTRY_NAME = "random.txt"; + private static final String INVALID_LOC_EXCEPTION = + "ZipFile invalid LOC header (bad signature)"; + private static final boolean DEBUG = false; + + private static File relativeFile = new File(ZIPFILE_NAME); + private static File absoluteFile = new File(ZIPFILE_NAME).getAbsoluteFile(); + private static boolean hasfileKeySupport; + + @BeforeAll + public static void setup() throws Exception { + createZipFile("test1"); + var attrs = Files.readAttributes(relativeFile.toPath(), BasicFileAttributes.class); + hasfileKeySupport = (attrs.fileKey() != null); + } + + @AfterAll + public static void cleanup() throws IOException { + Files.deleteIfExists(Path.of(ZIPFILE_NAME)); + } + + /* + * Monitor the internal "files" HashMap to ensure that we only + * create one mapping per unique zip file. + * + * This test also ensures that a new mapping is created + * if an update to an existing zip file is detected. + */ + @Test + public void testKeySourceMapping() throws Exception { + ZipFile absoluteZipFile; + HashMap internalMap; + int numSources; + try (ZipFile zipFile = new ZipFile(relativeFile)) { + Class source = Class.forName("java.util.zip.ZipFile$Source"); + Field filesMap = source.getDeclaredField("files"); + filesMap.setAccessible(true); + internalMap = (HashMap) filesMap.get(zipFile); + numSources = internalMap.size(); + // opening of same zip file shouldn't cause new Source + // to be constructed on filesystems which support fileKey() + absoluteZipFile = new ZipFile(absoluteFile); + if (hasfileKeySupport) { + assertEquals(numSources, internalMap.size()); + } else { + assertEquals(++numSources, internalMap.size()); + } + + // update the zip file, should expect a new Source Object + // ignore this part of test if file can't be updated (can't overwrite) + if (createZipFile("differentContent")) { + ZipFile z = new ZipFile(relativeFile); + // update of file should trigger new mapping + assertEquals(++numSources, internalMap.size()); + // new Source created, CEN structure should map fine + readZipFileContents(z); + // the old Source in use for old file, should no longer map correctly + IOException ioe = assertThrows(IOException.class, () -> readZipFileContents(absoluteZipFile)); + assertEquals(INVALID_LOC_EXCEPTION, ioe.getMessage()); + z.close(); + assertEquals(--numSources, internalMap.size()); + } + } + // with fileKey() support, the close() call shouldn't remove the + // Source entry just yet since we still have one reference to the file + if (hasfileKeySupport) { + assertEquals(numSources, internalMap.size()); + } else { + assertEquals(--numSources, internalMap.size()); + } + if (absoluteZipFile != null) { + absoluteZipFile.close(); + } + // now, the Source entry key should be removed + assertEquals(--numSources, internalMap.size()); + } + + private static void readZipFileContents(ZipFile zf) throws IOException { + var e = zf.entries(); + while (e.hasMoreElements()) { + InputStream is = zf.getInputStream(e.nextElement()); + String s = new String(is.readAllBytes()); + if (DEBUG) System.err.println(s); + } + } + + private static boolean createZipFile(String content) { + CRC32 crc32 = new CRC32(); + long t = System.currentTimeMillis(); + // let's have at least 2 entries created to ensure + // that a bad zip structure is detected if file is updated + int numEntries = new Random().nextInt(10) + 2; + File zipFile = new File(ZIPFILE_NAME); + try (FileOutputStream fos = new FileOutputStream(zipFile); + BufferedOutputStream bos = new BufferedOutputStream(fos); + ZipOutputStream zos = new ZipOutputStream(bos)) { + for (int i = 0; i < numEntries; i++) { + ZipEntry e = new ZipEntry(ZIPENTRY_NAME + i); + e.setMethod(ZipEntry.STORED); + byte[] toWrite = content.repeat(i+1).getBytes(); + e.setTime(t); + e.setSize(toWrite.length); + crc32.reset(); + crc32.update(toWrite); + e.setCrc(crc32.getValue()); + zos.putNextEntry(e); + zos.write(toWrite); + } + } catch (IOException e) { + // some systems mightn't allow file to be updated while open + System.err.println("error updating file. " + e); + return false; + } + return true; + } +} diff --git a/test/micro/org/openjdk/bench/java/util/zip/ZipFileOpen.java b/test/micro/org/openjdk/bench/java/util/zip/ZipFileOpen.java index ffbbc3d245ff9..e450fcfe9a0ae 100644 --- a/test/micro/org/openjdk/bench/java/util/zip/ZipFileOpen.java +++ b/test/micro/org/openjdk/bench/java/util/zip/ZipFileOpen.java @@ -29,6 +29,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.nio.file.Files; +import java.nio.file.Path; import java.util.concurrent.TimeUnit; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; @@ -50,6 +51,7 @@ public class ZipFileOpen { private int size; public File zipFile; + public File relativePathFile; @Setup(Level.Trial) public void beforeRun() throws IOException { @@ -74,6 +76,8 @@ public void beforeRun() throws IOException { } } zipFile = tempFile; + relativePathFile = Path.of(System.getProperty("user.dir")) + .relativize(zipFile.toPath()).toFile(); } @Benchmark @@ -90,4 +94,17 @@ public ZipFile openCloseZipFile() throws Exception { zf.close(); return zf; } + + @Benchmark + public void openCloseZipFilex2() throws Exception { + // A follow on from the openCloseZipFile benchmark. + // The initCEN logic should be called once per file, if + // opened multiple times and not closed, for the ZipFile + // under test if that file is identified by a unique value + // returned via attrs.fileKey() + ZipFile zf = new ZipFile(zipFile); + ZipFile zf2 = new ZipFile(relativePathFile); + zf.close(); + zf2.close(); + } } From 5ba9705d60fb3e3bab832aadd989b738471fcaf5 Mon Sep 17 00:00:00 2001 From: Thomas Stuefe Date: Mon, 23 Oct 2023 21:10:52 +0000 Subject: [PATCH 06/28] 8318485: Narrow klass shift should be zero if encoding range extends to 0x1_0000_0000 Reviewed-by: ccheung, iklam --- src/hotspot/os/linux/os_linux.cpp | 1 - src/hotspot/share/memory/metaspace.cpp | 5 ++ src/hotspot/share/oops/compressedKlass.cpp | 2 +- test/hotspot/gtest/runtime/test_os.cpp | 2 +- ...CompressedClassPointersEncodingScheme.java | 81 +++++++++++++++++++ .../jdk/test/lib/process/OutputAnalyzer.java | 21 +++++ 6 files changed, 109 insertions(+), 3 deletions(-) create mode 100644 test/hotspot/jtreg/runtime/CompressedOops/CompressedClassPointersEncodingScheme.java diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index 685fa174ac96a..db942feabcf52 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -5300,7 +5300,6 @@ void os::print_memory_mappings(char* addr, size_t bytes, outputStream* st) { if (num_found == 0) { st->print_cr("nothing."); } - st->cr(); } } diff --git a/src/hotspot/share/memory/metaspace.cpp b/src/hotspot/share/memory/metaspace.cpp index 2145bb68b3346..b0d47dcce0d16 100644 --- a/src/hotspot/share/memory/metaspace.cpp +++ b/src/hotspot/share/memory/metaspace.cpp @@ -778,6 +778,11 @@ void Metaspace::global_initialize() { if (rs.is_reserved()) { log_info(metaspace)("Successfully forced class space address to " PTR_FORMAT, p2i(base)); } else { + LogTarget(Debug, metaspace) lt; + if (lt.is_enabled()) { + LogStream ls(lt); + os::print_memory_mappings((char*)base, size, &ls); + } vm_exit_during_initialization( err_msg("CompressedClassSpaceBaseAddress=" PTR_FORMAT " given, but reserving class space failed.", CompressedClassSpaceBaseAddress)); diff --git a/src/hotspot/share/oops/compressedKlass.cpp b/src/hotspot/share/oops/compressedKlass.cpp index f9750c1b7c082..947085b02e3fa 100644 --- a/src/hotspot/share/oops/compressedKlass.cpp +++ b/src/hotspot/share/oops/compressedKlass.cpp @@ -80,7 +80,7 @@ void CompressedKlassPointers::initialize(address addr, size_t len) { // We may not even need a shift if the range fits into 32bit: const uint64_t UnscaledClassSpaceMax = (uint64_t(max_juint) + 1); - if (range < UnscaledClassSpaceMax) { + if (range <= UnscaledClassSpaceMax) { shift = 0; } else { shift = LogKlassAlignmentInBytes; diff --git a/test/hotspot/gtest/runtime/test_os.cpp b/test/hotspot/gtest/runtime/test_os.cpp index be6e22f5f4e96..035210218c46b 100644 --- a/test/hotspot/gtest/runtime/test_os.cpp +++ b/test/hotspot/gtest/runtime/test_os.cpp @@ -377,7 +377,7 @@ static inline bool can_reserve_executable_memory(void) { #endif // Test that os::release_memory() can deal with areas containing multiple mappings. -#define PRINT_MAPPINGS(s) { tty->print_cr("%s", s); os::print_memory_mappings((char*)p, total_range_len, tty); } +#define PRINT_MAPPINGS(s) { tty->print_cr("%s", s); os::print_memory_mappings((char*)p, total_range_len, tty); tty->cr(); } //#define PRINT_MAPPINGS // Release a range allocated with reserve_multiple carefully, to not trip mapping diff --git a/test/hotspot/jtreg/runtime/CompressedOops/CompressedClassPointersEncodingScheme.java b/test/hotspot/jtreg/runtime/CompressedOops/CompressedClassPointersEncodingScheme.java new file mode 100644 index 0000000000000..d67b6534049ae --- /dev/null +++ b/test/hotspot/jtreg/runtime/CompressedOops/CompressedClassPointersEncodingScheme.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @summary Testing that, faced with a given (possibly odd) mapping address of class space, the encoding + * scheme fits the address + * @requires vm.bits == 64 & !vm.graal.enabled + * @requires vm.flagless + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.management + * @run driver CompressedClassPointersEncodingScheme + */ + +import jdk.test.lib.Platform; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; +import jtreg.SkippedException; + +import java.io.IOException; + +public class CompressedClassPointersEncodingScheme { + + private static void test(long forceAddress, long classSpaceSize, long expectedEncodingBase, int expectedEncodingShift) throws IOException { + String forceAddressString = String.format("0x%016X", forceAddress).toLowerCase(); + String expectedEncodingBaseString = String.format("0x%016X", expectedEncodingBase).toLowerCase(); + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( + "-Xshare:off", // to make CompressedClassSpaceBaseAddress work + "-XX:+UnlockDiagnosticVMOptions", + "-XX:-UseCompressedOops", // keep VM from optimizing heap location + "-XX:CompressedClassSpaceBaseAddress=" + forceAddress, + "-XX:CompressedClassSpaceSize=" + classSpaceSize, + "-Xmx128m", + "-Xlog:metaspace*", + "-version"); + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + + output.reportDiagnosticSummary(); + + // We ignore cases where we were not able to map at the force address + if (output.contains("reserving class space failed")) { + throw new SkippedException("Skipping because we cannot force ccs to " + forceAddressString); + } + + output.shouldHaveExitValue(0); + output.shouldContain("Narrow klass base: " + expectedEncodingBaseString + ", Narrow klass shift: " + expectedEncodingShift); + } + + final static long K = 1024; + final static long M = K * 1024; + final static long G = M * 1024; + public static void main(String[] args) throws Exception { + // Test ccs nestling right at the end of the 4G range + // Expecting base=0, shift=0 + test(4 * G - 128 * M, 128 * M, 0, 0); + + // add more... + + } +} diff --git a/test/lib/jdk/test/lib/process/OutputAnalyzer.java b/test/lib/jdk/test/lib/process/OutputAnalyzer.java index 41b240b9e48c0..fab7b99196def 100644 --- a/test/lib/jdk/test/lib/process/OutputAnalyzer.java +++ b/test/lib/jdk/test/lib/process/OutputAnalyzer.java @@ -207,6 +207,27 @@ public OutputAnalyzer stderrShouldNotBeEmpty() { return this; } + /** + * Returns true if stdout contains the given string + */ + public boolean stdoutContains(String expectedString) { + return getStdout().contains(expectedString); + } + + /** + * Returns true if stderr contains the given string + */ + public boolean stderrContains(String expectedString) { + return getStderr().contains(expectedString); + } + + /** + * Returns true if either stdout or stderr contains the given string + */ + public boolean contains(String expectedString) { + return stdoutContains(expectedString) || stderrContains(expectedString); + } + /** * Verify that the stdout and stderr contents of output buffer contains the string * From 1b150117fd9ccb7ff8adc012b74d7d95e5219b94 Mon Sep 17 00:00:00 2001 From: Joe Darcy Date: Mon, 23 Oct 2023 22:08:12 +0000 Subject: [PATCH 07/28] 8318476: Add resource consumption note to BigInteger and BigDecimal Reviewed-by: alanb, bpb --- .../share/classes/java/math/BigDecimal.java | 23 ++++++++++- .../share/classes/java/math/BigInteger.java | 38 ++++++++++++++++++- 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/src/java.base/share/classes/java/math/BigDecimal.java b/src/java.base/share/classes/java/math/BigDecimal.java index 094c48bc02fbe..dd34ad245716e 100644 --- a/src/java.base/share/classes/java/math/BigDecimal.java +++ b/src/java.base/share/classes/java/math/BigDecimal.java @@ -279,7 +279,7 @@ * operations indicated by {@linkplain RoundingMode rounding modes} * are a proper superset of the IEEE 754 rounding-direction * attributes. - + * *

{@code BigDecimal} arithmetic will most resemble IEEE 754 * decimal arithmetic if a {@code MathContext} corresponding to an * IEEE 754 decimal format, such as {@linkplain MathContext#DECIMAL64 @@ -292,6 +292,27 @@ * such as dividing by zero, throw an {@code ArithmeticException} in * {@code BigDecimal} arithmetic. * + *

Algorithmic Complexity

+ * + * Operations on {@code BigDecimal} values have a range of algorithmic + * complexities; in general, those complexities are a function of both + * the size of the unscaled value as well as the size of the + * scale. For example, an {@linkplain BigDecimal#multiply(BigDecimal) + * exact multiply} of two {@code BigDecimal} values is subject to the + * same {@linkplain BigInteger##algorithmicComplexity complexity + * constraints} as {@code BigInteger} multiply of the unscaled + * values. In contrast, a {@code BigDecimal} value with a compact + * representation like {@code new BigDecimal(1E-1000000000)} has a + * {@link toPlainString} result with over one billion characters. + * + *

Operations may also allocate and compute on intermediate + * results, potentially those allocations may be as large as in + * proportion to the running time of the algorithm. + * + *

Users of {@code BigDecimal} concerned with bounding the running + * time or space of operations can screen out {@code BigDecimal} + * values with unscaled values or scales above a chosen magnitude. + * * @see BigInteger * @see MathContext * @see RoundingMode diff --git a/src/java.base/share/classes/java/math/BigInteger.java b/src/java.base/share/classes/java/math/BigInteger.java index d678a0f76a618..3b751c85c55c2 100644 --- a/src/java.base/share/classes/java/math/BigInteger.java +++ b/src/java.base/share/classes/java/math/BigInteger.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -118,6 +118,42 @@ * the full supported positive range of {@code BigInteger}. * The range must be at least 1 to 2500000000. * + * @apiNote + * As {@code BigInteger} values are + * arbitrary precision integers, the algorithmic complexity of the + * methods of this class varies and may be superlinear in the size of + * the input. For example, a method like {@link intValue()} would be + * expected to run in O(1), that is constant time, since with + * the current internal representation only a fixed-size component of + * the {@code BigInteger} needs to be accessed to perform the + * conversion to {@code int}. In contrast, a method like {@link not()} + * would be expected to run in O(n) time where n + * is the size of the {@code BigInteger} in bits, that is, to run in + * time proportional to the size of the input. For multiplying two + * {@code BigInteger} values of size n, a naive multiplication + * algorithm would run in time O(n2) and + * theoretical results indicate a multiplication algorithm for numbers + * using this category of representation must run in at least + * O(n log n). Common multiplication + * algorithms between the bounds of the naive and theoretical cases + * include the Karatsuba multiplication + * (O(n1.585)) and 3-way Toom-Cook + * multiplication (O(n1.465)). + * + *

A particular implementation of {@link multiply(BigInteger) + * multiply} is free to switch between different algorithms for + * different inputs, such as to improve actual running time to produce + * the product by using simpler algorithms for smaller inputs even if + * the simpler algorithm has a larger asymptotic complexity. + * + *

Operations may also allocate and compute on intermediate + * results, potentially those allocations may be as large as in + * proportion to the running time of the algorithm. + * + *

Users of {@code BigInteger} concerned with bounding the running + * time or space of operations can screen out {@code BigInteger} + * values above a chosen magnitude. + * * @implNote * In the reference implementation, BigInteger constructors and * operations throw {@code ArithmeticException} when the result is out From eb5916729defd37002f4096d4910849938e0fe3b Mon Sep 17 00:00:00 2001 From: Jie Fu Date: Tue, 24 Oct 2023 02:57:47 +0000 Subject: [PATCH 08/28] 8318691: runtime/CompressedOops/CompressedClassPointersEncodingScheme.java fails with release VMs Reviewed-by: ccheung --- .../CompressedOops/CompressedClassPointersEncodingScheme.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/runtime/CompressedOops/CompressedClassPointersEncodingScheme.java b/test/hotspot/jtreg/runtime/CompressedOops/CompressedClassPointersEncodingScheme.java index d67b6534049ae..c1ef73d836c6b 100644 --- a/test/hotspot/jtreg/runtime/CompressedOops/CompressedClassPointersEncodingScheme.java +++ b/test/hotspot/jtreg/runtime/CompressedOops/CompressedClassPointersEncodingScheme.java @@ -25,7 +25,7 @@ * @test * @summary Testing that, faced with a given (possibly odd) mapping address of class space, the encoding * scheme fits the address - * @requires vm.bits == 64 & !vm.graal.enabled + * @requires vm.bits == 64 & !vm.graal.enabled & vm.debug == true * @requires vm.flagless * @library /test/lib * @modules java.base/jdk.internal.misc From 728b858c787567fa4eed6dd44730dfdb8b30be0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Jeli=C5=84ski?= Date: Tue, 24 Oct 2023 05:36:43 +0000 Subject: [PATCH 09/28] 8318130: SocksSocketImpl needlessly encodes hostname for IPv6 addresses Reviewed-by: dfuchs, jpai, aefimov, michaelm --- .../classes/java/net/SocksSocketImpl.java | 16 +- .../Socks/SocksSocketProxySelectorTest.java | 195 ++++++++++++++++++ 2 files changed, 203 insertions(+), 8 deletions(-) create mode 100644 test/jdk/java/net/Socks/SocksSocketProxySelectorTest.java diff --git a/src/java.base/share/classes/java/net/SocksSocketImpl.java b/src/java.base/share/classes/java/net/SocksSocketImpl.java index 9353452efeb42..e6efddae65c25 100644 --- a/src/java.base/share/classes/java/net/SocksSocketImpl.java +++ b/src/java.base/share/classes/java/net/SocksSocketImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,6 +37,8 @@ import sun.net.spi.DefaultProxySelector; import sun.net.www.ParseUtil; +import static sun.net.util.IPAddressUtil.isIPv6LiteralAddress; + /** * SOCKS (V4 & V5) TCP socket implementation (RFC 1928). */ @@ -297,17 +299,15 @@ public ProxySelector run() { URI uri; // Use getHostString() to avoid reverse lookups String host = epoint.getHostString(); - // IPv6 literal? - if (epoint.getAddress() instanceof Inet6Address && - (!host.startsWith("[")) && (host.indexOf(':') >= 0)) { + if (isIPv6LiteralAddress(host)) { host = "[" + host + "]"; + } else { + host = ParseUtil.encodePath(host); } try { - uri = new URI("socket://" + ParseUtil.encodePath(host) + ":"+ epoint.getPort()); + uri = new URI("socket://" + host + ":"+ epoint.getPort()); } catch (URISyntaxException e) { - // This shouldn't happen - assert false : e; - uri = null; + throw new IOException("Failed to select a proxy", e); } Proxy p = null; IOException savedExc = null; diff --git a/test/jdk/java/net/Socks/SocksSocketProxySelectorTest.java b/test/jdk/java/net/Socks/SocksSocketProxySelectorTest.java new file mode 100644 index 0000000000000..e43edb4f6f7ac --- /dev/null +++ b/test/jdk/java/net/Socks/SocksSocketProxySelectorTest.java @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.Proxy; +import java.net.ProxySelector; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.SocketException; +import java.net.URI; +import java.net.UnknownHostException; +import java.util.List; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * @test + * @bug 8318130 + * @summary Tests that java.net.SocksSocketImpl produces correct arguments + * for proxy selector + * @run junit/othervm SocksSocketProxySelectorTest + */ +public class SocksSocketProxySelectorTest { + + public static final String SHORTEN_IPV6 = "((?<=\\[)0)?:(0:)+"; + + @BeforeAll + public static void beforeTest() { + ProxySelector.setDefault(new LoggingProxySelector()); + } + + // should match the host name + public static Stream ipLiterals() { + return Stream.of("127.0.0.1", + "[::1]", + "[fe80::1%1234567890]"); + } + + // should be wrapped in [ ] + public static Stream shortIpv6Literals() { + return Stream.of("::1", + "fe80::1%1234567890"); + } + + // with real interface names in scope + // should be wrapped in [ ], repeated 0's not trimmed + public static Stream linkLocalIpv6Literals() throws SocketException { + return NetworkInterface.networkInterfaces() + .flatMap(NetworkInterface::inetAddresses) + .filter(InetAddress::isLinkLocalAddress) + .map(InetAddress::getHostAddress); + } + + public static Stream hostNames() throws UnknownHostException { + return Stream.of( + InetAddress.getByAddress("localhost", new byte[] {127,0,0,1}), + InetAddress.getByAddress("bugs.openjdk.org", new byte[] {127,0,0,1}), + InetAddress.getByAddress("xn--kda4b0koi.com", new byte[] {127,0,0,1}) + ); + } + + /** + * Creates a socket connection, which internally triggers proxy selection for the target + * address. The test has been configured to use a {@link LoggingProxySelector ProxySelector} + * which throws an {@link IllegalArgumentException} with hostname in exception message. + * The test then verifies that the hostname matches the expected one. + * + * @throws Exception + */ + @ParameterizedTest + @MethodSource("ipLiterals") + public void testIpLiterals(String host) throws Exception { + try (Socket s1 = new Socket(host, 80)) { + fail("IOException was expected to be thrown, but wasn't"); + } catch (IOException ioe) { + // expected + // now verify the IOE was thrown for the correct expected reason + if (!(ioe.getCause() instanceof IllegalArgumentException iae)) { + // rethrow this so that the test output failure will capture the entire/real + // cause in its stacktrace + throw ioe; + } + assertNotNull(iae.getMessage(), "Host not found"); + assertEquals(host, + iae.getMessage().replaceFirst(SHORTEN_IPV6, "::"), + "Found unexpected host"); + } + } + + @ParameterizedTest + @MethodSource("shortIpv6Literals") + public void testShortIpv6Literals(String host) throws Exception { + try (Socket s1 = new Socket(host, 80)) { + fail("IOException was expected to be thrown, but wasn't"); + } catch (IOException ioe) { + // expected + // now verify the IOE was thrown for the correct expected reason + if (!(ioe.getCause() instanceof IllegalArgumentException iae)) { + // rethrow this so that the test output failure will capture the entire/real + // cause in its stacktrace + throw ioe; + } + assertNotNull(iae.getMessage(), "Host not found"); + assertEquals('[' + host + ']', + iae.getMessage().replaceFirst(SHORTEN_IPV6, "::"), + "Found unexpected host"); + } + } + + @ParameterizedTest + @MethodSource("linkLocalIpv6Literals") + public void testLinkLocalIpv6Literals(String host) throws Exception { + try (Socket s1 = new Socket(host, 80)) { + fail("IOException was expected to be thrown, but wasn't"); + } catch (IOException ioe) { + // expected + // now verify the IOE was thrown for the correct expected reason + if (!(ioe.getCause() instanceof IllegalArgumentException iae)) { + // rethrow this so that the test output failure will capture the entire/real + // cause in its stacktrace + throw ioe; + } + assertNotNull(iae.getMessage(), "Host not found"); + assertEquals('[' + host + ']', + iae.getMessage(), + "Found unexpected host"); + } + } + + @ParameterizedTest + @MethodSource("hostNames") + public void testHostNames(InetAddress host) throws Exception { + try (Socket s1 = new Socket(host, 80)) { + fail("IOException was expected to be thrown, but wasn't"); + } catch (IOException ioe) { + // expected + // now verify the IOE was thrown for the correct expected reason + if (!(ioe.getCause() instanceof IllegalArgumentException iae)) { + // rethrow this so that the test output failure will capture the entire/real + // cause in its stacktrace + throw ioe; + } + assertNotNull(iae.getMessage(), "Host not found"); + assertEquals(host.getHostName(), + iae.getMessage(), + "Found unexpected host"); + } + } + + /** + * A {@link ProxySelector} which throws an IllegalArgumentException + * with the given hostname in exception message + */ + private static final class LoggingProxySelector extends + ProxySelector { + + @Override + public List select(final URI uri) { + throw new IllegalArgumentException(uri.getHost()); + } + + @Override + public void connectFailed(URI uri, SocketAddress sa, IOException ioe) { + + } + } +} From 08f79148c6607bf2fce3710f112313e29c05ea90 Mon Sep 17 00:00:00 2001 From: Ioi Lam Date: Tue, 24 Oct 2023 06:34:50 +0000 Subject: [PATCH 10/28] 8305753: Allow JIT compilation for -Xshare:dump Reviewed-by: dholmes, matsaave, ccheung --- src/hotspot/share/runtime/arguments.cpp | 26 +++++++++++++------ src/java.base/share/man/java.1 | 12 +++++++++ .../jtreg/runtime/cds/DeterministicDump.java | 1 + .../cds/appcds/CommandLineFlagCombo.java | 7 +++-- 4 files changed, 36 insertions(+), 10 deletions(-) diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index df55639d81481..33686a3b6e911 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -131,6 +131,9 @@ char* Arguments::_ext_dirs = nullptr; // True if -Xshare:auto option was specified. static bool xshare_auto_cmd_line = false; +// True if -Xint/-Xmixed/-Xcomp were specified +static bool mode_flag_cmd_line = false; + bool PathString::set_value(const char *value, AllocFailType alloc_failmode) { char* new_value = AllocateHeap(strlen(value)+1, mtArguments, alloc_failmode); if (new_value == nullptr) { @@ -2600,13 +2603,16 @@ jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, bool* patch_m // -Xint } else if (match_option(option, "-Xint")) { set_mode_flags(_int); + mode_flag_cmd_line = true; // -Xmixed } else if (match_option(option, "-Xmixed")) { set_mode_flags(_mixed); + mode_flag_cmd_line = true; // -Xcomp } else if (match_option(option, "-Xcomp")) { // for testing the compiler; turn off all flags that inhibit compilation set_mode_flags(_comp); + mode_flag_cmd_line = true; // -Xshare:dump } else if (match_option(option, "-Xshare:dump")) { DumpSharedSpaces = true; @@ -3031,14 +3037,18 @@ jint Arguments::finalize_vm_init_args(bool patch_mod_javabase) { #if INCLUDE_CDS if (DumpSharedSpaces) { - // Compiler threads may concurrently update the class metadata (such as method entries), so it's - // unsafe with -Xshare:dump (which modifies the class metadata in place). Let's disable - // compiler just to be safe. - // - // Note: this is not a concern for dynamically dumping shared spaces, which makes a copy of the - // class metadata instead of modifying them in place. The copy is inaccessible to the compiler. - // TODO: revisit the following for the static archive case. - set_mode_flags(_int); + if (!mode_flag_cmd_line) { + // By default, -Xshare:dump runs in interpreter-only mode, which is required for deterministic archive. + // + // If your classlist is large and you don't care about deterministic dumping, you can use + // -Xshare:dump -Xmixed to improve dumping speed. + set_mode_flags(_int); + } else if (_mode == _comp) { + // -Xcomp may use excessive CPU for the test tiers. Also, -Xshare:dump runs a small and fixed set of + // Java code, so there's not much benefit in running -Xcomp. + log_info(cds)("reduced -Xcomp to -Xmixed for static dumping"); + set_mode_flags(_mixed); + } // String deduplication may cause CDS to iterate the strings in different order from one // run to another which resulting in non-determinstic CDS archives. diff --git a/src/java.base/share/man/java.1 b/src/java.base/share/man/java.1 index a72da6d7c118e..ece2607742420 100644 --- a/src/java.base/share/man/java.1 +++ b/src/java.base/share/man/java.1 @@ -5069,6 +5069,18 @@ The output of this command should contain the following text: \f[V][info][class,load] test.Hello source: shared objects file\f[R] .RE .RE +.PP +By default, when the \f[V]-Xshare:dump\f[R] option is used, the JVM runs +in interpreter-only mode (as if the \f[V]-Xint\f[R] option were +specified). +This is required for generating deterministic output in the shared +archive file. +I.e., the exact same archive will be generated, bit-for-bit, every time +you dump it. +However, if deterministic output is not needed, and you have a large +classlist, you can explicitly add \f[V]-Xmixed\f[R] to the command-line +to enable the JIT compiler. +This will speed up the archive creation. .SS Creating a Dynamic CDS Archive File with -XX:ArchiveClassesAtExit .PP Advantages of dynamic CDS archives are: diff --git a/test/hotspot/jtreg/runtime/cds/DeterministicDump.java b/test/hotspot/jtreg/runtime/cds/DeterministicDump.java index 57e4000fe7a09..8f9bb4a582985 100644 --- a/test/hotspot/jtreg/runtime/cds/DeterministicDump.java +++ b/test/hotspot/jtreg/runtime/cds/DeterministicDump.java @@ -79,6 +79,7 @@ static String dump(ArrayList args, String... more) throws Exception { String archiveName = logName + ".jsa"; String mapName = logName + ".map"; CDSOptions opts = (new CDSOptions()) + .addPrefix("-Xint") // Override any -Xmixed/-Xcomp flags from jtreg -vmoptions .addPrefix("-Xlog:cds=debug") .addPrefix("-Xlog:cds+map*=trace:file=" + mapName + ":none:filesize=0") .setArchiveName(archiveName) diff --git a/test/hotspot/jtreg/runtime/cds/appcds/CommandLineFlagCombo.java b/test/hotspot/jtreg/runtime/cds/appcds/CommandLineFlagCombo.java index baf59d02fed48..7b4a03850b8f5 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/CommandLineFlagCombo.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/CommandLineFlagCombo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -59,7 +59,10 @@ public class CommandLineFlagCombo { "-XX:+UseCompressedOops", "-XX:ObjectAlignmentInBytes=16", "-XX:ObjectAlignmentInBytes=32", - "-XX:ObjectAlignmentInBytes=64" + "-XX:ObjectAlignmentInBytes=64", + "-Xint", + "-Xmixed", + "-Xcomp", }; public static void main(String[] args) throws Exception { From 4bfe226870a15306b1e015c38fe3835f26b41fe6 Mon Sep 17 00:00:00 2001 From: Richard Reingruber Date: Tue, 24 Oct 2023 07:05:56 +0000 Subject: [PATCH 11/28] 8310031: Parallel: Implement better work distribution for large object arrays in old gen Co-authored-by: Albert Mingkun Yang Reviewed-by: tschatzl, ayang --- src/hotspot/share/gc/parallel/psCardTable.cpp | 351 +++++++++++------- src/hotspot/share/gc/parallel/psCardTable.hpp | 55 ++- .../share/gc/parallel/psPromotionManager.hpp | 1 + .../gc/parallel/psPromotionManager.inline.hpp | 5 + src/hotspot/share/gc/parallel/psScavenge.cpp | 9 +- 5 files changed, 264 insertions(+), 157 deletions(-) diff --git a/src/hotspot/share/gc/parallel/psCardTable.cpp b/src/hotspot/share/gc/parallel/psCardTable.cpp index d08ad1572612e..4d63cdb9a3f3a 100644 --- a/src/hotspot/share/gc/parallel/psCardTable.cpp +++ b/src/hotspot/share/gc/parallel/psCardTable.cpp @@ -33,6 +33,7 @@ #include "oops/access.inline.hpp" #include "oops/oop.inline.hpp" #include "runtime/prefetch.inline.hpp" +#include "utilities/spinYield.hpp" #include "utilities/align.hpp" // Checks an individual oop for missing precise marks. Mark @@ -123,70 +124,184 @@ static void prefetch_write(void *p) { } } -// postcondition: ret is a dirty card or end_card -CardTable::CardValue* PSCardTable::find_first_dirty_card(CardValue* const start_card, - CardValue* const end_card) { - for (CardValue* i_card = start_card; i_card < end_card; ++i_card) { - if (*i_card != PSCardTable::clean_card_val()) { - return i_card; - } +void PSCardTable::scan_obj_with_limit(PSPromotionManager* pm, + oop obj, + HeapWord* start, + HeapWord* end) { + if (!obj->is_typeArray()) { + prefetch_write(start); + pm->push_contents_bounded(obj, start, end); } - return end_card; } -// postcondition: ret is a clean card or end_card -// Note: if a part of an object is on a dirty card, all cards this object -// resides on are considered dirty. -CardTable::CardValue* PSCardTable::find_first_clean_card(ObjectStartArray* const start_array, - CardValue* const start_card, - CardValue* const end_card) { - assert(start_card == end_card || - *start_card != PSCardTable::clean_card_val(), "precondition"); - // Skip the first dirty card. - CardValue* i_card = start_card + 1; - while (i_card < end_card) { - if (*i_card != PSCardTable::clean_card_val()) { - i_card++; - continue; - } - assert(i_card - 1 >= start_card, "inv"); - assert(*(i_card - 1) != PSCardTable::clean_card_val(), "prev card must be dirty"); - // Find the final obj on the prev dirty card. - HeapWord* obj_addr = start_array->object_start(addr_for(i_card)-1); - HeapWord* obj_end_addr = obj_addr + cast_to_oop(obj_addr)->size(); - CardValue* final_card_by_obj = byte_for(obj_end_addr - 1); - assert(final_card_by_obj < end_card, "inv"); - if (final_card_by_obj <= i_card) { - return i_card; +void PSCardTable::pre_scavenge(HeapWord* old_gen_bottom, uint active_workers) { + _preprocessing_active_workers = active_workers; +} + +// The "shadow" table is a copy of the card table entries of the current stripe. +// It is used to separate card reading, clearing and redirtying which reduces +// complexity significantly. +class PSStripeShadowCardTable { + typedef CardTable::CardValue CardValue; + + const uint _card_shift; + const uint _card_size; + CardValue _table[PSCardTable::num_cards_in_stripe]; + const CardValue* _table_base; + +public: + PSStripeShadowCardTable(PSCardTable* pst, HeapWord* const start, HeapWord* const end) : + _card_shift(CardTable::card_shift()), + _card_size(CardTable::card_size()), + _table_base(_table - (uintptr_t(start) >> _card_shift)) { + size_t stripe_byte_size = pointer_delta(end, start) * HeapWordSize; + size_t copy_length = align_up(stripe_byte_size, _card_size) >> _card_shift; + // The end of the last stripe may not be card aligned as it is equal to old + // gen top at scavenge start. We should not clear the card containing old gen + // top if not card aligned because there can be promoted objects on that + // same card. If it was marked dirty because of the promoted objects and we + // cleared it, we would loose a card mark. + size_t clear_length = align_down(stripe_byte_size, _card_size) >> _card_shift; + CardValue* stripe_start_card = pst->byte_for(start); + memcpy(_table, stripe_start_card, copy_length); + memset(stripe_start_card, CardTable::clean_card_val(), clear_length); + } + + HeapWord* addr_for(const CardValue* const card) { + assert(card >= _table && card <= &_table[PSCardTable::num_cards_in_stripe], "out of bounds"); + return (HeapWord*) ((card - _table_base) << _card_shift); + } + + const CardValue* card_for(HeapWord* addr) { + return &_table_base[uintptr_t(addr) >> _card_shift]; + } + + bool is_dirty(const CardValue* const card) { + return !is_clean(card); + } + + bool is_clean(const CardValue* const card) { + assert(card >= _table && card < &_table[PSCardTable::num_cards_in_stripe], "out of bounds"); + return *card == PSCardTable::clean_card_val(); + } + + const CardValue* find_first_dirty_card(const CardValue* const start, + const CardValue* const end) { + for (const CardValue* i = start; i < end; ++i) { + if (is_dirty(i)) { + return i; + } } - // This final obj extends beyond i_card, check if this new card is dirty. - if (*final_card_by_obj == PSCardTable::clean_card_val()) { - return final_card_by_obj; + return end; + } + + const CardValue* find_first_clean_card(const CardValue* const start, + const CardValue* const end) { + for (const CardValue* i = start; i < end; ++i) { + if (is_clean(i)) { + return i; + } } - // This new card is dirty, continuing the search... - i_card = final_card_by_obj + 1; + return end; } - return end_card; -} +}; + +template +void PSCardTable::process_range(Func&& object_start, + PSPromotionManager* pm, + HeapWord* const start, + HeapWord* const end) { + assert(start < end, "precondition"); + assert(is_card_aligned(start), "precondition"); + + PSStripeShadowCardTable sct(this, start, end); + + // end might not be card-aligned. + const CardValue* end_card = sct.card_for(end - 1) + 1; -void PSCardTable::clear_cards(CardValue* const start, CardValue* const end) { - for (CardValue* i_card = start; i_card < end; ++i_card) { - *i_card = clean_card; + for (HeapWord* i_addr = start; i_addr < end; /* empty */) { + const CardValue* dirty_l = sct.find_first_dirty_card(sct.card_for(i_addr), end_card); + const CardValue* dirty_r = sct.find_first_clean_card(dirty_l, end_card); + + assert(dirty_l <= dirty_r, "inv"); + + if (dirty_l == dirty_r) { + assert(dirty_r == end_card, "inv"); + break; + } + + // Located a non-empty dirty chunk [dirty_l, dirty_r). + HeapWord* addr_l = sct.addr_for(dirty_l); + HeapWord* addr_r = MIN2(sct.addr_for(dirty_r), end); + + // Scan objects overlapping [addr_l, addr_r) limited to [start, end). + HeapWord* obj_addr = object_start(addr_l); + + while (true) { + assert(obj_addr < addr_r, "inv"); + + oop obj = cast_to_oop(obj_addr); + const bool is_obj_array = obj->is_objArray(); + HeapWord* const obj_end_addr = obj_addr + obj->size(); + + if (is_obj_array) { + // Always scan obj arrays precisely (they are always marked precisely) + // to avoid unnecessary work. + scan_obj_with_limit(pm, obj, addr_l, addr_r); + } else { + if (obj_addr < i_addr && i_addr > start) { + // Already scanned this object. Has been one that spans multiple dirty chunks. + // The second condition makes sure objects reaching in the stripe are scanned once. + } else { + scan_obj_with_limit(pm, obj, addr_l, end); + } + } + + if (obj_end_addr >= addr_r) { + i_addr = is_obj_array ? addr_r : obj_end_addr; + break; + } + + // Move to next obj inside this dirty chunk. + obj_addr = obj_end_addr; + } + + // Finished a dirty chunk. + pm->drain_stacks_cond_depth(); } } -void PSCardTable::scan_objects_in_range(PSPromotionManager* pm, - HeapWord* start, - HeapWord* end) { - HeapWord* obj_addr = start; - while (obj_addr < end) { - oop obj = cast_to_oop(obj_addr); - assert(oopDesc::is_oop(obj), "inv"); - prefetch_write(obj_addr); - pm->push_contents(obj); - obj_addr += obj->size(); +template +void PSCardTable::preprocess_card_table_parallel(Func&& object_start, + HeapWord* old_gen_bottom, + HeapWord* old_gen_top, + uint stripe_index, + uint n_stripes) { + const size_t num_cards_in_slice = num_cards_in_stripe * n_stripes; + CardValue* cur_card = byte_for(old_gen_bottom) + stripe_index * num_cards_in_stripe; + CardValue* const end_card = byte_for(old_gen_top - 1) + 1; + + for (/* empty */; cur_card < end_card; cur_card += num_cards_in_slice) { + HeapWord* stripe_addr = addr_for(cur_card); + if (is_dirty(cur_card)) { + // The first card of this stripe is already dirty, no need to see if the + // reaching-in object is a potentially imprecisely marked non-array + // object. + continue; + } + HeapWord* first_obj_addr = object_start(stripe_addr); + if (first_obj_addr == stripe_addr) { + // No object reaching into this stripe. + continue; + } + oop first_obj = cast_to_oop(first_obj_addr); + if (!first_obj->is_array() && is_dirty(byte_for(first_obj_addr))) { + // Found a non-array object reaching into the stripe that has + // potentially been marked imprecisely. Mark first card of the stripe + // dirty so it will be processed later. + *cur_card = dirty_card_val(); + } } - pm->drain_stacks_cond_depth(); } // We get passed the space_top value to prevent us from traversing into @@ -227,103 +342,61 @@ void PSCardTable::scan_objects_in_range(PSPromotionManager* pm, // slice_size_in_words to the start of stripe 0 in slice 0 to get to the start // of stripe 0 in slice 1. +// Scavenging and accesses to the card table are strictly limited to the stripe. +// In particular scavenging of an object crossing stripe boundaries is shared +// among the threads assigned to the stripes it resides on. This reduces +// complexity and enables shared scanning of large objects. +// It requires preprocessing of the card table though where imprecise card marks of +// objects crossing stripe boundaries are propagated to the first card of +// each stripe covered by the individual object. + void PSCardTable::scavenge_contents_parallel(ObjectStartArray* start_array, - MutableSpace* sp, - HeapWord* space_top, + HeapWord* old_gen_bottom, + HeapWord* old_gen_top, PSPromotionManager* pm, uint stripe_index, uint n_stripes) { - const size_t num_cards_in_stripe = 128; - const size_t stripe_size_in_words = num_cards_in_stripe * _card_size_in_words; - const size_t slice_size_in_words = stripe_size_in_words * n_stripes; - - HeapWord* cur_stripe_addr = sp->bottom() + stripe_index * stripe_size_in_words; - - for (/* empty */; cur_stripe_addr < space_top; cur_stripe_addr += slice_size_in_words) { - // exclusive - HeapWord* const cur_stripe_end_addr = MIN2(cur_stripe_addr + stripe_size_in_words, - space_top); - - // Process a stripe iff it contains any obj-start - if (!start_array->object_starts_in_range(cur_stripe_addr, cur_stripe_end_addr)) { - continue; + // ObjectStartArray queries can be expensive for large objects. We cache known objects. + struct { + HeapWord* start_addr; + HeapWord* end_addr; + } cached_obj {nullptr, old_gen_bottom}; + + // Queries must be monotonic because we don't check addr >= cached_obj.start_addr. + auto object_start = [&] (HeapWord* addr) { + if (addr < cached_obj.end_addr) { + assert(cached_obj.start_addr != nullptr, "inv"); + return cached_obj.start_addr; } + HeapWord* result = start_array->object_start(addr); - // Constraints: - // 1. range of cards checked for being dirty or clean: [iter_limit_l, iter_limit_r) - // 2. range of cards can be cleared: [clear_limit_l, clear_limit_r) - // 3. range of objs (obj-start) can be scanned: [first_obj_addr, cur_stripe_end_addr) - - CardValue* iter_limit_l; - CardValue* iter_limit_r; - CardValue* clear_limit_l; - CardValue* clear_limit_r; - - // Identify left ends and the first obj-start inside this stripe. - HeapWord* first_obj_addr = start_array->object_start(cur_stripe_addr); - if (first_obj_addr < cur_stripe_addr) { - // this obj belongs to previous stripe; can't clear any cards it occupies - first_obj_addr += cast_to_oop(first_obj_addr)->size(); - clear_limit_l = byte_for(first_obj_addr - 1) + 1; - iter_limit_l = byte_for(first_obj_addr); - } else { - assert(first_obj_addr == cur_stripe_addr, "inv"); - iter_limit_l = clear_limit_l = byte_for(cur_stripe_addr); - } + cached_obj.start_addr = result; + cached_obj.end_addr = result + cast_to_oop(result)->size(); - assert(cur_stripe_addr <= first_obj_addr, "inside this stripe"); - assert(first_obj_addr <= cur_stripe_end_addr, "can be empty"); + return result; + }; - { - // Identify right ends. - HeapWord* obj_addr = start_array->object_start(cur_stripe_end_addr - 1); - HeapWord* obj_end_addr = obj_addr + cast_to_oop(obj_addr)->size(); - assert(obj_end_addr >= cur_stripe_end_addr, "inv"); - clear_limit_r = byte_for(obj_end_addr); - iter_limit_r = byte_for(obj_end_addr - 1) + 1; - } - - assert(iter_limit_l <= clear_limit_l && - clear_limit_r <= iter_limit_r, "clear cards only if we iterate over them"); - - // Process dirty chunks, i.e. consecutive dirty cards [dirty_l, dirty_r), - // chunk by chunk inside [iter_limit_l, iter_limit_r). - CardValue* dirty_l; - CardValue* dirty_r; - - for (CardValue* cur_card = iter_limit_l; cur_card < iter_limit_r; cur_card = dirty_r + 1) { - dirty_l = find_first_dirty_card(cur_card, iter_limit_r); - dirty_r = find_first_clean_card(start_array, dirty_l, iter_limit_r); - assert(dirty_l <= dirty_r, "inv"); - - // empty - if (dirty_l == dirty_r) { - assert(dirty_r == iter_limit_r, "no more dirty cards in this stripe"); - break; - } - - assert(*dirty_l != clean_card, "inv"); - assert(*dirty_r == clean_card || dirty_r >= clear_limit_r, - "clean card or belonging to next stripe"); + // Prepare scavenge. + preprocess_card_table_parallel(object_start, old_gen_bottom, old_gen_top, stripe_index, n_stripes); - // Process this non-empty dirty chunk in two steps: - { - // 1. Clear card in [dirty_l, dirty_r) subject to [clear_limit_l, clear_limit_r) constraint - clear_cards(MAX2(dirty_l, clear_limit_l), - MIN2(dirty_r, clear_limit_r)); - } - - { - // 2. Scan objs in [dirty_l, dirty_r) subject to [first_obj_addr, cur_stripe_end_addr) constraint - HeapWord* obj_l = MAX2(start_array->object_start(addr_for(dirty_l)), - first_obj_addr); + // Sync with other workers. + Atomic::dec(&_preprocessing_active_workers); + SpinYield spin_yield; + while (Atomic::load_acquire(&_preprocessing_active_workers) > 0) { + spin_yield.wait(); + } - HeapWord* obj_r = MIN2(addr_for(dirty_r), - cur_stripe_end_addr); + // Scavenge + cached_obj = {nullptr, old_gen_bottom}; + const size_t stripe_size_in_words = num_cards_in_stripe * _card_size_in_words; + const size_t slice_size_in_words = stripe_size_in_words * n_stripes; + HeapWord* cur_addr = old_gen_bottom + stripe_index * stripe_size_in_words; + for (/* empty */; cur_addr < old_gen_top; cur_addr += slice_size_in_words) { + HeapWord* const stripe_l = cur_addr; + HeapWord* const stripe_r = MIN2(cur_addr + stripe_size_in_words, + old_gen_top); - scan_objects_in_range(pm, obj_l, obj_r); - } - } + process_range(object_start, pm, stripe_l, stripe_r); } } diff --git a/src/hotspot/share/gc/parallel/psCardTable.hpp b/src/hotspot/share/gc/parallel/psCardTable.hpp index 6953c15d37c2f..b0634d5c0b084 100644 --- a/src/hotspot/share/gc/parallel/psCardTable.hpp +++ b/src/hotspot/share/gc/parallel/psCardTable.hpp @@ -33,7 +33,35 @@ class ObjectStartArray; class PSPromotionManager; class PSCardTable: public CardTable { - private: + friend class PSStripeShadowCardTable; + static constexpr size_t num_cards_in_stripe = 128; + static_assert(num_cards_in_stripe >= 1, "progress"); + + volatile int _preprocessing_active_workers; + + bool is_dirty(CardValue* card) { + return !is_clean(card); + } + + bool is_clean(CardValue* card) { + return *card == clean_card_val(); + } + + // Iterate the stripes with the given index and copy imprecise card marks of + // objects reaching into a stripe to its first card. + template + void preprocess_card_table_parallel(Func&& object_start, + HeapWord* old_gen_bottom, + HeapWord* old_gen_top, + uint stripe_index, + uint n_stripes); + + // Scavenge contents on dirty cards of the given stripe [start, end). + template + void process_range(Func&& object_start, + PSPromotionManager* pm, + HeapWord* const start, + HeapWord* const end); void verify_all_young_refs_precise_helper(MemRegion mr); @@ -42,29 +70,24 @@ class PSCardTable: public CardTable { verify_card = CT_MR_BS_last_reserved + 5 }; - CardValue* find_first_dirty_card(CardValue* const start_card, - CardValue* const end_card); - - CardValue* find_first_clean_card(ObjectStartArray* start_array, - CardValue* const start_card, - CardValue* const end_card); - - void clear_cards(CardValue* const start, CardValue* const end); - - void scan_objects_in_range(PSPromotionManager* pm, - HeapWord* start, - HeapWord* end); + void scan_obj_with_limit(PSPromotionManager* pm, + oop obj, + HeapWord* start, + HeapWord* end); public: - PSCardTable(MemRegion whole_heap) : CardTable(whole_heap) {} + PSCardTable(MemRegion whole_heap) : CardTable(whole_heap), + _preprocessing_active_workers(0) {} static CardValue youngergen_card_val() { return youngergen_card; } static CardValue verify_card_val() { return verify_card; } // Scavenge support + void pre_scavenge(HeapWord* old_gen_bottom, uint active_workers); + // Scavenge contents of stripes with the given index. void scavenge_contents_parallel(ObjectStartArray* start_array, - MutableSpace* sp, - HeapWord* space_top, + HeapWord* old_gen_bottom, + HeapWord* old_gen_top, PSPromotionManager* pm, uint stripe_index, uint n_stripes); diff --git a/src/hotspot/share/gc/parallel/psPromotionManager.hpp b/src/hotspot/share/gc/parallel/psPromotionManager.hpp index a1d2b38db31fa..d053ffb6cc949 100644 --- a/src/hotspot/share/gc/parallel/psPromotionManager.hpp +++ b/src/hotspot/share/gc/parallel/psPromotionManager.hpp @@ -177,6 +177,7 @@ class PSPromotionManager { TASKQUEUE_STATS_ONLY(inline void record_steal(ScannerTask task);) void push_contents(oop obj); + void push_contents_bounded(oop obj, HeapWord* left, HeapWord* right); }; #endif // SHARE_GC_PARALLEL_PSPROMOTIONMANAGER_HPP diff --git a/src/hotspot/share/gc/parallel/psPromotionManager.inline.hpp b/src/hotspot/share/gc/parallel/psPromotionManager.inline.hpp index f702bc483481c..c1cbeb0f597bb 100644 --- a/src/hotspot/share/gc/parallel/psPromotionManager.inline.hpp +++ b/src/hotspot/share/gc/parallel/psPromotionManager.inline.hpp @@ -131,6 +131,11 @@ inline void PSPromotionManager::push_contents(oop obj) { } } +inline void PSPromotionManager::push_contents_bounded(oop obj, HeapWord* left, HeapWord* right) { + PSPushContentsClosure pcc(this); + obj->oop_iterate(&pcc, MemRegion(left, right)); +} + template inline oop PSPromotionManager::copy_to_survivor_space(oop o) { assert(should_scavenge(&o), "Sanity"); diff --git a/src/hotspot/share/gc/parallel/psScavenge.cpp b/src/hotspot/share/gc/parallel/psScavenge.cpp index 18e0edd88c9a0..5354d9257b9fe 100644 --- a/src/hotspot/share/gc/parallel/psScavenge.cpp +++ b/src/hotspot/share/gc/parallel/psScavenge.cpp @@ -87,7 +87,6 @@ static void scavenge_roots_work(ParallelRootType::Value root_type, uint worker_i assert(ParallelScavengeHeap::heap()->is_gc_active(), "called outside gc"); PSPromotionManager* pm = PSPromotionManager::gc_thread_promotion_manager(worker_id); - PSScavengeRootsClosure roots_closure(pm); PSPromoteRootsClosure roots_to_old_closure(pm); switch (root_type) { @@ -301,6 +300,11 @@ class ScavengeRootsTask : public WorkerTask { _is_old_gen_empty(old_gen->object_space()->is_empty()), _terminator(active_workers, PSPromotionManager::vm_thread_promotion_manager()->stack_array_depth()) { assert(_old_gen != nullptr, "Sanity"); + + if (!_is_old_gen_empty) { + PSCardTable* card_table = ParallelScavengeHeap::heap()->card_table(); + card_table->pre_scavenge(_old_gen->object_space()->bottom(), active_workers); + } } virtual void work(uint worker_id) { @@ -314,8 +318,9 @@ class ScavengeRootsTask : public WorkerTask { PSPromotionManager* pm = PSPromotionManager::gc_thread_promotion_manager(worker_id); PSCardTable* card_table = ParallelScavengeHeap::heap()->card_table(); + // The top of the old gen changes during scavenge when objects are promoted. card_table->scavenge_contents_parallel(_old_gen->start_array(), - _old_gen->object_space(), + _old_gen->object_space()->bottom(), _gen_top, pm, worker_id, From cb383c05b23ef4f6992796bdc5b27eb8386c65d5 Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Tue, 24 Oct 2023 07:09:44 +0000 Subject: [PATCH 12/28] 8318587: refresh libraries cache on AIX in print_vm_info Reviewed-by: mdoerr, lucy, stuefe --- src/hotspot/share/utilities/vmError.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/hotspot/share/utilities/vmError.cpp b/src/hotspot/share/utilities/vmError.cpp index df360335cc9a9..2f4aec97868a4 100644 --- a/src/hotspot/share/utilities/vmError.cpp +++ b/src/hotspot/share/utilities/vmError.cpp @@ -74,6 +74,10 @@ #include "jvmci/jvmci.hpp" #endif +#ifdef AIX +#include "loadlib_aix.hpp" +#endif + #ifndef PRODUCT #include #endif // PRODUCT @@ -1343,6 +1347,8 @@ void VMError::report(outputStream* st, bool _verbose) { void VMError::print_vm_info(outputStream* st) { char buf[O_BUFLEN]; + AIX_ONLY(LoadedLibraries::reload()); + report_vm_version(st, buf, sizeof(buf)); // STEP("printing summary") From 6d3cb459dc07ae6abfacd167d1d282457c2cd1b6 Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Tue, 24 Oct 2023 07:34:05 +0000 Subject: [PATCH 13/28] 8318591: avoid leaks in loadlib_aix.cpp reload_table() Reviewed-by: mdoerr, lucy --- src/hotspot/os/aix/loadlib_aix.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/hotspot/os/aix/loadlib_aix.cpp b/src/hotspot/os/aix/loadlib_aix.cpp index 3a71a78e45cea..bc71ca2e3101b 100644 --- a/src/hotspot/os/aix/loadlib_aix.cpp +++ b/src/hotspot/os/aix/loadlib_aix.cpp @@ -225,6 +225,7 @@ static bool reload_table() { lm->path = g_stringlist.add(ldi->ldinfo_filename); if (!lm->path) { trcVerbose("OOM."); + free(lm); goto cleanup; } @@ -246,6 +247,7 @@ static bool reload_table() { lm->member = g_stringlist.add(p_mbr_name); if (!lm->member) { trcVerbose("OOM."); + free(lm); goto cleanup; } } else { From fd332da1c8a689e91b7124fc342f02b6e0d3dff5 Mon Sep 17 00:00:00 2001 From: Pavel Rappo Date: Tue, 24 Oct 2023 08:46:10 +0000 Subject: [PATCH 14/28] 8317289: javadoc fails with -sourcepath if module-info.java contains import statements Reviewed-by: jlahoda --- .../com/sun/tools/javac/tree/TreeInfo.java | 11 +--- .../javadoc/internal/tool/ElementsTable.java | 5 +- .../testModules/TestSourcePathModule.java | 56 +++++++++++++++++++ 3 files changed, 59 insertions(+), 13 deletions(-) create mode 100644 test/langtools/jdk/javadoc/doclet/testModules/TestSourcePathModule.java diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java index b25c99d3b06a2..40dc8042a06a9 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1321,15 +1321,6 @@ public static boolean isModuleInfo(JCCompilationUnit tree) { && tree.getModuleDecl() != null; } - public static JCModuleDecl getModule(JCCompilationUnit t) { - if (t.defs.nonEmpty()) { - JCTree def = t.defs.head; - if (def.hasTag(MODULEDEF)) - return (JCModuleDecl) def; - } - return null; - } - public static boolean isPackageInfo(JCCompilationUnit tree) { return tree.sourcefile.isNameCompatible("package-info", JavaFileObject.Kind.SOURCE); } diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/ElementsTable.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/ElementsTable.java index 3f642658deb92..e915fc5f34c70 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/ElementsTable.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/ElementsTable.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -67,7 +67,6 @@ import com.sun.tools.javac.tree.JCTree.JCClassDecl; import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; import com.sun.tools.javac.tree.JCTree.JCModuleDecl; -import com.sun.tools.javac.tree.TreeInfo; import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.ListBuffer; import com.sun.tools.javac.util.Name; @@ -391,7 +390,7 @@ private String getModuleName(Location location) throws ToolException { "module-info", JavaFileObject.Kind.SOURCE); if (jfo != null) { JCCompilationUnit jcu = compiler.parse(jfo); - JCModuleDecl module = TreeInfo.getModule(jcu); + JCModuleDecl module = jcu.getModule(); if (module != null) { return module.getName().toString(); } diff --git a/test/langtools/jdk/javadoc/doclet/testModules/TestSourcePathModule.java b/test/langtools/jdk/javadoc/doclet/testModules/TestSourcePathModule.java new file mode 100644 index 0000000000000..eb80955d4988d --- /dev/null +++ b/test/langtools/jdk/javadoc/doclet/testModules/TestSourcePathModule.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.nio.file.Path; + +import javadoc.tester.JavadocTester; +import toolbox.ToolBox; + +/* + * @test + * @bug 8317289 + * @library /tools/lib ../../lib + * @modules jdk.javadoc/jdk.javadoc.internal.tool + * @build toolbox.ToolBox javadoc.tester.* + * @run main TestSourcePathModule + */ +public class TestSourcePathModule extends JavadocTester { + + public static void main(String... args) throws Exception { + new TestSourcePathModule().runTests(); + } + + @Test + public void testSourcePath(Path base) throws Exception { + Path src = base.resolve("src"); + new ToolBox().writeJavaFiles(src, """ + import java.lang.Object; + /** documentation */ + module m { } + """); + javadoc("-d", "out", + "-sourcepath", src.toString(), + "--module", "m"); + checkExit(Exit.OK); + } +} \ No newline at end of file From e6f23a90d4a53339a3c9c2b76fc5d317940e4472 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C3=B6=20Barany?= Date: Tue, 24 Oct 2023 08:58:15 +0000 Subject: [PATCH 15/28] 8315024: Vector API FP reduction tests should not test for exact equality Reviewed-by: epeter, thartmann --- .../vector/Double128VectorTests.java | 47 +++++++++++---- .../vector/Double256VectorTests.java | 47 +++++++++++---- .../vector/Double512VectorTests.java | 47 +++++++++++---- .../incubator/vector/Double64VectorTests.java | 47 +++++++++++---- .../vector/DoubleMaxVectorTests.java | 47 +++++++++++---- .../incubator/vector/Float128VectorTests.java | 47 +++++++++++---- .../incubator/vector/Float256VectorTests.java | 47 +++++++++++---- .../incubator/vector/Float512VectorTests.java | 47 +++++++++++---- .../incubator/vector/Float64VectorTests.java | 47 +++++++++++---- .../incubator/vector/FloatMaxVectorTests.java | 47 +++++++++++---- .../Unit-Reduction-Masked-op.template | 4 ++ .../templates/Unit-Reduction-op.template | 4 ++ .../vector/templates/Unit-header.template | 57 +++++++++++++++++++ 13 files changed, 415 insertions(+), 120 deletions(-) diff --git a/test/jdk/jdk/incubator/vector/Double128VectorTests.java b/test/jdk/jdk/incubator/vector/Double128VectorTests.java index 8336b59accbc2..5e81cb946bcaf 100644 --- a/test/jdk/jdk/incubator/vector/Double128VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Double128VectorTests.java @@ -60,6 +60,8 @@ public class Double128VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); + // for floating point reduction ops that may introduce rounding errors + private static final double RELATIVE_ROUNDING_ERROR = (double)0.000001; static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 128); @@ -119,15 +121,21 @@ interface FReductionAllOp { static void assertReductionArraysEquals(double[] r, double rc, double[] a, FReductionOp f, FReductionAllOp fa) { + assertReductionArraysEquals(r, rc, a, f, fa, (double)0.0); + } + + static void assertReductionArraysEquals(double[] r, double rc, double[] a, + FReductionOp f, FReductionAllOp fa, + double relativeError) { int i = 0; try { - Assert.assertEquals(rc, fa.apply(a)); + Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError)); for (; i < a.length; i += SPECIES.length()) { - Assert.assertEquals(r[i], f.apply(a, i)); + Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError)); } } catch (AssertionError e) { - Assert.assertEquals(rc, fa.apply(a), "Final result is incorrect!"); - Assert.assertEquals(r[i], f.apply(a, i), "at index #" + i); + Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError), "Final result is incorrect!"); + Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError), "at index #" + i); } } @@ -141,15 +149,22 @@ interface FReductionAllMaskedOp { static void assertReductionArraysEqualsMasked(double[] r, double rc, double[] a, boolean[] mask, FReductionMaskedOp f, FReductionAllMaskedOp fa) { + assertReductionArraysEqualsMasked(r, rc, a, mask, f, fa, (double)0.0); + } + + static void assertReductionArraysEqualsMasked(double[] r, double rc, double[] a, boolean[] mask, + FReductionMaskedOp f, FReductionAllMaskedOp fa, + double relativeError) { int i = 0; try { - Assert.assertEquals(rc, fa.apply(a, mask)); + Assert.assertEquals(rc, fa.apply(a, mask), Math.abs(rc * relativeError)); for (; i < a.length; i += SPECIES.length()) { - Assert.assertEquals(r[i], f.apply(a, i, mask)); + Assert.assertEquals(r[i], f.apply(a, i, mask), Math.abs(r[i] * +relativeError)); } } catch (AssertionError e) { - Assert.assertEquals(rc, fa.apply(a, mask), "Final result is incorrect!"); - Assert.assertEquals(r[i], f.apply(a, i, mask), "at index #" + i); + Assert.assertEquals(rc, fa.apply(a, mask), Math.abs(rc * relativeError), "Final result is incorrect!"); + Assert.assertEquals(r[i], f.apply(a, i, mask), Math.abs(r[i] * relativeError), "at index #" + i); } } @@ -1078,6 +1093,14 @@ static long bits(double e) { return fill(s * BUFFER_REPS, i -> (((double)(i + 1) == 0) ? 1 : (double)(i + 1))); }), + withToString("double[0.01 + (i / (i + 1))]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (double)0.01 + ((double)i / (i + 1))); + }), + withToString("double[i -> i % 17 == 0 ? cornerCaseValue(i) : 0.01 + (i / (i + 1))]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> i % 17 == 0 ? cornerCaseValue(i) : (double)0.01 + ((double)i / (i + 1))); + }), withToString("double[cornerCaseValue(i)]", (int s) -> { return fill(s * BUFFER_REPS, i -> cornerCaseValue(i)); @@ -2168,7 +2191,7 @@ static void ADDReduceDouble128VectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - Double128VectorTests::ADDReduce, Double128VectorTests::ADDReduceAll); + Double128VectorTests::ADDReduce, Double128VectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR); } static double ADDReduceMasked(double[] a, int idx, boolean[] mask) { @@ -2214,7 +2237,7 @@ static void ADDReduceDouble128VectorTestsMasked(IntFunction fa, IntFun } assertReductionArraysEqualsMasked(r, ra, a, mask, - Double128VectorTests::ADDReduceMasked, Double128VectorTests::ADDReduceAllMasked); + Double128VectorTests::ADDReduceMasked, Double128VectorTests::ADDReduceAllMasked, RELATIVE_ROUNDING_ERROR); } static double MULReduce(double[] a, int idx) { @@ -2257,7 +2280,7 @@ static void MULReduceDouble128VectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - Double128VectorTests::MULReduce, Double128VectorTests::MULReduceAll); + Double128VectorTests::MULReduce, Double128VectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR); } static double MULReduceMasked(double[] a, int idx, boolean[] mask) { @@ -2303,7 +2326,7 @@ static void MULReduceDouble128VectorTestsMasked(IntFunction fa, IntFun } assertReductionArraysEqualsMasked(r, ra, a, mask, - Double128VectorTests::MULReduceMasked, Double128VectorTests::MULReduceAllMasked); + Double128VectorTests::MULReduceMasked, Double128VectorTests::MULReduceAllMasked, RELATIVE_ROUNDING_ERROR); } static double MINReduce(double[] a, int idx) { diff --git a/test/jdk/jdk/incubator/vector/Double256VectorTests.java b/test/jdk/jdk/incubator/vector/Double256VectorTests.java index f170ccc406b51..ef4f3f0f97511 100644 --- a/test/jdk/jdk/incubator/vector/Double256VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Double256VectorTests.java @@ -60,6 +60,8 @@ public class Double256VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); + // for floating point reduction ops that may introduce rounding errors + private static final double RELATIVE_ROUNDING_ERROR = (double)0.000001; static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 256); @@ -119,15 +121,21 @@ interface FReductionAllOp { static void assertReductionArraysEquals(double[] r, double rc, double[] a, FReductionOp f, FReductionAllOp fa) { + assertReductionArraysEquals(r, rc, a, f, fa, (double)0.0); + } + + static void assertReductionArraysEquals(double[] r, double rc, double[] a, + FReductionOp f, FReductionAllOp fa, + double relativeError) { int i = 0; try { - Assert.assertEquals(rc, fa.apply(a)); + Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError)); for (; i < a.length; i += SPECIES.length()) { - Assert.assertEquals(r[i], f.apply(a, i)); + Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError)); } } catch (AssertionError e) { - Assert.assertEquals(rc, fa.apply(a), "Final result is incorrect!"); - Assert.assertEquals(r[i], f.apply(a, i), "at index #" + i); + Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError), "Final result is incorrect!"); + Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError), "at index #" + i); } } @@ -141,15 +149,22 @@ interface FReductionAllMaskedOp { static void assertReductionArraysEqualsMasked(double[] r, double rc, double[] a, boolean[] mask, FReductionMaskedOp f, FReductionAllMaskedOp fa) { + assertReductionArraysEqualsMasked(r, rc, a, mask, f, fa, (double)0.0); + } + + static void assertReductionArraysEqualsMasked(double[] r, double rc, double[] a, boolean[] mask, + FReductionMaskedOp f, FReductionAllMaskedOp fa, + double relativeError) { int i = 0; try { - Assert.assertEquals(rc, fa.apply(a, mask)); + Assert.assertEquals(rc, fa.apply(a, mask), Math.abs(rc * relativeError)); for (; i < a.length; i += SPECIES.length()) { - Assert.assertEquals(r[i], f.apply(a, i, mask)); + Assert.assertEquals(r[i], f.apply(a, i, mask), Math.abs(r[i] * +relativeError)); } } catch (AssertionError e) { - Assert.assertEquals(rc, fa.apply(a, mask), "Final result is incorrect!"); - Assert.assertEquals(r[i], f.apply(a, i, mask), "at index #" + i); + Assert.assertEquals(rc, fa.apply(a, mask), Math.abs(rc * relativeError), "Final result is incorrect!"); + Assert.assertEquals(r[i], f.apply(a, i, mask), Math.abs(r[i] * relativeError), "at index #" + i); } } @@ -1078,6 +1093,14 @@ static long bits(double e) { return fill(s * BUFFER_REPS, i -> (((double)(i + 1) == 0) ? 1 : (double)(i + 1))); }), + withToString("double[0.01 + (i / (i + 1))]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (double)0.01 + ((double)i / (i + 1))); + }), + withToString("double[i -> i % 17 == 0 ? cornerCaseValue(i) : 0.01 + (i / (i + 1))]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> i % 17 == 0 ? cornerCaseValue(i) : (double)0.01 + ((double)i / (i + 1))); + }), withToString("double[cornerCaseValue(i)]", (int s) -> { return fill(s * BUFFER_REPS, i -> cornerCaseValue(i)); @@ -2168,7 +2191,7 @@ static void ADDReduceDouble256VectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - Double256VectorTests::ADDReduce, Double256VectorTests::ADDReduceAll); + Double256VectorTests::ADDReduce, Double256VectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR); } static double ADDReduceMasked(double[] a, int idx, boolean[] mask) { @@ -2214,7 +2237,7 @@ static void ADDReduceDouble256VectorTestsMasked(IntFunction fa, IntFun } assertReductionArraysEqualsMasked(r, ra, a, mask, - Double256VectorTests::ADDReduceMasked, Double256VectorTests::ADDReduceAllMasked); + Double256VectorTests::ADDReduceMasked, Double256VectorTests::ADDReduceAllMasked, RELATIVE_ROUNDING_ERROR); } static double MULReduce(double[] a, int idx) { @@ -2257,7 +2280,7 @@ static void MULReduceDouble256VectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - Double256VectorTests::MULReduce, Double256VectorTests::MULReduceAll); + Double256VectorTests::MULReduce, Double256VectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR); } static double MULReduceMasked(double[] a, int idx, boolean[] mask) { @@ -2303,7 +2326,7 @@ static void MULReduceDouble256VectorTestsMasked(IntFunction fa, IntFun } assertReductionArraysEqualsMasked(r, ra, a, mask, - Double256VectorTests::MULReduceMasked, Double256VectorTests::MULReduceAllMasked); + Double256VectorTests::MULReduceMasked, Double256VectorTests::MULReduceAllMasked, RELATIVE_ROUNDING_ERROR); } static double MINReduce(double[] a, int idx) { diff --git a/test/jdk/jdk/incubator/vector/Double512VectorTests.java b/test/jdk/jdk/incubator/vector/Double512VectorTests.java index 406636bed0ec7..21209c23ea10e 100644 --- a/test/jdk/jdk/incubator/vector/Double512VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Double512VectorTests.java @@ -60,6 +60,8 @@ public class Double512VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); + // for floating point reduction ops that may introduce rounding errors + private static final double RELATIVE_ROUNDING_ERROR = (double)0.000001; static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 512); @@ -119,15 +121,21 @@ interface FReductionAllOp { static void assertReductionArraysEquals(double[] r, double rc, double[] a, FReductionOp f, FReductionAllOp fa) { + assertReductionArraysEquals(r, rc, a, f, fa, (double)0.0); + } + + static void assertReductionArraysEquals(double[] r, double rc, double[] a, + FReductionOp f, FReductionAllOp fa, + double relativeError) { int i = 0; try { - Assert.assertEquals(rc, fa.apply(a)); + Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError)); for (; i < a.length; i += SPECIES.length()) { - Assert.assertEquals(r[i], f.apply(a, i)); + Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError)); } } catch (AssertionError e) { - Assert.assertEquals(rc, fa.apply(a), "Final result is incorrect!"); - Assert.assertEquals(r[i], f.apply(a, i), "at index #" + i); + Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError), "Final result is incorrect!"); + Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError), "at index #" + i); } } @@ -141,15 +149,22 @@ interface FReductionAllMaskedOp { static void assertReductionArraysEqualsMasked(double[] r, double rc, double[] a, boolean[] mask, FReductionMaskedOp f, FReductionAllMaskedOp fa) { + assertReductionArraysEqualsMasked(r, rc, a, mask, f, fa, (double)0.0); + } + + static void assertReductionArraysEqualsMasked(double[] r, double rc, double[] a, boolean[] mask, + FReductionMaskedOp f, FReductionAllMaskedOp fa, + double relativeError) { int i = 0; try { - Assert.assertEquals(rc, fa.apply(a, mask)); + Assert.assertEquals(rc, fa.apply(a, mask), Math.abs(rc * relativeError)); for (; i < a.length; i += SPECIES.length()) { - Assert.assertEquals(r[i], f.apply(a, i, mask)); + Assert.assertEquals(r[i], f.apply(a, i, mask), Math.abs(r[i] * +relativeError)); } } catch (AssertionError e) { - Assert.assertEquals(rc, fa.apply(a, mask), "Final result is incorrect!"); - Assert.assertEquals(r[i], f.apply(a, i, mask), "at index #" + i); + Assert.assertEquals(rc, fa.apply(a, mask), Math.abs(rc * relativeError), "Final result is incorrect!"); + Assert.assertEquals(r[i], f.apply(a, i, mask), Math.abs(r[i] * relativeError), "at index #" + i); } } @@ -1078,6 +1093,14 @@ static long bits(double e) { return fill(s * BUFFER_REPS, i -> (((double)(i + 1) == 0) ? 1 : (double)(i + 1))); }), + withToString("double[0.01 + (i / (i + 1))]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (double)0.01 + ((double)i / (i + 1))); + }), + withToString("double[i -> i % 17 == 0 ? cornerCaseValue(i) : 0.01 + (i / (i + 1))]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> i % 17 == 0 ? cornerCaseValue(i) : (double)0.01 + ((double)i / (i + 1))); + }), withToString("double[cornerCaseValue(i)]", (int s) -> { return fill(s * BUFFER_REPS, i -> cornerCaseValue(i)); @@ -2168,7 +2191,7 @@ static void ADDReduceDouble512VectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - Double512VectorTests::ADDReduce, Double512VectorTests::ADDReduceAll); + Double512VectorTests::ADDReduce, Double512VectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR); } static double ADDReduceMasked(double[] a, int idx, boolean[] mask) { @@ -2214,7 +2237,7 @@ static void ADDReduceDouble512VectorTestsMasked(IntFunction fa, IntFun } assertReductionArraysEqualsMasked(r, ra, a, mask, - Double512VectorTests::ADDReduceMasked, Double512VectorTests::ADDReduceAllMasked); + Double512VectorTests::ADDReduceMasked, Double512VectorTests::ADDReduceAllMasked, RELATIVE_ROUNDING_ERROR); } static double MULReduce(double[] a, int idx) { @@ -2257,7 +2280,7 @@ static void MULReduceDouble512VectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - Double512VectorTests::MULReduce, Double512VectorTests::MULReduceAll); + Double512VectorTests::MULReduce, Double512VectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR); } static double MULReduceMasked(double[] a, int idx, boolean[] mask) { @@ -2303,7 +2326,7 @@ static void MULReduceDouble512VectorTestsMasked(IntFunction fa, IntFun } assertReductionArraysEqualsMasked(r, ra, a, mask, - Double512VectorTests::MULReduceMasked, Double512VectorTests::MULReduceAllMasked); + Double512VectorTests::MULReduceMasked, Double512VectorTests::MULReduceAllMasked, RELATIVE_ROUNDING_ERROR); } static double MINReduce(double[] a, int idx) { diff --git a/test/jdk/jdk/incubator/vector/Double64VectorTests.java b/test/jdk/jdk/incubator/vector/Double64VectorTests.java index c86b028107295..360445a6b7f2e 100644 --- a/test/jdk/jdk/incubator/vector/Double64VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Double64VectorTests.java @@ -60,6 +60,8 @@ public class Double64VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); + // for floating point reduction ops that may introduce rounding errors + private static final double RELATIVE_ROUNDING_ERROR = (double)0.000001; static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 64); @@ -119,15 +121,21 @@ interface FReductionAllOp { static void assertReductionArraysEquals(double[] r, double rc, double[] a, FReductionOp f, FReductionAllOp fa) { + assertReductionArraysEquals(r, rc, a, f, fa, (double)0.0); + } + + static void assertReductionArraysEquals(double[] r, double rc, double[] a, + FReductionOp f, FReductionAllOp fa, + double relativeError) { int i = 0; try { - Assert.assertEquals(rc, fa.apply(a)); + Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError)); for (; i < a.length; i += SPECIES.length()) { - Assert.assertEquals(r[i], f.apply(a, i)); + Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError)); } } catch (AssertionError e) { - Assert.assertEquals(rc, fa.apply(a), "Final result is incorrect!"); - Assert.assertEquals(r[i], f.apply(a, i), "at index #" + i); + Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError), "Final result is incorrect!"); + Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError), "at index #" + i); } } @@ -141,15 +149,22 @@ interface FReductionAllMaskedOp { static void assertReductionArraysEqualsMasked(double[] r, double rc, double[] a, boolean[] mask, FReductionMaskedOp f, FReductionAllMaskedOp fa) { + assertReductionArraysEqualsMasked(r, rc, a, mask, f, fa, (double)0.0); + } + + static void assertReductionArraysEqualsMasked(double[] r, double rc, double[] a, boolean[] mask, + FReductionMaskedOp f, FReductionAllMaskedOp fa, + double relativeError) { int i = 0; try { - Assert.assertEquals(rc, fa.apply(a, mask)); + Assert.assertEquals(rc, fa.apply(a, mask), Math.abs(rc * relativeError)); for (; i < a.length; i += SPECIES.length()) { - Assert.assertEquals(r[i], f.apply(a, i, mask)); + Assert.assertEquals(r[i], f.apply(a, i, mask), Math.abs(r[i] * +relativeError)); } } catch (AssertionError e) { - Assert.assertEquals(rc, fa.apply(a, mask), "Final result is incorrect!"); - Assert.assertEquals(r[i], f.apply(a, i, mask), "at index #" + i); + Assert.assertEquals(rc, fa.apply(a, mask), Math.abs(rc * relativeError), "Final result is incorrect!"); + Assert.assertEquals(r[i], f.apply(a, i, mask), Math.abs(r[i] * relativeError), "at index #" + i); } } @@ -1078,6 +1093,14 @@ static long bits(double e) { return fill(s * BUFFER_REPS, i -> (((double)(i + 1) == 0) ? 1 : (double)(i + 1))); }), + withToString("double[0.01 + (i / (i + 1))]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (double)0.01 + ((double)i / (i + 1))); + }), + withToString("double[i -> i % 17 == 0 ? cornerCaseValue(i) : 0.01 + (i / (i + 1))]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> i % 17 == 0 ? cornerCaseValue(i) : (double)0.01 + ((double)i / (i + 1))); + }), withToString("double[cornerCaseValue(i)]", (int s) -> { return fill(s * BUFFER_REPS, i -> cornerCaseValue(i)); @@ -2168,7 +2191,7 @@ static void ADDReduceDouble64VectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - Double64VectorTests::ADDReduce, Double64VectorTests::ADDReduceAll); + Double64VectorTests::ADDReduce, Double64VectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR); } static double ADDReduceMasked(double[] a, int idx, boolean[] mask) { @@ -2214,7 +2237,7 @@ static void ADDReduceDouble64VectorTestsMasked(IntFunction fa, IntFunc } assertReductionArraysEqualsMasked(r, ra, a, mask, - Double64VectorTests::ADDReduceMasked, Double64VectorTests::ADDReduceAllMasked); + Double64VectorTests::ADDReduceMasked, Double64VectorTests::ADDReduceAllMasked, RELATIVE_ROUNDING_ERROR); } static double MULReduce(double[] a, int idx) { @@ -2257,7 +2280,7 @@ static void MULReduceDouble64VectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - Double64VectorTests::MULReduce, Double64VectorTests::MULReduceAll); + Double64VectorTests::MULReduce, Double64VectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR); } static double MULReduceMasked(double[] a, int idx, boolean[] mask) { @@ -2303,7 +2326,7 @@ static void MULReduceDouble64VectorTestsMasked(IntFunction fa, IntFunc } assertReductionArraysEqualsMasked(r, ra, a, mask, - Double64VectorTests::MULReduceMasked, Double64VectorTests::MULReduceAllMasked); + Double64VectorTests::MULReduceMasked, Double64VectorTests::MULReduceAllMasked, RELATIVE_ROUNDING_ERROR); } static double MINReduce(double[] a, int idx) { diff --git a/test/jdk/jdk/incubator/vector/DoubleMaxVectorTests.java b/test/jdk/jdk/incubator/vector/DoubleMaxVectorTests.java index bcc7b6b0144ab..01d72b64d2d92 100644 --- a/test/jdk/jdk/incubator/vector/DoubleMaxVectorTests.java +++ b/test/jdk/jdk/incubator/vector/DoubleMaxVectorTests.java @@ -65,6 +65,8 @@ static VectorShape getMaxBit() { private static final int Max = 256; // juts so we can do N/Max + // for floating point reduction ops that may introduce rounding errors + private static final double RELATIVE_ROUNDING_ERROR = (double)0.000001; static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / Max); @@ -124,15 +126,21 @@ interface FReductionAllOp { static void assertReductionArraysEquals(double[] r, double rc, double[] a, FReductionOp f, FReductionAllOp fa) { + assertReductionArraysEquals(r, rc, a, f, fa, (double)0.0); + } + + static void assertReductionArraysEquals(double[] r, double rc, double[] a, + FReductionOp f, FReductionAllOp fa, + double relativeError) { int i = 0; try { - Assert.assertEquals(rc, fa.apply(a)); + Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError)); for (; i < a.length; i += SPECIES.length()) { - Assert.assertEquals(r[i], f.apply(a, i)); + Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError)); } } catch (AssertionError e) { - Assert.assertEquals(rc, fa.apply(a), "Final result is incorrect!"); - Assert.assertEquals(r[i], f.apply(a, i), "at index #" + i); + Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError), "Final result is incorrect!"); + Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError), "at index #" + i); } } @@ -146,15 +154,22 @@ interface FReductionAllMaskedOp { static void assertReductionArraysEqualsMasked(double[] r, double rc, double[] a, boolean[] mask, FReductionMaskedOp f, FReductionAllMaskedOp fa) { + assertReductionArraysEqualsMasked(r, rc, a, mask, f, fa, (double)0.0); + } + + static void assertReductionArraysEqualsMasked(double[] r, double rc, double[] a, boolean[] mask, + FReductionMaskedOp f, FReductionAllMaskedOp fa, + double relativeError) { int i = 0; try { - Assert.assertEquals(rc, fa.apply(a, mask)); + Assert.assertEquals(rc, fa.apply(a, mask), Math.abs(rc * relativeError)); for (; i < a.length; i += SPECIES.length()) { - Assert.assertEquals(r[i], f.apply(a, i, mask)); + Assert.assertEquals(r[i], f.apply(a, i, mask), Math.abs(r[i] * +relativeError)); } } catch (AssertionError e) { - Assert.assertEquals(rc, fa.apply(a, mask), "Final result is incorrect!"); - Assert.assertEquals(r[i], f.apply(a, i, mask), "at index #" + i); + Assert.assertEquals(rc, fa.apply(a, mask), Math.abs(rc * relativeError), "Final result is incorrect!"); + Assert.assertEquals(r[i], f.apply(a, i, mask), Math.abs(r[i] * relativeError), "at index #" + i); } } @@ -1083,6 +1098,14 @@ static long bits(double e) { return fill(s * BUFFER_REPS, i -> (((double)(i + 1) == 0) ? 1 : (double)(i + 1))); }), + withToString("double[0.01 + (i / (i + 1))]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (double)0.01 + ((double)i / (i + 1))); + }), + withToString("double[i -> i % 17 == 0 ? cornerCaseValue(i) : 0.01 + (i / (i + 1))]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> i % 17 == 0 ? cornerCaseValue(i) : (double)0.01 + ((double)i / (i + 1))); + }), withToString("double[cornerCaseValue(i)]", (int s) -> { return fill(s * BUFFER_REPS, i -> cornerCaseValue(i)); @@ -2173,7 +2196,7 @@ static void ADDReduceDoubleMaxVectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - DoubleMaxVectorTests::ADDReduce, DoubleMaxVectorTests::ADDReduceAll); + DoubleMaxVectorTests::ADDReduce, DoubleMaxVectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR); } static double ADDReduceMasked(double[] a, int idx, boolean[] mask) { @@ -2219,7 +2242,7 @@ static void ADDReduceDoubleMaxVectorTestsMasked(IntFunction fa, IntFun } assertReductionArraysEqualsMasked(r, ra, a, mask, - DoubleMaxVectorTests::ADDReduceMasked, DoubleMaxVectorTests::ADDReduceAllMasked); + DoubleMaxVectorTests::ADDReduceMasked, DoubleMaxVectorTests::ADDReduceAllMasked, RELATIVE_ROUNDING_ERROR); } static double MULReduce(double[] a, int idx) { @@ -2262,7 +2285,7 @@ static void MULReduceDoubleMaxVectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - DoubleMaxVectorTests::MULReduce, DoubleMaxVectorTests::MULReduceAll); + DoubleMaxVectorTests::MULReduce, DoubleMaxVectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR); } static double MULReduceMasked(double[] a, int idx, boolean[] mask) { @@ -2308,7 +2331,7 @@ static void MULReduceDoubleMaxVectorTestsMasked(IntFunction fa, IntFun } assertReductionArraysEqualsMasked(r, ra, a, mask, - DoubleMaxVectorTests::MULReduceMasked, DoubleMaxVectorTests::MULReduceAllMasked); + DoubleMaxVectorTests::MULReduceMasked, DoubleMaxVectorTests::MULReduceAllMasked, RELATIVE_ROUNDING_ERROR); } static double MINReduce(double[] a, int idx) { diff --git a/test/jdk/jdk/incubator/vector/Float128VectorTests.java b/test/jdk/jdk/incubator/vector/Float128VectorTests.java index 21de8e1bd966c..9f1b28a80c6a5 100644 --- a/test/jdk/jdk/incubator/vector/Float128VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Float128VectorTests.java @@ -60,6 +60,8 @@ public class Float128VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); + // for floating point reduction ops that may introduce rounding errors + private static final float RELATIVE_ROUNDING_ERROR = (float)0.000001; static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 128); @@ -119,15 +121,21 @@ interface FReductionAllOp { static void assertReductionArraysEquals(float[] r, float rc, float[] a, FReductionOp f, FReductionAllOp fa) { + assertReductionArraysEquals(r, rc, a, f, fa, (float)0.0); + } + + static void assertReductionArraysEquals(float[] r, float rc, float[] a, + FReductionOp f, FReductionAllOp fa, + float relativeError) { int i = 0; try { - Assert.assertEquals(rc, fa.apply(a)); + Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError)); for (; i < a.length; i += SPECIES.length()) { - Assert.assertEquals(r[i], f.apply(a, i)); + Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError)); } } catch (AssertionError e) { - Assert.assertEquals(rc, fa.apply(a), "Final result is incorrect!"); - Assert.assertEquals(r[i], f.apply(a, i), "at index #" + i); + Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError), "Final result is incorrect!"); + Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError), "at index #" + i); } } @@ -141,15 +149,22 @@ interface FReductionAllMaskedOp { static void assertReductionArraysEqualsMasked(float[] r, float rc, float[] a, boolean[] mask, FReductionMaskedOp f, FReductionAllMaskedOp fa) { + assertReductionArraysEqualsMasked(r, rc, a, mask, f, fa, (float)0.0); + } + + static void assertReductionArraysEqualsMasked(float[] r, float rc, float[] a, boolean[] mask, + FReductionMaskedOp f, FReductionAllMaskedOp fa, + float relativeError) { int i = 0; try { - Assert.assertEquals(rc, fa.apply(a, mask)); + Assert.assertEquals(rc, fa.apply(a, mask), Math.abs(rc * relativeError)); for (; i < a.length; i += SPECIES.length()) { - Assert.assertEquals(r[i], f.apply(a, i, mask)); + Assert.assertEquals(r[i], f.apply(a, i, mask), Math.abs(r[i] * +relativeError)); } } catch (AssertionError e) { - Assert.assertEquals(rc, fa.apply(a, mask), "Final result is incorrect!"); - Assert.assertEquals(r[i], f.apply(a, i, mask), "at index #" + i); + Assert.assertEquals(rc, fa.apply(a, mask), Math.abs(rc * relativeError), "Final result is incorrect!"); + Assert.assertEquals(r[i], f.apply(a, i, mask), Math.abs(r[i] * relativeError), "at index #" + i); } } @@ -1089,6 +1104,14 @@ static int bits(float e) { return fill(s * BUFFER_REPS, i -> (((float)(i + 1) == 0) ? 1 : (float)(i + 1))); }), + withToString("float[0.01 + (i / (i + 1))]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (float)0.01 + ((float)i / (i + 1))); + }), + withToString("float[i -> i % 17 == 0 ? cornerCaseValue(i) : 0.01 + (i / (i + 1))]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> i % 17 == 0 ? cornerCaseValue(i) : (float)0.01 + ((float)i / (i + 1))); + }), withToString("float[cornerCaseValue(i)]", (int s) -> { return fill(s * BUFFER_REPS, i -> cornerCaseValue(i)); @@ -2179,7 +2202,7 @@ static void ADDReduceFloat128VectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - Float128VectorTests::ADDReduce, Float128VectorTests::ADDReduceAll); + Float128VectorTests::ADDReduce, Float128VectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR); } static float ADDReduceMasked(float[] a, int idx, boolean[] mask) { @@ -2225,7 +2248,7 @@ static void ADDReduceFloat128VectorTestsMasked(IntFunction fa, IntFunct } assertReductionArraysEqualsMasked(r, ra, a, mask, - Float128VectorTests::ADDReduceMasked, Float128VectorTests::ADDReduceAllMasked); + Float128VectorTests::ADDReduceMasked, Float128VectorTests::ADDReduceAllMasked, RELATIVE_ROUNDING_ERROR); } static float MULReduce(float[] a, int idx) { @@ -2268,7 +2291,7 @@ static void MULReduceFloat128VectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - Float128VectorTests::MULReduce, Float128VectorTests::MULReduceAll); + Float128VectorTests::MULReduce, Float128VectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR); } static float MULReduceMasked(float[] a, int idx, boolean[] mask) { @@ -2314,7 +2337,7 @@ static void MULReduceFloat128VectorTestsMasked(IntFunction fa, IntFunct } assertReductionArraysEqualsMasked(r, ra, a, mask, - Float128VectorTests::MULReduceMasked, Float128VectorTests::MULReduceAllMasked); + Float128VectorTests::MULReduceMasked, Float128VectorTests::MULReduceAllMasked, RELATIVE_ROUNDING_ERROR); } static float MINReduce(float[] a, int idx) { diff --git a/test/jdk/jdk/incubator/vector/Float256VectorTests.java b/test/jdk/jdk/incubator/vector/Float256VectorTests.java index bd74e79d24b11..649b06a007d6c 100644 --- a/test/jdk/jdk/incubator/vector/Float256VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Float256VectorTests.java @@ -60,6 +60,8 @@ public class Float256VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); + // for floating point reduction ops that may introduce rounding errors + private static final float RELATIVE_ROUNDING_ERROR = (float)0.000001; static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 256); @@ -119,15 +121,21 @@ interface FReductionAllOp { static void assertReductionArraysEquals(float[] r, float rc, float[] a, FReductionOp f, FReductionAllOp fa) { + assertReductionArraysEquals(r, rc, a, f, fa, (float)0.0); + } + + static void assertReductionArraysEquals(float[] r, float rc, float[] a, + FReductionOp f, FReductionAllOp fa, + float relativeError) { int i = 0; try { - Assert.assertEquals(rc, fa.apply(a)); + Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError)); for (; i < a.length; i += SPECIES.length()) { - Assert.assertEquals(r[i], f.apply(a, i)); + Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError)); } } catch (AssertionError e) { - Assert.assertEquals(rc, fa.apply(a), "Final result is incorrect!"); - Assert.assertEquals(r[i], f.apply(a, i), "at index #" + i); + Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError), "Final result is incorrect!"); + Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError), "at index #" + i); } } @@ -141,15 +149,22 @@ interface FReductionAllMaskedOp { static void assertReductionArraysEqualsMasked(float[] r, float rc, float[] a, boolean[] mask, FReductionMaskedOp f, FReductionAllMaskedOp fa) { + assertReductionArraysEqualsMasked(r, rc, a, mask, f, fa, (float)0.0); + } + + static void assertReductionArraysEqualsMasked(float[] r, float rc, float[] a, boolean[] mask, + FReductionMaskedOp f, FReductionAllMaskedOp fa, + float relativeError) { int i = 0; try { - Assert.assertEquals(rc, fa.apply(a, mask)); + Assert.assertEquals(rc, fa.apply(a, mask), Math.abs(rc * relativeError)); for (; i < a.length; i += SPECIES.length()) { - Assert.assertEquals(r[i], f.apply(a, i, mask)); + Assert.assertEquals(r[i], f.apply(a, i, mask), Math.abs(r[i] * +relativeError)); } } catch (AssertionError e) { - Assert.assertEquals(rc, fa.apply(a, mask), "Final result is incorrect!"); - Assert.assertEquals(r[i], f.apply(a, i, mask), "at index #" + i); + Assert.assertEquals(rc, fa.apply(a, mask), Math.abs(rc * relativeError), "Final result is incorrect!"); + Assert.assertEquals(r[i], f.apply(a, i, mask), Math.abs(r[i] * relativeError), "at index #" + i); } } @@ -1089,6 +1104,14 @@ static int bits(float e) { return fill(s * BUFFER_REPS, i -> (((float)(i + 1) == 0) ? 1 : (float)(i + 1))); }), + withToString("float[0.01 + (i / (i + 1))]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (float)0.01 + ((float)i / (i + 1))); + }), + withToString("float[i -> i % 17 == 0 ? cornerCaseValue(i) : 0.01 + (i / (i + 1))]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> i % 17 == 0 ? cornerCaseValue(i) : (float)0.01 + ((float)i / (i + 1))); + }), withToString("float[cornerCaseValue(i)]", (int s) -> { return fill(s * BUFFER_REPS, i -> cornerCaseValue(i)); @@ -2179,7 +2202,7 @@ static void ADDReduceFloat256VectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - Float256VectorTests::ADDReduce, Float256VectorTests::ADDReduceAll); + Float256VectorTests::ADDReduce, Float256VectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR); } static float ADDReduceMasked(float[] a, int idx, boolean[] mask) { @@ -2225,7 +2248,7 @@ static void ADDReduceFloat256VectorTestsMasked(IntFunction fa, IntFunct } assertReductionArraysEqualsMasked(r, ra, a, mask, - Float256VectorTests::ADDReduceMasked, Float256VectorTests::ADDReduceAllMasked); + Float256VectorTests::ADDReduceMasked, Float256VectorTests::ADDReduceAllMasked, RELATIVE_ROUNDING_ERROR); } static float MULReduce(float[] a, int idx) { @@ -2268,7 +2291,7 @@ static void MULReduceFloat256VectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - Float256VectorTests::MULReduce, Float256VectorTests::MULReduceAll); + Float256VectorTests::MULReduce, Float256VectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR); } static float MULReduceMasked(float[] a, int idx, boolean[] mask) { @@ -2314,7 +2337,7 @@ static void MULReduceFloat256VectorTestsMasked(IntFunction fa, IntFunct } assertReductionArraysEqualsMasked(r, ra, a, mask, - Float256VectorTests::MULReduceMasked, Float256VectorTests::MULReduceAllMasked); + Float256VectorTests::MULReduceMasked, Float256VectorTests::MULReduceAllMasked, RELATIVE_ROUNDING_ERROR); } static float MINReduce(float[] a, int idx) { diff --git a/test/jdk/jdk/incubator/vector/Float512VectorTests.java b/test/jdk/jdk/incubator/vector/Float512VectorTests.java index 527900594f4ca..5bd3932cef158 100644 --- a/test/jdk/jdk/incubator/vector/Float512VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Float512VectorTests.java @@ -60,6 +60,8 @@ public class Float512VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); + // for floating point reduction ops that may introduce rounding errors + private static final float RELATIVE_ROUNDING_ERROR = (float)0.000001; static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 512); @@ -119,15 +121,21 @@ interface FReductionAllOp { static void assertReductionArraysEquals(float[] r, float rc, float[] a, FReductionOp f, FReductionAllOp fa) { + assertReductionArraysEquals(r, rc, a, f, fa, (float)0.0); + } + + static void assertReductionArraysEquals(float[] r, float rc, float[] a, + FReductionOp f, FReductionAllOp fa, + float relativeError) { int i = 0; try { - Assert.assertEquals(rc, fa.apply(a)); + Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError)); for (; i < a.length; i += SPECIES.length()) { - Assert.assertEquals(r[i], f.apply(a, i)); + Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError)); } } catch (AssertionError e) { - Assert.assertEquals(rc, fa.apply(a), "Final result is incorrect!"); - Assert.assertEquals(r[i], f.apply(a, i), "at index #" + i); + Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError), "Final result is incorrect!"); + Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError), "at index #" + i); } } @@ -141,15 +149,22 @@ interface FReductionAllMaskedOp { static void assertReductionArraysEqualsMasked(float[] r, float rc, float[] a, boolean[] mask, FReductionMaskedOp f, FReductionAllMaskedOp fa) { + assertReductionArraysEqualsMasked(r, rc, a, mask, f, fa, (float)0.0); + } + + static void assertReductionArraysEqualsMasked(float[] r, float rc, float[] a, boolean[] mask, + FReductionMaskedOp f, FReductionAllMaskedOp fa, + float relativeError) { int i = 0; try { - Assert.assertEquals(rc, fa.apply(a, mask)); + Assert.assertEquals(rc, fa.apply(a, mask), Math.abs(rc * relativeError)); for (; i < a.length; i += SPECIES.length()) { - Assert.assertEquals(r[i], f.apply(a, i, mask)); + Assert.assertEquals(r[i], f.apply(a, i, mask), Math.abs(r[i] * +relativeError)); } } catch (AssertionError e) { - Assert.assertEquals(rc, fa.apply(a, mask), "Final result is incorrect!"); - Assert.assertEquals(r[i], f.apply(a, i, mask), "at index #" + i); + Assert.assertEquals(rc, fa.apply(a, mask), Math.abs(rc * relativeError), "Final result is incorrect!"); + Assert.assertEquals(r[i], f.apply(a, i, mask), Math.abs(r[i] * relativeError), "at index #" + i); } } @@ -1089,6 +1104,14 @@ static int bits(float e) { return fill(s * BUFFER_REPS, i -> (((float)(i + 1) == 0) ? 1 : (float)(i + 1))); }), + withToString("float[0.01 + (i / (i + 1))]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (float)0.01 + ((float)i / (i + 1))); + }), + withToString("float[i -> i % 17 == 0 ? cornerCaseValue(i) : 0.01 + (i / (i + 1))]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> i % 17 == 0 ? cornerCaseValue(i) : (float)0.01 + ((float)i / (i + 1))); + }), withToString("float[cornerCaseValue(i)]", (int s) -> { return fill(s * BUFFER_REPS, i -> cornerCaseValue(i)); @@ -2179,7 +2202,7 @@ static void ADDReduceFloat512VectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - Float512VectorTests::ADDReduce, Float512VectorTests::ADDReduceAll); + Float512VectorTests::ADDReduce, Float512VectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR); } static float ADDReduceMasked(float[] a, int idx, boolean[] mask) { @@ -2225,7 +2248,7 @@ static void ADDReduceFloat512VectorTestsMasked(IntFunction fa, IntFunct } assertReductionArraysEqualsMasked(r, ra, a, mask, - Float512VectorTests::ADDReduceMasked, Float512VectorTests::ADDReduceAllMasked); + Float512VectorTests::ADDReduceMasked, Float512VectorTests::ADDReduceAllMasked, RELATIVE_ROUNDING_ERROR); } static float MULReduce(float[] a, int idx) { @@ -2268,7 +2291,7 @@ static void MULReduceFloat512VectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - Float512VectorTests::MULReduce, Float512VectorTests::MULReduceAll); + Float512VectorTests::MULReduce, Float512VectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR); } static float MULReduceMasked(float[] a, int idx, boolean[] mask) { @@ -2314,7 +2337,7 @@ static void MULReduceFloat512VectorTestsMasked(IntFunction fa, IntFunct } assertReductionArraysEqualsMasked(r, ra, a, mask, - Float512VectorTests::MULReduceMasked, Float512VectorTests::MULReduceAllMasked); + Float512VectorTests::MULReduceMasked, Float512VectorTests::MULReduceAllMasked, RELATIVE_ROUNDING_ERROR); } static float MINReduce(float[] a, int idx) { diff --git a/test/jdk/jdk/incubator/vector/Float64VectorTests.java b/test/jdk/jdk/incubator/vector/Float64VectorTests.java index a07c4f19b1346..1922be94f403f 100644 --- a/test/jdk/jdk/incubator/vector/Float64VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Float64VectorTests.java @@ -60,6 +60,8 @@ public class Float64VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); + // for floating point reduction ops that may introduce rounding errors + private static final float RELATIVE_ROUNDING_ERROR = (float)0.000001; static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 64); @@ -119,15 +121,21 @@ interface FReductionAllOp { static void assertReductionArraysEquals(float[] r, float rc, float[] a, FReductionOp f, FReductionAllOp fa) { + assertReductionArraysEquals(r, rc, a, f, fa, (float)0.0); + } + + static void assertReductionArraysEquals(float[] r, float rc, float[] a, + FReductionOp f, FReductionAllOp fa, + float relativeError) { int i = 0; try { - Assert.assertEquals(rc, fa.apply(a)); + Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError)); for (; i < a.length; i += SPECIES.length()) { - Assert.assertEquals(r[i], f.apply(a, i)); + Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError)); } } catch (AssertionError e) { - Assert.assertEquals(rc, fa.apply(a), "Final result is incorrect!"); - Assert.assertEquals(r[i], f.apply(a, i), "at index #" + i); + Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError), "Final result is incorrect!"); + Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError), "at index #" + i); } } @@ -141,15 +149,22 @@ interface FReductionAllMaskedOp { static void assertReductionArraysEqualsMasked(float[] r, float rc, float[] a, boolean[] mask, FReductionMaskedOp f, FReductionAllMaskedOp fa) { + assertReductionArraysEqualsMasked(r, rc, a, mask, f, fa, (float)0.0); + } + + static void assertReductionArraysEqualsMasked(float[] r, float rc, float[] a, boolean[] mask, + FReductionMaskedOp f, FReductionAllMaskedOp fa, + float relativeError) { int i = 0; try { - Assert.assertEquals(rc, fa.apply(a, mask)); + Assert.assertEquals(rc, fa.apply(a, mask), Math.abs(rc * relativeError)); for (; i < a.length; i += SPECIES.length()) { - Assert.assertEquals(r[i], f.apply(a, i, mask)); + Assert.assertEquals(r[i], f.apply(a, i, mask), Math.abs(r[i] * +relativeError)); } } catch (AssertionError e) { - Assert.assertEquals(rc, fa.apply(a, mask), "Final result is incorrect!"); - Assert.assertEquals(r[i], f.apply(a, i, mask), "at index #" + i); + Assert.assertEquals(rc, fa.apply(a, mask), Math.abs(rc * relativeError), "Final result is incorrect!"); + Assert.assertEquals(r[i], f.apply(a, i, mask), Math.abs(r[i] * relativeError), "at index #" + i); } } @@ -1089,6 +1104,14 @@ static int bits(float e) { return fill(s * BUFFER_REPS, i -> (((float)(i + 1) == 0) ? 1 : (float)(i + 1))); }), + withToString("float[0.01 + (i / (i + 1))]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (float)0.01 + ((float)i / (i + 1))); + }), + withToString("float[i -> i % 17 == 0 ? cornerCaseValue(i) : 0.01 + (i / (i + 1))]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> i % 17 == 0 ? cornerCaseValue(i) : (float)0.01 + ((float)i / (i + 1))); + }), withToString("float[cornerCaseValue(i)]", (int s) -> { return fill(s * BUFFER_REPS, i -> cornerCaseValue(i)); @@ -2179,7 +2202,7 @@ static void ADDReduceFloat64VectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - Float64VectorTests::ADDReduce, Float64VectorTests::ADDReduceAll); + Float64VectorTests::ADDReduce, Float64VectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR); } static float ADDReduceMasked(float[] a, int idx, boolean[] mask) { @@ -2225,7 +2248,7 @@ static void ADDReduceFloat64VectorTestsMasked(IntFunction fa, IntFuncti } assertReductionArraysEqualsMasked(r, ra, a, mask, - Float64VectorTests::ADDReduceMasked, Float64VectorTests::ADDReduceAllMasked); + Float64VectorTests::ADDReduceMasked, Float64VectorTests::ADDReduceAllMasked, RELATIVE_ROUNDING_ERROR); } static float MULReduce(float[] a, int idx) { @@ -2268,7 +2291,7 @@ static void MULReduceFloat64VectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - Float64VectorTests::MULReduce, Float64VectorTests::MULReduceAll); + Float64VectorTests::MULReduce, Float64VectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR); } static float MULReduceMasked(float[] a, int idx, boolean[] mask) { @@ -2314,7 +2337,7 @@ static void MULReduceFloat64VectorTestsMasked(IntFunction fa, IntFuncti } assertReductionArraysEqualsMasked(r, ra, a, mask, - Float64VectorTests::MULReduceMasked, Float64VectorTests::MULReduceAllMasked); + Float64VectorTests::MULReduceMasked, Float64VectorTests::MULReduceAllMasked, RELATIVE_ROUNDING_ERROR); } static float MINReduce(float[] a, int idx) { diff --git a/test/jdk/jdk/incubator/vector/FloatMaxVectorTests.java b/test/jdk/jdk/incubator/vector/FloatMaxVectorTests.java index 1c42c670b87ac..3e72f9077f624 100644 --- a/test/jdk/jdk/incubator/vector/FloatMaxVectorTests.java +++ b/test/jdk/jdk/incubator/vector/FloatMaxVectorTests.java @@ -65,6 +65,8 @@ static VectorShape getMaxBit() { private static final int Max = 256; // juts so we can do N/Max + // for floating point reduction ops that may introduce rounding errors + private static final float RELATIVE_ROUNDING_ERROR = (float)0.000001; static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / Max); @@ -124,15 +126,21 @@ interface FReductionAllOp { static void assertReductionArraysEquals(float[] r, float rc, float[] a, FReductionOp f, FReductionAllOp fa) { + assertReductionArraysEquals(r, rc, a, f, fa, (float)0.0); + } + + static void assertReductionArraysEquals(float[] r, float rc, float[] a, + FReductionOp f, FReductionAllOp fa, + float relativeError) { int i = 0; try { - Assert.assertEquals(rc, fa.apply(a)); + Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError)); for (; i < a.length; i += SPECIES.length()) { - Assert.assertEquals(r[i], f.apply(a, i)); + Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError)); } } catch (AssertionError e) { - Assert.assertEquals(rc, fa.apply(a), "Final result is incorrect!"); - Assert.assertEquals(r[i], f.apply(a, i), "at index #" + i); + Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError), "Final result is incorrect!"); + Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError), "at index #" + i); } } @@ -146,15 +154,22 @@ interface FReductionAllMaskedOp { static void assertReductionArraysEqualsMasked(float[] r, float rc, float[] a, boolean[] mask, FReductionMaskedOp f, FReductionAllMaskedOp fa) { + assertReductionArraysEqualsMasked(r, rc, a, mask, f, fa, (float)0.0); + } + + static void assertReductionArraysEqualsMasked(float[] r, float rc, float[] a, boolean[] mask, + FReductionMaskedOp f, FReductionAllMaskedOp fa, + float relativeError) { int i = 0; try { - Assert.assertEquals(rc, fa.apply(a, mask)); + Assert.assertEquals(rc, fa.apply(a, mask), Math.abs(rc * relativeError)); for (; i < a.length; i += SPECIES.length()) { - Assert.assertEquals(r[i], f.apply(a, i, mask)); + Assert.assertEquals(r[i], f.apply(a, i, mask), Math.abs(r[i] * +relativeError)); } } catch (AssertionError e) { - Assert.assertEquals(rc, fa.apply(a, mask), "Final result is incorrect!"); - Assert.assertEquals(r[i], f.apply(a, i, mask), "at index #" + i); + Assert.assertEquals(rc, fa.apply(a, mask), Math.abs(rc * relativeError), "Final result is incorrect!"); + Assert.assertEquals(r[i], f.apply(a, i, mask), Math.abs(r[i] * relativeError), "at index #" + i); } } @@ -1094,6 +1109,14 @@ static int bits(float e) { return fill(s * BUFFER_REPS, i -> (((float)(i + 1) == 0) ? 1 : (float)(i + 1))); }), + withToString("float[0.01 + (i / (i + 1))]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (float)0.01 + ((float)i / (i + 1))); + }), + withToString("float[i -> i % 17 == 0 ? cornerCaseValue(i) : 0.01 + (i / (i + 1))]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> i % 17 == 0 ? cornerCaseValue(i) : (float)0.01 + ((float)i / (i + 1))); + }), withToString("float[cornerCaseValue(i)]", (int s) -> { return fill(s * BUFFER_REPS, i -> cornerCaseValue(i)); @@ -2184,7 +2207,7 @@ static void ADDReduceFloatMaxVectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - FloatMaxVectorTests::ADDReduce, FloatMaxVectorTests::ADDReduceAll); + FloatMaxVectorTests::ADDReduce, FloatMaxVectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR); } static float ADDReduceMasked(float[] a, int idx, boolean[] mask) { @@ -2230,7 +2253,7 @@ static void ADDReduceFloatMaxVectorTestsMasked(IntFunction fa, IntFunct } assertReductionArraysEqualsMasked(r, ra, a, mask, - FloatMaxVectorTests::ADDReduceMasked, FloatMaxVectorTests::ADDReduceAllMasked); + FloatMaxVectorTests::ADDReduceMasked, FloatMaxVectorTests::ADDReduceAllMasked, RELATIVE_ROUNDING_ERROR); } static float MULReduce(float[] a, int idx) { @@ -2273,7 +2296,7 @@ static void MULReduceFloatMaxVectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - FloatMaxVectorTests::MULReduce, FloatMaxVectorTests::MULReduceAll); + FloatMaxVectorTests::MULReduce, FloatMaxVectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR); } static float MULReduceMasked(float[] a, int idx, boolean[] mask) { @@ -2319,7 +2342,7 @@ static void MULReduceFloatMaxVectorTestsMasked(IntFunction fa, IntFunct } assertReductionArraysEqualsMasked(r, ra, a, mask, - FloatMaxVectorTests::MULReduceMasked, FloatMaxVectorTests::MULReduceAllMasked); + FloatMaxVectorTests::MULReduceMasked, FloatMaxVectorTests::MULReduceAllMasked, RELATIVE_ROUNDING_ERROR); } static float MINReduce(float[] a, int idx) { diff --git a/test/jdk/jdk/incubator/vector/templates/Unit-Reduction-Masked-op.template b/test/jdk/jdk/incubator/vector/templates/Unit-Reduction-Masked-op.template index 8af236eb0e3f7..3b306ce387aac 100644 --- a/test/jdk/jdk/incubator/vector/templates/Unit-Reduction-Masked-op.template +++ b/test/jdk/jdk/incubator/vector/templates/Unit-Reduction-Masked-op.template @@ -3,5 +3,9 @@ static void [[TEST]]Reduce$vectorteststype$Masked(IntFunction<$type$[]> fa, IntFunction fm) { [[KERNEL]] assertReductionArraysEqualsMasked(r, ra, a, mask, +#if[FP] + $vectorteststype$::[[TEST]]ReduceMasked, $vectorteststype$::[[TEST]]ReduceAllMasked, RELATIVE_ROUNDING_ERROR); +#else[FP] $vectorteststype$::[[TEST]]ReduceMasked, $vectorteststype$::[[TEST]]ReduceAllMasked); +#end[FP] } diff --git a/test/jdk/jdk/incubator/vector/templates/Unit-Reduction-op.template b/test/jdk/jdk/incubator/vector/templates/Unit-Reduction-op.template index e6bcdb83c2e02..5c68968870840 100644 --- a/test/jdk/jdk/incubator/vector/templates/Unit-Reduction-op.template +++ b/test/jdk/jdk/incubator/vector/templates/Unit-Reduction-op.template @@ -3,5 +3,9 @@ static void [[TEST]]Reduce$vectorteststype$(IntFunction<$type$[]> fa) { [[KERNEL]] assertReductionArraysEquals(r, ra, a, +#if[FP] + $vectorteststype$::[[TEST]]Reduce, $vectorteststype$::[[TEST]]ReduceAll, RELATIVE_ROUNDING_ERROR); +#else[FP] $vectorteststype$::[[TEST]]Reduce, $vectorteststype$::[[TEST]]ReduceAll); +#end[FP] } diff --git a/test/jdk/jdk/incubator/vector/templates/Unit-header.template b/test/jdk/jdk/incubator/vector/templates/Unit-header.template index 0f460c231dc85..87364836f7cbf 100644 --- a/test/jdk/jdk/incubator/vector/templates/Unit-header.template +++ b/test/jdk/jdk/incubator/vector/templates/Unit-header.template @@ -92,6 +92,10 @@ public class $vectorteststype$ extends AbstractVectorTest { #if[BITWISE] private static final $type$ CONST_SHIFT = $Boxtype$.SIZE / 2; #end[BITWISE] +#if[FP] + // for floating point reduction ops that may introduce rounding errors + private static final $type$ RELATIVE_ROUNDING_ERROR = ($type$)0.000001; +#end[FP] static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / $bits$); @@ -151,6 +155,9 @@ public class $vectorteststype$ extends AbstractVectorTest { static void assertReductionArraysEquals($type$[] r, $type$ rc, $type$[] a, FReductionOp f, FReductionAllOp fa) { +#if[FP] + assertReductionArraysEquals(r, rc, a, f, fa, ($type$)0.0); +#else[FP] int i = 0; try { Assert.assertEquals(rc, fa.apply(a)); @@ -161,7 +168,25 @@ public class $vectorteststype$ extends AbstractVectorTest { Assert.assertEquals(rc, fa.apply(a), "Final result is incorrect!"); Assert.assertEquals(r[i], f.apply(a, i), "at index #" + i); } +#end[FP] + } +#if[FP] + + static void assertReductionArraysEquals($type$[] r, $type$ rc, $type$[] a, + FReductionOp f, FReductionAllOp fa, + $type$ relativeError) { + int i = 0; + try { + Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError)); + for (; i < a.length; i += SPECIES.length()) { + Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError)); + } + } catch (AssertionError e) { + Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError), "Final result is incorrect!"); + Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError), "at index #" + i); + } } +#end[FP] interface FReductionMaskedOp { $type$ apply($type$[] a, int idx, boolean[] mask); @@ -173,6 +198,9 @@ public class $vectorteststype$ extends AbstractVectorTest { static void assertReductionArraysEqualsMasked($type$[] r, $type$ rc, $type$[] a, boolean[] mask, FReductionMaskedOp f, FReductionAllMaskedOp fa) { +#if[FP] + assertReductionArraysEqualsMasked(r, rc, a, mask, f, fa, ($type$)0.0); +#else[FP] int i = 0; try { Assert.assertEquals(rc, fa.apply(a, mask)); @@ -183,7 +211,26 @@ public class $vectorteststype$ extends AbstractVectorTest { Assert.assertEquals(rc, fa.apply(a, mask), "Final result is incorrect!"); Assert.assertEquals(r[i], f.apply(a, i, mask), "at index #" + i); } +#end[FP] + } +#if[FP] + + static void assertReductionArraysEqualsMasked($type$[] r, $type$ rc, $type$[] a, boolean[] mask, + FReductionMaskedOp f, FReductionAllMaskedOp fa, + $type$ relativeError) { + int i = 0; + try { + Assert.assertEquals(rc, fa.apply(a, mask), Math.abs(rc * relativeError)); + for (; i < a.length; i += SPECIES.length()) { + Assert.assertEquals(r[i], f.apply(a, i, mask), Math.abs(r[i] * +relativeError)); + } + } catch (AssertionError e) { + Assert.assertEquals(rc, fa.apply(a, mask), Math.abs(rc * relativeError), "Final result is incorrect!"); + Assert.assertEquals(r[i], f.apply(a, i, mask), Math.abs(r[i] * relativeError), "at index #" + i); + } } +#end[FP] #if[!Long] interface FReductionOpLong { @@ -1147,6 +1194,16 @@ public class $vectorteststype$ extends AbstractVectorTest { return fill(s * BUFFER_REPS, i -> ((($type$)(i + 1) == 0) ? 1 : ($type$)(i + 1))); }), +#if[FP] + withToString("$type$[0.01 + (i / (i + 1))]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> ($type$)0.01 + (($type$)i / (i + 1))); + }), + withToString("$type$[i -> i % 17 == 0 ? cornerCaseValue(i) : 0.01 + (i / (i + 1))]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> i % 17 == 0 ? cornerCaseValue(i) : ($type$)0.01 + (($type$)i / (i + 1))); + }), +#end[FP] withToString("$type$[cornerCaseValue(i)]", (int s) -> { return fill(s * BUFFER_REPS, i -> cornerCaseValue(i)); From a644670cc6afc3bd6cedaa7f0d7bb75c1de90417 Mon Sep 17 00:00:00 2001 From: Martin Doerr Date: Tue, 24 Oct 2023 09:09:33 +0000 Subject: [PATCH 16/28] 8318609: Upcall stubs should be smaller Co-authored-by: Jorn Vernee Reviewed-by: rrich, jvernee --- .../cpu/aarch64/stubGenerator_aarch64.cpp | 18 +++++++++++++ .../cpu/aarch64/upcallLinker_aarch64.cpp | 19 ++----------- src/hotspot/cpu/ppc/stubGenerator_ppc.cpp | 17 ++++++++++++ src/hotspot/cpu/ppc/upcallLinker_ppc.cpp | 20 +++----------- src/hotspot/cpu/riscv/stubGenerator_riscv.cpp | 17 ++++++++++++ src/hotspot/cpu/riscv/upcallLinker_riscv.cpp | 19 +++---------- src/hotspot/cpu/s390/stubGenerator_s390.cpp | 17 ++++++++++++ src/hotspot/cpu/s390/upcallLinker_s390.cpp | 20 +++----------- src/hotspot/cpu/x86/stubGenerator_x86_64.cpp | 21 +++++++++++++++ src/hotspot/cpu/x86/stubGenerator_x86_64.hpp | 3 +++ src/hotspot/cpu/x86/upcallLinker_x86_64.cpp | 27 +++---------------- src/hotspot/share/code/codeBlob.cpp | 12 +++------ src/hotspot/share/code/codeBlob.hpp | 10 ++----- src/hotspot/share/prims/upcallLinker.cpp | 4 ++- src/hotspot/share/prims/upcallLinker.hpp | 6 +++-- src/hotspot/share/runtime/sharedRuntime.cpp | 2 +- src/hotspot/share/runtime/stubRoutines.cpp | 2 ++ src/hotspot/share/runtime/stubRoutines.hpp | 7 +++++ 18 files changed, 129 insertions(+), 112 deletions(-) diff --git a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp index 6d2e26285530b..7b36f42986e41 100644 --- a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp @@ -41,6 +41,7 @@ #include "oops/objArrayKlass.hpp" #include "oops/oop.inline.hpp" #include "prims/methodHandles.hpp" +#include "prims/upcallLinker.hpp" #include "runtime/atomic.hpp" #include "runtime/continuation.hpp" #include "runtime/continuationEntry.inline.hpp" @@ -7326,6 +7327,21 @@ class StubGenerator: public StubCodeGenerator { #endif // INCLUDE_JFR + // exception handler for upcall stubs + address generate_upcall_stub_exception_handler() { + StubCodeMark mark(this, "StubRoutines", "upcall stub exception handler"); + address start = __ pc(); + + // Native caller has no idea how to handle exceptions, + // so we just crash here. Up to callee to catch exceptions. + __ verify_oop(r0); + __ movptr(rscratch1, CAST_FROM_FN_PTR(uint64_t, UpcallLinker::handle_uncaught_exception)); + __ blr(rscratch1); + __ should_not_reach_here(); + + return start; + } + // Continuation point for throwing of implicit exceptions that are // not handled in the current activation. Fabricates an exception // oop and initiates normal exception dispatching in this @@ -8377,6 +8393,8 @@ class StubGenerator: public StubCodeGenerator { #endif // LINUX + StubRoutines::_upcall_stub_exception_handler = generate_upcall_stub_exception_handler(); + StubRoutines::aarch64::set_completed(); // Inidicate that arraycopy and zero_blocks stubs are generated } diff --git a/src/hotspot/cpu/aarch64/upcallLinker_aarch64.cpp b/src/hotspot/cpu/aarch64/upcallLinker_aarch64.cpp index 57cc9fe62742b..fb2f52facafae 100644 --- a/src/hotspot/cpu/aarch64/upcallLinker_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/upcallLinker_aarch64.cpp @@ -217,6 +217,7 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, __ block_comment("{ on_entry"); __ lea(c_rarg0, Address(sp, frame_data_offset)); + __ movptr(c_rarg1, (intptr_t)receiver); __ movptr(rscratch1, CAST_FROM_FN_PTR(uint64_t, UpcallLinker::on_entry)); __ blr(rscratch1); __ mov(rthread, r0); @@ -233,9 +234,7 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, __ block_comment("} argument shuffle"); __ block_comment("{ receiver "); - __ movptr(shuffle_reg, (intptr_t)receiver); - __ resolve_jobject(shuffle_reg, rscratch1, rscratch2); - __ mov(j_rarg0, shuffle_reg); + __ get_vm_result(j_rarg0, rthread); __ block_comment("} receiver "); __ mov_metadata(rmethod, entry); @@ -306,19 +305,6 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, ////////////////////////////////////////////////////////////////////////////// - __ block_comment("{ exception handler"); - - intptr_t exception_handler_offset = __ pc() - start; - - // Native caller has no idea how to handle exceptions, - // so we just crash here. Up to callee to catch exceptions. - __ verify_oop(r0); - __ movptr(rscratch1, CAST_FROM_FN_PTR(uint64_t, UpcallLinker::handle_uncaught_exception)); - __ blr(rscratch1); - __ should_not_reach_here(); - - __ block_comment("} exception handler"); - _masm->flush(); #ifndef PRODUCT @@ -334,7 +320,6 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, UpcallStub* blob = UpcallStub::create(name, &buffer, - exception_handler_offset, receiver, in_ByteSize(frame_data_offset)); diff --git a/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp b/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp index c06f22e0fbb78..efcc8c897216d 100644 --- a/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp +++ b/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp @@ -36,6 +36,7 @@ #include "oops/objArrayKlass.hpp" #include "oops/oop.inline.hpp" #include "prims/methodHandles.hpp" +#include "prims/upcallLinker.hpp" #include "runtime/continuation.hpp" #include "runtime/continuationEntry.inline.hpp" #include "runtime/frame.inline.hpp" @@ -4717,6 +4718,20 @@ class StubGenerator: public StubCodeGenerator { #endif // INCLUDE_JFR + // exception handler for upcall stubs + address generate_upcall_stub_exception_handler() { + StubCodeMark mark(this, "StubRoutines", "upcall stub exception handler"); + address start = __ pc(); + + // Native caller has no idea how to handle exceptions, + // so we just crash here. Up to callee to catch exceptions. + __ verify_oop(R3_ARG1); + __ load_const_optimized(R12_scratch2, CAST_FROM_FN_PTR(uint64_t, UpcallLinker::handle_uncaught_exception), R0); + __ call_c(R12_scratch2); + __ should_not_reach_here(); + + return start; + } // Initialization void generate_initial_stubs() { @@ -4796,6 +4811,8 @@ class StubGenerator: public StubCodeGenerator { // arraycopy stubs used by compilers generate_arraycopy_stubs(); + + StubRoutines::_upcall_stub_exception_handler = generate_upcall_stub_exception_handler(); } void generate_compiler_stubs() { diff --git a/src/hotspot/cpu/ppc/upcallLinker_ppc.cpp b/src/hotspot/cpu/ppc/upcallLinker_ppc.cpp index 4cb86ad573c26..aaa9952656c1b 100644 --- a/src/hotspot/cpu/ppc/upcallLinker_ppc.cpp +++ b/src/hotspot/cpu/ppc/upcallLinker_ppc.cpp @@ -115,7 +115,7 @@ static void restore_callee_saved_registers(MacroAssembler* _masm, const ABIDescr __ block_comment("} restore_callee_saved_regs "); } -static const int upcall_stub_code_base_size = 1536; // depends on GC (resolve_jobject) +static const int upcall_stub_code_base_size = 1024; static const int upcall_stub_size_per_arg = 16; // arg save & restore + move address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, @@ -217,6 +217,7 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, __ block_comment("{ on_entry"); __ load_const_optimized(call_target_address, CAST_FROM_FN_PTR(uint64_t, UpcallLinker::on_entry), R0); __ addi(R3_ARG1, R1_SP, frame_data_offset); + __ load_const_optimized(R4_ARG2, (intptr_t)receiver, R0); __ call_c(call_target_address); __ mr(R16_thread, R3_RET); __ block_comment("} on_entry"); @@ -232,8 +233,7 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, __ block_comment("} argument shuffle"); __ block_comment("{ receiver "); - __ load_const_optimized(R3_ARG1, (intptr_t)receiver, R0); - __ resolve_jobject(R3_ARG1, tmp, R31, MacroAssembler::PRESERVATION_FRAME_LR_GP_FP_REGS); // kills R31 + __ get_vm_result(R3_ARG1); __ block_comment("} receiver "); __ load_const_optimized(R19_method, (intptr_t)entry); @@ -314,19 +314,6 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, ////////////////////////////////////////////////////////////////////////////// - __ block_comment("{ exception handler"); - - intptr_t exception_handler_offset = __ pc() - start; - - // Native caller has no idea how to handle exceptions, - // so we just crash here. Up to callee to catch exceptions. - __ verify_oop(R3_ARG1); - __ load_const_optimized(call_target_address, CAST_FROM_FN_PTR(uint64_t, UpcallLinker::handle_uncaught_exception), R0); - __ call_c(call_target_address); - __ should_not_reach_here(); - - __ block_comment("} exception handler"); - _masm->flush(); #ifndef PRODUCT @@ -342,7 +329,6 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, UpcallStub* blob = UpcallStub::create(name, &buffer, - exception_handler_offset, receiver, in_ByteSize(frame_data_offset)); #ifndef ABI_ELFv2 diff --git a/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp b/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp index 6f302f747bbf3..2431df1178dfa 100644 --- a/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp +++ b/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp @@ -38,6 +38,7 @@ #include "oops/objArrayKlass.hpp" #include "oops/oop.inline.hpp" #include "prims/methodHandles.hpp" +#include "prims/upcallLinker.hpp" #include "runtime/continuation.hpp" #include "runtime/continuationEntry.inline.hpp" #include "runtime/frame.inline.hpp" @@ -4504,6 +4505,20 @@ class StubGenerator: public StubCodeGenerator { #endif // INCLUDE_JFR + // exception handler for upcall stubs + address generate_upcall_stub_exception_handler() { + StubCodeMark mark(this, "StubRoutines", "upcall stub exception handler"); + address start = __ pc(); + + // Native caller has no idea how to handle exceptions, + // so we just crash here. Up to callee to catch exceptions. + __ verify_oop(x10); // return a exception oop in a0 + __ rt_call(CAST_FROM_FN_PTR(address, UpcallLinker::handle_uncaught_exception)); + __ should_not_reach_here(); + + return start; + } + #undef __ // Initialization @@ -4588,6 +4603,8 @@ class StubGenerator: public StubCodeGenerator { StubRoutines::riscv::_method_entry_barrier = generate_method_entry_barrier(); } + StubRoutines::_upcall_stub_exception_handler = generate_upcall_stub_exception_handler(); + StubRoutines::riscv::set_completed(); } diff --git a/src/hotspot/cpu/riscv/upcallLinker_riscv.cpp b/src/hotspot/cpu/riscv/upcallLinker_riscv.cpp index 6d605d716af9e..de90694945b0d 100644 --- a/src/hotspot/cpu/riscv/upcallLinker_riscv.cpp +++ b/src/hotspot/cpu/riscv/upcallLinker_riscv.cpp @@ -114,7 +114,7 @@ static void restore_callee_saved_registers(MacroAssembler* _masm, const ABIDescr __ block_comment("} restore_callee_saved_regs "); } -static const int upcall_stub_code_base_size = 2048; +static const int upcall_stub_code_base_size = 1024; static const int upcall_stub_size_per_arg = 16; address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, @@ -218,6 +218,7 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, __ block_comment("{ on_entry"); __ la(c_rarg0, Address(sp, frame_data_offset)); + __ movptr(c_rarg1, (intptr_t) receiver); __ rt_call(CAST_FROM_FN_PTR(address, UpcallLinker::on_entry)); __ mv(xthread, x10); __ reinit_heapbase(); @@ -255,9 +256,7 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, __ block_comment("} argument shuffle"); __ block_comment("{ receiver "); - __ movptr(shuffle_reg, (intptr_t) receiver); - __ resolve_jobject(shuffle_reg, t0, t1); - __ mv(j_rarg0, shuffle_reg); + __ get_vm_result(j_rarg0, xthread); __ block_comment("} receiver "); __ mov_metadata(xmethod, entry); @@ -326,17 +325,6 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, ////////////////////////////////////////////////////////////////////////////// - __ block_comment("{ exception handler"); - - intptr_t exception_handler_offset = __ pc() - start; - - // Native caller has no idea how to handle exceptions, - // so we just crash here. Up to callee to catch exceptions. - __ verify_oop(x10); // return a exception oop in a0 - __ rt_call(CAST_FROM_FN_PTR(address, UpcallLinker::handle_uncaught_exception)); - __ should_not_reach_here(); - - __ block_comment("} exception handler"); __ flush(); #ifndef PRODUCT @@ -352,7 +340,6 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, UpcallStub* blob = UpcallStub::create(name, &buffer, - exception_handler_offset, receiver, in_ByteSize(frame_data_offset)); #ifndef PRODUCT diff --git a/src/hotspot/cpu/s390/stubGenerator_s390.cpp b/src/hotspot/cpu/s390/stubGenerator_s390.cpp index a26bcd04de26f..64d8d9fa978c8 100644 --- a/src/hotspot/cpu/s390/stubGenerator_s390.cpp +++ b/src/hotspot/cpu/s390/stubGenerator_s390.cpp @@ -37,6 +37,7 @@ #include "oops/objArrayKlass.hpp" #include "oops/oop.inline.hpp" #include "prims/methodHandles.hpp" +#include "prims/upcallLinker.hpp" #include "runtime/frame.inline.hpp" #include "runtime/handles.inline.hpp" #include "runtime/javaThread.hpp" @@ -3094,6 +3095,21 @@ class StubGenerator: public StubCodeGenerator { #endif // INCLUDE_JFR + // exception handler for upcall stubs + address generate_upcall_stub_exception_handler() { + StubCodeMark mark(this, "StubRoutines", "upcall stub exception handler"); + address start = __ pc(); + + // Native caller has no idea how to handle exceptions, + // so we just crash here. Up to callee to catch exceptions. + __ verify_oop(Z_ARG1); + __ load_const_optimized(Z_R1_scratch, CAST_FROM_FN_PTR(uint64_t, UpcallLinker::handle_uncaught_exception)); + __ call_c(Z_R1_scratch); + __ should_not_reach_here(); + + return start; + } + void generate_initial_stubs() { // Generates all stubs and initializes the entry points. @@ -3174,6 +3190,7 @@ class StubGenerator: public StubCodeGenerator { StubRoutines::zarch::_nmethod_entry_barrier = generate_nmethod_entry_barrier(); } + StubRoutines::_upcall_stub_exception_handler = generate_upcall_stub_exception_handler(); } void generate_compiler_stubs() { diff --git a/src/hotspot/cpu/s390/upcallLinker_s390.cpp b/src/hotspot/cpu/s390/upcallLinker_s390.cpp index b748ec547ccf1..884f334c8062c 100644 --- a/src/hotspot/cpu/s390/upcallLinker_s390.cpp +++ b/src/hotspot/cpu/s390/upcallLinker_s390.cpp @@ -114,7 +114,7 @@ static void restore_callee_saved_registers(MacroAssembler* _masm, const ABIDescr __ block_comment("} restore_callee_saved_regs "); } -static const int upcall_stub_code_base_size = 1024; // depends on GC (resolve_jobject) +static const int upcall_stub_code_base_size = 1024; static const int upcall_stub_size_per_arg = 16; // arg save & restore + move address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, BasicType* in_sig_bt, int total_in_args, @@ -202,6 +202,7 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, __ block_comment("{ on_entry"); __ load_const_optimized(call_target_address, CAST_FROM_FN_PTR(uint64_t, UpcallLinker::on_entry)); __ z_aghik(Z_ARG1, Z_SP, frame_data_offset); + __ load_const_optimized(Z_ARG2, (intptr_t)receiver); __ call(call_target_address); __ z_lgr(Z_thread, Z_RET); __ block_comment("} on_entry"); @@ -212,8 +213,7 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, __ block_comment("} argument shuffle"); __ block_comment("{ receiver "); - __ load_const_optimized(Z_ARG1, (intptr_t)receiver); - __ resolve_jobject(Z_ARG1, Z_tmp_1, Z_tmp_2); + __ get_vm_result(Z_ARG1); __ block_comment("} receiver "); __ load_const_optimized(Z_method, (intptr_t)entry); @@ -266,19 +266,6 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, ////////////////////////////////////////////////////////////////////////////// - __ block_comment("{ exception handler"); - - intptr_t exception_handler_offset = __ pc() - start; - - // Native caller has no idea how to handle exceptions, - // so we just crash here. Up to callee to catch exceptions. - __ verify_oop(Z_ARG1); - __ load_const_optimized(call_target_address, CAST_FROM_FN_PTR(uint64_t, UpcallLinker::handle_uncaught_exception)); - __ call_c(call_target_address); - __ should_not_reach_here(); - - __ block_comment("} exception handler"); - _masm->flush(); #ifndef PRODUCT @@ -293,7 +280,6 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, UpcallStub* blob = UpcallStub::create(name, &buffer, - exception_handler_offset, receiver, in_ByteSize(frame_data_offset)); #ifndef PRODUCT diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp index e071583b10ba7..15109a6af1bfd 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp @@ -32,6 +32,7 @@ #include "gc/shared/gc_globals.hpp" #include "memory/universe.hpp" #include "prims/jvmtiExport.hpp" +#include "prims/upcallLinker.hpp" #include "runtime/arguments.hpp" #include "runtime/javaThread.hpp" #include "runtime/sharedRuntime.hpp" @@ -3887,6 +3888,24 @@ address StubGenerator::generate_throw_exception(const char* name, return stub->entry_point(); } +// exception handler for upcall stubs +address StubGenerator::generate_upcall_stub_exception_handler() { + StubCodeMark mark(this, "StubRoutines", "upcall stub exception handler"); + address start = __ pc(); + + // native caller has no idea how to handle exceptions + // we just crash here. Up to callee to catch exceptions. + __ verify_oop(rax); + __ vzeroupper(); + __ mov(c_rarg0, rax); + __ andptr(rsp, -StackAlignmentInBytes); // align stack as required by ABI + __ subptr(rsp, frame::arg_reg_save_area_bytes); // windows + __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, UpcallLinker::handle_uncaught_exception))); + __ should_not_reach_here(); + + return start; +} + void StubGenerator::create_control_words() { // Round to nearest, 64-bit mode, exceptions masked StubRoutines::x86::_mxcsr_std = 0x1F80; @@ -4039,6 +4058,8 @@ void StubGenerator::generate_final_stubs() { if (UseVectorizedMismatchIntrinsic) { StubRoutines::_vectorizedMismatch = generate_vectorizedMismatch(); } + + StubRoutines::_upcall_stub_exception_handler = generate_upcall_stub_exception_handler(); } void StubGenerator::generate_compiler_stubs() { diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp b/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp index 3b568ace836b7..109c98f83bd2a 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp @@ -565,6 +565,9 @@ class StubGenerator: public StubCodeGenerator { Register arg1 = noreg, Register arg2 = noreg); + // shared exception handler for FFM upcall stubs + address generate_upcall_stub_exception_handler(); + void create_control_words(); // Initialization diff --git a/src/hotspot/cpu/x86/upcallLinker_x86_64.cpp b/src/hotspot/cpu/x86/upcallLinker_x86_64.cpp index dfce6aef52d3e..395877f147907 100644 --- a/src/hotspot/cpu/x86/upcallLinker_x86_64.cpp +++ b/src/hotspot/cpu/x86/upcallLinker_x86_64.cpp @@ -165,7 +165,7 @@ static void restore_callee_saved_registers(MacroAssembler* _masm, const ABIDescr __ block_comment("} restore_callee_saved_regs "); } -static const int upcall_stub_code_base_size = 2048; +static const int upcall_stub_code_base_size = 1024; static const int upcall_stub_size_per_arg = 16; address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, @@ -272,6 +272,7 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, __ block_comment("{ on_entry"); __ vzeroupper(); __ lea(c_rarg0, Address(rsp, frame_data_offset)); + __ movptr(c_rarg1, (intptr_t)receiver); // stack already aligned __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, UpcallLinker::on_entry))); __ movptr(r15_thread, rax); @@ -288,9 +289,7 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, __ block_comment("} argument shuffle"); __ block_comment("{ receiver "); - __ movptr(rscratch1, (intptr_t)receiver); - __ resolve_jobject(rscratch1, r15_thread, rscratch2); - __ movptr(j_rarg0, rscratch1); + __ get_vm_result(j_rarg0, r15_thread); __ block_comment("} receiver "); __ mov_metadata(rbx, entry); @@ -361,27 +360,8 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, ////////////////////////////////////////////////////////////////////////////// - __ block_comment("{ exception handler"); - - intptr_t exception_handler_offset = __ pc() - start; - - // TODO: this is always the same, can we bypass and call handle_uncaught_exception directly? - - // native caller has no idea how to handle exceptions - // we just crash here. Up to callee to catch exceptions. - __ verify_oop(rax); - __ vzeroupper(); - __ mov(c_rarg0, rax); - __ andptr(rsp, -StackAlignmentInBytes); // align stack as required by ABI - __ subptr(rsp, frame::arg_reg_save_area_bytes); // windows (not really needed) - __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, UpcallLinker::handle_uncaught_exception))); - __ should_not_reach_here(); - - __ block_comment("} exception handler"); - _masm->flush(); - #ifndef PRODUCT stringStream ss; ss.print("upcall_stub_%s", entry->signature()->as_C_string()); @@ -395,7 +375,6 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, UpcallStub* blob = UpcallStub::create(name, &buffer, - exception_handler_offset, receiver, in_ByteSize(frame_data_offset)); diff --git a/src/hotspot/share/code/codeBlob.cpp b/src/hotspot/share/code/codeBlob.cpp index 9011bbfc1c2ca..c49fca717c9d5 100644 --- a/src/hotspot/share/code/codeBlob.cpp +++ b/src/hotspot/share/code/codeBlob.cpp @@ -738,12 +738,9 @@ void DeoptimizationBlob::print_value_on(outputStream* st) const { // Implementation of UpcallStub -UpcallStub::UpcallStub(const char* name, CodeBuffer* cb, int size, - intptr_t exception_handler_offset, - jobject receiver, ByteSize frame_data_offset) : +UpcallStub::UpcallStub(const char* name, CodeBuffer* cb, int size, jobject receiver, ByteSize frame_data_offset) : RuntimeBlob(name, cb, sizeof(UpcallStub), size, CodeOffsets::frame_never_safe, 0 /* no frame size */, /* oop maps = */ nullptr, /* caller must gc arguments = */ false), - _exception_handler_offset(exception_handler_offset), _receiver(receiver), _frame_data_offset(frame_data_offset) { CodeCache::commit(this); @@ -753,17 +750,14 @@ void* UpcallStub::operator new(size_t s, unsigned size) throw() { return CodeCache::allocate(size, CodeBlobType::NonNMethod); } -UpcallStub* UpcallStub::create(const char* name, CodeBuffer* cb, - intptr_t exception_handler_offset, - jobject receiver, ByteSize frame_data_offset) { +UpcallStub* UpcallStub::create(const char* name, CodeBuffer* cb, jobject receiver, ByteSize frame_data_offset) { ThreadInVMfromUnknown __tiv; // get to VM state in case we block on CodeCache_lock UpcallStub* blob = nullptr; unsigned int size = CodeBlob::allocation_size(cb, sizeof(UpcallStub)); { MutexLocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); - blob = new (size) UpcallStub(name, cb, size, - exception_handler_offset, receiver, frame_data_offset); + blob = new (size) UpcallStub(name, cb, size, receiver, frame_data_offset); } // Track memory usage statistic after releasing CodeCache_lock MemoryService::track_code_cache_memory_usage(); diff --git a/src/hotspot/share/code/codeBlob.hpp b/src/hotspot/share/code/codeBlob.hpp index d5618f8662259..4e7093bed2476 100644 --- a/src/hotspot/share/code/codeBlob.hpp +++ b/src/hotspot/share/code/codeBlob.hpp @@ -733,13 +733,10 @@ class UpcallLinker; class UpcallStub: public RuntimeBlob { friend class UpcallLinker; private: - intptr_t _exception_handler_offset; jobject _receiver; ByteSize _frame_data_offset; - UpcallStub(const char* name, CodeBuffer* cb, int size, - intptr_t exception_handler_offset, - jobject receiver, ByteSize frame_data_offset); + UpcallStub(const char* name, CodeBuffer* cb, int size, jobject receiver, ByteSize frame_data_offset); void* operator new(size_t s, unsigned size) throw(); @@ -754,13 +751,10 @@ class UpcallStub: public RuntimeBlob { FrameData* frame_data_for_frame(const frame& frame) const; public: // Creation - static UpcallStub* create(const char* name, CodeBuffer* cb, - intptr_t exception_handler_offset, - jobject receiver, ByteSize frame_data_offset); + static UpcallStub* create(const char* name, CodeBuffer* cb, jobject receiver, ByteSize frame_data_offset); static void free(UpcallStub* blob); - address exception_handler() { return code_begin() + _exception_handler_offset; } jobject receiver() { return _receiver; } JavaFrameAnchor* jfa_for_frame(const frame& frame) const; diff --git a/src/hotspot/share/prims/upcallLinker.cpp b/src/hotspot/share/prims/upcallLinker.cpp index 1576c0115254d..8358649ac95fa 100644 --- a/src/hotspot/share/prims/upcallLinker.cpp +++ b/src/hotspot/share/prims/upcallLinker.cpp @@ -74,7 +74,7 @@ JavaThread* UpcallLinker::maybe_attach_and_get_thread() { } // modelled after JavaCallWrapper::JavaCallWrapper -JavaThread* UpcallLinker::on_entry(UpcallStub::FrameData* context) { +JavaThread* UpcallLinker::on_entry(UpcallStub::FrameData* context, jobject receiver) { JavaThread* thread = maybe_attach_and_get_thread(); guarantee(thread->thread_state() == _thread_in_native, "wrong thread state for upcall"); context->thread = thread; @@ -109,6 +109,8 @@ JavaThread* UpcallLinker::on_entry(UpcallStub::FrameData* context) { debug_only(thread->inc_java_call_counter()); thread->set_active_handles(context->new_handles); // install new handle block and reset Java frame linkage + thread->set_vm_result(JNIHandles::resolve(receiver)); + return thread; } diff --git a/src/hotspot/share/prims/upcallLinker.hpp b/src/hotspot/share/prims/upcallLinker.hpp index 3f8c717e5014f..529cb7c391192 100644 --- a/src/hotspot/share/prims/upcallLinker.hpp +++ b/src/hotspot/share/prims/upcallLinker.hpp @@ -32,10 +32,9 @@ class JavaThread; class UpcallLinker { private: - static void handle_uncaught_exception(oop exception); static JavaThread* maybe_attach_and_get_thread(); - static JavaThread* on_entry(UpcallStub::FrameData* context); + static JavaThread* on_entry(UpcallStub::FrameData* context, jobject receiver); static void on_exit(UpcallStub::FrameData* context); public: static address make_upcall_stub(jobject mh, Method* entry, @@ -44,6 +43,9 @@ class UpcallLinker { BasicType ret_type, jobject jabi, jobject jconv, bool needs_return_buffer, int ret_buf_size); + + // public for stubGenerator + static void handle_uncaught_exception(oop exception); }; #endif // SHARE_VM_PRIMS_UPCALLLINKER_HPP diff --git a/src/hotspot/share/runtime/sharedRuntime.cpp b/src/hotspot/share/runtime/sharedRuntime.cpp index 31a3844367215..536944a61dcca 100644 --- a/src/hotspot/share/runtime/sharedRuntime.cpp +++ b/src/hotspot/share/runtime/sharedRuntime.cpp @@ -520,7 +520,7 @@ address SharedRuntime::raw_exception_handler_for_return_address(JavaThread* curr return StubRoutines::catch_exception_entry(); } if (blob != nullptr && blob->is_upcall_stub()) { - return ((UpcallStub*)blob)->exception_handler(); + return StubRoutines::upcall_stub_exception_handler(); } // Interpreted code if (Interpreter::contains(return_address)) { diff --git a/src/hotspot/share/runtime/stubRoutines.cpp b/src/hotspot/share/runtime/stubRoutines.cpp index bea2a934bc603..faeaedac55e1e 100644 --- a/src/hotspot/share/runtime/stubRoutines.cpp +++ b/src/hotspot/share/runtime/stubRoutines.cpp @@ -188,6 +188,8 @@ JFR_ONLY(address StubRoutines::_jfr_write_checkpoint = nullptr;) JFR_ONLY(RuntimeStub* StubRoutines::_jfr_return_lease_stub = nullptr;) JFR_ONLY(address StubRoutines::_jfr_return_lease = nullptr;) +address StubRoutines::_upcall_stub_exception_handler = nullptr; + // Initialization // // Note: to break cycle with universe initialization, stubs are generated in two phases. diff --git a/src/hotspot/share/runtime/stubRoutines.hpp b/src/hotspot/share/runtime/stubRoutines.hpp index d62c3913f2533..77d968263f75b 100644 --- a/src/hotspot/share/runtime/stubRoutines.hpp +++ b/src/hotspot/share/runtime/stubRoutines.hpp @@ -269,6 +269,8 @@ class StubRoutines: AllStatic { static address _vector_f_math[VectorSupport::NUM_VEC_SIZES][VectorSupport::NUM_SVML_OP]; static address _vector_d_math[VectorSupport::NUM_VEC_SIZES][VectorSupport::NUM_SVML_OP]; + static address _upcall_stub_exception_handler; + public: // Initialization/Testing static void initialize_initial_stubs(); // must happen before universe::genesis @@ -465,6 +467,11 @@ class StubRoutines: AllStatic { JFR_ONLY(static address jfr_write_checkpoint() { return _jfr_write_checkpoint; }) JFR_ONLY(static address jfr_return_lease() { return _jfr_return_lease; }) + static address upcall_stub_exception_handler() { + assert(_upcall_stub_exception_handler != nullptr, "not implemented"); + return _upcall_stub_exception_handler; + } + static address select_fill_function(BasicType t, bool aligned, const char* &name); // From 4a16d111b16e556f904fb80075631b8439a9482e Mon Sep 17 00:00:00 2001 From: Evgeny Nikitin Date: Tue, 24 Oct 2023 09:16:10 +0000 Subject: [PATCH 17/28] 8318455: Fix the compiler/sharedstubs/SharedTrampolineTest.java and SharedStubToInterpTest.java Reviewed-by: thartmann, shade, eastigeevich --- test/hotspot/jtreg/ProblemList.txt | 2 -- .../jtreg/compiler/sharedstubs/SharedStubToInterpTest.java | 5 +++-- .../jtreg/compiler/sharedstubs/SharedTrampolineTest.java | 6 ++++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index 8973b094b4ab2..fd4119d10c242 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -73,8 +73,6 @@ compiler/vectorapi/VectorLogicalOpIdentityTest.java 8302459 linux-x64,windows-x6 compiler/jvmci/TestUncaughtErrorInCompileMethod.java 8309073 generic-all -compiler/sharedstubs/SharedTrampolineTest.java 8318455 generic-all - compiler/codecache/CheckLargePages.java 8317831 linux-x64 ############################################################################# diff --git a/test/hotspot/jtreg/compiler/sharedstubs/SharedStubToInterpTest.java b/test/hotspot/jtreg/compiler/sharedstubs/SharedStubToInterpTest.java index cd8471317ffc4..3e578225e5307 100644 --- a/test/hotspot/jtreg/compiler/sharedstubs/SharedStubToInterpTest.java +++ b/test/hotspot/jtreg/compiler/sharedstubs/SharedStubToInterpTest.java @@ -51,9 +51,10 @@ public class SharedStubToInterpTest { private final static int ITERATIONS_TO_HEAT_LOOP = 20_000; - private static void runTest(String test) throws Exception { + private static void runTest(String compiler, String test) throws Exception { String testClassName = SharedStubToInterpTest.class.getName() + "$" + test; ArrayList command = new ArrayList(); + command.add(compiler); command.add("-XX:+UnlockDiagnosticVMOptions"); command.add("-Xbatch"); command.add("-XX:+PrintRelocations"); @@ -80,7 +81,7 @@ private static void runTest(String test) throws Exception { public static void main(String[] args) throws Exception { String[] methods = new String[] { "StaticMethodTest", "FinalClassTest", "FinalMethodTest"}; for (String methodName : methods) { - runTest(methodName); + runTest(args[0], methodName); } } diff --git a/test/hotspot/jtreg/compiler/sharedstubs/SharedTrampolineTest.java b/test/hotspot/jtreg/compiler/sharedstubs/SharedTrampolineTest.java index 49158fecd046b..1cfa210e3d8a2 100644 --- a/test/hotspot/jtreg/compiler/sharedstubs/SharedTrampolineTest.java +++ b/test/hotspot/jtreg/compiler/sharedstubs/SharedTrampolineTest.java @@ -28,6 +28,7 @@ * @bug 8280152 * @library /test/lib * + * @requires vm.compiler2.enabled * @requires vm.opt.TieredCompilation == null * @requires os.arch=="aarch64" | os.arch=="riscv64" * @requires vm.debug @@ -46,9 +47,10 @@ public class SharedTrampolineTest { private final static int ITERATIONS_TO_HEAT_LOOP = 20_000; - private static void runTest(String test) throws Exception { + private static void runTest(String compiler, String test) throws Exception { String testClassName = SharedTrampolineTest.class.getName() + "$" + test; ArrayList command = new ArrayList(); + command.add(compiler); command.add("-XX:+UnlockDiagnosticVMOptions"); command.add("-Xbatch"); command.add("-XX:+PrintRelocations"); @@ -72,7 +74,7 @@ private static void runTest(String test) throws Exception { public static void main(String[] args) throws Exception { String[] tests = new String[] {"StaticMethodTest"}; for (String test : tests) { - runTest(test); + runTest(args[0], test); } } From 0baa9ecd76c9be33a1939e47dcae320bc8b65e96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johan=20Sj=C3=B6len?= Date: Tue, 24 Oct 2023 10:08:21 +0000 Subject: [PATCH 18/28] 8318474: Fix memory reporter for thread_count Reviewed-by: mbaesken, azafari --- src/hotspot/share/services/memReporter.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/hotspot/share/services/memReporter.cpp b/src/hotspot/share/services/memReporter.cpp index a277fec5c80d9..235ce96dba7fe 100644 --- a/src/hotspot/share/services/memReporter.cpp +++ b/src/hotspot/share/services/memReporter.cpp @@ -236,7 +236,6 @@ void MemSummaryReporter::report_summary_of_type(MEMFLAGS flag, MallocMemory* thread_stack_memory = _malloc_snapshot->by_type(mtThreadStack); const char* scale = current_scale(); // report thread count - assert(ThreadStackTracker::thread_count() == 0, "Not used"); out->print_cr("%27s (threads #" SIZE_FORMAT ")", " ", thread_stack_memory->malloc_count()); out->print("%27s (Stack: " SIZE_FORMAT "%s", " ", amount_in_current_scale(thread_stack_memory->malloc_size()), scale); From 3f446c51564e0358d74d4929dc2fa1b9263ac3b2 Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Tue, 24 Oct 2023 10:12:44 +0000 Subject: [PATCH 19/28] 8318528: Rename TestUnstructuredLocking test Reviewed-by: phh, dholmes --- ...cturedLocking.jasm => TestOutOfOrderUnlocking.jasm} | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) rename test/hotspot/jtreg/runtime/locking/{TestUnstructuredLocking.jasm => TestOutOfOrderUnlocking.jasm} (88%) diff --git a/test/hotspot/jtreg/runtime/locking/TestUnstructuredLocking.jasm b/test/hotspot/jtreg/runtime/locking/TestOutOfOrderUnlocking.jasm similarity index 88% rename from test/hotspot/jtreg/runtime/locking/TestUnstructuredLocking.jasm rename to test/hotspot/jtreg/runtime/locking/TestOutOfOrderUnlocking.jasm index 7e663f78ddc1a..99abbf6193af2 100644 --- a/test/hotspot/jtreg/runtime/locking/TestUnstructuredLocking.jasm +++ b/test/hotspot/jtreg/runtime/locking/TestOutOfOrderUnlocking.jasm @@ -25,17 +25,17 @@ /* * @test id=int * @summary Check that monitorenter A; monitorenter B; monitorexit A; monitorexit B; works - * @compile TestUnstructuredLocking.jasm - * @run main/othervm -Xint TestUnstructuredLocking + * @compile TestOutOfOrderUnlocking.jasm + * @run main/othervm -Xint TestOutOfOrderUnlocking */ /* * @test id=comp * @summary Check that monitorenter A; monitorenter B; monitorexit A; monitorexit B; works, with -Xcomp - * @compile TestUnstructuredLocking.jasm - * @run main/othervm -Xcomp TestUnstructuredLocking + * @compile TestOutOfOrderUnlocking.jasm + * @run main/othervm -Xcomp TestOutOfOrderUnlocking */ -super public class TestUnstructuredLocking version 64:0 { +super public class TestOutOfOrderUnlocking version 64:0 { public static Method main:"([Ljava/lang/String;)V" stack 2 locals 4 { new class java/lang/Object; From 9bfa0829700412291ed26e7bb006b8cb63208870 Mon Sep 17 00:00:00 2001 From: Raffaello Giulietti Date: Tue, 24 Oct 2023 10:20:26 +0000 Subject: [PATCH 20/28] 8318646: Integer#parseInt("") throws empty NumberFormatException message Reviewed-by: redestad, alanb, bpb, darcy, uschindler --- src/java.base/share/classes/java/lang/Integer.java | 6 +++--- src/java.base/share/classes/java/lang/Long.java | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/java.base/share/classes/java/lang/Integer.java b/src/java.base/share/classes/java/lang/Integer.java index 9c19970434c60..1c5b3c414bab6 100644 --- a/src/java.base/share/classes/java/lang/Integer.java +++ b/src/java.base/share/classes/java/lang/Integer.java @@ -562,7 +562,7 @@ public static int parseInt(String s, int radix) int len = s.length(); if (len == 0) { - throw new NumberFormatException(""); + throw NumberFormatException.forInputString("", radix); } int digit = ~0xFF; int i = 0; @@ -637,7 +637,7 @@ public static int parseInt(CharSequence s, int beginIndex, int endIndex, int rad * and by not updating i anywhere else. */ if (beginIndex == endIndex) { - throw new NumberFormatException(""); + throw NumberFormatException.forInputString("", radix); } int digit = ~0xFF; int i = beginIndex; @@ -827,7 +827,7 @@ public static int parseUnsignedInt(CharSequence s, int beginIndex, int endIndex, * and by not updating i anywhere else. */ if (beginIndex == endIndex) { - throw new NumberFormatException(""); + throw NumberFormatException.forInputString("", radix); } int i = beginIndex; char firstChar = s.charAt(i++); diff --git a/src/java.base/share/classes/java/lang/Long.java b/src/java.base/share/classes/java/lang/Long.java index 481bdbf5569fd..70bd2d62add0f 100644 --- a/src/java.base/share/classes/java/lang/Long.java +++ b/src/java.base/share/classes/java/lang/Long.java @@ -592,7 +592,7 @@ public static long parseLong(String s, int radix) int len = s.length(); if (len == 0) { - throw new NumberFormatException(""); + throw NumberFormatException.forInputString("", radix); } int digit = ~0xFF; int i = 0; @@ -667,7 +667,7 @@ public static long parseLong(CharSequence s, int beginIndex, int endIndex, int r * and by not updating i anywhere else. */ if (beginIndex == endIndex) { - throw new NumberFormatException(""); + throw NumberFormatException.forInputString("", radix); } int digit = ~0xFF; // ~0xFF means firstChar char is sign int i = beginIndex; @@ -864,7 +864,7 @@ public static long parseUnsignedLong(CharSequence s, int beginIndex, int endInde * and by not updating i anywhere else. */ if (beginIndex == endIndex) { - throw new NumberFormatException(""); + throw NumberFormatException.forInputString("", radix); } int i = beginIndex; char firstChar = s.charAt(i++); From 5224e979a1cbce1b015e81aa7761743f60357d73 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Tue, 24 Oct 2023 10:38:58 +0000 Subject: [PATCH 21/28] 8293713: java/net/httpclient/BufferingSubscriberTest.java fails in timeout, blocked in submission publisher Co-authored-by: Jaikiran Pai Reviewed-by: alanb --- .../share/classes/java/util/concurrent/SubmissionPublisher.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/java.base/share/classes/java/util/concurrent/SubmissionPublisher.java b/src/java.base/share/classes/java/util/concurrent/SubmissionPublisher.java index 4aed7639eb1e7..b27c57114d620 100644 --- a/src/java.base/share/classes/java/util/concurrent/SubmissionPublisher.java +++ b/src/java.base/share/classes/java/util/concurrent/SubmissionPublisher.java @@ -1059,7 +1059,7 @@ static final class BufferedSubscription final Subscriber subscriber; final BiConsumer, ? super Throwable> onNextHandler; Executor executor; // null on error - Thread waiter; // blocked producer thread + volatile Thread waiter; // blocked producer thread Throwable pendingError; // holds until onError issued BufferedSubscription next; // used only by publisher BufferedSubscription nextRetry; // used only by publisher From bf1a14e3672b7d92b10d16210faf4fd99a860731 Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Tue, 24 Oct 2023 12:18:33 +0000 Subject: [PATCH 22/28] 8316470: Incorrect error location for "invalid permits clause" depending on file order Reviewed-by: vromero --- .../com/sun/tools/javac/comp/Attr.java | 202 +++++++++--------- .../javac/sealed/SealedErrorPositions.java | 147 +++++++++++++ 2 files changed, 248 insertions(+), 101 deletions(-) create mode 100644 test/langtools/tools/javac/sealed/SealedErrorPositions.java diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index 4b44be198d15d..189ce74b11500 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -5404,125 +5404,125 @@ void attribClass(ClassSymbol c) throws CompletionFailure { // Get environment current at the point of class definition. Env env = typeEnvs.get(c); - if (c.isSealed() && - !c.isEnum() && - !c.isPermittedExplicit && - c.permitted.isEmpty()) { - log.error(TreeInfo.diagnosticPositionFor(c, env.tree), Errors.SealedClassMustHaveSubclasses); - } - - if (c.isSealed()) { - Set permittedTypes = new HashSet<>(); - boolean sealedInUnnamed = c.packge().modle == syms.unnamedModule || c.packge().modle == syms.noModule; - for (Symbol subTypeSym : c.permitted) { - boolean isTypeVar = false; - if (subTypeSym.type.getTag() == TYPEVAR) { - isTypeVar = true; //error recovery - log.error(TreeInfo.diagnosticPositionFor(subTypeSym, env.tree), - Errors.InvalidPermitsClause(Fragments.IsATypeVariable(subTypeSym.type))); - } - if (subTypeSym.isAnonymous() && !c.isEnum()) { - log.error(TreeInfo.diagnosticPositionFor(subTypeSym, env.tree), Errors.LocalClassesCantExtendSealed(Fragments.Anonymous)); - } - if (permittedTypes.contains(subTypeSym)) { - DiagnosticPosition pos = - env.enclClass.permitting.stream() - .filter(permittedExpr -> TreeInfo.diagnosticPositionFor(subTypeSym, permittedExpr, true) != null) - .limit(2).collect(List.collector()).get(1); - log.error(pos, Errors.InvalidPermitsClause(Fragments.IsDuplicated(subTypeSym.type))); - } else { - permittedTypes.add(subTypeSym); - } - if (sealedInUnnamed) { - if (subTypeSym.packge() != c.packge()) { + // The info.lint field in the envs stored in typeEnvs is deliberately uninitialized, + // because the annotations were not available at the time the env was created. Therefore, + // we look up the environment chain for the first enclosing environment for which the + // lint value is set. Typically, this is the parent env, but might be further if there + // are any envs created as a result of TypeParameter nodes. + Env lintEnv = env; + while (lintEnv.info.lint == null) + lintEnv = lintEnv.next; + + // Having found the enclosing lint value, we can initialize the lint value for this class + env.info.lint = lintEnv.info.lint.augment(c); + + Lint prevLint = chk.setLint(env.info.lint); + JavaFileObject prev = log.useSource(c.sourcefile); + ResultInfo prevReturnRes = env.info.returnResult; + + try { + if (c.isSealed() && + !c.isEnum() && + !c.isPermittedExplicit && + c.permitted.isEmpty()) { + log.error(TreeInfo.diagnosticPositionFor(c, env.tree), Errors.SealedClassMustHaveSubclasses); + } + + if (c.isSealed()) { + Set permittedTypes = new HashSet<>(); + boolean sealedInUnnamed = c.packge().modle == syms.unnamedModule || c.packge().modle == syms.noModule; + for (Symbol subTypeSym : c.permitted) { + boolean isTypeVar = false; + if (subTypeSym.type.getTag() == TYPEVAR) { + isTypeVar = true; //error recovery log.error(TreeInfo.diagnosticPositionFor(subTypeSym, env.tree), - Errors.ClassInUnnamedModuleCantExtendSealedInDiffPackage(c) - ); + Errors.InvalidPermitsClause(Fragments.IsATypeVariable(subTypeSym.type))); } - } else if (subTypeSym.packge().modle != c.packge().modle) { - log.error(TreeInfo.diagnosticPositionFor(subTypeSym, env.tree), - Errors.ClassInModuleCantExtendSealedInDiffModule(c, c.packge().modle) - ); - } - if (subTypeSym == c.type.tsym || types.isSuperType(subTypeSym.type, c.type)) { - log.error(TreeInfo.diagnosticPositionFor(subTypeSym, ((JCClassDecl)env.tree).permitting), - Errors.InvalidPermitsClause( - subTypeSym == c.type.tsym ? - Fragments.MustNotBeSameClass : - Fragments.MustNotBeSupertype(subTypeSym.type) - ) - ); - } else if (!isTypeVar) { - boolean thisIsASuper = types.directSupertypes(subTypeSym.type) - .stream() - .anyMatch(d -> d.tsym == c); - if (!thisIsASuper) { + if (subTypeSym.isAnonymous() && !c.isEnum()) { + log.error(TreeInfo.diagnosticPositionFor(subTypeSym, env.tree), Errors.LocalClassesCantExtendSealed(Fragments.Anonymous)); + } + if (permittedTypes.contains(subTypeSym)) { + DiagnosticPosition pos = + env.enclClass.permitting.stream() + .filter(permittedExpr -> TreeInfo.diagnosticPositionFor(subTypeSym, permittedExpr, true) != null) + .limit(2).collect(List.collector()).get(1); + log.error(pos, Errors.InvalidPermitsClause(Fragments.IsDuplicated(subTypeSym.type))); + } else { + permittedTypes.add(subTypeSym); + } + if (sealedInUnnamed) { + if (subTypeSym.packge() != c.packge()) { + log.error(TreeInfo.diagnosticPositionFor(subTypeSym, env.tree), + Errors.ClassInUnnamedModuleCantExtendSealedInDiffPackage(c) + ); + } + } else if (subTypeSym.packge().modle != c.packge().modle) { log.error(TreeInfo.diagnosticPositionFor(subTypeSym, env.tree), - Errors.InvalidPermitsClause(Fragments.DoesntExtendSealed(subTypeSym.type))); + Errors.ClassInModuleCantExtendSealedInDiffModule(c, c.packge().modle) + ); + } + if (subTypeSym == c.type.tsym || types.isSuperType(subTypeSym.type, c.type)) { + log.error(TreeInfo.diagnosticPositionFor(subTypeSym, ((JCClassDecl)env.tree).permitting), + Errors.InvalidPermitsClause( + subTypeSym == c.type.tsym ? + Fragments.MustNotBeSameClass : + Fragments.MustNotBeSupertype(subTypeSym.type) + ) + ); + } else if (!isTypeVar) { + boolean thisIsASuper = types.directSupertypes(subTypeSym.type) + .stream() + .anyMatch(d -> d.tsym == c); + if (!thisIsASuper) { + log.error(TreeInfo.diagnosticPositionFor(subTypeSym, env.tree), + Errors.InvalidPermitsClause(Fragments.DoesntExtendSealed(subTypeSym.type))); + } } } } - } - List sealedSupers = types.directSupertypes(c.type) - .stream() - .filter(s -> s.tsym.isSealed()) - .map(s -> (ClassSymbol) s.tsym) - .collect(List.collector()); + List sealedSupers = types.directSupertypes(c.type) + .stream() + .filter(s -> s.tsym.isSealed()) + .map(s -> (ClassSymbol) s.tsym) + .collect(List.collector()); - if (sealedSupers.isEmpty()) { - if ((c.flags_field & Flags.NON_SEALED) != 0) { - boolean hasErrorSuper = false; + if (sealedSupers.isEmpty()) { + if ((c.flags_field & Flags.NON_SEALED) != 0) { + boolean hasErrorSuper = false; - hasErrorSuper |= types.directSupertypes(c.type) - .stream() - .anyMatch(s -> s.tsym.kind == Kind.ERR); + hasErrorSuper |= types.directSupertypes(c.type) + .stream() + .anyMatch(s -> s.tsym.kind == Kind.ERR); - ClassType ct = (ClassType) c.type; + ClassType ct = (ClassType) c.type; - hasErrorSuper |= !ct.isCompound() && ct.interfaces_field != ct.all_interfaces_field; + hasErrorSuper |= !ct.isCompound() && ct.interfaces_field != ct.all_interfaces_field; - if (!hasErrorSuper) { - log.error(TreeInfo.diagnosticPositionFor(c, env.tree), Errors.NonSealedWithNoSealedSupertype(c)); + if (!hasErrorSuper) { + log.error(TreeInfo.diagnosticPositionFor(c, env.tree), Errors.NonSealedWithNoSealedSupertype(c)); + } + } + } else { + if (c.isDirectlyOrIndirectlyLocal() && !c.isEnum()) { + log.error(TreeInfo.diagnosticPositionFor(c, env.tree), Errors.LocalClassesCantExtendSealed(c.isAnonymous() ? Fragments.Anonymous : Fragments.Local)); } - } - } else { - if (c.isDirectlyOrIndirectlyLocal() && !c.isEnum()) { - log.error(TreeInfo.diagnosticPositionFor(c, env.tree), Errors.LocalClassesCantExtendSealed(c.isAnonymous() ? Fragments.Anonymous : Fragments.Local)); - } - if (!c.type.isCompound()) { - for (ClassSymbol supertypeSym : sealedSupers) { - if (!supertypeSym.permitted.contains(c.type.tsym)) { - log.error(TreeInfo.diagnosticPositionFor(c.type.tsym, env.tree), Errors.CantInheritFromSealed(supertypeSym)); + if (!c.type.isCompound()) { + for (ClassSymbol supertypeSym : sealedSupers) { + if (!supertypeSym.permitted.contains(c.type.tsym)) { + log.error(TreeInfo.diagnosticPositionFor(c.type.tsym, env.tree), Errors.CantInheritFromSealed(supertypeSym)); + } + } + if (!c.isNonSealed() && !c.isFinal() && !c.isSealed()) { + log.error(TreeInfo.diagnosticPositionFor(c, env.tree), + c.isInterface() ? + Errors.NonSealedOrSealedExpected : + Errors.NonSealedSealedOrFinalExpected); } - } - if (!c.isNonSealed() && !c.isFinal() && !c.isSealed()) { - log.error(TreeInfo.diagnosticPositionFor(c, env.tree), - c.isInterface() ? - Errors.NonSealedOrSealedExpected : - Errors.NonSealedSealedOrFinalExpected); } } - } - // The info.lint field in the envs stored in typeEnvs is deliberately uninitialized, - // because the annotations were not available at the time the env was created. Therefore, - // we look up the environment chain for the first enclosing environment for which the - // lint value is set. Typically, this is the parent env, but might be further if there - // are any envs created as a result of TypeParameter nodes. - Env lintEnv = env; - while (lintEnv.info.lint == null) - lintEnv = lintEnv.next; - - // Having found the enclosing lint value, we can initialize the lint value for this class - env.info.lint = lintEnv.info.lint.augment(c); - - Lint prevLint = chk.setLint(env.info.lint); - JavaFileObject prev = log.useSource(c.sourcefile); - ResultInfo prevReturnRes = env.info.returnResult; - - try { deferredLintHandler.flush(env.tree); env.info.returnResult = null; // java.lang.Enum may not be subclassed by a non-enum diff --git a/test/langtools/tools/javac/sealed/SealedErrorPositions.java b/test/langtools/tools/javac/sealed/SealedErrorPositions.java new file mode 100644 index 0000000000000..8140dbdceb079 --- /dev/null +++ b/test/langtools/tools/javac/sealed/SealedErrorPositions.java @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug 8316470 + * @summary Verify correct source file is set while reporting errors for sealing from Attr + * @library /tools/lib + * @modules jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.main + * jdk.compiler/com.sun.tools.javac.util + * @build toolbox.ToolBox toolbox.JavacTask + * @run main SealedErrorPositions +*/ + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; + +import toolbox.TestRunner; +import toolbox.JavacTask; +import toolbox.Task; +import toolbox.ToolBox; + +public class SealedErrorPositions extends TestRunner { + + ToolBox tb; + + public static void main(String... args) throws Exception { + new SealedErrorPositions().runTests(); + } + + SealedErrorPositions() { + super(System.err); + tb = new ToolBox(); + } + + public void runTests() throws Exception { + runTests(m -> new Object[] { Paths.get(m.getName()) }); + } + + @Test + public void testDoesNotExtendErrorPosition(Path base) throws IOException { + Path current = base.resolve("."); + Path src = current.resolve("src"); + tb.writeJavaFiles(src, + """ + package test; + sealed class C permits A, B { } + """, + """ + package test; + final class A extends C { } + """, + """ + package test; + final class B { } + """); + Path test = src.resolve("test"); + Path classes = current.resolve("classes"); + + Files.createDirectories(classes); + + var log = + new JavacTask(tb) + .options("-XDrawDiagnostics", + "-implicit:none", + "-sourcepath", src.toString()) + .outdir(classes) + .files(test.resolve("A.java")) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + List expectedErrors = List.of( + "C.java:2:27: compiler.err.invalid.permits.clause: (compiler.misc.doesnt.extend.sealed: test.B)", + "1 error"); + + if (!expectedErrors.equals(log)) { + throw new AssertionError("Incorrect errors, expected: " + expectedErrors + + ", actual: " + log); + } + } + + @Test + public void testEmptyImplicitPermitsErrorPosition(Path base) throws IOException { + Path current = base.resolve("."); + Path src = current.resolve("src"); + tb.writeJavaFiles(src, + """ + package test; + sealed class C { } + """, + """ + package test; + final class A extends C { } + """); + Path test = src.resolve("test"); + Path classes = current.resolve("classes"); + + Files.createDirectories(classes); + + var log = + new JavacTask(tb) + .options("-XDrawDiagnostics", + "-implicit:none", + "-sourcepath", src.toString()) + .outdir(classes) + .files(test.resolve("A.java")) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + List expectedErrors = List.of( + "C.java:2:8: compiler.err.sealed.class.must.have.subclasses", + "A.java:2:7: compiler.err.cant.inherit.from.sealed: test.C", + "2 errors"); + + if (!expectedErrors.equals(log)) { + throw new AssertionError("Incorrect errors, expected: " + expectedErrors + + ", actual: " + log); + } + } + +} From d4b761242d91aa1bcadc438cce0a9465c0f8b23d Mon Sep 17 00:00:00 2001 From: Varada M Date: Tue, 24 Oct 2023 12:24:04 +0000 Subject: [PATCH 23/28] 8318240: [AIX] Cleaners.java test failure Reviewed-by: mbaesken, asteiner --- .../classes/sun/security/jgss/wrapper/SunNativeProvider.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/SunNativeProvider.java b/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/SunNativeProvider.java index 78e93e9836c2e..6eb61cd8b9e03 100644 --- a/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/SunNativeProvider.java +++ b/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/SunNativeProvider.java @@ -104,6 +104,9 @@ public HashMap run() { // Full path needed, DLL is in jre/bin StaticProperty.javaHome() + "\\bin\\sspi_bridge.dll", }; + case AIX -> new String[]{ + "/opt/freeware/lib64/libgssapi_krb5.so", + }; default -> new String[0]; }; } else { From 21d8a4725fa89803216a96d4716a2cb0b4b1820c Mon Sep 17 00:00:00 2001 From: Leo Korinth Date: Tue, 24 Oct 2023 12:31:59 +0000 Subject: [PATCH 24/28] 8318701: Fix copyright year Reviewed-by: egahlin --- src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedEvent.java | 2 +- test/jdk/jdk/jfr/jvm/TestEventWriterLog.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedEvent.java b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedEvent.java index 9ec117ffc887a..7ffcaabd57cf1 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedEvent.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/test/jdk/jdk/jfr/jvm/TestEventWriterLog.java b/test/jdk/jdk/jfr/jvm/TestEventWriterLog.java index 9f11ee0486399..e152b00acae93 100644 --- a/test/jdk/jdk/jfr/jvm/TestEventWriterLog.java +++ b/test/jdk/jdk/jfr/jvm/TestEventWriterLog.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it From d1077d6f141d6e51f697271286833e01013080d6 Mon Sep 17 00:00:00 2001 From: Jorn Vernee Date: Tue, 24 Oct 2023 12:33:59 +0000 Subject: [PATCH 25/28] 8316046: x64 platforms unecessarily save xmm16-31 when UseAVX >= 3 Reviewed-by: mcimadamore, djelinski --- .../jdk/internal/foreign/abi/x64/sysv/CallArranger.java | 4 +++- .../jdk/internal/foreign/abi/x64/windows/CallArranger.java | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/x64/sysv/CallArranger.java b/src/java.base/share/classes/jdk/internal/foreign/abi/x64/sysv/CallArranger.java index 75e8d0926a193..9dadc54b53b01 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/x64/sysv/CallArranger.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/x64/sysv/CallArranger.java @@ -76,7 +76,9 @@ public class CallArranger { new VMStorage[] { xmm0, xmm1 }, 2, new VMStorage[] { r10, r11 }, - new VMStorage[] { xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15 }, + new VMStorage[] { xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15, + xmm16, xmm17, xmm18, xmm19, xmm20, xmm21, xmm22, xmm23, + xmm24, xmm25, xmm26, xmm27, xmm28, xmm29, xmm30, xmm31 }, 16, 0, //no shadow space r10, r11 // scratch 1 & 2 diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/x64/windows/CallArranger.java b/src/java.base/share/classes/jdk/internal/foreign/abi/x64/windows/CallArranger.java index 4d4ad65b1c6c6..dc66ba9832c48 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/x64/windows/CallArranger.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/x64/windows/CallArranger.java @@ -66,7 +66,9 @@ public class CallArranger { new VMStorage[] { xmm0 }, 0, new VMStorage[] { rax, r10, r11 }, - new VMStorage[] { xmm4, xmm5 }, + new VMStorage[] { xmm4, xmm5, + xmm16, xmm17, xmm18, xmm19, xmm20, xmm21, xmm22, xmm23, + xmm24, xmm25, xmm26, xmm27, xmm28, xmm29, xmm30, xmm31 }, 16, 32, r10, r11 // scratch 1 & 2 From f9795d0d09a82cafb3e79ad8667e505c194d745b Mon Sep 17 00:00:00 2001 From: Hamlin Li Date: Tue, 24 Oct 2023 13:17:28 +0000 Subject: [PATCH 26/28] 8318222: RISC-V: C2 CmpU3 8318223: RISC-V: C2 CmpUL3 Reviewed-by: rehn, fyang --- .../cpu/riscv/macroAssembler_riscv.cpp | 29 ++++++++++++--- .../cpu/riscv/macroAssembler_riscv.hpp | 6 ++++ src/hotspot/cpu/riscv/riscv.ad | 36 +++++++++++++++++++ 3 files changed, 67 insertions(+), 4 deletions(-) diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp index 060284c0cd4d0..7548408c9a35e 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp @@ -4404,8 +4404,8 @@ void MacroAssembler::sign_extend(Register dst, Register src, int bits) { } } -void MacroAssembler::cmp_l2i(Register dst, Register src1, Register src2, Register tmp) -{ +void MacroAssembler::cmp_x2i(Register dst, Register src1, Register src2, + Register tmp, bool is_signed) { if (src1 == src2) { mv(dst, zr); return; @@ -4424,14 +4424,35 @@ void MacroAssembler::cmp_l2i(Register dst, Register src1, Register src2, Registe } // installs 1 if gt else 0 - slt(dst, right, left); + if (is_signed) { + slt(dst, right, left); + } else { + sltu(dst, right, left); + } bnez(dst, done); - slt(dst, left, right); + if (is_signed) { + slt(dst, left, right); + } else { + sltu(dst, left, right); + } // dst = -1 if lt; else if eq , dst = 0 neg(dst, dst); bind(done); } +void MacroAssembler::cmp_l2i(Register dst, Register src1, Register src2, Register tmp) +{ + cmp_x2i(dst, src1, src2, tmp); +} + +void MacroAssembler::cmp_ul2i(Register dst, Register src1, Register src2, Register tmp) { + cmp_x2i(dst, src1, src2, tmp, false); +} + +void MacroAssembler::cmp_uw2i(Register dst, Register src1, Register src2, Register tmp) { + cmp_x2i(dst, src1, src2, tmp, false); +} + // The java_calling_convention describes stack locations as ideal slots on // a frame with no abi restrictions. Since we must observe abi restrictions // (like the placement of the register window) the slots must be biased by diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp index 9c664d9d27886..c5df08f47fa85 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp @@ -1394,11 +1394,17 @@ class MacroAssembler: public Assembler { void zero_extend(Register dst, Register src, int bits); void sign_extend(Register dst, Register src, int bits); +private: + void cmp_x2i(Register dst, Register src1, Register src2, Register tmp, bool is_signed = true); + +public: // compare src1 and src2 and get -1/0/1 in dst. // if [src1 > src2], dst = 1; // if [src1 == src2], dst = 0; // if [src1 < src2], dst = -1; void cmp_l2i(Register dst, Register src1, Register src2, Register tmp = t0); + void cmp_ul2i(Register dst, Register src1, Register src2, Register tmp = t0); + void cmp_uw2i(Register dst, Register src1, Register src2, Register tmp = t0); // support for argument shuffling void move32_64(VMRegPair src, VMRegPair dst, Register tmp = t0); diff --git a/src/hotspot/cpu/riscv/riscv.ad b/src/hotspot/cpu/riscv/riscv.ad index 553c189891a9b..4d9621b328eaa 100644 --- a/src/hotspot/cpu/riscv/riscv.ad +++ b/src/hotspot/cpu/riscv/riscv.ad @@ -8641,6 +8641,42 @@ instruct cmpL3_reg_reg(iRegINoSp dst, iRegL op1, iRegL op2) ins_pipe(pipe_class_default); %} +instruct cmpUL3_reg_reg(iRegINoSp dst, iRegL op1, iRegL op2) +%{ + match(Set dst (CmpUL3 op1 op2)); + + ins_cost(ALU_COST * 3 + BRANCH_COST); + format %{ "sltu $dst, $op2, $op1\t#@cmpUL3_reg_reg\n\t" + "bnez $dst, done\n\t" + "sltu $dst, $op1, $op2\n\t" + "neg $dst, $dst\t#@cmpUL3_reg_reg" + %} + ins_encode %{ + __ cmp_ul2i(t0, as_Register($op1$$reg), as_Register($op2$$reg)); + __ mv(as_Register($dst$$reg), t0); + %} + + ins_pipe(pipe_class_default); +%} + +instruct cmpU3_reg_reg(iRegINoSp dst, iRegI op1, iRegI op2) +%{ + match(Set dst (CmpU3 op1 op2)); + + ins_cost(ALU_COST * 3 + BRANCH_COST); + format %{ "sltu $dst, $op2, $op1\t#@cmpU3_reg_reg\n\t" + "bnez $dst, done\n\t" + "sltu $dst, $op1, $op2\n\t" + "neg $dst, $dst\t#@cmpU3_reg_reg" + %} + ins_encode %{ + __ cmp_uw2i(t0, as_Register($op1$$reg), as_Register($op2$$reg)); + __ mv(as_Register($dst$$reg), t0); + %} + + ins_pipe(pipe_class_default); +%} + instruct cmpLTMask_reg_reg(iRegINoSp dst, iRegI p, iRegI q) %{ match(Set dst (CmpLTMask p q)); From 8879c78d62e3c1f325def56d131f62c479bfdaa9 Mon Sep 17 00:00:00 2001 From: Doug Simon Date: Tue, 24 Oct 2023 13:32:26 +0000 Subject: [PATCH 27/28] 8317689: [JVMCI] include error message when CreateJavaVM in libgraal fails Reviewed-by: phofer, thartmann, never --- src/hotspot/share/compiler/compileBroker.cpp | 4 +++- src/hotspot/share/jvmci/jvmciEnv.cpp | 21 +++++++++++++------- src/hotspot/share/jvmci/jvmciEnv.hpp | 7 +++++++ src/hotspot/share/jvmci/jvmciRuntime.cpp | 6 ++++-- src/hotspot/share/jvmci/jvmciRuntime.hpp | 6 ++++-- src/hotspot/share/prims/whitebox.cpp | 2 +- 6 files changed, 33 insertions(+), 13 deletions(-) diff --git a/src/hotspot/share/compiler/compileBroker.cpp b/src/hotspot/share/compiler/compileBroker.cpp index f265271857dc1..22a9eb4dcaa55 100644 --- a/src/hotspot/share/compiler/compileBroker.cpp +++ b/src/hotspot/share/compiler/compileBroker.cpp @@ -2211,7 +2211,9 @@ void CompileBroker::invoke_compiler_on_method(CompileTask* task) { } else { JVMCIEnv env(thread, &compile_state, __FILE__, __LINE__); if (env.init_error() != JNI_OK) { - failure_reason = os::strdup(err_msg("Error attaching to libjvmci (err: %d)", env.init_error()), mtJVMCI); + const char* msg = env.init_error_msg(); + failure_reason = os::strdup(err_msg("Error attaching to libjvmci (err: %d, %s)", + env.init_error(), msg == nullptr ? "unknown" : msg), mtJVMCI); bool reason_on_C_heap = true; // In case of JNI_ENOMEM, there's a good chance a subsequent attempt to create libjvmci or attach to it // might succeed. Other errors most likely indicate a non-recoverable error in the JVMCI runtime. diff --git a/src/hotspot/share/jvmci/jvmciEnv.cpp b/src/hotspot/share/jvmci/jvmciEnv.cpp index bab3de8c3355d..24af7715a5b1e 100644 --- a/src/hotspot/share/jvmci/jvmciEnv.cpp +++ b/src/hotspot/share/jvmci/jvmciEnv.cpp @@ -147,13 +147,14 @@ void JVMCIEnv::init_env_mode_runtime(JavaThread* thread, JNIEnv* parent_env) { _is_hotspot = false; _runtime = JVMCI::compiler_runtime(thread); - _env = _runtime->init_shared_library_javavm(&_init_error); + _env = _runtime->init_shared_library_javavm(&_init_error, &_init_error_msg); if (_env != nullptr) { // Creating the JVMCI shared library VM also attaches the current thread _detach_on_close = true; } else if (_init_error != JNI_OK) { // Caller creating this JVMCIEnv must handle the error. - JVMCI_event_1("[%s:%d] Error creating libjvmci (err: %d)", _file, _line, _init_error); + JVMCI_event_1("[%s:%d] Error creating libjvmci (err: %d, %s)", _file, _line, + _init_error, _init_error_msg == nullptr ? "unknown" : _init_error_msg); return; } else { _runtime->GetEnv(thread, (void**)&parent_env, JNI_VERSION_1_2); @@ -195,17 +196,17 @@ void JVMCIEnv::init_env_mode_runtime(JavaThread* thread, JNIEnv* parent_env) { } JVMCIEnv::JVMCIEnv(JavaThread* thread, JVMCICompileState* compile_state, const char* file, int line): - _throw_to_caller(false), _file(file), _line(line), _init_error(JNI_OK), _compile_state(compile_state) { + _throw_to_caller(false), _file(file), _line(line), _init_error(JNI_OK), _init_error_msg(nullptr), _compile_state(compile_state) { init_env_mode_runtime(thread, nullptr); } JVMCIEnv::JVMCIEnv(JavaThread* thread, const char* file, int line): - _throw_to_caller(false), _file(file), _line(line), _init_error(JNI_OK), _compile_state(nullptr) { + _throw_to_caller(false), _file(file), _line(line), _init_error(JNI_OK), _init_error_msg(nullptr), _compile_state(nullptr) { init_env_mode_runtime(thread, nullptr); } JVMCIEnv::JVMCIEnv(JavaThread* thread, JNIEnv* parent_env, const char* file, int line): - _throw_to_caller(true), _file(file), _line(line), _init_error(JNI_OK), _compile_state(nullptr) { + _throw_to_caller(true), _file(file), _line(line), _init_error(JNI_OK), _init_error_msg(nullptr), _compile_state(nullptr) { assert(parent_env != nullptr, "npe"); init_env_mode_runtime(thread, parent_env); assert(_env == nullptr || parent_env == _env, "mismatched JNIEnvironment"); @@ -218,6 +219,7 @@ void JVMCIEnv::init(JavaThread* thread, bool is_hotspot, const char* file, int l _file = file; _line = line; _init_error = JNI_OK; + _init_error_msg = nullptr; if (is_hotspot) { _env = nullptr; _pop_frame_on_close = false; @@ -237,7 +239,8 @@ void JVMCIEnv::check_init(JVMCI_TRAPS) { if (_init_error == JNI_ENOMEM) { JVMCI_THROW_MSG(OutOfMemoryError, "JNI_ENOMEM creating or attaching to libjvmci"); } - JVMCI_THROW_MSG(InternalError, err_msg("Error creating or attaching to libjvmci (err: %d)", _init_error)); + JVMCI_THROW_MSG(InternalError, err_msg("Error creating or attaching to libjvmci (err: %d, description: %s)", + _init_error, _init_error_msg == nullptr ? "unknown" : _init_error_msg)); } void JVMCIEnv::check_init(TRAPS) { @@ -247,7 +250,8 @@ void JVMCIEnv::check_init(TRAPS) { if (_init_error == JNI_ENOMEM) { THROW_MSG(vmSymbols::java_lang_OutOfMemoryError(), "JNI_ENOMEM creating or attaching to libjvmci"); } - THROW_MSG(vmSymbols::java_lang_OutOfMemoryError(), err_msg("Error creating or attaching to libjvmci (err: %d)", _init_error)); + THROW_MSG(vmSymbols::java_lang_OutOfMemoryError(), err_msg("Error creating or attaching to libjvmci (err: %d, description: %s)", + _init_error, _init_error_msg == nullptr ? "unknown" : _init_error_msg)); } // Prints a pending exception (if any) and its stack trace to st. @@ -572,6 +576,9 @@ jboolean JVMCIEnv::transfer_pending_exception(JavaThread* THREAD, JVMCIEnv* peer } JVMCIEnv::~JVMCIEnv() { + if (_init_error_msg != nullptr) { + os::free((void*) _init_error_msg); + } if (_init_error != JNI_OK) { return; } diff --git a/src/hotspot/share/jvmci/jvmciEnv.hpp b/src/hotspot/share/jvmci/jvmciEnv.hpp index 59600b97fe15a..ace0cc53352c3 100644 --- a/src/hotspot/share/jvmci/jvmciEnv.hpp +++ b/src/hotspot/share/jvmci/jvmciEnv.hpp @@ -172,6 +172,7 @@ class JVMCIEnv : public ResourceObj { int _init_error; // JNI code returned when creating or attaching to a libjvmci isolate. // If not JNI_OK, the JVMCIEnv is invalid and should not be used apart from // calling init_error(). + const char* _init_error_msg; // Message for _init_error if available. C heap allocated. // Translates an exception on the HotSpot heap (i.e., hotspot_env) to an exception on // the shared library heap (i.e., jni_env). The translation includes the stack and cause(s) of `throwable`. @@ -217,6 +218,12 @@ class JVMCIEnv : public ResourceObj { return _init_error; } + // Gets a message describing a non-zero init_error(). + // Valid as long as this JVMCIEnv is valid. + const char* init_error_msg() { + return _init_error_msg; + } + // Checks the value of init_error() and throws an exception in `JVMCI_TRAPS` // (which must not be this) if it is not JNI_OK. void check_init(JVMCI_TRAPS); diff --git a/src/hotspot/share/jvmci/jvmciRuntime.cpp b/src/hotspot/share/jvmci/jvmciRuntime.cpp index 68177fa8acd0f..226c4f5dea199 100644 --- a/src/hotspot/share/jvmci/jvmciRuntime.cpp +++ b/src/hotspot/share/jvmci/jvmciRuntime.cpp @@ -1253,7 +1253,7 @@ bool JVMCIRuntime::detach_thread(JavaThread* thread, const char* reason, bool ca return destroyed_javavm; } -JNIEnv* JVMCIRuntime::init_shared_library_javavm(int* create_JavaVM_err) { +JNIEnv* JVMCIRuntime::init_shared_library_javavm(int* create_JavaVM_err, const char** err_msg) { MutexLocker locker(_lock); JavaVM* javaVM = _shared_library_javavm; if (javaVM == nullptr) { @@ -1280,7 +1280,7 @@ JNIEnv* JVMCIRuntime::init_shared_library_javavm(int* create_JavaVM_err) { JavaVMInitArgs vm_args; vm_args.version = JNI_VERSION_1_2; vm_args.ignoreUnrecognized = JNI_TRUE; - JavaVMOption options[5]; + JavaVMOption options[6]; jlong javaVM_id = 0; // Protocol: JVMCI shared library JavaVM should support a non-standard "_javavm_id" @@ -1297,6 +1297,8 @@ JNIEnv* JVMCIRuntime::init_shared_library_javavm(int* create_JavaVM_err) { options[3].extraInfo = (void*) _fatal; options[4].optionString = (char*) "_fatal_log"; options[4].extraInfo = (void*) _fatal_log; + options[5].optionString = (char*) "_createvm_errorstr"; + options[5].extraInfo = (void*) err_msg; vm_args.version = JNI_VERSION_1_2; vm_args.options = options; diff --git a/src/hotspot/share/jvmci/jvmciRuntime.hpp b/src/hotspot/share/jvmci/jvmciRuntime.hpp index a3f464cd724c1..d3898be4ce0ae 100644 --- a/src/hotspot/share/jvmci/jvmciRuntime.hpp +++ b/src/hotspot/share/jvmci/jvmciRuntime.hpp @@ -280,8 +280,10 @@ class JVMCIRuntime: public CHeapObj { // If the JavaVM was created by this call, then the thread-local JNI // interface pointer for the JavaVM is returned otherwise null is returned. // If this method tried to create the JavaVM but failed, the error code returned - // by JNI_CreateJavaVM is returned in create_JavaVM_err. - JNIEnv* init_shared_library_javavm(int* create_JavaVM_err); + // by JNI_CreateJavaVM is returned in create_JavaVM_err and, if available, an + // error message is malloc'ed and assigned to err_msg. The caller is responsible + // for freeing err_msg. + JNIEnv* init_shared_library_javavm(int* create_JavaVM_err, const char** err_msg); // Determines if the JVMCI shared library JavaVM exists for this runtime. bool has_shared_library_javavm() { return _shared_library_javavm != nullptr; } diff --git a/src/hotspot/share/prims/whitebox.cpp b/src/hotspot/share/prims/whitebox.cpp index e86217f8de694..e0f1b071c3043 100644 --- a/src/hotspot/share/prims/whitebox.cpp +++ b/src/hotspot/share/prims/whitebox.cpp @@ -381,7 +381,7 @@ WB_ENTRY(jboolean, WB_IsGCSupportedByJVMCICompiler(JNIEnv* env, jobject o, jint if (EnableJVMCI) { // Enter the JVMCI env that will be used by the CompileBroker. JVMCIEnv jvmciEnv(thread, __FILE__, __LINE__); - return jvmciEnv.runtime()->is_gc_supported(&jvmciEnv, (CollectedHeap::Name)name); + return jvmciEnv.init_error() == JNI_OK && jvmciEnv.runtime()->is_gc_supported(&jvmciEnv, (CollectedHeap::Name)name); } #endif return false; From e67550cfec4dbd1c8c2c9869dda34fa09a5c274b Mon Sep 17 00:00:00 2001 From: Claes Redestad Date: Tue, 24 Oct 2023 13:32:41 +0000 Subject: [PATCH 28/28] 8318509: x86 count_positives intrinsic broken for -XX:AVX3Threshold=0 Reviewed-by: thartmann, jbhateja, epeter --- src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp | 26 ++-- .../intrinsics/string/TestCountPositives.java | 123 +++++++++++------- .../intrinsics/string/TestHasNegatives.java | 97 ++++++++------ 3 files changed, 153 insertions(+), 93 deletions(-) diff --git a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp index 2154601f2f26f..29413e5457c57 100644 --- a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp @@ -3853,13 +3853,11 @@ void C2_MacroAssembler::count_positives(Register ary1, Register len, VM_Version::supports_bmi2()) { Label test_64_loop, test_tail, BREAK_LOOP; - Register tmp3_aliased = len; - movl(tmp1, len); vpxor(vec2, vec2, vec2, Assembler::AVX_512bit); - andl(tmp1, 64 - 1); // tail count (in chars) 0x3F - andl(len, ~(64 - 1)); // vector count (in chars) + andl(tmp1, 0x0000003f); // tail count (in chars) 0x3F + andl(len, 0xffffffc0); // vector count (in chars) jccb(Assembler::zero, test_tail); lea(ary1, Address(ary1, len, Address::times_1)); @@ -3879,12 +3877,17 @@ void C2_MacroAssembler::count_positives(Register ary1, Register len, testl(tmp1, -1); jcc(Assembler::zero, DONE); + + // check the tail for absense of negatives // ~(~0 << len) applied up to two times (for 32-bit scenario) #ifdef _LP64 - mov64(tmp3_aliased, 0xFFFFFFFFFFFFFFFF); - shlxq(tmp3_aliased, tmp3_aliased, tmp1); - notq(tmp3_aliased); - kmovql(mask2, tmp3_aliased); + { + Register tmp3_aliased = len; + mov64(tmp3_aliased, 0xFFFFFFFFFFFFFFFF); + shlxq(tmp3_aliased, tmp3_aliased, tmp1); + notq(tmp3_aliased); + kmovql(mask2, tmp3_aliased); + } #else Label k_init; jmp(k_init); @@ -3916,8 +3919,13 @@ void C2_MacroAssembler::count_positives(Register ary1, Register len, ktestq(mask1, mask2); jcc(Assembler::zero, DONE); + // do a full check for negative registers in the tail + movl(len, tmp1); // tmp1 holds low 6-bit from original len; + // ary1 already pointing to the right place + jmpb(TAIL_START); + bind(BREAK_LOOP); - // At least one byte in the last 64 bytes is negative. + // At least one byte in the last 64 byte block was negative. // Set up to look at the last 64 bytes as if they were a tail lea(ary1, Address(ary1, len, Address::times_1)); addptr(result, len); diff --git a/test/hotspot/jtreg/compiler/intrinsics/string/TestCountPositives.java b/test/hotspot/jtreg/compiler/intrinsics/string/TestCountPositives.java index afc308c37dd7a..76ef476615927 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/string/TestCountPositives.java +++ b/test/hotspot/jtreg/compiler/intrinsics/string/TestCountPositives.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,88 +21,121 @@ * questions. */ -package compiler.intrinsics.string; - /* * @test - * @bug 8999999 + * @bug 8281146 * @summary Validates StringCoding.countPositives intrinsic with a small range of tests. + * @key randomness * @library /compiler/patches + * @library /test/lib * * @build java.base/java.lang.Helper * @run main compiler.intrinsics.string.TestCountPositives */ +/* + * @test + * @bug 8281146 8318509 + * @summary Validates StringCoding.countPositives intrinsic for AVX3 works with and without + * AVX3Threshold=0 + * @key randomness + * @library /compiler/patches + * @library /test/lib + * + * @build java.base/java.lang.Helper + * @requires vm.cpu.features ~= ".*avx512.*" + * @run main/othervm/timeout=1200 -XX:UseAVX=3 compiler.intrinsics.string.TestCountPositives + * @run main/othervm/timeout=1200 -XX:UseAVX=3 -XX:+UnlockDiagnosticVMOptions -XX:AVX3Threshold=0 compiler.intrinsics.string.TestCountPositives + */ +/** + * This test was derived from compiler.intrinsics.string.TestHasNegatives + */ +package compiler.intrinsics.string; + +import java.lang.Helper; +import java.util.Random; +import java.util.stream.IntStream; + +import jdk.test.lib.Utils; public class TestCountPositives { - private static byte[] tBa = new byte[4096 + 16]; + private static byte[] bytes = new byte[4096 + 32]; + + private static final Random RANDOM = Utils.getRandomInstance(); /** * Completely initialize the test array, preparing it for tests of the * StringCoding.hasNegatives method with a given array segment offset, - * length, and number of negative bytes. + * length, and number of negative bytes. The lowest index that will be + * negative is marked by negOffset */ - public static void initialize(int off, int len, int neg) { - assert (len + off <= tBa.length); + public static void initialize(int off, int len, int neg, int negOffset) { + assert (len + off <= bytes.length); // insert "canary" (negative) values before offset for (int i = 0; i < off; ++i) { - tBa[i] = (byte) (((i + 15) & 0x7F) | 0x80); + bytes[i] = (byte) (((i + 15) & 0x7F) | 0x80); } // fill the array segment for (int i = off; i < len + off; ++i) { - tBa[i] = (byte) (((i - off + 15) & 0x7F)); + bytes[i] = (byte) (((i - off + 15) & 0x7F)); } if (neg != 0) { // modify a number (neg) disparate array bytes inside // segment to be negative. - int div = (neg > 1) ? (len - 1) / (neg - 1) : 0; - int idx; - for (int i = 0; i < neg; ++i) { - idx = off + (len - 1) - div * i; - tBa[idx] = (byte) (0x80 | tBa[idx]); + for (int i = 0; i < neg; i++) { + int idx = off + RANDOM.nextInt(len - negOffset) + negOffset; + bytes[idx] = (byte) (0x80 | bytes[idx]); } } // insert "canary" negative values after array segment - for (int i = len + off; i < tBa.length; ++i) { - tBa[i] = (byte) (((i + 15) & 0x7F) | 0x80); + for (int i = len + off; i < bytes.length; ++i) { + bytes[i] = (byte) (((i + 15) & 0x7F) | 0x80); } } - /** Sizes of array segments to test. */ - private static int sizes[] = { 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 13, 17, 19, 23, 37, 61, 131, - 4099 }; - /** * Test different array segment sizes, offsets, and number of negative * bytes. */ public static void test_countPositives() throws Exception { - int len, off; - int ng; + for (int off = 0; off < 16; off++) { // starting offset of array segment + // Test all array segment sizes 1-63 + for (int len = 1; len < 64; len++) { + test_countPositives(off, len, 0, 0); + test_countPositives(off, len, 1, 0); + test_countPositives(off, len, RANDOM.nextInt(30) + 2, 0); + } + // Test a random selection of sizes between 64 and 4099, inclusive + for (int i = 0; i < 20; i++) { + int len = 64 + RANDOM.nextInt(4100 - 64); + test_countPositives(off, len, 0, 0); + test_countPositives(off, len, 1, 0); + test_countPositives(off, len, RANDOM.nextInt(len) + 2, 0); + } + for (int len : new int[] { 128, 2048 }) { + // test with negatives only in a 1-63 byte tail + int tail = RANDOM.nextInt(63) + 1; + int ng = RANDOM.nextInt(tail) + 1; + test_countPositives(off, len + tail, ng, len); + } + } + } - for (ng = 0; ng < 57; ++ng) { // number of negatives in array segment - for (off = 0; off < 8; ++off) { // starting offset of array segment - for (int i = 0; i < sizes.length; ++i) { // array segment size - // choice - len = sizes[i]; - if (len + off > tBa.length) - continue; - initialize(off, len, ng); - int calculated = Helper.StringCodingCountPositives(tBa, off, len); - int expected = countPositives(tBa, off, len); - if (calculated != expected) { - if (expected != len && calculated >= 0 && calculated < expected) { - // allow intrinsics to return early with a lower value, - // but only if we're not expecting the full length (no - // negative bytes) - continue; - } - throw new Exception("Failed test countPositives " + "offset: " + off + " " - + "length: " + len + " " + "return: " + calculated + " expected: " + expected + " negatives: " - + ng); - } - } + private static void test_countPositives(int off, int len, int ng, int ngOffset) throws Exception { + assert (len + off < bytes.length); + initialize(off, len, ng, ngOffset); + int calculated = Helper.StringCodingCountPositives(bytes, off, len); + int expected = countPositives(bytes, off, len); + if (calculated != expected) { + if (expected != len && ng >= 0 && calculated >= 0 && calculated < expected) { + // allow intrinsics to return early with a lower value, + // but only if we're not expecting the full length (no + // negative bytes) + return; } + throw new Exception("Failed test countPositives " + "offset: " + off + " " + + "length: " + len + " " + "return: " + calculated + " expected: " + expected + " negatives: " + + ng + " offset: " + ngOffset); } } diff --git a/test/hotspot/jtreg/compiler/intrinsics/string/TestHasNegatives.java b/test/hotspot/jtreg/compiler/intrinsics/string/TestHasNegatives.java index d73ea3f0139d6..6edf2dc2e5676 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/string/TestHasNegatives.java +++ b/test/hotspot/jtreg/compiler/intrinsics/string/TestHasNegatives.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,20 +26,39 @@ * @bug 8054307 * @summary Validates StringCoding.hasNegatives intrinsic with a small range of tests. * @library /compiler/patches + * @library /test/lib * * @build java.base/java.lang.Helper * @run main compiler.intrinsics.string.TestHasNegatives */ +/* + * @test + * @bug 8054307 8318509 + * @summary Validates StringCoding.hasNegatives intrinsic for AVX3 works with and without + * AVX3Threshold=0 + * @key randomness + * @library /compiler/patches + * @library /test/lib + * + * @build java.base/java.lang.Helper + * @requires vm.cpu.features ~= ".*avx512.*" + * @run main/othervm/timeout=1200 -XX:UseAVX=3 compiler.intrinsics.string.TestHasNegatives + * @run main/othervm/timeout=1200 -XX:UseAVX=3 -XX:+UnlockDiagnosticVMOptions -XX:AVX3Threshold=0 compiler.intrinsics.string.TestHasNegatives + */ package compiler.intrinsics.string; -/* - * @summary Validates StringCoding.hasNegatives intrinsic with a small - * range of tests. - */ +import java.lang.Helper; +import java.util.Random; +import java.util.stream.IntStream; + +import jdk.test.lib.Utils; + public class TestHasNegatives { - private static byte[] tBa = new byte[4096 + 16]; + private static byte[] bytes = new byte[4096 + 32]; + + private static final Random RANDOM = Utils.getRandomInstance(); /** * Completely initialize the test array, preparing it for tests of the @@ -47,60 +66,60 @@ public class TestHasNegatives { * length, and number of negative bytes. */ public static void initialize(int off, int len, int neg) { - assert (len + off <= tBa.length); + assert (len + off <= bytes.length); // insert "canary" (negative) values before offset for (int i = 0; i < off; ++i) { - tBa[i] = (byte) (((i + 15) & 0x7F) | 0x80); + bytes[i] = (byte) (((i + 15) & 0x7F) | 0x80); } // fill the array segment for (int i = off; i < len + off; ++i) { - tBa[i] = (byte) (((i - off + 15) & 0x7F)); + bytes[i] = (byte) (((i - off + 15) & 0x7F)); } if (neg != 0) { // modify a number (neg) disparate array bytes inside // segment to be negative. - int div = (neg > 1) ? (len - 1) / (neg - 1) : 0; - int idx; - for (int i = 0; i < neg; ++i) { - idx = off + (len - 1) - div * i; - tBa[idx] = (byte) (0x80 | tBa[idx]); + for (int i = 0; i < neg; i++) { + int idx = off + RANDOM.nextInt(len); + bytes[idx] = (byte) (0x80 | bytes[idx]); } } // insert "canary" negative values after array segment - for (int i = len + off; i < tBa.length; ++i) { - tBa[i] = (byte) (((i + 15) & 0x7F) | 0x80); + for (int i = len + off; i < bytes.length; ++i) { + bytes[i] = (byte) (((i + 15) & 0x7F) | 0x80); } } - /** Sizes of array segments to test. */ - private static int sizes[] = { 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 13, 17, 19, 23, 37, 61, 131, - 4099 }; - /** * Test different array segment sizes, offsets, and number of negative * bytes. */ public static void test_hasNegatives() throws Exception { - int len, off; - int ng; - boolean r; - - for (ng = 0; ng < 57; ++ng) { // number of negatives in array segment - for (off = 0; off < 8; ++off) { // starting offset of array segment - for (int i = 0; i < sizes.length; ++i) { // array segment size - // choice - len = sizes[i]; - if (len + off > tBa.length) - continue; - initialize(off, len, ng); - r = Helper.StringCodingHasNegatives(tBa, off, len); - if (r ^ ((ng == 0) ? false : true)) { - throw new Exception("Failed test hasNegatives " + "offset: " + off + " " - + "length: " + len + " " + "return: " + r + " " + "negatives: " - + ng); - } - } + for (int off = 0; off < 16; off++) { // starting offset of array segment + // Test all array segment sizes 1-63 + for (int len = 1; len < 64; len++) { + test_hasNegatives(off, len, 0); + test_hasNegatives(off, len, 1); + test_hasNegatives(off, len, RANDOM.nextInt(30) + 2); } + // Test a random selection of sizes between 64 and 4099, inclusive + for (int i = 0; i < 20; i++) { + int len = 64 + RANDOM.nextInt(4100 - 64); + test_hasNegatives(off, len, 0); + test_hasNegatives(off, len, 1); + test_hasNegatives(off, len, RANDOM.nextInt(len) + 2); + } + } + } + + private static void test_hasNegatives(int off, int len, int maxNegatives) throws Exception { + assert (len + off < bytes.length); + initialize(off, len, maxNegatives); + boolean expected = (maxNegatives > 0); + boolean actual = Helper.StringCodingHasNegatives(bytes, off, len); + if (actual != expected) { + throw new Exception("Failed test hasNegatives " + "offset: " + off + " " + + "length: " + len + " " + "return: " + actual + " " + "negatives: " + + maxNegatives); } }