8314949: linux PPC64 Big Endian: Implementation of Foreign Function & Memory API

Reviewed-by: mcimadamore, jvernee
This commit is contained in:
Martin Doerr 2023-09-06 08:26:48 +00:00
parent a01b3fb8e9
commit f6c203e616
11 changed files with 303 additions and 16 deletions

View File

@ -47,7 +47,7 @@ bool ABIDescriptor::is_volatile_reg(FloatRegister reg) const {
}
bool ForeignGlobals::is_foreign_linker_supported() {
#ifdef ABI_ELFv2
#ifdef LINUX
return true;
#else
return false;

View File

@ -39,6 +39,7 @@ public enum CABI {
LINUX_AARCH_64,
MAC_OS_AARCH_64,
WIN_AARCH_64,
LINUX_PPC_64,
LINUX_PPC_64_LE,
LINUX_RISCV_64,
LINUX_S390,
@ -74,6 +75,10 @@ public enum CABI {
// The Linux ABI follows the standard AAPCS ABI
return LINUX_AARCH_64;
}
} else if (arch.equals("ppc64")) {
if (OperatingSystem.isLinux()) {
return LINUX_PPC_64;
}
} else if (arch.equals("ppc64le")) {
if (OperatingSystem.isLinux()) {
return LINUX_PPC_64_LE;

View File

@ -30,6 +30,7 @@ import jdk.internal.foreign.abi.aarch64.linux.LinuxAArch64Linker;
import jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64Linker;
import jdk.internal.foreign.abi.aarch64.windows.WindowsAArch64Linker;
import jdk.internal.foreign.abi.fallback.FallbackLinker;
import jdk.internal.foreign.abi.ppc64.linux.LinuxPPC64Linker;
import jdk.internal.foreign.abi.ppc64.linux.LinuxPPC64leLinker;
import jdk.internal.foreign.abi.riscv64.linux.LinuxRISCV64Linker;
import jdk.internal.foreign.abi.s390.linux.LinuxS390Linker;
@ -60,7 +61,8 @@ import java.util.Set;
public abstract sealed class AbstractLinker implements Linker permits LinuxAArch64Linker, MacOsAArch64Linker,
SysVx64Linker, WindowsAArch64Linker,
Windowsx64Linker, LinuxPPC64leLinker,
Windowsx64Linker,
LinuxPPC64Linker, LinuxPPC64leLinker,
LinuxRISCV64Linker, LinuxS390Linker,
FallbackLinker {

View File

@ -276,6 +276,18 @@ public sealed interface Binding {
return Dup.INSTANCE;
}
static ShiftLeft shiftLeft(int shiftAmount) {
if (shiftAmount <= 0)
throw new IllegalArgumentException("shiftAmount must be positive");
return new ShiftLeft(shiftAmount);
}
static ShiftRight shiftRight(int shiftAmount) {
if (shiftAmount <= 0)
throw new IllegalArgumentException("shiftAmount must be positive");
return new ShiftRight(shiftAmount);
}
static Binding cast(Class<?> fromType, Class<?> toType) {
if (fromType == int.class) {
if (toType == boolean.class) {
@ -286,6 +298,8 @@ public sealed interface Binding {
return Cast.INT_TO_SHORT;
} else if (toType == char.class) {
return Cast.INT_TO_CHAR;
} else if (toType == long.class) {
return Cast.INT_TO_LONG;
}
} else if (toType == int.class) {
if (fromType == boolean.class) {
@ -296,6 +310,24 @@ public sealed interface Binding {
return Cast.SHORT_TO_INT;
} else if (fromType == char.class) {
return Cast.CHAR_TO_INT;
} else if (fromType == long.class) {
return Cast.LONG_TO_INT;
}
} else if (fromType == long.class) {
if (toType == byte.class) {
return Cast.LONG_TO_BYTE;
} else if (toType == short.class) {
return Cast.LONG_TO_SHORT;
} else if (toType == char.class) {
return Cast.LONG_TO_CHAR;
}
} else if (toType == long.class) {
if (fromType == byte.class) {
return Cast.BYTE_TO_LONG;
} else if (fromType == short.class) {
return Cast.SHORT_TO_LONG;
} else if (fromType == char.class) {
return Cast.CHAR_TO_LONG;
}
}
throw new IllegalArgumentException("Unknown conversion: " + fromType + " -> " + toType);
@ -387,6 +419,24 @@ public sealed interface Binding {
return this;
}
// Converts to long if needed then shifts left by the given number of Bytes.
public Binding.Builder shiftLeft(int shiftAmount, Class<?> type) {
if (type != long.class) {
bindings.add(Binding.cast(type, long.class));
}
bindings.add(Binding.shiftLeft(shiftAmount));
return this;
}
// Shifts right by the given number of Bytes then converts from long if needed.
public Binding.Builder shiftRight(int shiftAmount, Class<?> type) {
bindings.add(Binding.shiftRight(shiftAmount));
if (type != long.class) {
bindings.add(Binding.cast(long.class, type));
}
return this;
}
public List<Binding> build() {
return List.copyOf(bindings);
}
@ -670,6 +720,52 @@ public sealed interface Binding {
}
}
/**
* ShiftLeft([shiftAmount])
* Shifts the Bytes on the top of the operand stack (64 bit unsigned).
* Shifts left by the given number of Bytes.
*/
record ShiftLeft(int shiftAmount) implements Binding {
@Override
public void verify(Deque<Class<?>> stack) {
Class<?> last = stack.pop();
SharedUtils.checkType(last, long.class);
stack.push(long.class);
}
@Override
public void interpret(Deque<Object> stack, StoreFunc storeFunc,
LoadFunc loadFunc, SegmentAllocator allocator) {
long l = (long) stack.pop();
l <<= (shiftAmount * Byte.SIZE);
stack.push(l);
}
}
/**
* ShiftRight([shiftAmount])
* Shifts the Bytes on the top of the operand stack (64 bit unsigned).
* Shifts right by the given number of Bytes.
*/
record ShiftRight(int shiftAmount) implements Binding {
@Override
public void verify(Deque<Class<?>> stack) {
Class<?> last = stack.pop();
SharedUtils.checkType(last, long.class);
stack.push(long.class);
}
@Override
public void interpret(Deque<Object> stack, StoreFunc storeFunc,
LoadFunc loadFunc, SegmentAllocator allocator) {
long l = (long) stack.pop();
l >>>= (shiftAmount * Byte.SIZE);
stack.push(l);
}
}
/**
* CAST([fromType], [toType])
* Pop a [fromType] from the stack, convert it to [toType], and push the resulting
@ -690,10 +786,21 @@ public sealed interface Binding {
INT_TO_BYTE(int.class, byte.class),
INT_TO_CHAR(int.class, char.class),
INT_TO_SHORT(int.class, short.class),
INT_TO_LONG(int.class, long.class),
BOOLEAN_TO_INT(boolean.class, int.class),
BYTE_TO_INT(byte.class, int.class),
CHAR_TO_INT(char.class, int.class),
SHORT_TO_INT(short.class, int.class);
SHORT_TO_INT(short.class, int.class),
LONG_TO_INT(long.class, int.class),
LONG_TO_BYTE(long.class, byte.class),
LONG_TO_SHORT(long.class, short.class),
LONG_TO_CHAR(long.class, char.class),
BYTE_TO_LONG(byte.class, long.class),
SHORT_TO_LONG(short.class, long.class),
CHAR_TO_LONG(char.class, long.class);
private final Class<?> fromType;
private final Class<?> toType;

View File

@ -39,6 +39,8 @@ import jdk.internal.foreign.abi.Binding.BufferStore;
import jdk.internal.foreign.abi.Binding.Cast;
import jdk.internal.foreign.abi.Binding.Copy;
import jdk.internal.foreign.abi.Binding.Dup;
import jdk.internal.foreign.abi.Binding.ShiftLeft;
import jdk.internal.foreign.abi.Binding.ShiftRight;
import jdk.internal.foreign.abi.Binding.UnboxAddress;
import jdk.internal.foreign.abi.Binding.VMLoad;
import jdk.internal.foreign.abi.Binding.VMStore;
@ -463,6 +465,8 @@ public class BindingSpecializer {
case BoxAddress boxAddress -> emitBoxAddress(boxAddress);
case UnboxAddress unused -> emitUnboxAddress();
case Dup unused -> emitDupBinding();
case ShiftLeft shiftLeft -> emitShiftLeft(shiftLeft);
case ShiftRight shiftRight -> emitShiftRight(shiftRight);
case Cast cast -> emitCast(cast);
}
}
@ -725,6 +729,20 @@ public class BindingSpecializer {
pushType(dupType);
}
private void emitShiftLeft(ShiftLeft shiftLeft) {
popType(long.class);
cb.constantInstruction(shiftLeft.shiftAmount() * Byte.SIZE);
cb.lshl();
pushType(long.class);
}
private void emitShiftRight(ShiftRight shiftRight) {
popType(long.class);
cb.constantInstruction(shiftRight.shiftAmount() * Byte.SIZE);
cb.lushr();
pushType(long.class);
}
private void emitCast(Cast cast) {
Class<?> fromType = cast.fromType();
Class<?> toType = cast.toType();
@ -744,6 +762,11 @@ public class BindingSpecializer {
case INT_TO_BYTE -> cb.i2b();
case INT_TO_CHAR -> cb.i2c();
case INT_TO_SHORT -> cb.i2s();
case BYTE_TO_LONG, CHAR_TO_LONG, SHORT_TO_LONG, INT_TO_LONG -> cb.i2l();
case LONG_TO_BYTE -> { cb.l2i(); cb.i2b(); }
case LONG_TO_SHORT -> { cb.l2i(); cb.i2s(); }
case LONG_TO_CHAR -> { cb.l2i(); cb.i2c(); }
case LONG_TO_INT -> cb.l2i();
case BOOLEAN_TO_INT, BYTE_TO_INT, CHAR_TO_INT, SHORT_TO_INT -> {
// no-op in bytecode
}

View File

@ -32,6 +32,8 @@ import jdk.internal.foreign.abi.Binding.BufferStore;
import jdk.internal.foreign.abi.Binding.Cast;
import jdk.internal.foreign.abi.Binding.Copy;
import jdk.internal.foreign.abi.Binding.Dup;
import jdk.internal.foreign.abi.Binding.ShiftLeft;
import jdk.internal.foreign.abi.Binding.ShiftRight;
import jdk.internal.foreign.abi.Binding.UnboxAddress;
import jdk.internal.foreign.abi.Binding.VMLoad;
import jdk.internal.foreign.abi.Binding.VMStore;
@ -220,6 +222,8 @@ public class CallingSequenceBuilder {
case Copy unused -> true;
case UnboxAddress unused -> true;
case Dup unused -> true;
case ShiftLeft unused -> true;
case ShiftRight unused -> true;
case Cast unused -> true;
case VMLoad unused -> false;
@ -254,6 +258,8 @@ public class CallingSequenceBuilder {
case Allocate unused -> true;
case BoxAddress unused -> true;
case Dup unused -> true;
case ShiftLeft unused -> true;
case ShiftRight unused -> true;
case Cast unused -> true;
case VMStore unused -> false;

View File

@ -33,6 +33,7 @@ import jdk.internal.foreign.abi.aarch64.linux.LinuxAArch64Linker;
import jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64Linker;
import jdk.internal.foreign.abi.aarch64.windows.WindowsAArch64Linker;
import jdk.internal.foreign.abi.fallback.FallbackLinker;
import jdk.internal.foreign.abi.ppc64.linux.LinuxPPC64Linker;
import jdk.internal.foreign.abi.ppc64.linux.LinuxPPC64leLinker;
import jdk.internal.foreign.abi.riscv64.linux.LinuxRISCV64Linker;
import jdk.internal.foreign.abi.s390.linux.LinuxS390Linker;
@ -241,6 +242,7 @@ public final class SharedUtils {
case LINUX_AARCH_64 -> LinuxAArch64Linker.getInstance();
case MAC_OS_AARCH_64 -> MacOsAArch64Linker.getInstance();
case WIN_AARCH_64 -> WindowsAArch64Linker.getInstance();
case LINUX_PPC_64 -> LinuxPPC64Linker.getInstance();
case LINUX_PPC_64_LE -> LinuxPPC64leLinker.getInstance();
case LINUX_RISCV_64 -> LinuxRISCV64Linker.getInstance();
case LINUX_S390 -> LinuxS390Linker.getInstance();

View File

@ -0,0 +1,37 @@
/*
* Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* 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.internal.foreign.abi.ppc64;
/**
* PPC64 CallArranger specialized for ABI v1.
*/
public class ABIv1CallArranger extends CallArranger {
@Override
protected boolean useABIv2() {
return false;
}
}

View File

@ -25,11 +25,13 @@
*/
package jdk.internal.foreign.abi.ppc64;
import jdk.internal.foreign.abi.ppc64.CallArranger;
/**
* PPC64 CallArranger specialized for ABI v2.
*/
public class ABIv2CallArranger extends CallArranger {
// Currently no specific content, but CallArranger detects usage of ABIv2 for this class.
@Override
protected boolean useABIv2() {
return true;
}
}

View File

@ -35,7 +35,6 @@ import jdk.internal.foreign.abi.DowncallLinker;
import jdk.internal.foreign.abi.LinkerOptions;
import jdk.internal.foreign.abi.SharedUtils;
import jdk.internal.foreign.abi.VMStorage;
import jdk.internal.foreign.abi.ppc64.ABIv2CallArranger;
import java.lang.foreign.AddressLayout;
import java.lang.foreign.FunctionDescriptor;
@ -62,7 +61,7 @@ import static jdk.internal.foreign.abi.ppc64.PPC64Architecture.Regs.*;
* public constants CallArranger.ABIv1/2.
*/
public abstract class CallArranger {
final boolean useABIv2 = (this instanceof ABIv2CallArranger);
final boolean useABIv2 = useABIv2();
private static final int STACK_SLOT_SIZE = 8;
private static final int MAX_COPY_SIZE = 8;
@ -90,8 +89,14 @@ public abstract class CallArranger {
protected CallArranger() {}
public static final CallArranger ABIv1 = new ABIv1CallArranger();
public static final CallArranger ABIv2 = new ABIv2CallArranger();
/**
* Select ABI version
*/
protected abstract boolean useABIv2();
public Bindings getBindings(MethodType mt, FunctionDescriptor cDesc, boolean forUpcall) {
return getBindings(mt, cDesc, forUpcall, LinkerOptions.empty());
}
@ -214,11 +219,23 @@ public abstract class CallArranger {
return reg;
}
/* The struct is split into 8-byte chunks, and those chunks are passed in registers or on the stack.
ABIv1 requires shifting if the struct occupies more than one 8-byte chunk and the last one is not full.
Here's an example for passing an 11 byte struct with ABIv1:
offset : 0 .... 32 ..... 64 ..... 96 .... 128
values : xxxxxxxx|yyyyyyyy|zzzzzz??|???????? (can't touch bits 96..128)
Load into int : V +--------+
| |
+--------+ |
V V
In register : ????????|??zzzzzz (LSBs are zz...z)
Shift left : zzzzzz00|00000000 (LSBs are 00...0)
Write long : V V
Result : xxxxxxxx|yyyyyyyy|zzzzzz00|00000000
*/
// Regular struct, no HFA.
VMStorage[] structAlloc(MemoryLayout layout) {
// TODO: Big Endian can't pass partially used slots correctly in some cases with:
// !useABIv2 && layout.byteSize() > 8 && layout.byteSize() % 8 != 0
// Allocate enough gp slots (regs and stack) such that the struct fits in them.
int numChunks = (int) Utils.alignUp(layout.byteSize(), MAX_COPY_SIZE) / MAX_COPY_SIZE;
VMStorage[] result = new VMStorage[numChunks];
@ -332,16 +349,26 @@ public abstract class CallArranger {
case STRUCT_REGISTER -> {
assert carrier == MemorySegment.class;
VMStorage[] regs = storageCalculator.structAlloc(layout);
final boolean isLargeABIv1Struct = !useABIv2 && layout.byteSize() > MAX_COPY_SIZE;
long offset = 0;
for (VMStorage storage : regs) {
// Last slot may be partly used.
final long size = Math.min(layout.byteSize() - offset, MAX_COPY_SIZE);
int shiftAmount = 0;
Class<?> type = SharedUtils.primitiveCarrierForSize(size, false);
if (offset + size < layout.byteSize()) {
bindings.dup();
} else if (isLargeABIv1Struct) {
// Last slot requires shift.
shiftAmount = MAX_COPY_SIZE - (int) size;
}
bindings.bufferLoad(offset, type, (int) size);
if (shiftAmount != 0) {
bindings.shiftLeft(shiftAmount, type)
.vmStore(storage, long.class);
} else {
bindings.vmStore(storage, type);
}
bindings.bufferLoad(offset, type, (int) size)
.vmStore(storage, type);
offset += size;
}
}
@ -410,14 +437,25 @@ public abstract class CallArranger {
assert carrier == MemorySegment.class;
bindings.allocate(layout);
VMStorage[] regs = storageCalculator.structAlloc(layout);
final boolean isLargeABIv1Struct = !useABIv2 && layout.byteSize() > MAX_COPY_SIZE;
long offset = 0;
for (VMStorage storage : regs) {
// Last slot may be partly used.
final long size = Math.min(layout.byteSize() - offset, MAX_COPY_SIZE);
int shiftAmount = 0;
Class<?> type = SharedUtils.primitiveCarrierForSize(size, false);
bindings.dup()
.vmLoad(storage, type)
.bufferStore(offset, type, (int) size);
if (isLargeABIv1Struct && offset + size >= layout.byteSize()) {
// Last slot requires shift.
shiftAmount = MAX_COPY_SIZE - (int) size;
}
bindings.dup();
if (shiftAmount != 0) {
bindings.vmLoad(storage, long.class)
.shiftRight(shiftAmount, type);
} else {
bindings.vmLoad(storage, type);
}
bindings.bufferStore(offset, type, (int) size);
offset += size;
}
}

View File

@ -0,0 +1,65 @@
/*
* Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* 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.internal.foreign.abi.ppc64.linux;
import jdk.internal.foreign.abi.AbstractLinker;
import jdk.internal.foreign.abi.LinkerOptions;
import jdk.internal.foreign.abi.ppc64.CallArranger;
import java.lang.foreign.FunctionDescriptor;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.nio.ByteOrder;
public final class LinuxPPC64Linker extends AbstractLinker {
public static LinuxPPC64Linker getInstance() {
final class Holder {
private static final LinuxPPC64Linker INSTANCE = new LinuxPPC64Linker();
}
return Holder.INSTANCE;
}
private LinuxPPC64Linker() {
// Ensure there is only one instance
}
@Override
protected MethodHandle arrangeDowncall(MethodType inferredMethodType, FunctionDescriptor function, LinkerOptions options) {
return CallArranger.ABIv1.arrangeDowncall(inferredMethodType, function, options);
}
@Override
protected UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescriptor function, LinkerOptions options) {
return CallArranger.ABIv1.arrangeUpcall(targetType, function, options);
}
@Override
protected ByteOrder linkerByteOrder() {
return ByteOrder.BIG_ENDIAN;
}
}