diff --git a/make/jdk/src/classes/build/tools/classlist/HelloClasslist.java b/make/jdk/src/classes/build/tools/classlist/HelloClasslist.java index 1b930ca7527..9434889e0bb 100644 --- a/make/jdk/src/classes/build/tools/classlist/HelloClasslist.java +++ b/make/jdk/src/classes/build/tools/classlist/HelloClasslist.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,6 +31,8 @@ */ package build.tools.classlist; +import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.Linker; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; @@ -59,6 +61,7 @@ public class HelloClasslist { private static final Logger LOGGER = Logger.getLogger("Hello"); + @SuppressWarnings("restricted") public static void main(String ... args) throws Throwable { FileSystems.getDefault(); @@ -141,6 +144,7 @@ public class HelloClasslist { HelloClasslist.class.getMethod("staticMethod_V").invoke(null); var obj = HelloClasslist.class.getMethod("staticMethod_L_L", Object.class).invoke(null, instance); HelloClasslist.class.getField("field").get(instance); + MethodHandles.Lookup.ClassOption.class.getEnumConstants(); // A selection of trivial and relatively common MH operations invoke(MethodHandles.identity(double.class), 1.0); @@ -160,6 +164,9 @@ public class HelloClasslist { case B b -> b.b; default -> 17; }; + // record run-time methods + o.equals(new B(5)); + o.hashCode(); LOGGER.log(Level.FINE, "Value: " + value); // The Striped64$Cell is loaded rarely only when there's a contention among @@ -167,6 +174,10 @@ public class HelloClasslist { // an inconsistency in the classlist between builds (see JDK-8295951). // To avoid the problem, load the class explicitly. Class striped64Class = Class.forName("java.util.concurrent.atomic.Striped64$Cell"); + + // Initialize FFM linkers + var signature = FunctionDescriptor.ofVoid(); + Linker.nativeLinker().downcallHandle(signature); } public HelloClasslist() {} diff --git a/src/java.base/share/classes/java/lang/foreign/Linker.java b/src/java.base/share/classes/java/lang/foreign/Linker.java index 6e7aae9f72b..8f1cab22b86 100644 --- a/src/java.base/share/classes/java/lang/foreign/Linker.java +++ b/src/java.base/share/classes/java/lang/foreign/Linker.java @@ -859,10 +859,11 @@ public sealed interface Linker permits AbstractLinker { * @see #captureStateLayout() */ static Option captureCallState(String... capturedState) { - Set set = Stream.of(Objects.requireNonNull(capturedState)) + int set = Stream.of(Objects.requireNonNull(capturedState)) .map(Objects::requireNonNull) .map(CapturableState::forName) - .collect(Collectors.toSet()); + .mapToInt(state -> 1 << state.ordinal()) + .sum(); return new LinkerOptions.CaptureCallState(set); } diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/AbstractLinker.java b/src/java.base/share/classes/jdk/internal/foreign/abi/AbstractLinker.java index 19b235963a3..401d3e9a9c5 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/AbstractLinker.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/AbstractLinker.java @@ -71,7 +71,20 @@ public abstract sealed class AbstractLinker implements Linker permits LinuxAArch MemorySegment makeStub(MethodHandle target, Arena arena); } - private record LinkRequest(FunctionDescriptor descriptor, LinkerOptions options) {} + private record LinkRequest(FunctionDescriptor descriptor, LinkerOptions options) { + // Overrides for boot performance + @Override + public boolean equals(Object obj) { + return obj instanceof LinkRequest other && + other.descriptor.equals(descriptor) && + other.options.equals(options); + } + + @Override + public int hashCode() { + return descriptor.hashCode() * 1237 + options.hashCode(); + } + } private final SoftReferenceCache DOWNCALL_CACHE = new SoftReferenceCache<>(); private final SoftReferenceCache UPCALL_CACHE = new SoftReferenceCache<>(); private final Set CANONICAL_LAYOUTS_CACHE = new HashSet<>(canonicalLayouts().values()); @@ -308,22 +321,30 @@ public abstract sealed class AbstractLinker implements Linker permits LinuxAArch case StructLayout sl -> MemoryLayout.structLayout(stripNames(sl.memberLayouts())); case UnionLayout ul -> MemoryLayout.unionLayout(stripNames(ul.memberLayouts())); case SequenceLayout sl -> MemoryLayout.sequenceLayout(sl.elementCount(), stripNames(sl.elementLayout())); - case AddressLayout al -> al.targetLayout() - .map(tl -> al.withoutName().withTargetLayout(stripNames(tl))) // restricted - .orElseGet(al::withoutName); + case AddressLayout al -> { + var stripped = al.withoutName(); + var target = al.targetLayout(); + if (target.isPresent()) + stripped = stripped.withTargetLayout(stripNames(target.get())); + yield stripped; + } default -> ml.withoutName(); // ValueLayout and PaddingLayout }; } private static MemoryLayout[] stripNames(List layouts) { - return layouts.stream() - .map(AbstractLinker::stripNames) - .toArray(MemoryLayout[]::new); + var ret = new MemoryLayout[layouts.size()]; + for (int i = 0; i < ret.length; i++) { + ret[i] = stripNames(layouts.get(i)); + } + return ret; } private static FunctionDescriptor stripNames(FunctionDescriptor function) { - return function.returnLayout() - .map(rl -> FunctionDescriptor.of(stripNames(rl), stripNames(function.argumentLayouts()))) - .orElseGet(() -> FunctionDescriptor.ofVoid(stripNames(function.argumentLayouts()))); + var retLayout = function.returnLayout(); + if (retLayout.isEmpty()) { + return FunctionDescriptor.ofVoid(stripNames(function.argumentLayouts())); + } + return FunctionDescriptor.of(stripNames(retLayout.get()), stripNames(function.argumentLayouts())); } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/CapturableState.java b/src/java.base/share/classes/jdk/internal/foreign/abi/CapturableState.java index 7fb62c56bf1..d95e713541f 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/CapturableState.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/CapturableState.java @@ -30,6 +30,7 @@ import jdk.internal.util.OperatingSystem; import java.lang.foreign.MemoryLayout; import java.lang.foreign.StructLayout; import java.lang.foreign.ValueLayout; +import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -42,6 +43,11 @@ public enum CapturableState { public static final StructLayout LAYOUT = MemoryLayout.structLayout( supportedStates().map(CapturableState::layout).toArray(MemoryLayout[]::new)); + public static final List BY_ORDINAL = List.of(values()); + + static { + assert (BY_ORDINAL.size() < Integer.SIZE); // Update LinkerOptions.CaptureCallState + } private final String stateName; private final ValueLayout layout; diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/LinkerOptions.java b/src/java.base/share/classes/jdk/internal/foreign/abi/LinkerOptions.java index 5ca410f40e2..4ada4b23417 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/LinkerOptions.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/LinkerOptions.java @@ -26,6 +26,7 @@ package jdk.internal.foreign.abi; import java.lang.foreign.FunctionDescriptor; import java.lang.foreign.Linker; +import java.util.EnumSet; import java.util.HashMap; import java.util.Map; import java.util.Objects; @@ -137,18 +138,57 @@ public class LinkerOptions { throw new IllegalArgumentException("Index '" + index + "' not in bounds for descriptor: " + descriptor); } } + + @Override + public int hashCode() { + return index; + } + + @Override + public boolean equals(Object obj) { + return obj instanceof FirstVariadicArg that && index == that.index; + } } - public record CaptureCallState(Set saved) implements LinkerOptionImpl { + public record CaptureCallState(int compact) implements LinkerOptionImpl { @Override public void validateForDowncall(FunctionDescriptor descriptor) { // done during construction } + + public Set saved() { + var set = EnumSet.noneOf(CapturableState.class); + int mask = compact; + int i = 0; + while (mask != 0) { + if ((mask & 1) == 1) { + set.add(CapturableState.BY_ORDINAL.get(i)); + } + mask >>= 1; + i++; + } + return set; + } + + + @Override + public boolean equals(Object obj) { + return obj instanceof CaptureCallState that && compact == that.compact; + } + + @Override + public int hashCode() { + return compact; + } } - public record Critical(boolean allowHeapAccess) implements LinkerOptionImpl { - public static Critical ALLOW_HEAP = new Critical(true); - public static Critical DONT_ALLOW_HEAP = new Critical(false); + public enum Critical implements LinkerOptionImpl { + ALLOW_HEAP, + DONT_ALLOW_HEAP; + + public boolean allowHeapAccess() { + return ordinal() == 0; // this == ALLOW_HEAP + } @Override public void validateForDowncall(FunctionDescriptor descriptor) { diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/NativeEntryPoint.java b/src/java.base/share/classes/jdk/internal/foreign/abi/NativeEntryPoint.java index abcc6027919..3940f996e0d 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/NativeEntryPoint.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/NativeEntryPoint.java @@ -48,7 +48,29 @@ public class NativeEntryPoint { private record CacheKey(MethodType methodType, ABIDescriptor abi, List argMoves, List retMoves, boolean needsReturnBuffer, int capturedStateMask, - boolean needsTransition) {} + boolean needsTransition) { + + @Override + public boolean equals(Object o) { + if (!(o instanceof CacheKey other)) return false; + + return methodType == other.methodType && abi == other.abi && capturedStateMask == other.capturedStateMask + && needsTransition == other.needsTransition && needsReturnBuffer == other.needsReturnBuffer + && argMoves.equals(other.argMoves) && retMoves.equals(other.retMoves); + } + + @Override + public int hashCode() { + int result = System.identityHashCode(methodType); + result = 31 * result + abi.hashCode(); + result = 31 * result + argMoves.hashCode(); + result = 31 * result + retMoves.hashCode(); + result = 31 * result + Boolean.hashCode(needsReturnBuffer); + result = 31 * result + capturedStateMask; + result = 31 * result + Boolean.hashCode(needsTransition); + return result; + } + } private NativeEntryPoint(MethodType methodType, long downcallStubAddress) { this.methodType = methodType; diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/VMStorage.java b/src/java.base/share/classes/jdk/internal/foreign/abi/VMStorage.java index 1237650e026..0be1675ccd8 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/VMStorage.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/VMStorage.java @@ -24,6 +24,8 @@ */ package jdk.internal.foreign.abi; +import java.util.Objects; + /** * * @param type the type of storage. e.g. stack, or which register type (GP, FP, vector) @@ -32,7 +34,7 @@ package jdk.internal.foreign.abi; * @param indexOrOffset the index is either a register number within a type, or * a stack offset in bytes if type = stack. * (a particular platform might add a bias to this in generate code) - * @param debugName the debug name + * @param debugName the debug name, mostly derived from type */ public record VMStorage(byte type, short segmentMaskOrSize, @@ -43,4 +45,14 @@ public record VMStorage(byte type, this(type, segmentMaskOrSize, indexOrOffset, "Stack@" + indexOrOffset); } + @Override + public int hashCode() { + return Objects.hash(type, segmentMaskOrSize, indexOrOffset); + } + + @Override + public boolean equals(Object obj) { + return obj instanceof VMStorage that && + type == that.type && segmentMaskOrSize == that.segmentMaskOrSize && indexOrOffset == that.indexOrOffset; + } }