8305093: Linker cache should not take layout names into account

Reviewed-by: mcimadamore
This commit is contained in:
Jorn Vernee 2023-05-01 13:41:00 +00:00
parent d437c61f5b
commit 67dd841432
2 changed files with 90 additions and 5 deletions

View File

@ -34,7 +34,9 @@ import jdk.internal.foreign.abi.riscv64.linux.LinuxRISCV64Linker;
import jdk.internal.foreign.abi.x64.sysv.SysVx64Linker;
import jdk.internal.foreign.abi.x64.windows.Windowsx64Linker;
import jdk.internal.foreign.layout.AbstractLayout;
import jdk.internal.foreign.layout.ValueLayouts;
import java.lang.foreign.AddressLayout;
import java.lang.foreign.GroupLayout;
import java.lang.foreign.MemoryLayout;
import java.lang.foreign.Arena;
@ -48,6 +50,7 @@ import java.lang.foreign.UnionLayout;
import java.lang.foreign.ValueLayout;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.util.List;
import java.nio.ByteOrder;
import java.util.Objects;
@ -69,6 +72,7 @@ public abstract sealed class AbstractLinker implements Linker permits LinuxAArch
Objects.requireNonNull(function);
Objects.requireNonNull(options);
checkLayouts(function);
function = stripNames(function);
LinkerOptions optionSet = LinkerOptions.forDowncall(function, options);
return DOWNCALL_CACHE.get(new LinkRequest(function, optionSet), linkRequest -> {
@ -88,6 +92,7 @@ public abstract sealed class AbstractLinker implements Linker permits LinuxAArch
Objects.requireNonNull(function);
checkLayouts(function);
SharedUtils.checkExceptions(target);
function = stripNames(function);
LinkerOptions optionSet = LinkerOptions.forUpcall(function, options);
MethodType type = function.toMethodType();
@ -174,6 +179,32 @@ public abstract sealed class AbstractLinker implements Linker permits LinuxAArch
}
}
private static MemoryLayout stripNames(MemoryLayout ml) {
// we don't care about transferring alignment and byte order here
// since the linker already restricts those such that they will always be the same
return switch (ml) {
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)))
.orElseGet(al::withoutName);
default -> ml.withoutName(); // ValueLayout and PaddingLayout
};
}
private static MemoryLayout[] stripNames(List<MemoryLayout> layouts) {
return layouts.stream()
.map(AbstractLinker::stripNames)
.toArray(MemoryLayout[]::new);
}
private static FunctionDescriptor stripNames(FunctionDescriptor function) {
return function.returnLayout()
.map(rl -> FunctionDescriptor.of(stripNames(rl), stripNames(function.argumentLayouts())))
.orElseGet(() -> FunctionDescriptor.ofVoid(stripNames(function.argumentLayouts())));
}
private void checkByteOrder(ValueLayout vl) {
if (vl.order() != linkerByteOrder()) {
throw new IllegalArgumentException("Layout does not have the right byte order: " + vl);

View File

@ -35,20 +35,74 @@ import java.lang.foreign.FunctionDescriptor;
import java.lang.foreign.Linker;
import java.lang.invoke.MethodHandle;
import static java.lang.foreign.MemoryLayout.*;
import static java.lang.foreign.ValueLayout.JAVA_CHAR;
import static java.lang.foreign.ValueLayout.JAVA_SHORT;
import static org.testng.Assert.assertSame;
import static org.testng.Assert.assertNotSame;
public class TestLinker extends NativeTestHelper {
@Test
public void testLinkerOptionsCache() {
record LinkRequest(FunctionDescriptor descriptor, Linker.Option... options) {}
@Test(dataProvider = "notSameCases")
public void testLinkerOptionsCache(LinkRequest l1, LinkRequest l2) {
Linker linker = Linker.nativeLinker();
FunctionDescriptor descriptor = FunctionDescriptor.ofVoid(C_INT, C_INT);
MethodHandle mh1 = linker.downcallHandle(descriptor);
MethodHandle mh2 = linker.downcallHandle(descriptor, Linker.Option.firstVariadicArg(1));
MethodHandle mh1 = linker.downcallHandle(l1.descriptor(), l1.options());
MethodHandle mh2 = linker.downcallHandle(l2.descriptor(), l2.options());
// assert that these are 2 distinct link request. No caching allowed
assertNotSame(mh1, mh2);
}
@DataProvider
public static Object[][] notSameCases() {
FunctionDescriptor fd_II_V = FunctionDescriptor.ofVoid(C_INT, C_INT);
return new Object[][]{
{new LinkRequest(fd_II_V), new LinkRequest(fd_II_V, Linker.Option.firstVariadicArg(1))},
{new LinkRequest(FunctionDescriptor.ofVoid(JAVA_SHORT)), new LinkRequest(FunctionDescriptor.ofVoid(JAVA_CHAR))},
{new LinkRequest(FunctionDescriptor.ofVoid(JAVA_SHORT)), new LinkRequest(FunctionDescriptor.ofVoid(JAVA_CHAR))},
};
}
@Test(dataProvider = "namedDescriptors")
public void testNamedLinkerCache(FunctionDescriptor f1, FunctionDescriptor f2) {
Linker linker = Linker.nativeLinker();
MethodHandle mh1 = linker.downcallHandle(f1);
MethodHandle mh2 = linker.downcallHandle(f2);
// assert that these are the same link request, even though layout names differ
assertSame(mh1, mh2);
}
@DataProvider
public static Object[][] namedDescriptors() {
return new Object[][]{
{ FunctionDescriptor.ofVoid(C_INT),
FunctionDescriptor.ofVoid(C_INT.withName("x")) },
{ FunctionDescriptor.ofVoid(structLayout(C_INT)),
FunctionDescriptor.ofVoid(structLayout(C_INT).withName("x")) },
{ FunctionDescriptor.ofVoid(structLayout(C_INT)),
FunctionDescriptor.ofVoid(structLayout(C_INT.withName("x"))) },
{ FunctionDescriptor.ofVoid(structLayout(C_INT, paddingLayout(32), C_LONG_LONG)),
FunctionDescriptor.ofVoid(structLayout(C_INT, paddingLayout(32), C_LONG_LONG.withName("x"))) },
{ FunctionDescriptor.ofVoid(structLayout(C_INT, paddingLayout(32), C_LONG_LONG)),
FunctionDescriptor.ofVoid(structLayout(C_INT, paddingLayout(32).withName("x"), C_LONG_LONG)) },
{ FunctionDescriptor.ofVoid(structLayout(sequenceLayout(1, C_INT))),
FunctionDescriptor.ofVoid(structLayout(sequenceLayout(1, C_INT).withName("x"))) },
{ FunctionDescriptor.ofVoid(structLayout(sequenceLayout(1, C_INT))),
FunctionDescriptor.ofVoid(structLayout(sequenceLayout(1, C_INT.withName("x")))) },
{ FunctionDescriptor.ofVoid(unionLayout(C_INT)),
FunctionDescriptor.ofVoid(unionLayout(C_INT).withName("x")) },
{ FunctionDescriptor.ofVoid(unionLayout(C_INT)),
FunctionDescriptor.ofVoid(unionLayout(C_INT.withName("x"))) },
{ FunctionDescriptor.ofVoid(C_POINTER),
FunctionDescriptor.ofVoid(C_POINTER.withName("x")) },
{ FunctionDescriptor.ofVoid(C_POINTER.withTargetLayout(C_INT)),
FunctionDescriptor.ofVoid(C_POINTER.withTargetLayout(C_INT.withName("x"))) },
{ FunctionDescriptor.ofVoid(C_POINTER.withTargetLayout(C_INT)),
FunctionDescriptor.ofVoid(C_POINTER.withName("x").withTargetLayout(C_INT.withName("x"))) },
};
}
@DataProvider
public static Object[][] invalidIndexCases() {
return new Object[][]{