mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 12:09:14 +00:00
8354996: Reduce dynamic code generation for a single downcall
Reviewed-by: jvernee
This commit is contained in:
parent
fa2a9d1e10
commit
5d2d1ab574
@ -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() {}
|
||||
|
||||
@ -859,10 +859,11 @@ public sealed interface Linker permits AbstractLinker {
|
||||
* @see #captureStateLayout()
|
||||
*/
|
||||
static Option captureCallState(String... capturedState) {
|
||||
Set<CapturableState> 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);
|
||||
}
|
||||
|
||||
|
||||
@ -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<LinkRequest, MethodHandle> DOWNCALL_CACHE = new SoftReferenceCache<>();
|
||||
private final SoftReferenceCache<LinkRequest, UpcallStubFactory> UPCALL_CACHE = new SoftReferenceCache<>();
|
||||
private final Set<MemoryLayout> 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<MemoryLayout> 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()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<CapturableState> BY_ORDINAL = List.of(values());
|
||||
|
||||
static {
|
||||
assert (BY_ORDINAL.size() < Integer.SIZE); // Update LinkerOptions.CaptureCallState
|
||||
}
|
||||
|
||||
private final String stateName;
|
||||
private final ValueLayout layout;
|
||||
|
||||
@ -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<CapturableState> saved) implements LinkerOptionImpl {
|
||||
public record CaptureCallState(int compact) implements LinkerOptionImpl {
|
||||
@Override
|
||||
public void validateForDowncall(FunctionDescriptor descriptor) {
|
||||
// done during construction
|
||||
}
|
||||
|
||||
public Set<CapturableState> 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) {
|
||||
|
||||
@ -48,7 +48,29 @@ public class NativeEntryPoint {
|
||||
private record CacheKey(MethodType methodType, ABIDescriptor abi,
|
||||
List<VMStorage> argMoves, List<VMStorage> 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;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user